diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bf9aa06b23..68a24d86e1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,14 +3,16 @@ /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/authorization/policy-assignment/ @Azure/avm-ptn-authorization-policyassignment-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/ptn/authorization/role-assignment/ @Azure/avm-ptn-authorization-roleassignment-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep @Azure/avm-core-team-technical-bicep +#/avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep /avm/res/analysis-services/server/ @Azure/avm-res-analysisservices-server-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/api-management/service/ @Azure/avm-res-apimanagement-service-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/app/container-app/ @Azure/avm-res-app-containerapp-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/app/managed-environment/ @Azure/avm-res-app-managedenvironment-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/app-configuration/configuration-store/ @Azure/avm-res-appconfiguration-configurationstore-module-owners-bicep @Azure/avm-core-team-technical-bicep #/avm/res/authorization/lock/ @Azure/avm-res-authorization-lock-module-owners-bicep @Azure/avm-core-team-technical-bicep -#/avm/res/authorization/policy-assignment/ @Azure/avm-res-authorization-policyassignment-module-owners-bicep @Azure/avm-core-team-technical-bicep #/avm/res/authorization/policy-definition/ @Azure/avm-res-authorization-policydefinition-module-owners-bicep @Azure/avm-core-team-technical-bicep #/avm/res/authorization/policy-exemption/ @Azure/avm-res-authorization-policyexemption-module-owners-bicep @Azure/avm-core-team-technical-bicep #/avm/res/authorization/policy-set-definition/ @Azure/avm-res-authorization-policysetdefinition-module-owners-bicep @Azure/avm-core-team-technical-bicep @@ -76,7 +78,7 @@ #/avm/res/managed-services/registration-definition/ @Azure/avm-res-managedservices-registrationdefinition-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/management/management-group/ @Azure/avm-res-management-managementgroup-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/net-app/net-app-account/ @Azure/avm-res-netapp-netappaccount-module-owners-bicep @Azure/avm-core-team-technical-bicep -#/avm/res/network/application-gateway/ @Azure/avm-res-network-applicationgateway-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/res/network/application-gateway/ @Azure/avm-res-network-applicationgateway-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/network/application-gateway-web-application-firewall-policy/ @Azure/avm-res-network-applicationgatewaywebapplicationfirewallpolicy-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/network/application-security-group/ @Azure/avm-res-network-applicationsecuritygroup-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/network/azure-firewall/ @Azure/avm-res-network-azurefirewall-module-owners-bicep @Azure/avm-core-team-technical-bicep @@ -129,7 +131,8 @@ /avm/res/service-fabric/cluster/ @Azure/avm-res-servicefabric-cluster-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/signal-r-service/signal-r/ @Azure/avm-res-signalrservice-signalr-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/signal-r-service/web-pub-sub/ @Azure/avm-res-signalrservice-webpubsub-module-owners-bicep @Azure/avm-core-team-technical-bicep -#/avm/res/sql/managed-instance/ @Azure/avm-res-sql-managedinstance-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/res/sql/instance-pool/ @Azure/avm-res-sql-instancepool-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/res/sql/managed-instance/ @Azure/avm-res-sql-managedinstance-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/sql/server/ @Azure/avm-res-sql-server-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/storage/storage-account/ @Azure/avm-res-storage-storageaccount-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/synapse/private-link-hub/ @Azure/avm-res-synapse-privatelinkhub-module-owners-bicep @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 77d58e66d1..9a7da432aa 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/authorization/policy-assignment" + - "avm/ptn/authorization/role-assignment" # - "avm/ptn/avd-lza/insights" # - "avm/ptn/avd-lza/management-plane" # - "avm/ptn/avd-lza/networking" @@ -109,7 +111,7 @@ body: # - "avm/res/managed-services/registration-definition" - "avm/res/management/management-group" - "avm/res/net-app/net-app-account" - # - "avm/res/network/application-gateway" + - "avm/res/network/application-gateway" - "avm/res/network/application-gateway-web-application-firewall-policy" - "avm/res/network/application-security-group" - "avm/res/network/azure-firewall" @@ -162,7 +164,8 @@ body: - "avm/res/service-fabric/cluster" - "avm/res/signal-r-service/signal-r" - "avm/res/signal-r-service/web-pub-sub" - # - "avm/res/sql/managed-instance" + - "avm/res/sql/instance-pool" + - "avm/res/sql/managed-instance" - "avm/res/sql/server" - "avm/res/storage/storage-account" - "avm/res/synapse/private-link-hub" diff --git a/.github/workflows/avm.platform.manage-workflow-issue.yml b/.github/workflows/avm.platform.manage-workflow-issue.yml index 9c80793d18..9b36fb37ad 100644 --- a/.github/workflows/avm.platform.manage-workflow-issue.yml +++ b/.github/workflows/avm.platform.manage-workflow-issue.yml @@ -1,4 +1,5 @@ -name: "avm.platform.manage-workflow-issue" +# Workflow for creating issues for failing workflows +name: .Platform - Manage workflow issue on: schedule: @@ -18,8 +19,8 @@ jobs: - uses: tibdex/github-app-token@v2 id: generate-token with: - app_id: ${{ secrets.APP_ID }} - private_key: ${{ secrets.APP_PRIVATE_KEY }} + app_id: ${{ secrets.TEAM_LINTER_APP_ID }} + private_key: ${{ secrets.TEAM_LINTER_PRIVATE_KEY }} - name: Manage issues shell: pwsh env: diff --git a/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml b/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml index 48cc265747..84f3f7b138 100644 --- a/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml +++ b/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml @@ -1,5 +1,5 @@ # Workflow for notifying and assigning issues on creation -name: avm.platform.set-avm-github-issue-owner-config +name: .Platform - Set AVM GitHub issue owner config on: issues: diff --git a/.github/workflows/avm.platform.sync-avm-modules-list.yml b/.github/workflows/avm.platform.sync-avm-modules-list.yml index 7557309692..06c584a7ff 100644 --- a/.github/workflows/avm.platform.sync-avm-modules-list.yml +++ b/.github/workflows/avm.platform.sync-avm-modules-list.yml @@ -1,3 +1,4 @@ +# Workflow to create an issue, if AVM module list is not in sync with CSV file name: .Platform - Sync AVM module list on: diff --git a/.github/workflows/avm.ptn.authorization.policy-assignment.yml b/.github/workflows/avm.ptn.authorization.policy-assignment.yml new file mode 100644 index 0000000000..89b9f28b0f --- /dev/null +++ b/.github/workflows/avm.ptn.authorization.policy-assignment.yml @@ -0,0 +1,86 @@ +name: "avm.ptn.authorization.policy-assignment" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + 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 + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.authorization.policy-assignment.yml" + - "avm/ptn/authorization/policy-assignment/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/ptn/authorization/policy-assignment" + workflowPath: ".github/workflows/avm.ptn.authorization.policy-assignment.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.authorization.role-assignment.yml b/.github/workflows/avm.ptn.authorization.role-assignment.yml new file mode 100644 index 0000000000..c9ad828152 --- /dev/null +++ b/.github/workflows/avm.ptn.authorization.role-assignment.yml @@ -0,0 +1,86 @@ +name: "avm.ptn.authorization.role-assignment" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + 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 + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.authorization.role-assignment.yml" + - "avm/ptn/authorization/role-assignment/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/ptn/authorization/role-assignment" + workflowPath: ".github/workflows/avm.ptn.authorization.role-assignment.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.res.network.application-gateway.yml b/.github/workflows/avm.res.network.application-gateway.yml new file mode 100644 index 0000000000..e55979ee5e --- /dev/null +++ b/.github/workflows/avm.res.network.application-gateway.yml @@ -0,0 +1,86 @@ +name: "avm.res.network.application-gateway" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + 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 + push: + branches: + - main + - avm-application-gateway + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.network.application-gateway.yml" + - "avm/res/network/application-gateway/**" + - "avm/utilities/pipelines/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/network/application-gateway" + workflowPath: ".github/workflows/avm.res.network.application-gateway.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.res.sql.instance-pool.yml b/.github/workflows/avm.res.sql.instance-pool.yml new file mode 100644 index 0000000000..75f6103864 --- /dev/null +++ b/.github/workflows/avm.res.sql.instance-pool.yml @@ -0,0 +1,86 @@ +name: "avm.res.sql.instance-pool" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + 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: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.sql.instance-pool.yml" + - "avm/res/sql/instance-pool/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/sql/instance-pool" + workflowPath: ".github/workflows/avm.res.sql.instance-pool.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.res.sql.managed-instance.yml b/.github/workflows/avm.res.sql.managed-instance.yml new file mode 100644 index 0000000000..54bb6be439 --- /dev/null +++ b/.github/workflows/avm.res.sql.managed-instance.yml @@ -0,0 +1,85 @@ +name: "avm.res.sql.managed-instance" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + 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 + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.sql.managed-instance.yml" + - "avm/res/sql/managed-instance/**" + - "avm/utilities/pipelines/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/sql/managed-instance" + workflowPath: ".github/workflows/avm.res.sql.managed-instance.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/avm/ptn/authorization/policy-assignment/README.md b/avm/ptn/authorization/policy-assignment/README.md new file mode 100644 index 0000000000..040d6979dd --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/README.md @@ -0,0 +1,1063 @@ +# Policy Assignments (All scopes) `[Microsoft.Authorization/policyAssignments]` + +This module deploys a Policy Assignment at a Management Group, Subscription or Resource Group scope. + +## 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/policyAssignments` | [2022-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-06-01/policyAssignments) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | + +## 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/authorization/policy-assignment:`. + +- [Policy Assignments (Management Group scope)](#example-1-policy-assignments-management-group-scope) +- [Policy Assignments (Management Group scope)](#example-2-policy-assignments-management-group-scope) +- [Policy Assignments (Resource Group)](#example-3-policy-assignments-resource-group) +- [Policy Assignments (Resource Group)](#example-4-policy-assignments-resource-group) +- [Policy Assignments (Subscription)](#example-5-policy-assignments-subscription) +- [Policy Assignments (Subscription)](#example-6-policy-assignments-subscription) + +### Example 1: _Policy Assignments (Management Group scope)_ + +This module deploys a Policy Assignment at a Management Group scope using minimal parameters. + + +
+ +via Bicep module + +```bicep +module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { + name: 'policyAssignmentDeployment' + params: { + // Required parameters + name: 'apamgmin001' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + // Non-required parameters + location: '' + metadata: { + assignedBy: 'Bicep' + } + } +} +``` + +
+

+ +

+ +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": "apamgmin001" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" + }, + // Non-required parameters + "location": { + "value": "" + }, + "metadata": { + "value": { + "assignedBy": "Bicep" + } + } + } +} +``` + +
+

+ +### Example 2: _Policy Assignments (Management Group scope)_ + +This module deploys a Policy Assignment at a Management Group scope using common parameters. + + +

+ +via Bicep module + +```bicep +module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { + name: 'policyAssignmentDeployment' + params: { + // Required parameters + name: 'apamgmax001' + policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611' + // Non-required parameters + description: '[Description] Policy Assignment at the management group scope' + displayName: '[Display Name] Policy Assignment at the management group scope' + enforcementMode: 'DoNotEnforce' + identity: 'SystemAssigned' + location: '' + managementGroupId: '' + metadata: { + assignedBy: 'Bicep' + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/${subscriptionId}/resourceGroups/validation-rg' + ] + overrides: [ + { + kind: 'policyEffect' + selectors: [ + { + in: [ + 'ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent' + 'ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent' + ] + kind: 'policyDefinitionReferenceId' + } + ] + value: 'Disabled' + } + ] + parameters: { + effect: { + value: 'Disabled' + } + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + } + resourceSelectors: [ + { + name: 'resourceSelector-test' + selectors: [ + { + in: [ + 'Microsoft.Compute/virtualMachines' + ] + kind: 'resourceType' + } + { + in: [ + 'westeurope' + ] + kind: 'resourceLocation' + } + ] + } + ] + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + } +} +``` + +
+

+ +

+ +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": "apamgmax001" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611" + }, + // Non-required parameters + "description": { + "value": "[Description] Policy Assignment at the management group scope" + }, + "displayName": { + "value": "[Display Name] Policy Assignment at the management group scope" + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "identity": { + "value": "SystemAssigned" + }, + "location": { + "value": "" + }, + "managementGroupId": { + "value": "" + }, + "metadata": { + "value": { + "assignedBy": "Bicep", + "category": "Security", + "version": "1.0" + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "notScopes": { + "value": [ + "/subscriptions/${subscriptionId}/resourceGroups/validation-rg" + ] + }, + "overrides": { + "value": [ + { + "kind": "policyEffect", + "selectors": [ + { + "in": [ + "ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent", + "ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent" + ], + "kind": "policyDefinitionReferenceId" + } + ], + "value": "Disabled" + } + ] + }, + "parameters": { + "value": { + "effect": { + "value": "Disabled" + }, + "enableCollectionOfSqlQueriesForSecurityResearch": { + "value": false + } + } + }, + "resourceSelectors": { + "value": [ + { + "name": "resourceSelector-test", + "selectors": [ + { + "in": [ + "Microsoft.Compute/virtualMachines" + ], + "kind": "resourceType" + }, + { + "in": [ + "westeurope" + ], + "kind": "resourceLocation" + } + ] + } + ] + }, + "roleDefinitionIds": { + "value": [ + "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + ] + } + } +} +``` + +
+

+ +### Example 3: _Policy Assignments (Resource Group)_ + +This module deploys a Policy Assignment at a Resource Group scope using minimal parameters. + + +

+ +via Bicep module + +```bicep +module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { + name: 'policyAssignmentDeployment' + params: { + // Required parameters + name: 'apargmin001' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + // Non-required parameters + location: '' + metadata: { + assignedBy: 'Bicep' + } + resourceGroupName: '' + subscriptionId: '' + } +} +``` + +
+

+ +

+ +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": "apargmin001" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" + }, + // Non-required parameters + "location": { + "value": "" + }, + "metadata": { + "value": { + "assignedBy": "Bicep" + } + }, + "resourceGroupName": { + "value": "" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 4: _Policy Assignments (Resource Group)_ + +This module deploys a Policy Assignment at a Resource Group scope using common parameters. + + +

+ +via Bicep module + +```bicep +module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { + name: 'policyAssignmentDeployment' + params: { + // Required parameters + name: 'apargmax001' + policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611' + // Non-required parameters + description: '[Description] Policy Assignment at the resource group scope' + displayName: '[Display Name] Policy Assignment at the resource group scope' + enforcementMode: 'DoNotEnforce' + identity: 'UserAssigned' + location: '' + metadata: { + assignedBy: 'Bicep' + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '' + ] + overrides: [ + { + kind: 'policyEffect' + selectors: [ + { + in: [ + 'ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent' + 'ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent' + ] + kind: 'policyDefinitionReferenceId' + } + ] + value: 'Disabled' + } + ] + parameters: { + effect: { + value: 'Disabled' + } + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + } + resourceGroupName: '' + resourceSelectors: [ + { + name: 'resourceSelector-test' + selectors: [ + { + in: [ + 'Microsoft.Compute/virtualMachines' + ] + kind: 'resourceType' + } + { + in: [ + 'westeurope' + ] + kind: 'resourceLocation' + } + ] + } + ] + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + subscriptionId: '' + userAssignedIdentityId: '' + } +} +``` + +
+

+ +

+ +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": "apargmax001" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611" + }, + // Non-required parameters + "description": { + "value": "[Description] Policy Assignment at the resource group scope" + }, + "displayName": { + "value": "[Display Name] Policy Assignment at the resource group scope" + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "identity": { + "value": "UserAssigned" + }, + "location": { + "value": "" + }, + "metadata": { + "value": { + "assignedBy": "Bicep", + "category": "Security", + "version": "1.0" + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "notScopes": { + "value": [ + "" + ] + }, + "overrides": { + "value": [ + { + "kind": "policyEffect", + "selectors": [ + { + "in": [ + "ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent", + "ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent" + ], + "kind": "policyDefinitionReferenceId" + } + ], + "value": "Disabled" + } + ] + }, + "parameters": { + "value": { + "effect": { + "value": "Disabled" + }, + "enableCollectionOfSqlQueriesForSecurityResearch": { + "value": false + } + } + }, + "resourceGroupName": { + "value": "" + }, + "resourceSelectors": { + "value": [ + { + "name": "resourceSelector-test", + "selectors": [ + { + "in": [ + "Microsoft.Compute/virtualMachines" + ], + "kind": "resourceType" + }, + { + "in": [ + "westeurope" + ], + "kind": "resourceLocation" + } + ] + } + ] + }, + "roleDefinitionIds": { + "value": [ + "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + ] + }, + "subscriptionId": { + "value": "" + }, + "userAssignedIdentityId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 5: _Policy Assignments (Subscription)_ + +This module deploys a Policy Assignment at a Subscription scope using common parameters. + + +

+ +via Bicep module + +```bicep +module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { + name: 'policyAssignmentDeployment' + params: { + // Required parameters + name: 'apasubmin001' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + // Non-required parameters + location: '' + metadata: { + assignedBy: 'Bicep' + category: 'Security' + version: '1.0' + } + subscriptionId: '' + } +} +``` + +
+

+ +

+ +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": "apasubmin001" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" + }, + // Non-required parameters + "location": { + "value": "" + }, + "metadata": { + "value": { + "assignedBy": "Bicep", + "category": "Security", + "version": "1.0" + } + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 6: _Policy Assignments (Subscription)_ + +This module deploys a Policy Assignment at a Subscription scope using common parameters. + + +

+ +via Bicep module + +```bicep +module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { + name: 'policyAssignmentDeployment' + params: { + // Required parameters + name: 'apasubmax001' + policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611' + // Non-required parameters + description: '[Description] Policy Assignment at the subscription scope' + displayName: '[Display Name] Policy Assignment at the subscription scope' + enforcementMode: 'DoNotEnforce' + identity: 'UserAssigned' + location: '' + metadata: { + assignedBy: 'Bicep' + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/${subscriptionId}/resourceGroups/validation-rg' + ] + overrides: [ + { + kind: 'policyEffect' + selectors: [ + { + in: [ + 'ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent' + 'ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent' + ] + kind: 'policyDefinitionReferenceId' + } + ] + value: 'Disabled' + } + ] + parameters: { + effect: { + value: 'Disabled' + } + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + } + resourceSelectors: [ + { + name: 'resourceSelector-test' + selectors: [ + { + in: [ + 'Microsoft.Compute/virtualMachines' + ] + kind: 'resourceType' + } + { + in: [ + 'westeurope' + ] + kind: 'resourceLocation' + } + ] + } + ] + roleDefinitionIds: [ + '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + subscriptionId: '' + userAssignedIdentityId: '' + } +} +``` + +
+

+ +

+ +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": "apasubmax001" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611" + }, + // Non-required parameters + "description": { + "value": "[Description] Policy Assignment at the subscription scope" + }, + "displayName": { + "value": "[Display Name] Policy Assignment at the subscription scope" + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "identity": { + "value": "UserAssigned" + }, + "location": { + "value": "" + }, + "metadata": { + "value": { + "assignedBy": "Bicep", + "category": "Security", + "version": "1.0" + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "notScopes": { + "value": [ + "/subscriptions/${subscriptionId}/resourceGroups/validation-rg" + ] + }, + "overrides": { + "value": [ + { + "kind": "policyEffect", + "selectors": [ + { + "in": [ + "ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent", + "ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent" + ], + "kind": "policyDefinitionReferenceId" + } + ], + "value": "Disabled" + } + ] + }, + "parameters": { + "value": { + "effect": { + "value": "Disabled" + }, + "enableCollectionOfSqlQueriesForSecurityResearch": { + "value": false + } + } + }, + "resourceSelectors": { + "value": [ + { + "name": "resourceSelector-test", + "selectors": [ + { + "in": [ + "Microsoft.Compute/virtualMachines" + ], + "kind": "resourceType" + }, + { + "in": [ + "westeurope" + ], + "kind": "resourceLocation" + } + ] + } + ] + }, + "roleDefinitionIds": { + "value": [ + "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + ] + }, + "subscriptionId": { + "value": "" + }, + "userAssignedIdentityId": { + "value": "" + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope, 64 characters for subscription and resource group scopes. | +| [`policyDefinitionId`](#parameter-policydefinitionid) | string | Specifies the ID of the policy definition or policy set definition being assigned. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`description`](#parameter-description) | string | This message will be part of response in case of policy violation. | +| [`displayName`](#parameter-displayname) | string | The display name of the policy assignment. Maximum length is 128 characters. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`enforcementMode`](#parameter-enforcementmode) | string | The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce. | +| [`identity`](#parameter-identity) | string | The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`managementGroupId`](#parameter-managementgroupid) | string | The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment. | +| [`metadata`](#parameter-metadata) | object | The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| [`nonComplianceMessages`](#parameter-noncompliancemessages) | array | The messages that describe why a resource is non-compliant with the policy. | +| [`notScopes`](#parameter-notscopes) | array | The policy excluded scopes. | +| [`overrides`](#parameter-overrides) | array | The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition. | +| [`parameters`](#parameter-parameters) | object | Parameters for the policy assignment if needed. | +| [`resourceGroupName`](#parameter-resourcegroupname) | string | The Target Scope for the Policy. The name of the resource group for the policy assignment. | +| [`resourceSelectors`](#parameter-resourceselectors) | array | The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location. | +| [`roleDefinitionIds`](#parameter-roledefinitionids) | array | The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition. | +| [`subscriptionId`](#parameter-subscriptionid) | string | The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. | +| [`userAssignedIdentityId`](#parameter-userassignedidentityid) | string | The Resource ID for the user assigned identity to assign to the policy assignment. | + +### Parameter: `name` + +Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope, 64 characters for subscription and resource group scopes. + +- Required: Yes +- Type: string + +### Parameter: `policyDefinitionId` + +Specifies the ID of the policy definition or policy set definition being assigned. + +- Required: Yes +- Type: string + +### Parameter: `description` + +This message will be part of response in case of policy violation. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `displayName` + +The display name of the policy assignment. Maximum length is 128 characters. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `enforcementMode` + +The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce. + +- Required: No +- Type: string +- Default: `'Default'` +- Allowed: + ```Bicep + [ + 'Default' + 'DoNotEnforce' + ] + ``` + +### Parameter: `identity` + +The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions. + +- Required: No +- Type: string +- Default: `'SystemAssigned'` +- Allowed: + ```Bicep + [ + 'None' + 'SystemAssigned' + 'UserAssigned' + ] + ``` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[deployment().location]` + +### Parameter: `managementGroupId` + +The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment. + +- Required: No +- Type: string +- Default: `[managementGroup().name]` + +### Parameter: `metadata` + +The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `nonComplianceMessages` + +The messages that describe why a resource is non-compliant with the policy. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `notScopes` + +The policy excluded scopes. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `overrides` + +The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `parameters` + +Parameters for the policy assignment if needed. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `resourceGroupName` + +The Target Scope for the Policy. The name of the resource group for the policy assignment. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `resourceSelectors` + +The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `roleDefinitionIds` + +The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `subscriptionId` + +The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `userAssignedIdentityId` + +The Resource ID for the user assigned identity to assign to the policy assignment. + +- Required: No +- Type: string +- Default: `''` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | Policy Assignment Name. | +| `principalId` | string | Policy Assignment principal ID. | +| `resourceId` | string | Policy Assignment resource ID. | + +## 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/ptn/authorization/policy-assignment/main.bicep b/avm/ptn/authorization/policy-assignment/main.bicep new file mode 100644 index 0000000000..286669834a --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/main.bicep @@ -0,0 +1,172 @@ +metadata name = 'Policy Assignments (All scopes)' +metadata description = 'This module deploys a Policy Assignment at a Management Group, Subscription or Resource Group scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope, 64 characters for subscription and resource group scopes.') +param name string + +@sys.description('Optional. This message will be part of response in case of policy violation.') +param description string = '' + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Required. Specifies the ID of the policy definition or policy set definition being assigned.') +param policyDefinitionId string + +@sys.description('Optional. Parameters for the policy assignment if needed.') +param parameters object = {} + +@sys.description('Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning \'Modify\' policy definitions.') +@allowed([ + 'SystemAssigned' + 'UserAssigned' + 'None' +]) +param identity string = 'SystemAssigned' + +@sys.description('Optional. The Resource ID for the user assigned identity to assign to the policy assignment.') +param userAssignedIdentityId string = '' + +@sys.description('Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition.') +param roleDefinitionIds array = [] + +@sys.description('Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The messages that describe why a resource is non-compliant with the policy.') +param nonComplianceMessages array = [] + +@sys.description('Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce.') +@allowed([ + 'Default' + 'DoNotEnforce' +]) +param enforcementMode string = 'Default' + +@sys.description('Optional. The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment.') +param subscriptionId string = '' + +@sys.description('Optional. The Target Scope for the Policy. The name of the resource group for the policy assignment.') +param resourceGroupName string = '' + +@sys.description('Optional. The policy excluded scopes.') +param notScopes array = [] + +@sys.description('Optional. Location for all resources.') +param location string = deployment().location + +@sys.description('Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition.') +param overrides array = [] + +@sys.description('Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location.') +param resourceSelectors array = [] + +@sys.description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: take('46d3xbcp.ptn.authorization-policyassignment.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}', 64) + 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' + } + } + } + } +} + +module policyAssignment_mg 'modules/management-group.bicep' = if (empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-PolicyAssignment-MG-Module' + scope: managementGroup(managementGroupId) + params: { + name: name + policyDefinitionId: policyDefinitionId + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + parameters: !empty(parameters) ? parameters : {} + identity: identity + userAssignedIdentityId: userAssignedIdentityId + roleDefinitionIds: !empty(roleDefinitionIds) ? roleDefinitionIds : [] + metadata: !empty(metadata) ? metadata : {} + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + managementGroupId: managementGroupId + location: location + overrides: !empty(overrides) ? overrides : [] + resourceSelectors: !empty(resourceSelectors) ? resourceSelectors : [] + } +} + +module policyAssignment_sub 'modules/subscription.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-PolicyAssignment-Sub-Module' + scope: subscription(subscriptionId) + params: { + name: name + policyDefinitionId: policyDefinitionId + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + parameters: !empty(parameters) ? parameters : {} + identity: identity + userAssignedIdentityId: userAssignedIdentityId + roleDefinitionIds: !empty(roleDefinitionIds) ? roleDefinitionIds : [] + metadata: !empty(metadata) ? metadata : {} + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + subscriptionId: subscriptionId + location: location + overrides: !empty(overrides) ? overrides : [] + resourceSelectors: !empty(resourceSelectors) ? resourceSelectors : [] + } +} + +module policyAssignment_rg 'modules/resource-group.bicep' = if (!empty(resourceGroupName) && !empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicyAssignment-RG-Module' + scope: resourceGroup(subscriptionId, resourceGroupName) + params: { + name: name + policyDefinitionId: policyDefinitionId + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + parameters: !empty(parameters) ? parameters : {} + identity: identity + userAssignedIdentityId: userAssignedIdentityId + roleDefinitionIds: !empty(roleDefinitionIds) ? roleDefinitionIds : [] + metadata: !empty(metadata) ? metadata : {} + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + subscriptionId: subscriptionId + location: location + overrides: !empty(overrides) ? overrides : [] + resourceSelectors: !empty(resourceSelectors) ? resourceSelectors : [] + } +} + +@sys.description('Policy Assignment Name.') +output name string = empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_mg.outputs.name : (!empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_sub.outputs.name : policyAssignment_rg.outputs.name) + +@sys.description('Policy Assignment principal ID.') +output principalId string = empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_mg.outputs.principalId : (!empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_sub.outputs.principalId : policyAssignment_rg.outputs.principalId) + +@sys.description('Policy Assignment resource ID.') +output resourceId string = empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_mg.outputs.resourceId : (!empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_sub.outputs.resourceId : policyAssignment_rg.outputs.resourceId) + +@sys.description('The location the resource was deployed into.') +output location string = empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_mg.outputs.location : (!empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_sub.outputs.location : policyAssignment_rg.outputs.location) diff --git a/avm/ptn/authorization/policy-assignment/main.json b/avm/ptn/authorization/policy-assignment/main.json new file mode 100644 index 0000000000..97fcaefd0c --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/main.json @@ -0,0 +1,989 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "3274497359181095240" + }, + "name": "Policy Assignments (All scopes)", + "description": "This module deploys a Policy Assignment at a Management Group, Subscription or Resource Group scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope, 64 characters for subscription and resource group scopes." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. This message will be part of response in case of policy violation." + } + }, + "displayName": { + "type": "string", + "defaultValue": "", + "maxLength": 128, + "metadata": { + "description": "Optional. The display name of the policy assignment. Maximum length is 128 characters." + } + }, + "policyDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. Specifies the ID of the policy definition or policy set definition being assigned." + } + }, + "parameters": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Parameters for the policy assignment if needed." + } + }, + "identity": { + "type": "string", + "defaultValue": "SystemAssigned", + "allowedValues": [ + "SystemAssigned", + "UserAssigned", + "None" + ], + "metadata": { + "description": "Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions." + } + }, + "userAssignedIdentityId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Resource ID for the user assigned identity to assign to the policy assignment." + } + }, + "roleDefinitionIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs." + } + }, + "nonComplianceMessages": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The messages that describe why a resource is non-compliant with the policy." + } + }, + "enforcementMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "DoNotEnforce" + ], + "metadata": { + "description": "Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce." + } + }, + "managementGroupId": { + "type": "string", + "defaultValue": "[managementGroup().name]", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment." + } + }, + "subscriptionId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment." + } + }, + "resourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The name of the resource group for the policy assignment." + } + }, + "notScopes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy excluded scopes." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "overrides": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition." + } + }, + "resourceSelectors": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[take(format('46d3xbcp.ptn.authorization-policyassignment.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4)), 64)]", + "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" + } + } + } + } + }, + { + "condition": "[and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PolicyAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))]", + "scope": "[format('Microsoft.Management/managementGroups/{0}', parameters('managementGroupId'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "policyDefinitionId": { + "value": "[parameters('policyDefinitionId')]" + }, + "displayName": "[if(not(empty(parameters('displayName'))), createObject('value', parameters('displayName')), createObject('value', ''))]", + "description": "[if(not(empty(parameters('description'))), createObject('value', parameters('description')), createObject('value', ''))]", + "parameters": "[if(not(empty(parameters('parameters'))), createObject('value', parameters('parameters')), createObject('value', createObject()))]", + "identity": { + "value": "[parameters('identity')]" + }, + "userAssignedIdentityId": { + "value": "[parameters('userAssignedIdentityId')]" + }, + "roleDefinitionIds": "[if(not(empty(parameters('roleDefinitionIds'))), createObject('value', parameters('roleDefinitionIds')), createObject('value', createArray()))]", + "metadata": "[if(not(empty(parameters('metadata'))), createObject('value', parameters('metadata')), createObject('value', createObject()))]", + "nonComplianceMessages": "[if(not(empty(parameters('nonComplianceMessages'))), createObject('value', parameters('nonComplianceMessages')), createObject('value', createArray()))]", + "enforcementMode": { + "value": "[parameters('enforcementMode')]" + }, + "notScopes": "[if(not(empty(parameters('notScopes'))), createObject('value', parameters('notScopes')), createObject('value', createArray()))]", + "managementGroupId": { + "value": "[parameters('managementGroupId')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "overrides": "[if(not(empty(parameters('overrides'))), createObject('value', parameters('overrides')), createObject('value', createArray()))]", + "resourceSelectors": "[if(not(empty(parameters('resourceSelectors'))), createObject('value', parameters('resourceSelectors')), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "15016174258473942920" + }, + "name": "Policy Assignments (Management Group scope)", + "description": "This module deploys a Policy Assignment at a Management Group scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Required. Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. This message will be part of response in case of policy violation." + } + }, + "displayName": { + "type": "string", + "defaultValue": "", + "maxLength": 128, + "metadata": { + "description": "Optional. The display name of the policy assignment. Maximum length is 128 characters." + } + }, + "policyDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. Specifies the ID of the policy definition or policy set definition being assigned." + } + }, + "parameters": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Parameters for the policy assignment if needed." + } + }, + "identity": { + "type": "string", + "defaultValue": "SystemAssigned", + "allowedValues": [ + "SystemAssigned", + "UserAssigned", + "None" + ], + "metadata": { + "description": "Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions." + } + }, + "userAssignedIdentityId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Resource ID for the user assigned identity to assign to the policy assignment." + } + }, + "roleDefinitionIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs." + } + }, + "nonComplianceMessages": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The messages that describe why a resource is non-compliant with the policy." + } + }, + "enforcementMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "DoNotEnforce" + ], + "metadata": { + "description": "Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce." + } + }, + "managementGroupId": { + "type": "string", + "defaultValue": "[managementGroup().name]", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment." + } + }, + "notScopes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy excluded scopes." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "overrides": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition." + } + }, + "resourceSelectors": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location." + } + } + }, + "variables": { + "identityVar": "[if(equals(parameters('identity'), 'SystemAssigned'), createObject('type', parameters('identity')), if(equals(parameters('identity'), 'UserAssigned'), createObject('type', parameters('identity'), 'userAssignedIdentities', createObject(format('{0}', parameters('userAssignedIdentityId')), createObject())), null()))]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/policyAssignments", + "apiVersion": "2022-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "properties": { + "displayName": "[if(not(empty(parameters('displayName'))), parameters('displayName'), null())]", + "metadata": "[if(not(empty(parameters('metadata'))), parameters('metadata'), null())]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "policyDefinitionId": "[parameters('policyDefinitionId')]", + "parameters": "[parameters('parameters')]", + "nonComplianceMessages": "[if(not(empty(parameters('nonComplianceMessages'))), parameters('nonComplianceMessages'), createArray())]", + "enforcementMode": "[parameters('enforcementMode')]", + "notScopes": "[if(not(empty(parameters('notScopes'))), parameters('notScopes'), createArray())]", + "overrides": "[if(not(empty(parameters('overrides'))), parameters('overrides'), createArray())]", + "resourceSelectors": "[if(not(empty(parameters('resourceSelectors'))), parameters('resourceSelectors'), createArray())]" + }, + "identity": "[variables('identityVar')]" + }, + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('roleDefinitionIds'))]" + }, + "condition": "[and(not(empty(parameters('roleDefinitionIds'))), equals(parameters('identity'), 'SystemAssigned'))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('managementGroupId'), parameters('roleDefinitionIds')[copyIndex()], parameters('location'), parameters('name'))]", + "properties": { + "roleDefinitionId": "[parameters('roleDefinitionIds')[copyIndex()]]", + "principalId": "[reference(extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyAssignments', parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "Policy Assignment Name." + }, + "value": "[parameters('name')]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Policy Assignment principal ID." + }, + "value": "[coalesce(tryGet(tryGet(reference(extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Policy Assignment resource ID." + }, + "value": "[extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyAssignments', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full').location]" + } + } + } + } + }, + { + "condition": "[and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PolicyAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[parameters('subscriptionId')]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "policyDefinitionId": { + "value": "[parameters('policyDefinitionId')]" + }, + "displayName": "[if(not(empty(parameters('displayName'))), createObject('value', parameters('displayName')), createObject('value', ''))]", + "description": "[if(not(empty(parameters('description'))), createObject('value', parameters('description')), createObject('value', ''))]", + "parameters": "[if(not(empty(parameters('parameters'))), createObject('value', parameters('parameters')), createObject('value', createObject()))]", + "identity": { + "value": "[parameters('identity')]" + }, + "userAssignedIdentityId": { + "value": "[parameters('userAssignedIdentityId')]" + }, + "roleDefinitionIds": "[if(not(empty(parameters('roleDefinitionIds'))), createObject('value', parameters('roleDefinitionIds')), createObject('value', createArray()))]", + "metadata": "[if(not(empty(parameters('metadata'))), createObject('value', parameters('metadata')), createObject('value', createObject()))]", + "nonComplianceMessages": "[if(not(empty(parameters('nonComplianceMessages'))), createObject('value', parameters('nonComplianceMessages')), createObject('value', createArray()))]", + "enforcementMode": { + "value": "[parameters('enforcementMode')]" + }, + "notScopes": "[if(not(empty(parameters('notScopes'))), createObject('value', parameters('notScopes')), createObject('value', createArray()))]", + "subscriptionId": { + "value": "[parameters('subscriptionId')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "overrides": "[if(not(empty(parameters('overrides'))), createObject('value', parameters('overrides')), createObject('value', createArray()))]", + "resourceSelectors": "[if(not(empty(parameters('resourceSelectors'))), createObject('value', parameters('resourceSelectors')), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "977404313320239280" + }, + "name": "Policy Assignments (Subscription scope)", + "description": "This module deploys a Policy Assignment at a Subscription scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 64, + "metadata": { + "description": "Required. Specifies the name of the policy assignment. Maximum length is 64 characters for subscription scope." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. This message will be part of response in case of policy violation." + } + }, + "displayName": { + "type": "string", + "defaultValue": "", + "maxLength": 128, + "metadata": { + "description": "Optional. The display name of the policy assignment. Maximum length is 128 characters." + } + }, + "policyDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. Specifies the ID of the policy definition or policy set definition being assigned." + } + }, + "parameters": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Parameters for the policy assignment if needed." + } + }, + "identity": { + "type": "string", + "defaultValue": "SystemAssigned", + "allowedValues": [ + "SystemAssigned", + "UserAssigned", + "None" + ], + "metadata": { + "description": "Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions." + } + }, + "userAssignedIdentityId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Resource ID for the user assigned identity to assign to the policy assignment." + } + }, + "roleDefinitionIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs." + } + }, + "nonComplianceMessages": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The messages that describe why a resource is non-compliant with the policy." + } + }, + "enforcementMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "DoNotEnforce" + ], + "metadata": { + "description": "Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce." + } + }, + "notScopes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy excluded scopes." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "overrides": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition." + } + }, + "resourceSelectors": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location." + } + }, + "subscriptionId": { + "type": "string", + "defaultValue": "[subscription().subscriptionId]", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment." + } + } + }, + "variables": { + "identityVar": "[if(equals(parameters('identity'), 'SystemAssigned'), createObject('type', parameters('identity')), if(equals(parameters('identity'), 'UserAssigned'), createObject('type', parameters('identity'), 'userAssignedIdentities', createObject(format('{0}', parameters('userAssignedIdentityId')), createObject())), null()))]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/policyAssignments", + "apiVersion": "2022-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "properties": { + "displayName": "[if(not(empty(parameters('displayName'))), parameters('displayName'), null())]", + "metadata": "[if(not(empty(parameters('metadata'))), parameters('metadata'), null())]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "policyDefinitionId": "[parameters('policyDefinitionId')]", + "parameters": "[parameters('parameters')]", + "nonComplianceMessages": "[if(not(empty(parameters('nonComplianceMessages'))), parameters('nonComplianceMessages'), createArray())]", + "enforcementMode": "[parameters('enforcementMode')]", + "notScopes": "[if(not(empty(parameters('notScopes'))), parameters('notScopes'), createArray())]", + "overrides": "[if(not(empty(parameters('overrides'))), parameters('overrides'), createArray())]", + "resourceSelectors": "[if(not(empty(parameters('resourceSelectors'))), parameters('resourceSelectors'), createArray())]" + }, + "identity": "[variables('identityVar')]" + }, + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('roleDefinitionIds'))]" + }, + "condition": "[and(not(empty(parameters('roleDefinitionIds'))), equals(parameters('identity'), 'SystemAssigned'))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('subscriptionId'), parameters('roleDefinitionIds')[copyIndex()], parameters('location'), parameters('name'))]", + "properties": { + "roleDefinitionId": "[parameters('roleDefinitionIds')[copyIndex()]]", + "principalId": "[reference(subscriptionResourceId('Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Authorization/policyAssignments', parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "Policy Assignment Name." + }, + "value": "[parameters('name')]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Policy Assignment principal ID." + }, + "value": "[coalesce(tryGet(tryGet(reference(subscriptionResourceId('Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Policy Assignment resource ID." + }, + "value": "[subscriptionResourceId('Microsoft.Authorization/policyAssignments', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full').location]" + } + } + } + } + }, + { + "condition": "[and(not(empty(parameters('resourceGroupName'))), not(empty(parameters('subscriptionId'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PolicyAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[parameters('subscriptionId')]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "policyDefinitionId": { + "value": "[parameters('policyDefinitionId')]" + }, + "displayName": "[if(not(empty(parameters('displayName'))), createObject('value', parameters('displayName')), createObject('value', ''))]", + "description": "[if(not(empty(parameters('description'))), createObject('value', parameters('description')), createObject('value', ''))]", + "parameters": "[if(not(empty(parameters('parameters'))), createObject('value', parameters('parameters')), createObject('value', createObject()))]", + "identity": { + "value": "[parameters('identity')]" + }, + "userAssignedIdentityId": { + "value": "[parameters('userAssignedIdentityId')]" + }, + "roleDefinitionIds": "[if(not(empty(parameters('roleDefinitionIds'))), createObject('value', parameters('roleDefinitionIds')), createObject('value', createArray()))]", + "metadata": "[if(not(empty(parameters('metadata'))), createObject('value', parameters('metadata')), createObject('value', createObject()))]", + "nonComplianceMessages": "[if(not(empty(parameters('nonComplianceMessages'))), createObject('value', parameters('nonComplianceMessages')), createObject('value', createArray()))]", + "enforcementMode": { + "value": "[parameters('enforcementMode')]" + }, + "notScopes": "[if(not(empty(parameters('notScopes'))), createObject('value', parameters('notScopes')), createObject('value', createArray()))]", + "subscriptionId": { + "value": "[parameters('subscriptionId')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "overrides": "[if(not(empty(parameters('overrides'))), createObject('value', parameters('overrides')), createObject('value', createArray()))]", + "resourceSelectors": "[if(not(empty(parameters('resourceSelectors'))), createObject('value', parameters('resourceSelectors')), 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.26.170.59819", + "templateHash": "17106923564853756802" + }, + "name": "Policy Assignments (Resource Group scope)", + "description": "This module deploys a Policy Assignment at a Resource Group scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 64, + "metadata": { + "description": "Required. Specifies the name of the policy assignment. Maximum length is 64 characters for resource group scope." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. This message will be part of response in case of policy violation." + } + }, + "displayName": { + "type": "string", + "defaultValue": "", + "maxLength": 128, + "metadata": { + "description": "Optional. The display name of the policy assignment. Maximum length is 128 characters." + } + }, + "policyDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. Specifies the ID of the policy definition or policy set definition being assigned." + } + }, + "parameters": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Parameters for the policy assignment if needed." + } + }, + "identity": { + "type": "string", + "defaultValue": "SystemAssigned", + "allowedValues": [ + "SystemAssigned", + "UserAssigned", + "None" + ], + "metadata": { + "description": "Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions." + } + }, + "userAssignedIdentityId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Resource ID for the user assigned identity to assign to the policy assignment." + } + }, + "roleDefinitionIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs." + } + }, + "nonComplianceMessages": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The messages that describe why a resource is non-compliant with the policy." + } + }, + "enforcementMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "DoNotEnforce" + ], + "metadata": { + "description": "Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce." + } + }, + "notScopes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy excluded scopes." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "overrides": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition." + } + }, + "resourceSelectors": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location." + } + }, + "subscriptionId": { + "type": "string", + "defaultValue": "[subscription().subscriptionId]", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment." + } + }, + "resourceGroupName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The name of the resource group for the policy assignment. If not provided, will use the current scope for deployment." + } + } + }, + "variables": { + "identityVar": "[if(equals(parameters('identity'), 'SystemAssigned'), createObject('type', parameters('identity')), if(equals(parameters('identity'), 'UserAssigned'), createObject('type', parameters('identity'), 'userAssignedIdentities', createObject(format('{0}', parameters('userAssignedIdentityId')), createObject())), null()))]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/policyAssignments", + "apiVersion": "2022-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "properties": { + "displayName": "[if(not(empty(parameters('displayName'))), parameters('displayName'), null())]", + "metadata": "[if(not(empty(parameters('metadata'))), parameters('metadata'), null())]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "policyDefinitionId": "[parameters('policyDefinitionId')]", + "parameters": "[parameters('parameters')]", + "nonComplianceMessages": "[if(not(empty(parameters('nonComplianceMessages'))), parameters('nonComplianceMessages'), createArray())]", + "enforcementMode": "[parameters('enforcementMode')]", + "notScopes": "[if(not(empty(parameters('notScopes'))), parameters('notScopes'), createArray())]", + "overrides": "[if(not(empty(parameters('overrides'))), parameters('overrides'), createArray())]", + "resourceSelectors": "[if(not(empty(parameters('resourceSelectors'))), parameters('resourceSelectors'), createArray())]" + }, + "identity": "[variables('identityVar')]" + }, + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('roleDefinitionIds'))]" + }, + "condition": "[and(not(empty(parameters('roleDefinitionIds'))), equals(parameters('identity'), 'SystemAssigned'))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('subscriptionId'), parameters('resourceGroupName'), parameters('roleDefinitionIds')[copyIndex()], parameters('location'), parameters('name'))]", + "properties": { + "roleDefinitionId": "[parameters('roleDefinitionIds')[copyIndex()]]", + "principalId": "[reference(resourceId('Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.Authorization/policyAssignments', parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "Policy Assignment Name." + }, + "value": "[parameters('name')]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Policy Assignment principal ID." + }, + "value": "[coalesce(tryGet(tryGet(reference(resourceId('Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Policy Assignment resource ID." + }, + "value": "[resourceId('Microsoft.Authorization/policyAssignments', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the policy was assigned to." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full').location]" + } + } + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "Policy Assignment Name." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.name.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.name.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.name.value))]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Policy Assignment principal ID." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.principalId.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.principalId.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.principalId.value))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Policy Assignment resource ID." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.resourceId.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.resourceId.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.resourceId.value))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.location.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.location.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.location.value))]" + } + } +} \ No newline at end of file diff --git a/avm/ptn/authorization/policy-assignment/modules/management-group.bicep b/avm/ptn/authorization/policy-assignment/modules/management-group.bicep new file mode 100644 index 0000000000..721899031a --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/modules/management-group.bicep @@ -0,0 +1,112 @@ +metadata name = 'Policy Assignments (Management Group scope)' +metadata description = 'This module deploys a Policy Assignment at a Management Group scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope.') +@maxLength(24) +param name string + +@sys.description('Optional. This message will be part of response in case of policy violation.') +param description string = '' + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Required. Specifies the ID of the policy definition or policy set definition being assigned.') +param policyDefinitionId string + +@sys.description('Optional. Parameters for the policy assignment if needed.') +param parameters object = {} + +@sys.description('Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning \'Modify\' policy definitions.') +@allowed([ + 'SystemAssigned' + 'UserAssigned' + 'None' +]) +param identity string = 'SystemAssigned' + +@sys.description('Optional. The Resource ID for the user assigned identity to assign to the policy assignment.') +param userAssignedIdentityId string = '' + +@sys.description('Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition.') +param roleDefinitionIds array = [] + +@sys.description('Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The messages that describe why a resource is non-compliant with the policy.') +param nonComplianceMessages array = [] + +@sys.description('Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce.') +@allowed([ + 'Default' + 'DoNotEnforce' +]) +param enforcementMode string = 'Default' + +@sys.description('Optional. The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The policy excluded scopes.') +param notScopes array = [] + +@sys.description('Optional. Location for all resources.') +param location string = deployment().location + +@sys.description('Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition.') +param overrides array = [] + +@sys.description('Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location.') +param resourceSelectors array = [] + +var identityVar = identity == 'SystemAssigned' ? { + type: identity +} : identity == 'UserAssigned' ? { + type: identity + userAssignedIdentities: { + '${userAssignedIdentityId}': {} + } +} : null + +resource policyAssignment 'Microsoft.Authorization/policyAssignments@2022-06-01' = { + name: name + location: location + properties: { + displayName: !empty(displayName) ? displayName : null + metadata: !empty(metadata) ? metadata : null + description: !empty(description) ? description : null + policyDefinitionId: policyDefinitionId + parameters: parameters + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + overrides: !empty(overrides) ? overrides : [] + resourceSelectors: !empty(resourceSelectors) ? resourceSelectors : [] + } + identity: identityVar +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for roleDefinitionId in roleDefinitionIds: if (!empty(roleDefinitionIds) && identity == 'SystemAssigned') { + name: guid(managementGroupId, roleDefinitionId, location, name) + properties: { + roleDefinitionId: roleDefinitionId + principalId: policyAssignment.identity.principalId + principalType: 'ServicePrincipal' + } +}] + +@sys.description('Policy Assignment Name.') +output name string = policyAssignment.name + +@sys.description('Policy Assignment principal ID.') +output principalId string = policyAssignment.?identity.?principalId ?? '' + +@sys.description('Policy Assignment resource ID.') +output resourceId string = policyAssignment.id + +@sys.description('The location the resource was deployed into.') +output location string = policyAssignment.location diff --git a/avm/ptn/authorization/policy-assignment/modules/resource-group.bicep b/avm/ptn/authorization/policy-assignment/modules/resource-group.bicep new file mode 100644 index 0000000000..8850bf099c --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/modules/resource-group.bicep @@ -0,0 +1,119 @@ +metadata name = 'Policy Assignments (Resource Group scope)' +metadata description = 'This module deploys a Policy Assignment at a Resource Group scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'resourceGroup' + +@sys.description('Required. Specifies the name of the policy assignment. Maximum length is 64 characters for resource group scope.') +@maxLength(64) +param name string + +@sys.description('Optional. This message will be part of response in case of policy violation.') +param description string = '' + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Required. Specifies the ID of the policy definition or policy set definition being assigned.') +param policyDefinitionId string + +@sys.description('Optional. Parameters for the policy assignment if needed.') +param parameters object = {} + +@sys.description('Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning \'Modify\' policy definitions.') +@allowed([ + 'SystemAssigned' + 'UserAssigned' + 'None' +]) +param identity string = 'SystemAssigned' + +@sys.description('Optional. The Resource ID for the user assigned identity to assign to the policy assignment.') +param userAssignedIdentityId string = '' + +@sys.description('Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition.') +param roleDefinitionIds array = [] + +@sys.description('Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The messages that describe why a resource is non-compliant with the policy.') +param nonComplianceMessages array = [] + +@sys.description('Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce.') +@allowed([ + 'Default' + 'DoNotEnforce' +]) +param enforcementMode string = 'Default' + +@sys.description('Optional. The policy excluded scopes.') +param notScopes array = [] + +@sys.description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@sys.description('Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition.') +param overrides array = [] + +@sys.description('Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location.') +param resourceSelectors array = [] + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The Target Scope for the Policy. The name of the resource group for the policy assignment. If not provided, will use the current scope for deployment.') +param resourceGroupName string = resourceGroup().name + + +var identityVar = identity == 'SystemAssigned' ? { + type: identity +} : identity == 'UserAssigned' ? { + type: identity + userAssignedIdentities: { + '${userAssignedIdentityId}': {} + } +} : null + +resource policyAssignment 'Microsoft.Authorization/policyAssignments@2022-06-01' = { + name: name + location: location + properties: { + displayName: !empty(displayName) ? displayName : null + metadata: !empty(metadata) ? metadata : null + description: !empty(description) ? description : null + policyDefinitionId: policyDefinitionId + parameters: parameters + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + overrides: !empty(overrides) ? overrides : [] + resourceSelectors: !empty(resourceSelectors) ? resourceSelectors : [] + } + identity: identityVar +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for roleDefinitionId in roleDefinitionIds: if (!empty(roleDefinitionIds) && identity == 'SystemAssigned') { + name: guid(subscriptionId, resourceGroupName, roleDefinitionId, location, name) + properties: { + roleDefinitionId: roleDefinitionId + principalId: policyAssignment.identity.principalId + principalType: 'ServicePrincipal' + } +}] + +@sys.description('Policy Assignment Name.') +output name string = policyAssignment.name + +@sys.description('Policy Assignment principal ID.') +output principalId string = policyAssignment.?identity.?principalId ?? '' + +@sys.description('Policy Assignment resource ID.') +output resourceId string = policyAssignment.id + +@sys.description('The name of the resource group the policy was assigned to.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The location the resource was deployed into.') +output location string = policyAssignment.location diff --git a/avm/ptn/authorization/policy-assignment/modules/subscription.bicep b/avm/ptn/authorization/policy-assignment/modules/subscription.bicep new file mode 100644 index 0000000000..1740e32987 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/modules/subscription.bicep @@ -0,0 +1,112 @@ +metadata name = 'Policy Assignments (Subscription scope)' +metadata description = 'This module deploys a Policy Assignment at a Subscription scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'subscription' + +@sys.description('Required. Specifies the name of the policy assignment. Maximum length is 64 characters for subscription scope.') +@maxLength(64) +param name string + +@sys.description('Optional. This message will be part of response in case of policy violation.') +param description string = '' + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Required. Specifies the ID of the policy definition or policy set definition being assigned.') +param policyDefinitionId string + +@sys.description('Optional. Parameters for the policy assignment if needed.') +param parameters object = {} + +@sys.description('Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning \'Modify\' policy definitions.') +@allowed([ + 'SystemAssigned' + 'UserAssigned' + 'None' +]) +param identity string = 'SystemAssigned' + +@sys.description('Optional. The Resource ID for the user assigned identity to assign to the policy assignment.') +param userAssignedIdentityId string = '' + +@sys.description('Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition.') +param roleDefinitionIds array = [] + +@sys.description('Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The messages that describe why a resource is non-compliant with the policy.') +param nonComplianceMessages array = [] + +@sys.description('Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce.') +@allowed([ + 'Default' + 'DoNotEnforce' +]) +param enforcementMode string = 'Default' + +@sys.description('Optional. The policy excluded scopes.') +param notScopes array = [] + +@sys.description('Optional. Location for all resources.') +param location string = deployment().location + +@sys.description('Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition.') +param overrides array = [] + +@sys.description('Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location.') +param resourceSelectors array = [] + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +var identityVar = identity == 'SystemAssigned' ? { + type: identity +} : identity == 'UserAssigned' ? { + type: identity + userAssignedIdentities: { + '${userAssignedIdentityId}': {} + } +} : null + +resource policyAssignment 'Microsoft.Authorization/policyAssignments@2022-06-01' = { + name: name + location: location + properties: { + displayName: !empty(displayName) ? displayName : null + metadata: !empty(metadata) ? metadata : null + description: !empty(description) ? description : null + policyDefinitionId: policyDefinitionId + parameters: parameters + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + overrides: !empty(overrides) ? overrides : [] + resourceSelectors: !empty(resourceSelectors) ? resourceSelectors : [] + } + identity: identityVar +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for roleDefinitionId in roleDefinitionIds: if (!empty(roleDefinitionIds) && identity == 'SystemAssigned') { + name: guid(subscriptionId, roleDefinitionId, location, name) + properties: { + roleDefinitionId: roleDefinitionId + principalId: policyAssignment.identity.principalId + principalType: 'ServicePrincipal' + } +}] + +@sys.description('Policy Assignment Name.') +output name string = policyAssignment.name + +@sys.description('Policy Assignment principal ID.') +output principalId string = policyAssignment.?identity.?principalId ?? '' + +@sys.description('Policy Assignment resource ID.') +output resourceId string = policyAssignment.id + +@sys.description('The location the resource was deployed into.') +output location string = policyAssignment.location diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/mg.defaults/main.test.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/mg.defaults/main.test.bicep new file mode 100644 index 0000000000..2c043ce041 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/mg.defaults/main.test.bicep @@ -0,0 +1,33 @@ +targetScope = 'managementGroup' +metadata name = 'Policy Assignments (Management Group scope)' +metadata description = 'This module deploys a Policy Assignment at a Management Group scope using minimal parameters.' + +// ========== // +// Parameters // +// ========== // + +@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 = 'apamgmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + //Audit VMs that do not use managed disks + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + metadata: { + assignedBy: 'Bicep' + } + location: resourceLocation + } +} diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/mg.max/main.test.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/mg.max/main.test.bicep new file mode 100644 index 0000000000..07d5b2309a --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/mg.max/main.test.bicep @@ -0,0 +1,96 @@ +targetScope = 'managementGroup' +metadata name = 'Policy Assignments (Management Group scope)' +metadata description = 'This module deploys a Policy Assignment at a Management Group scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@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 = 'apamgmax' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = '#_subscriptionId_#' + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + //Configure Azure Defender for SQL agents on virtual machines + policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611' + description: '[Description] Policy Assignment at the management group scope' + displayName: '[Display Name] Policy Assignment at the management group scope' + enforcementMode: 'DoNotEnforce' + identity: 'SystemAssigned' + location: resourceLocation + managementGroupId: last(split(managementGroup().id, '/')) + metadata: { + category: 'Security' + version: '1.0' + assignedBy: 'Bicep' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/${subscriptionId}/resourceGroups/validation-rg' + ] + parameters: { + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + effect: { + value: 'Disabled' + } + } + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' // Contributor role + ] + overrides: [ + { + kind: 'policyEffect' + value: 'Disabled' + selectors: [ + { + kind: 'policyDefinitionReferenceId' + in: [ + 'ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent' + 'ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent' + ] + } + ] + } + ] + resourceSelectors: [ + { + name: 'resourceSelector-test' + selectors: [ + { + kind: 'resourceType' + in: [ + 'Microsoft.Compute/virtualMachines' + ] + } + { + kind: 'resourceLocation' + in: [ + 'westeurope' + ] + } + ] + } + ] + } +} diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/rg.defaults/main.test.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/rg.defaults/main.test.bicep new file mode 100644 index 0000000000..ff1eaeb6a7 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/rg.defaults/main.test.bicep @@ -0,0 +1,57 @@ +targetScope = 'managementGroup' +metadata name = 'Policy Assignments (Resource Group)' +metadata description = 'This module deploys a Policy Assignment at a Resource Group scope using minimal parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.policyassignments-${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 = 'apargmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module resourceGroupDeploy 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + //Audit VMs that do not use managed disks + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + metadata: { + assignedBy: 'Bicep' + } + location: resourceLocation + resourceGroupName: resourceGroupDeploy.outputs.name + subscriptionId: subscriptionId + } +} diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/dependencies.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/dependencies.bicep new file mode 100644 index 0000000000..f4151d61c7 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/dependencies.bicep @@ -0,0 +1,33 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + accessPolicies: [] + } +} + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/main.test.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/main.test.bicep new file mode 100644 index 0000000000..9dabc3b7a1 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/main.test.bicep @@ -0,0 +1,127 @@ +targetScope = 'managementGroup' +metadata name = 'Policy Assignments (Resource Group)' +metadata description = 'This module deploys a Policy Assignment at a Resource Group scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.policyassignments-${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 = 'apargmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module resourceGroupDeploy 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup(subscriptionId, resourceGroupName) + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + //Configure Azure Defender for SQL agents on virtual machines + policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611' + description: '[Description] Policy Assignment at the resource group scope' + displayName: '[Display Name] Policy Assignment at the resource group scope' + enforcementMode: 'DoNotEnforce' + identity: 'UserAssigned' + location: resourceLocation + metadata: { + category: 'Security' + version: '1.0' + assignedBy: 'Bicep' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + nestedDependencies.outputs.keyVaultResourceId + ] + parameters: { + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + effect: { + value: 'Disabled' + } + } + resourceGroupName: resourceGroupDeploy.outputs.name + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + overrides: [ + { + kind: 'policyEffect' + value: 'Disabled' + selectors: [ + { + kind: 'policyDefinitionReferenceId' + in: [ + 'ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent' + 'ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent' + ] + } + ] + } + ] + resourceSelectors: [ + { + name: 'resourceSelector-test' + selectors: [ + { + kind: 'resourceType' + in: [ + 'Microsoft.Compute/virtualMachines' + ] + } + { + kind: 'resourceLocation' + in: [ + 'westeurope' + ] + } + ] + } + ] + subscriptionId: subscriptionId + userAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId + } +} diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/sub.defaults/main.test.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.defaults/main.test.bicep new file mode 100644 index 0000000000..1cd7910296 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.defaults/main.test.bicep @@ -0,0 +1,39 @@ +targetScope = 'managementGroup' +metadata name = 'Policy Assignments (Subscription)' +metadata description = 'This module deploys a Policy Assignment at a Subscription scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@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 = 'apasubmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = '#_subscriptionId_#' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + //Audit VMs that do not use managed disks + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + subscriptionId: subscriptionId + metadata: { + category: 'Security' + version: '1.0' + assignedBy: 'Bicep' + } + location: resourceLocation + } +} diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/dependencies.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/dependencies.bicep new file mode 100644 index 0000000000..71b8f3cee1 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/dependencies.bicep @@ -0,0 +1,13 @@ +@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 + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/interim.dependencies.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/interim.dependencies.bicep new file mode 100644 index 0000000000..119660120c --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/interim.dependencies.bicep @@ -0,0 +1,28 @@ +targetScope = 'subscription' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Required. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: managedIdentityName + location: location + } +} + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = nestedDependencies.outputs.managedIdentityResourceId diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/main.test.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/main.test.bicep new file mode 100644 index 0000000000..0d90ce4c8c --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/main.test.bicep @@ -0,0 +1,126 @@ +targetScope = 'managementGroup' +metadata name = 'Policy Assignments (Subscription)' +metadata description = 'This module deploys a Policy Assignment at a Subscription scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.policyassignments-${serviceShort}-rg' + +@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 = 'apasubmax' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module resourceGroup 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } +} + +module nestedDependencies 'interim.dependencies.bicep' = { + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + resourceGroupName: resourceGroupName + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + //Configure Azure Defender for SQL agents on virtual machines + policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611' + description: '[Description] Policy Assignment at the subscription scope' + displayName: '[Display Name] Policy Assignment at the subscription scope' + enforcementMode: 'DoNotEnforce' + identity: 'UserAssigned' + location: resourceLocation + metadata: { + category: 'Security' + version: '1.0' + assignedBy: 'Bicep' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/${subscriptionId}/resourceGroups/validation-rg' + ] + parameters: { + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + effect: { + value: 'Disabled' + } + } + roleDefinitionIds: [ + '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + overrides: [ + { + kind: 'policyEffect' + value: 'Disabled' + selectors: [ + { + kind: 'policyDefinitionReferenceId' + in: [ + 'ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent' + 'ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent' + ] + } + ] + } + ] + resourceSelectors: [ + { + name: 'resourceSelector-test' + selectors: [ + { + kind: 'resourceType' + in: [ + 'Microsoft.Compute/virtualMachines' + ] + } + { + kind: 'resourceLocation' + in: [ + 'westeurope' + ] + } + ] + } + ] + subscriptionId: subscriptionId + userAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId + } +} diff --git a/avm/ptn/authorization/policy-assignment/version.json b/avm/ptn/authorization/policy-assignment/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/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/authorization/role-assignment/README.md b/avm/ptn/authorization/role-assignment/README.md new file mode 100644 index 0000000000..dd1e9be0ca --- /dev/null +++ b/avm/ptn/authorization/role-assignment/README.md @@ -0,0 +1,562 @@ +# Role Assignments (All scopes) `[Microsoft.Authorization/roleAssignments]` + +This module deploys a Role Assignment at a Management Group, Subscription or Resource Group scope. + +## 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/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | + +## 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/authorization/role-assignment:`. + +- [Role Assignments (Management Group scope)](#example-1-role-assignments-management-group-scope) +- [Role Assignments (Management Group scope)](#example-2-role-assignments-management-group-scope) +- [Role Assignments (Resource Group scope)](#example-3-role-assignments-resource-group-scope) +- [Role Assignments (Resource Group)](#example-4-role-assignments-resource-group) +- [Role Assignments (Subscription scope)](#example-5-role-assignments-subscription-scope) +- [Role Assignments (Subscription scope)](#example-6-role-assignments-subscription-scope) + +### Example 1: _Role Assignments (Management Group scope)_ + +This module deploys a Role Assignment at a Management Group scope using minimal parameters. + + +

+ +via Bicep module + +```bicep +module roleAssignment 'br/public:avm/ptn/authorization/role-assignment:' = { + name: 'roleAssignmentDeployment' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Resource Policy Contributor' + // Non-required parameters + location: '' + principalType: 'ServicePrincipal' + } +} +``` + +
+

+ +

+ +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 + "principalId": { + "value": "" + }, + "roleDefinitionIdOrName": { + "value": "Resource Policy Contributor" + }, + // Non-required parameters + "location": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + } + } +} +``` + +
+

+ +### Example 2: _Role Assignments (Management Group scope)_ + +This module deploys a Role Assignment at a Management Group scope using common parameters. + + +

+ +via Bicep module + +```bicep +module roleAssignment 'br/public:avm/ptn/authorization/role-assignment:' = { + name: 'roleAssignmentDeployment' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Management Group Reader' + // Non-required parameters + description: 'Role Assignment (management group scope)' + location: '' + managementGroupId: '' + principalType: 'ServicePrincipal' + } +} +``` + +
+

+ +

+ +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 + "principalId": { + "value": "" + }, + "roleDefinitionIdOrName": { + "value": "Management Group Reader" + }, + // Non-required parameters + "description": { + "value": "Role Assignment (management group scope)" + }, + "location": { + "value": "" + }, + "managementGroupId": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + } + } +} +``` + +
+

+ +### Example 3: _Role Assignments (Resource Group scope)_ + +This module deploys a Role Assignment at a Resource Group scope using minimal parameters. + + +

+ +via Bicep module + +```bicep +module roleAssignment 'br/public:avm/ptn/authorization/role-assignment:' = { + name: 'roleAssignmentDeployment' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + // Non-required parameters + location: '' + principalType: 'ServicePrincipal' + resourceGroupName: '' + subscriptionId: '' + } +} +``` + +
+

+ +

+ +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 + "principalId": { + "value": "" + }, + "roleDefinitionIdOrName": { + "value": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11" + }, + // Non-required parameters + "location": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "resourceGroupName": { + "value": "" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 4: _Role Assignments (Resource Group)_ + +This module deploys a Role Assignment at a Resource Group scope using common parameters. + + +

+ +via Bicep module + +```bicep +module roleAssignment 'br/public:avm/ptn/authorization/role-assignment:' = { + name: 'roleAssignmentDeployment' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Reader' + // Non-required parameters + description: 'Role Assignment (resource group scope)' + location: '' + principalType: 'ServicePrincipal' + resourceGroupName: '' + subscriptionId: '' + } +} +``` + +
+

+ +

+ +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 + "principalId": { + "value": "" + }, + "roleDefinitionIdOrName": { + "value": "Reader" + }, + // Non-required parameters + "description": { + "value": "Role Assignment (resource group scope)" + }, + "location": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "resourceGroupName": { + "value": "" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 5: _Role Assignments (Subscription scope)_ + +This module deploys a Role Assignment at a Subscription scope using minimal parameters. + + +

+ +via Bicep module + +```bicep +module roleAssignment 'br/public:avm/ptn/authorization/role-assignment:' = { + name: 'roleAssignmentDeployment' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: '' + // Non-required parameters + location: '' + principalType: 'ServicePrincipal' + subscriptionId: '' + } +} +``` + +
+

+ +

+ +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 + "principalId": { + "value": "" + }, + "roleDefinitionIdOrName": { + "value": "" + }, + // Non-required parameters + "location": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 6: _Role Assignments (Subscription scope)_ + +This module deploys a Role Assignment at a Subscription scope using common parameters. + + +

+ +via Bicep module + +```bicep +module roleAssignment 'br/public:avm/ptn/authorization/role-assignment:' = { + name: 'roleAssignmentDeployment' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Reader' + // Non-required parameters + description: 'Role Assignment (subscription scope)' + location: '' + principalType: 'ServicePrincipal' + subscriptionId: '' + } +} +``` + +
+

+ +

+ +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 + "principalId": { + "value": "" + }, + "roleDefinitionIdOrName": { + "value": "Reader" + }, + // Non-required parameters + "description": { + "value": "Role Assignment (subscription scope)" + }, + "location": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-principalid) | string | The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). | +| [`roleDefinitionIdOrName`](#parameter-roledefinitionidorname) | string | You can provide either the display name of the role definition (must be configured in the variable `builtInRoleNames`), 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-condition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. | +| [`conditionVersion`](#parameter-conditionversion) | string | Version of the condition. Currently accepted value is "2.0". | +| [`delegatedManagedIdentityResourceId`](#parameter-delegatedmanagedidentityresourceid) | string | ID of the delegated managed identity resource. | +| [`description`](#parameter-description) | string | The description of the role assignment. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`location`](#parameter-location) | string | Location deployment metadata. | +| [`managementGroupId`](#parameter-managementgroupid) | string | Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment. | +| [`principalType`](#parameter-principaltype) | string | The principal type of the assigned principal ID. | +| [`resourceGroupName`](#parameter-resourcegroupname) | string | Name of the Resource Group to assign the RBAC role to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided RBAC role to the resource group. | +| [`subscriptionId`](#parameter-subscriptionid) | string | Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription. | + +### Parameter: `principalId` + +The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). + +- Required: Yes +- Type: string + +### Parameter: `roleDefinitionIdOrName` + +You can provide either the display name of the role definition (must be configured in the variable `builtInRoleNames`), or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `conditionVersion` + +Version of the condition. Currently accepted value is "2.0". + +- Required: No +- Type: string +- Default: `'2.0'` +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `delegatedManagedIdentityResourceId` + +ID of the delegated managed identity resource. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `description` + +The description of the role assignment. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `location` + +Location deployment metadata. + +- Required: No +- Type: string +- Default: `[deployment().location]` + +### Parameter: `managementGroupId` + +Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment. + +- Required: No +- Type: string +- Default: `[managementGroup().name]` + +### Parameter: `principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Default: `''` +- Allowed: + ```Bicep + [ + '' + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `resourceGroupName` + +Name of the Resource Group to assign the RBAC role to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided RBAC role to the resource group. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `subscriptionId` + +Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription. + +- Required: No +- Type: string +- Default: `''` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Assignment. | +| `resourceId` | string | The resource ID of the Role Assignment. | +| `scope` | string | The scope this Role Assignment applies to. | + +## 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/ptn/authorization/role-assignment/main.bicep b/avm/ptn/authorization/role-assignment/main.bicep new file mode 100644 index 0000000000..36669e0e95 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/main.bicep @@ -0,0 +1,127 @@ +metadata name = 'Role Assignments (All scopes)' +metadata description = 'This module deploys a Role Assignment at a Management Group, Subscription or Resource Group scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'managementGroup' + +@sys.description('Required. You can provide either the display name of the role definition (must be configured in the variable `builtInRoleNames`), or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleDefinitionIdOrName string + +@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).') +param principalId string + +@sys.description('Optional. Name of the Resource Group to assign the RBAC role to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided RBAC role to the resource group.') +param resourceGroupName string = '' + +@sys.description('Optional. Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription.') +param subscriptionId string = '' + +@sys.description('Optional. Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. ID of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') +param condition string = '' + +@sys.description('Optional. Version of the condition. Currently accepted value is "2.0".') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.ptn.authorization-roleassignment.${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' + } + } + } + } + location:location + } + +module roleAssignment_mg 'modules/management-group.bicep' = if (empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-RoleAssignment-MG-Module' + scope: managementGroup(managementGroupId) + params: { + roleDefinitionIdOrName: roleDefinitionIdOrName + principalId: principalId + managementGroupId: managementGroupId + description: !empty(description) ? description : '' + principalType: !empty(principalType) ? principalType : '' + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : '' + conditionVersion: conditionVersion + condition: !empty(condition) ? condition : '' + } +} + +module roleAssignment_sub 'modules/subscription.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-RoleAssignment-Sub-Module' + scope: subscription(subscriptionId) + params: { + roleDefinitionIdOrName: roleDefinitionIdOrName + principalId: principalId + subscriptionId: subscriptionId + description: !empty(description) ? description : '' + principalType: !empty(principalType) ? principalType : '' + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : '' + conditionVersion: conditionVersion + condition: !empty(condition) ? condition : '' + } +} + +module roleAssignment_rg 'modules/resource-group.bicep' = if (!empty(resourceGroupName) && !empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-RoleAssignment-RG-Module' + scope: resourceGroup(subscriptionId, resourceGroupName) + params: { + roleDefinitionIdOrName: roleDefinitionIdOrName + principalId: principalId + subscriptionId: subscriptionId + resourceGroupName: resourceGroupName + description: !empty(description) ? description : '' + principalType: !empty(principalType) ? principalType : '' + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : '' + conditionVersion: conditionVersion + condition: !empty(condition) ? condition : '' + } +} + +@sys.description('The GUID of the Role Assignment.') +output name string = empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_mg.outputs.name : (!empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_sub.outputs.name : roleAssignment_rg.outputs.name) + +@sys.description('The resource ID of the Role Assignment.') +output resourceId string = empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_mg.outputs.resourceId : (!empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_sub.outputs.resourceId : roleAssignment_rg.outputs.resourceId) + +@sys.description('The scope this Role Assignment applies to.') +output scope string = empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_mg.outputs.scope : (!empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_sub.outputs.scope : roleAssignment_rg.outputs.scope) diff --git a/avm/ptn/authorization/role-assignment/main.json b/avm/ptn/authorization/role-assignment/main.json new file mode 100644 index 0000000000..d8570e9088 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/main.json @@ -0,0 +1,666 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "3360936249154372689" + }, + "name": "Role Assignments (All scopes)", + "description": "This module deploys a Role Assignment at a Management Group, Subscription or Resource Group scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. You can provide either the display name of the role definition (must be configured in the variable `builtInRoleNames`), or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity)." + } + }, + "resourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Name of the Resource Group to assign the RBAC role to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided RBAC role to the resource group." + } + }, + "subscriptionId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription." + } + }, + "managementGroupId": { + "type": "string", + "defaultValue": "[managementGroup().name]", + "metadata": { + "description": "Optional. Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. Location deployment metadata." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. ID of the delegated managed identity resource." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to." + } + }, + "conditionVersion": { + "type": "string", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition. Currently accepted value is \"2.0\"." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.ptn.authorization-roleassignment.{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" + } + } + } + }, + "location": "[parameters('location')]" + }, + { + "condition": "[and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-RoleAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))]", + "scope": "[format('Microsoft.Management/managementGroups/{0}', parameters('managementGroupId'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "roleDefinitionIdOrName": { + "value": "[parameters('roleDefinitionIdOrName')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + }, + "managementGroupId": { + "value": "[parameters('managementGroupId')]" + }, + "description": "[if(not(empty(parameters('description'))), createObject('value', parameters('description')), createObject('value', ''))]", + "principalType": "[if(not(empty(parameters('principalType'))), createObject('value', parameters('principalType')), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), createObject('value', parameters('delegatedManagedIdentityResourceId')), createObject('value', ''))]", + "conditionVersion": { + "value": "[parameters('conditionVersion')]" + }, + "condition": "[if(not(empty(parameters('condition'))), createObject('value', parameters('condition')), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "13749459126745145624" + }, + "name": "Role Assignments (Management Group scope)", + "description": "This module deploys a Role Assignment at a Management Group scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. You can provide either the display name of the role definition (must be configured in the variable `builtInRoleNames`), or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity)." + } + }, + "managementGroupId": { + "type": "string", + "defaultValue": "[managementGroup().name]", + "metadata": { + "description": "Optional. Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. ID of the delegated managed identity resource." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to." + } + }, + "conditionVersion": { + "type": "string", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition. Currently accepted value is \"2.0\"." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Resource Policy Contributor": "/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608", + "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')]", + "Management Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ac63b705-f282-497d-ac71-919bf39d939d')]" + }, + "roleDefinitionIdVar": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('managementGroupId'), variables('roleDefinitionIdVar'), parameters('principalId'))]", + "properties": { + "roleDefinitionId": "[variables('roleDefinitionIdVar')]", + "principalId": "[parameters('principalId')]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The GUID of the Role Assignment." + }, + "value": "[guid(parameters('managementGroupId'), variables('roleDefinitionIdVar'), parameters('principalId'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Role Assignment." + }, + "value": "[extensionResourceId(managementGroup().id, 'Microsoft.Authorization/roleAssignments', guid(parameters('managementGroupId'), variables('roleDefinitionIdVar'), parameters('principalId')))]" + }, + "scope": { + "type": "string", + "metadata": { + "description": "The scope this Role Assignment applies to." + }, + "value": "[resourceId('Microsoft.Management/managementGroups', parameters('managementGroupId'))]" + } + } + } + } + }, + { + "condition": "[and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-RoleAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[parameters('subscriptionId')]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "roleDefinitionIdOrName": { + "value": "[parameters('roleDefinitionIdOrName')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + }, + "subscriptionId": { + "value": "[parameters('subscriptionId')]" + }, + "description": "[if(not(empty(parameters('description'))), createObject('value', parameters('description')), createObject('value', ''))]", + "principalType": "[if(not(empty(parameters('principalType'))), createObject('value', parameters('principalType')), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), createObject('value', parameters('delegatedManagedIdentityResourceId')), createObject('value', ''))]", + "conditionVersion": { + "value": "[parameters('conditionVersion')]" + }, + "condition": "[if(not(empty(parameters('condition'))), createObject('value', parameters('condition')), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "4516670639800961845" + }, + "name": "Role Assignments (Subscription scope)", + "description": "This module deploys a Role Assignment at a Subscription scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. You can provide either the display name of the role definition (must be configured in the variable `builtInRoleNames`), or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity)." + } + }, + "subscriptionId": { + "type": "string", + "defaultValue": "[subscription().subscriptionId]", + "metadata": { + "description": "Optional. Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. ID of the delegated managed identity resource." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to." + } + }, + "conditionVersion": { + "type": "string", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition. Currently accepted value is \"2.0\"." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + } + }, + "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')]" + }, + "roleDefinitionIdVar": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('subscriptionId'), variables('roleDefinitionIdVar'), parameters('principalId'))]", + "properties": { + "roleDefinitionId": "[variables('roleDefinitionIdVar')]", + "principalId": "[parameters('principalId')]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The GUID of the Role Assignment." + }, + "value": "[guid(parameters('subscriptionId'), variables('roleDefinitionIdVar'), parameters('principalId'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Role Assignment." + }, + "value": "[subscriptionResourceId('Microsoft.Authorization/roleAssignments', guid(parameters('subscriptionId'), variables('roleDefinitionIdVar'), parameters('principalId')))]" + }, + "subscriptionName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the role assignment was applied at." + }, + "value": "[subscription().displayName]" + }, + "scope": { + "type": "string", + "metadata": { + "description": "The scope this Role Assignment applies to." + }, + "value": "[subscription().id]" + } + } + } + } + }, + { + "condition": "[and(not(empty(parameters('resourceGroupName'))), not(empty(parameters('subscriptionId'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-RoleAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[parameters('subscriptionId')]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "roleDefinitionIdOrName": { + "value": "[parameters('roleDefinitionIdOrName')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + }, + "subscriptionId": { + "value": "[parameters('subscriptionId')]" + }, + "resourceGroupName": { + "value": "[parameters('resourceGroupName')]" + }, + "description": "[if(not(empty(parameters('description'))), createObject('value', parameters('description')), createObject('value', ''))]", + "principalType": "[if(not(empty(parameters('principalType'))), createObject('value', parameters('principalType')), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), createObject('value', parameters('delegatedManagedIdentityResourceId')), createObject('value', ''))]", + "conditionVersion": { + "value": "[parameters('conditionVersion')]" + }, + "condition": "[if(not(empty(parameters('condition'))), createObject('value', parameters('condition')), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "7241874480439813582" + }, + "name": "Role Assignments (Resource Group scope)", + "description": "This module deploys a Role Assignment at a Resource Group scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. You can provide either the display name of the role definition (must be configured in the variable `builtInRoleNames`), or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity)." + } + }, + "resourceGroupName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "Optional. Name of the Resource Group to assign the RBAC role to. If not provided, will use the current scope for deployment." + } + }, + "subscriptionId": { + "type": "string", + "defaultValue": "[subscription().subscriptionId]", + "metadata": { + "description": "Optional. Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. ID of the delegated managed identity resource." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to." + } + }, + "conditionVersion": { + "type": "string", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition. Currently accepted value is \"2.0\"." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + } + }, + "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')]" + }, + "roleDefinitionIdVar": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('subscriptionId'), parameters('resourceGroupName'), variables('roleDefinitionIdVar'), parameters('principalId'))]", + "properties": { + "roleDefinitionId": "[variables('roleDefinitionIdVar')]", + "principalId": "[parameters('principalId')]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The GUID of the Role Assignment." + }, + "value": "[guid(parameters('subscriptionId'), parameters('resourceGroupName'), variables('roleDefinitionIdVar'), parameters('principalId'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Role Assignment." + }, + "value": "[resourceId('Microsoft.Authorization/roleAssignments', guid(parameters('subscriptionId'), parameters('resourceGroupName'), variables('roleDefinitionIdVar'), parameters('principalId')))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the role assignment was applied at." + }, + "value": "[resourceGroup().name]" + }, + "scope": { + "type": "string", + "metadata": { + "description": "The scope this Role Assignment applies to." + }, + "value": "[resourceGroup().id]" + } + } + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The GUID of the Role Assignment." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.name.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.name.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.name.value))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Role Assignment." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.resourceId.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.resourceId.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.resourceId.value))]" + }, + "scope": { + "type": "string", + "metadata": { + "description": "The scope this Role Assignment applies to." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.scope.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.scope.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.scope.value))]" + } + } +} \ No newline at end of file diff --git a/avm/ptn/authorization/role-assignment/modules/management-group.bicep b/avm/ptn/authorization/role-assignment/modules/management-group.bicep new file mode 100644 index 0000000000..b1a51d40f9 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/modules/management-group.bicep @@ -0,0 +1,75 @@ +metadata name = 'Role Assignments (Management Group scope)' +metadata description = 'This module deploys a Role Assignment at a Management Group scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'managementGroup' + +@sys.description('Required. You can provide either the display name of the role definition (must be configured in the variable `builtInRoleNames`), or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleDefinitionIdOrName string + +@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).') +param principalId string + +@sys.description('Optional. Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. ID of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') +param condition string = '' + +@sys.description('Optional. Version of the condition. Currently accepted value is "2.0".') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Resource Policy Contributor': '/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608' + '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') + 'Management Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ac63b705-f282-497d-ac71-919bf39d939d') +} + +var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName) + + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(managementGroupId, roleDefinitionIdVar, principalId) + properties: { + roleDefinitionId: roleDefinitionIdVar + principalId: principalId + description: !empty(description) ? description : null + principalType: !empty(principalType) ? any(principalType) : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + condition: !empty(condition) ? condition : null + } +} + +@sys.description('The GUID of the Role Assignment.') +output name string = roleAssignment.name + +@sys.description('The resource ID of the Role Assignment.') +output resourceId string = roleAssignment.id + +@sys.description('The scope this Role Assignment applies to.') +output scope string = az.resourceId('Microsoft.Management/managementGroups', managementGroupId) diff --git a/avm/ptn/authorization/role-assignment/modules/resource-group.bicep b/avm/ptn/authorization/role-assignment/modules/resource-group.bicep new file mode 100644 index 0000000000..459bde3f99 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/modules/resource-group.bicep @@ -0,0 +1,78 @@ +metadata name = 'Role Assignments (Resource Group scope)' +metadata description = 'This module deploys a Role Assignment at a Resource Group scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'resourceGroup' + +@sys.description('Required. You can provide either the display name of the role definition (must be configured in the variable `builtInRoleNames`), or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleDefinitionIdOrName string + +@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).') +param principalId string + +@sys.description('Optional. Name of the Resource Group to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param resourceGroupName string = resourceGroup().name + +@sys.description('Optional. Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. ID of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') +param condition string = '' + +@sys.description('Optional. Version of the condition. Currently accepted value is "2.0".') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string + +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('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName) + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscriptionId, resourceGroupName, roleDefinitionIdVar, principalId) + properties: { + roleDefinitionId: roleDefinitionIdVar + principalId: principalId + description: !empty(description) ? description : null + principalType: !empty(principalType) ? any(principalType) : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + condition: !empty(condition) ? condition : null + } +} + +@sys.description('The GUID of the Role Assignment.') +output name string = roleAssignment.name + +@sys.description('The resource ID of the Role Assignment.') +output resourceId string = roleAssignment.id + +@sys.description('The name of the resource group the role assignment was applied at.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The scope this Role Assignment applies to.') +output scope string = resourceGroup().id diff --git a/avm/ptn/authorization/role-assignment/modules/subscription.bicep b/avm/ptn/authorization/role-assignment/modules/subscription.bicep new file mode 100644 index 0000000000..2a41bc6b07 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/modules/subscription.bicep @@ -0,0 +1,75 @@ +metadata name = 'Role Assignments (Subscription scope)' +metadata description = 'This module deploys a Role Assignment at a Subscription scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'subscription' + +@sys.description('Required. You can provide either the display name of the role definition (must be configured in the variable `builtInRoleNames`), or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleDefinitionIdOrName string + +@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).') +param principalId string + +@sys.description('Optional. Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. ID of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') +param condition string = '' + +@sys.description('Optional. Version of the condition. Currently accepted value is "2.0".') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +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('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName) + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscriptionId, roleDefinitionIdVar, principalId) + properties: { + roleDefinitionId: roleDefinitionIdVar + principalId: principalId + description: !empty(description) ? description : null + principalType: !empty(principalType) ? any(principalType) : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + condition: !empty(condition) ? condition : null + } +} + +@sys.description('The GUID of the Role Assignment.') +output name string = roleAssignment.name + +@sys.description('The resource ID of the Role Assignment.') +output resourceId string = roleAssignment.id + +@sys.description('The name of the resource group the role assignment was applied at.') +output subscriptionName string = subscription().displayName + +@sys.description('The scope this Role Assignment applies to.') +output scope string = subscription().id diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/dependencies.bicep new file mode 100644 index 0000000000..d367770432 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/dependencies.bicep @@ -0,0 +1,13 @@ +@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 + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/interim.dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/interim.dependencies.bicep new file mode 100644 index 0000000000..55ada1deea --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/interim.dependencies.bicep @@ -0,0 +1,28 @@ +targetScope = 'subscription' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Required. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: managedIdentityName + location: location + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = nestedDependencies.outputs.managedIdentityPrincipalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/main.test.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/main.test.bicep new file mode 100644 index 0000000000..0dfff91298 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/main.test.bicep @@ -0,0 +1,53 @@ +targetScope = 'managementGroup' +metadata name = 'Role Assignments (Management Group scope)' +metadata description = 'This module deploys a Role Assignment at a Management Group scope using minimal parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.roleassignments-${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 = 'aramgmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module nestedDependencies 'interim.dependencies.bicep' = { + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-${serviceShort}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + resourceGroupName: resourceGroupName + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Resource Policy Contributor' + principalType: 'ServicePrincipal' + location: resourceLocation + } +} diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/dependencies.bicep new file mode 100644 index 0000000000..d367770432 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/dependencies.bicep @@ -0,0 +1,13 @@ +@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 + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/interim.dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/interim.dependencies.bicep new file mode 100644 index 0000000000..55ada1deea --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/interim.dependencies.bicep @@ -0,0 +1,28 @@ +targetScope = 'subscription' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Required. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: managedIdentityName + location: location + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = nestedDependencies.outputs.managedIdentityPrincipalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/main.test.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/main.test.bicep new file mode 100644 index 0000000000..b7e9d1bd87 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/main.test.bicep @@ -0,0 +1,55 @@ +targetScope = 'managementGroup' +metadata name = 'Role Assignments (Management Group scope)' +metadata description = 'This module deploys a Role Assignment at a Management Group scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.roleassignments-${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 = 'aramgmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module nestedDependencies 'interim.dependencies.bicep' = { + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-${serviceShort}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + resourceGroupName: resourceGroupName + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Management Group Reader' + description: 'Role Assignment (management group scope)' + managementGroupId: last(split(managementGroup().id, '/')) + principalType: 'ServicePrincipal' + location: resourceLocation + } +} diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/rg.default/dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/rg.default/dependencies.bicep new file mode 100644 index 0000000000..5681a89989 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/rg.default/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/rg.default/main.test.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/rg.default/main.test.bicep new file mode 100644 index 0000000000..99cebb3035 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/rg.default/main.test.bicep @@ -0,0 +1,65 @@ +targetScope = 'managementGroup' +metadata name = 'Role Assignments (Resource Group scope)' +metadata description = 'This module deploys a Role Assignment at a Resource Group scope using minimal parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.roleassignments-${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 = 'arargmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription.') +param subscriptionId string = '#_subscriptionId_#' + + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module resourceGroupDeploy 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } + + +} +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup(subscriptionId, resourceGroupName) + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalType: 'ServicePrincipal' + location: resourceLocation + subscriptionId: subscriptionId + resourceGroupName: resourceGroupName + } +} diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/rg.max/dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/rg.max/dependencies.bicep new file mode 100644 index 0000000000..5681a89989 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/rg.max/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/rg.max/main.test.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/rg.max/main.test.bicep new file mode 100644 index 0000000000..b154901d94 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/rg.max/main.test.bicep @@ -0,0 +1,64 @@ +targetScope = 'managementGroup' +metadata name = 'Role Assignments (Resource Group)' +metadata description = 'This module deploys a Role Assignment at a Resource Group scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.roleassignments-${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 = 'arargmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module resourceGroupDeploy 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup(subscriptionId, resourceGroupName) + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Reader' + description: 'Role Assignment (resource group scope)' + principalType: 'ServicePrincipal' + location: resourceLocation + subscriptionId: subscriptionId + resourceGroupName: resourceGroupName + } +} diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/dependencies.bicep new file mode 100644 index 0000000000..d367770432 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/dependencies.bicep @@ -0,0 +1,13 @@ +@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 + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/interim.dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/interim.dependencies.bicep new file mode 100644 index 0000000000..55ada1deea --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/interim.dependencies.bicep @@ -0,0 +1,28 @@ +targetScope = 'subscription' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Required. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: managedIdentityName + location: location + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = nestedDependencies.outputs.managedIdentityPrincipalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/main.test.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/main.test.bicep new file mode 100644 index 0000000000..4b1543c60b --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/main.test.bicep @@ -0,0 +1,65 @@ +targetScope = 'managementGroup' +metadata name = 'Role Assignments (Subscription scope)' +metadata description = 'This module deploys a Role Assignment at a Subscription scope using minimal parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.roleassignments-${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 = 'arasubmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= + + +module resourceGroup 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } +} + +module nestedDependencies 'interim.dependencies.bicep' = { + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + resourceGroupName: resourceGroupName + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + principalType: 'ServicePrincipal' + location: resourceLocation + subscriptionId: subscriptionId + } +} diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/dependencies.bicep new file mode 100644 index 0000000000..d367770432 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/dependencies.bicep @@ -0,0 +1,13 @@ +@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 + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/interim.dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/interim.dependencies.bicep new file mode 100644 index 0000000000..55ada1deea --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/interim.dependencies.bicep @@ -0,0 +1,28 @@ +targetScope = 'subscription' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Required. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: managedIdentityName + location: location + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = nestedDependencies.outputs.managedIdentityPrincipalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/main.test.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/main.test.bicep new file mode 100644 index 0000000000..0f41d75951 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/main.test.bicep @@ -0,0 +1,66 @@ +targetScope = 'managementGroup' +metadata name = 'Role Assignments (Subscription scope)' +metadata description = 'This module deploys a Role Assignment at a Subscription scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.roleassignments-${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 = 'arasubmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= + +module resourceGroup 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } + + +} +module nestedDependencies 'interim.dependencies.bicep' = { + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + resourceGroupName: resourceGroupName + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Reader' + description: 'Role Assignment (subscription scope)' + principalType: 'ServicePrincipal' + location: resourceLocation + subscriptionId: subscriptionId + } +} diff --git a/avm/ptn/authorization/role-assignment/version.json b/avm/ptn/authorization/role-assignment/version.json new file mode 100644 index 0000000000..7fa401bdf7 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/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/res/api-management/service/README.md b/avm/res/api-management/service/README.md index 4b29ffb6c3..b8ca4b6ec8 100644 --- a/avm/res/api-management/service/README.md +++ b/avm/res/api-management/service/README.md @@ -1464,6 +1464,7 @@ The instance size of this API Management service. - Allowed: ```Bicep [ + 0 1 2 ] diff --git a/avm/res/api-management/service/main.bicep b/avm/res/api-management/service/main.bicep index 6224b65b85..429f44edce 100644 --- a/avm/res/api-management/service/main.bicep +++ b/avm/res/api-management/service/main.bicep @@ -66,6 +66,7 @@ param sku string = 'Developer' @description('Optional. The instance size of this API Management service.') @allowed([ + 0 1 2 ]) diff --git a/avm/res/api-management/service/main.json b/avm/res/api-management/service/main.json index 9f44ef9ee9..3a2f0a0a9c 100644 --- a/avm/res/api-management/service/main.json +++ b/avm/res/api-management/service/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "4652975451954294914" + "templateHash": "7048259745072727177" }, "name": "API Management Services", "description": "This module deploys an API Management Service.", @@ -381,6 +381,7 @@ "type": "int", "defaultValue": 1, "allowedValues": [ + 0, 1, 2 ], diff --git a/avm/res/container-service/managed-cluster/README.md b/avm/res/container-service/managed-cluster/README.md index e5154ea6db..18b51cc75b 100644 --- a/avm/res/container-service/managed-cluster/README.md +++ b/avm/res/container-service/managed-cluster/README.md @@ -64,6 +64,9 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:' @@ -112,9 +112,6 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:", @@ -339,9 +336,6 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:**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/res/network/application-gateway:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module applicationGateway 'br/public:avm/res/network/application-gateway:' = { + name: 'applicationGatewayDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + backendAddressPools: [ + { + name: 'backendAddressPool1' + } + ] + backendHttpSettingsCollection: [ + { + name: 'backendHttpSettings1' + properties: { + cookieBasedAffinity: 'Disabled' + port: 80 + protocol: 'Http' + } + } + ] + frontendIPConfigurations: [ + { + name: 'frontendIPConfig1' + properties: { + publicIPAddress: { + id: '' + } + } + } + ] + frontendPorts: [ + { + name: 'frontendPort1' + properties: { + port: 80 + } + } + ] + gatewayIPConfigurations: [ + { + name: 'publicIPConfig1' + properties: { + subnet: { + id: '' + } + } + } + ] + httpListeners: [ + { + name: 'httpListener1' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostName: 'www.contoso.com' + protocol: 'Http' + } + } + ] + location: '' + requestRoutingRules: [ + { + name: 'requestRoutingRule1' + properties: { + backendAddressPool: { + id: '' + } + backendHttpSettings: { + id: '' + } + httpListener: { + id: '' + } + priority: 100 + ruleType: 'Basic' + } + } + ] + webApplicationFirewallConfiguration: { + enabled: false + } + zones: [ + '1' + '2' + '3' + ] + } +} +``` + +
+

+ +

+ +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 + "backendAddressPools": { + "value": [ + { + "name": "backendAddressPool1" + } + ] + }, + "backendHttpSettingsCollection": { + "value": [ + { + "name": "backendHttpSettings1", + "properties": { + "cookieBasedAffinity": "Disabled", + "port": 80, + "protocol": "Http" + } + } + ] + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "frontendIPConfig1", + "properties": { + "publicIPAddress": { + "id": "" + } + } + } + ] + }, + "frontendPorts": { + "value": [ + { + "name": "frontendPort1", + "properties": { + "port": 80 + } + } + ] + }, + "gatewayIPConfigurations": { + "value": [ + { + "name": "publicIPConfig1", + "properties": { + "subnet": { + "id": "" + } + } + } + ] + }, + "httpListeners": { + "value": [ + { + "name": "httpListener1", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostName": "www.contoso.com", + "protocol": "Http" + } + } + ] + }, + "location": { + "value": "" + }, + "requestRoutingRules": { + "value": [ + { + "name": "requestRoutingRule1", + "properties": { + "backendAddressPool": { + "id": "" + }, + "backendHttpSettings": { + "id": "" + }, + "httpListener": { + "id": "" + }, + "priority": 100, + "ruleType": "Basic" + } + } + ] + }, + "webApplicationFirewallConfiguration": { + "value": { + "enabled": false + } + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + } + } +} +``` + +
+

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

+ +via Bicep module + +```bicep +module applicationGateway 'br/public:avm/res/network/application-gateway:' = { + name: 'applicationGatewayDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + backendAddressPools: [ + { + name: 'appServiceBackendPool' + properties: { + backendAddresses: [ + { + fqdn: 'aghapp.azurewebsites.net' + } + ] + } + } + { + name: 'privateVmBackendPool' + properties: { + backendAddresses: [ + { + ipAddress: '10.0.0.4' + } + ] + } + } + ] + backendHttpSettingsCollection: [ + { + name: 'appServiceBackendHttpsSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: true + port: 443 + protocol: 'Https' + requestTimeout: 30 + } + } + { + name: 'privateVmHttpSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: false + port: 80 + probe: { + id: '' + } + protocol: 'Http' + requestTimeout: 30 + } + } + ] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + enableHttp2: true + enableTelemetry: '' + frontendIPConfigurations: [ + { + name: 'private' + properties: { + privateIPAddress: '10.0.0.20' + privateIPAllocationMethod: 'Static' + subnet: { + id: '' + } + } + } + { + name: 'public' + properties: { + privateIPAllocationMethod: 'Dynamic' + privateLinkConfiguration: { + id: '' + } + publicIPAddress: { + id: '' + } + } + } + ] + frontendPorts: [ + { + name: 'port443' + properties: { + port: 443 + } + } + { + name: 'port4433' + properties: { + port: 4433 + } + } + { + name: 'port80' + properties: { + port: 80 + } + } + { + name: 'port8080' + properties: { + port: 8080 + } + } + ] + gatewayIPConfigurations: [ + { + name: 'apw-ip-configuration' + properties: { + subnet: { + id: '' + } + } + } + ] + httpListeners: [ + { + name: 'public443' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '' + } + } + } + { + name: 'private4433' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '' + } + } + } + { + name: 'httpRedirect80' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + { + name: 'httpRedirect8080' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + ] + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentities: { + userAssignedResourceIds: [ + '' + ] + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'public' + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + privateLinkConfigurations: [ + { + id: '' + name: 'pvtlink01' + properties: { + ipConfigurations: [ + { + id: '' + name: 'privateLinkIpConfig1' + properties: { + primary: false + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: '' + } + } + } + ] + } + } + ] + probes: [ + { + name: 'privateVmHttpSettingProbe' + properties: { + host: '10.0.0.4' + interval: 60 + match: { + statusCodes: [ + '200' + '401' + ] + } + minServers: 3 + path: '/' + pickHostNameFromBackendHttpSettings: false + protocol: 'Http' + timeout: 15 + unhealthyThreshold: 5 + } + } + ] + redirectConfigurations: [ + { + name: 'httpRedirect80' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '' + } + ] + targetListener: { + id: '' + } + } + } + { + name: 'httpRedirect8080' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '' + } + ] + targetListener: { + id: '' + } + } + } + ] + requestRoutingRules: [ + { + name: 'public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting' + properties: { + backendAddressPool: { + id: '' + } + backendHttpSettings: { + id: '' + } + httpListener: { + id: '' + } + priority: 200 + ruleType: 'Basic' + } + } + { + name: 'private4433-privateVmHttpSetting-privateVmHttpSetting' + properties: { + backendAddressPool: { + id: '' + } + backendHttpSettings: { + id: '' + } + httpListener: { + id: '' + } + priority: 250 + ruleType: 'Basic' + } + } + { + name: 'httpRedirect80-public443' + properties: { + httpListener: { + id: '' + } + priority: 300 + redirectConfiguration: { + id: '' + } + ruleType: 'Basic' + } + } + { + name: 'httpRedirect8080-private4433' + properties: { + httpListener: { + id: '' + } + priority: 350 + redirectConfiguration: { + id: '' + } + rewriteRuleSet: { + id: '' + } + ruleType: 'Basic' + } + } + ] + rewriteRuleSets: [ + { + id: '' + name: 'customRewrite' + properties: { + rewriteRules: [ + { + actionSet: { + requestHeaderConfigurations: [ + { + headerName: 'Content-Type' + headerValue: 'JSON' + } + { + headerName: 'someheader' + } + ] + responseHeaderConfigurations: [] + } + conditions: [] + name: 'NewRewrite' + ruleSequence: 100 + } + ] + } + } + ] + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' + } + ] + sku: 'WAF_v2' + sslCertificates: [ + { + name: 'az-apgw-x-001-ssl-certificate' + properties: { + keyVaultSecretId: '' + } + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + webApplicationFirewallConfiguration: { + disabledRuleGroups: [ + { + ruleGroupName: 'Known-CVEs' + } + { + ruleGroupName: 'REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION' + } + { + ruleGroupName: 'REQUEST-941-APPLICATION-ATTACK-XSS' + } + ] + enabled: true + exclusions: [ + { + matchVariable: 'RequestHeaderNames' + selector: 'hola' + selectorMatchOperator: 'StartsWith' + } + ] + fileUploadLimitInMb: 100 + firewallMode: 'Detection' + maxRequestBodySizeInKb: 128 + requestBodyCheck: true + ruleSetType: 'OWASP' + ruleSetVersion: '3.0' + } + zones: [ + '1' + '2' + '3' + ] + } +} +``` + +
+

+ +

+ +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 + "backendAddressPools": { + "value": [ + { + "name": "appServiceBackendPool", + "properties": { + "backendAddresses": [ + { + "fqdn": "aghapp.azurewebsites.net" + } + ] + } + }, + { + "name": "privateVmBackendPool", + "properties": { + "backendAddresses": [ + { + "ipAddress": "10.0.0.4" + } + ] + } + } + ] + }, + "backendHttpSettingsCollection": { + "value": [ + { + "name": "appServiceBackendHttpsSetting", + "properties": { + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": true, + "port": 443, + "protocol": "Https", + "requestTimeout": 30 + } + }, + { + "name": "privateVmHttpSetting", + "properties": { + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": false, + "port": 80, + "probe": { + "id": "" + }, + "protocol": "Http", + "requestTimeout": 30 + } + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "enableHttp2": { + "value": true + }, + "enableTelemetry": { + "value": "" + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "private", + "properties": { + "privateIPAddress": "10.0.0.20", + "privateIPAllocationMethod": "Static", + "subnet": { + "id": "" + } + } + }, + { + "name": "public", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "privateLinkConfiguration": { + "id": "" + }, + "publicIPAddress": { + "id": "" + } + } + } + ] + }, + "frontendPorts": { + "value": [ + { + "name": "port443", + "properties": { + "port": 443 + } + }, + { + "name": "port4433", + "properties": { + "port": 4433 + } + }, + { + "name": "port80", + "properties": { + "port": 80 + } + }, + { + "name": "port8080", + "properties": { + "port": 8080 + } + } + ] + }, + "gatewayIPConfigurations": { + "value": [ + { + "name": "apw-ip-configuration", + "properties": { + "subnet": { + "id": "" + } + } + } + ] + }, + "httpListeners": { + "value": [ + { + "name": "public443", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "https", + "requireServerNameIndication": false, + "sslCertificate": { + "id": "" + } + } + }, + { + "name": "private4433", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "https", + "requireServerNameIndication": false, + "sslCertificate": { + "id": "" + } + } + }, + { + "name": "httpRedirect80", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "Http", + "requireServerNameIndication": false + } + }, + { + "name": "httpRedirect8080", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "Http", + "requireServerNameIndication": false + } + } + ] + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "" + ] + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "public", + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + ] + }, + "privateLinkConfigurations": { + "value": [ + { + "id": "", + "name": "pvtlink01", + "properties": { + "ipConfigurations": [ + { + "id": "", + "name": "privateLinkIpConfig1", + "properties": { + "primary": false, + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "" + } + } + } + ] + } + } + ] + }, + "probes": { + "value": [ + { + "name": "privateVmHttpSettingProbe", + "properties": { + "host": "10.0.0.4", + "interval": 60, + "match": { + "statusCodes": [ + "200", + "401" + ] + }, + "minServers": 3, + "path": "/", + "pickHostNameFromBackendHttpSettings": false, + "protocol": "Http", + "timeout": 15, + "unhealthyThreshold": 5 + } + } + ] + }, + "redirectConfigurations": { + "value": [ + { + "name": "httpRedirect80", + "properties": { + "includePath": true, + "includeQueryString": true, + "redirectType": "Permanent", + "requestRoutingRules": [ + { + "id": "" + } + ], + "targetListener": { + "id": "" + } + } + }, + { + "name": "httpRedirect8080", + "properties": { + "includePath": true, + "includeQueryString": true, + "redirectType": "Permanent", + "requestRoutingRules": [ + { + "id": "" + } + ], + "targetListener": { + "id": "" + } + } + } + ] + }, + "requestRoutingRules": { + "value": [ + { + "name": "public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting", + "properties": { + "backendAddressPool": { + "id": "" + }, + "backendHttpSettings": { + "id": "" + }, + "httpListener": { + "id": "" + }, + "priority": 200, + "ruleType": "Basic" + } + }, + { + "name": "private4433-privateVmHttpSetting-privateVmHttpSetting", + "properties": { + "backendAddressPool": { + "id": "" + }, + "backendHttpSettings": { + "id": "" + }, + "httpListener": { + "id": "" + }, + "priority": 250, + "ruleType": "Basic" + } + }, + { + "name": "httpRedirect80-public443", + "properties": { + "httpListener": { + "id": "" + }, + "priority": 300, + "redirectConfiguration": { + "id": "" + }, + "ruleType": "Basic" + } + }, + { + "name": "httpRedirect8080-private4433", + "properties": { + "httpListener": { + "id": "" + }, + "priority": 350, + "redirectConfiguration": { + "id": "" + }, + "rewriteRuleSet": { + "id": "" + }, + "ruleType": "Basic" + } + } + ] + }, + "rewriteRuleSets": { + "value": [ + { + "id": "", + "name": "customRewrite", + "properties": { + "rewriteRules": [ + { + "actionSet": { + "requestHeaderConfigurations": [ + { + "headerName": "Content-Type", + "headerValue": "JSON" + }, + { + "headerName": "someheader" + } + ], + "responseHeaderConfigurations": [] + }, + "conditions": [], + "name": "NewRewrite", + "ruleSequence": 100 + } + ] + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" + } + ] + }, + "sku": { + "value": "WAF_v2" + }, + "sslCertificates": { + "value": [ + { + "name": "az-apgw-x-001-ssl-certificate", + "properties": { + "keyVaultSecretId": "" + } + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + }, + "webApplicationFirewallConfiguration": { + "value": { + "disabledRuleGroups": [ + { + "ruleGroupName": "Known-CVEs" + }, + { + "ruleGroupName": "REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION" + }, + { + "ruleGroupName": "REQUEST-941-APPLICATION-ATTACK-XSS" + } + ], + "enabled": true, + "exclusions": [ + { + "matchVariable": "RequestHeaderNames", + "selector": "hola", + "selectorMatchOperator": "StartsWith" + } + ], + "fileUploadLimitInMb": 100, + "firewallMode": "Detection", + "maxRequestBodySizeInKb": 128, + "requestBodyCheck": true, + "ruleSetType": "OWASP", + "ruleSetVersion": "3.0" + } + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + } + } +} +``` + +
+

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

+ +via Bicep module + +```bicep +module applicationGateway 'br/public:avm/res/network/application-gateway:' = { + name: 'applicationGatewayDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + backendAddressPools: [ + { + name: 'appServiceBackendPool' + properties: { + backendAddresses: [ + { + fqdn: 'aghapp.azurewebsites.net' + } + ] + } + } + { + name: 'privateVmBackendPool' + properties: { + backendAddresses: [ + { + ipAddress: '10.0.0.4' + } + ] + } + } + ] + backendHttpSettingsCollection: [ + { + name: 'appServiceBackendHttpsSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: true + port: 443 + protocol: 'Https' + requestTimeout: 30 + } + } + { + name: 'privateVmHttpSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: false + port: 80 + probe: { + id: '' + } + protocol: 'Http' + requestTimeout: 30 + } + } + ] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + enableHttp2: true + enableTelemetry: '' + frontendIPConfigurations: [ + { + name: 'private' + properties: { + privateIPAddress: '10.0.0.20' + privateIPAllocationMethod: 'Static' + subnet: { + id: '' + } + } + } + { + name: 'public' + properties: { + privateIPAllocationMethod: 'Dynamic' + privateLinkConfiguration: { + id: '' + } + publicIPAddress: { + id: '' + } + } + } + ] + frontendPorts: [ + { + name: 'port443' + properties: { + port: 443 + } + } + { + name: 'port4433' + properties: { + port: 4433 + } + } + { + name: 'port80' + properties: { + port: 80 + } + } + { + name: 'port8080' + properties: { + port: 8080 + } + } + ] + gatewayIPConfigurations: [ + { + name: 'apw-ip-configuration' + properties: { + subnet: { + id: '' + } + } + } + ] + httpListeners: [ + { + name: 'public443' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '' + } + } + } + { + name: 'private4433' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '' + } + } + } + { + name: 'httpRedirect80' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + { + name: 'httpRedirect8080' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + ] + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentities: { + userAssignedResourceIds: [ + '' + ] + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'public' + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + privateLinkConfigurations: [ + { + id: '' + name: 'pvtlink01' + properties: { + ipConfigurations: [ + { + id: '' + name: 'privateLinkIpConfig1' + properties: { + primary: false + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: '' + } + } + } + ] + } + } + ] + probes: [ + { + name: 'privateVmHttpSettingProbe' + properties: { + host: '10.0.0.4' + interval: 60 + match: { + statusCodes: [ + '200' + '401' + ] + } + minServers: 3 + path: '/' + pickHostNameFromBackendHttpSettings: false + protocol: 'Http' + timeout: 15 + unhealthyThreshold: 5 + } + } + ] + redirectConfigurations: [ + { + name: 'httpRedirect80' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '' + } + ] + targetListener: { + id: '' + } + } + } + { + name: 'httpRedirect8080' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '' + } + ] + targetListener: { + id: '' + } + } + } + ] + requestRoutingRules: [ + { + name: 'public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting' + properties: { + backendAddressPool: { + id: '' + } + backendHttpSettings: { + id: '' + } + httpListener: { + id: '' + } + priority: 200 + ruleType: 'Basic' + } + } + { + name: 'private4433-privateVmHttpSetting-privateVmHttpSetting' + properties: { + backendAddressPool: { + id: '' + } + backendHttpSettings: { + id: '' + } + httpListener: { + id: '' + } + priority: 250 + ruleType: 'Basic' + } + } + { + name: 'httpRedirect80-public443' + properties: { + httpListener: { + id: '' + } + priority: 300 + redirectConfiguration: { + id: '' + } + ruleType: 'Basic' + } + } + { + name: 'httpRedirect8080-private4433' + properties: { + httpListener: { + id: '' + } + priority: 350 + redirectConfiguration: { + id: '' + } + rewriteRuleSet: { + id: '' + } + ruleType: 'Basic' + } + } + ] + rewriteRuleSets: [ + { + id: '' + name: 'customRewrite' + properties: { + rewriteRules: [ + { + actionSet: { + requestHeaderConfigurations: [ + { + headerName: 'Content-Type' + headerValue: 'JSON' + } + { + headerName: 'someheader' + } + ] + responseHeaderConfigurations: [] + } + conditions: [] + name: 'NewRewrite' + ruleSequence: 100 + } + ] + } + } + ] + sku: 'WAF_v2' + sslCertificates: [ + { + name: 'az-apgw-x-001-ssl-certificate' + properties: { + keyVaultSecretId: '' + } + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + webApplicationFirewallConfiguration: { + enabled: true + fileUploadLimitInMb: 100 + firewallMode: 'Prevention' + maxRequestBodySizeInKb: 128 + requestBodyCheck: true + ruleSetType: 'OWASP' + ruleSetVersion: '3.0' + } + zones: [ + '1' + '2' + '3' + ] + } +} +``` + +
+

+ +

+ +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 + "backendAddressPools": { + "value": [ + { + "name": "appServiceBackendPool", + "properties": { + "backendAddresses": [ + { + "fqdn": "aghapp.azurewebsites.net" + } + ] + } + }, + { + "name": "privateVmBackendPool", + "properties": { + "backendAddresses": [ + { + "ipAddress": "10.0.0.4" + } + ] + } + } + ] + }, + "backendHttpSettingsCollection": { + "value": [ + { + "name": "appServiceBackendHttpsSetting", + "properties": { + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": true, + "port": 443, + "protocol": "Https", + "requestTimeout": 30 + } + }, + { + "name": "privateVmHttpSetting", + "properties": { + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": false, + "port": 80, + "probe": { + "id": "" + }, + "protocol": "Http", + "requestTimeout": 30 + } + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "enableHttp2": { + "value": true + }, + "enableTelemetry": { + "value": "" + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "private", + "properties": { + "privateIPAddress": "10.0.0.20", + "privateIPAllocationMethod": "Static", + "subnet": { + "id": "" + } + } + }, + { + "name": "public", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "privateLinkConfiguration": { + "id": "" + }, + "publicIPAddress": { + "id": "" + } + } + } + ] + }, + "frontendPorts": { + "value": [ + { + "name": "port443", + "properties": { + "port": 443 + } + }, + { + "name": "port4433", + "properties": { + "port": 4433 + } + }, + { + "name": "port80", + "properties": { + "port": 80 + } + }, + { + "name": "port8080", + "properties": { + "port": 8080 + } + } + ] + }, + "gatewayIPConfigurations": { + "value": [ + { + "name": "apw-ip-configuration", + "properties": { + "subnet": { + "id": "" + } + } + } + ] + }, + "httpListeners": { + "value": [ + { + "name": "public443", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "https", + "requireServerNameIndication": false, + "sslCertificate": { + "id": "" + } + } + }, + { + "name": "private4433", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "https", + "requireServerNameIndication": false, + "sslCertificate": { + "id": "" + } + } + }, + { + "name": "httpRedirect80", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "Http", + "requireServerNameIndication": false + } + }, + { + "name": "httpRedirect8080", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "Http", + "requireServerNameIndication": false + } + } + ] + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "" + ] + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "public", + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + ] + }, + "privateLinkConfigurations": { + "value": [ + { + "id": "", + "name": "pvtlink01", + "properties": { + "ipConfigurations": [ + { + "id": "", + "name": "privateLinkIpConfig1", + "properties": { + "primary": false, + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "" + } + } + } + ] + } + } + ] + }, + "probes": { + "value": [ + { + "name": "privateVmHttpSettingProbe", + "properties": { + "host": "10.0.0.4", + "interval": 60, + "match": { + "statusCodes": [ + "200", + "401" + ] + }, + "minServers": 3, + "path": "/", + "pickHostNameFromBackendHttpSettings": false, + "protocol": "Http", + "timeout": 15, + "unhealthyThreshold": 5 + } + } + ] + }, + "redirectConfigurations": { + "value": [ + { + "name": "httpRedirect80", + "properties": { + "includePath": true, + "includeQueryString": true, + "redirectType": "Permanent", + "requestRoutingRules": [ + { + "id": "" + } + ], + "targetListener": { + "id": "" + } + } + }, + { + "name": "httpRedirect8080", + "properties": { + "includePath": true, + "includeQueryString": true, + "redirectType": "Permanent", + "requestRoutingRules": [ + { + "id": "" + } + ], + "targetListener": { + "id": "" + } + } + } + ] + }, + "requestRoutingRules": { + "value": [ + { + "name": "public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting", + "properties": { + "backendAddressPool": { + "id": "" + }, + "backendHttpSettings": { + "id": "" + }, + "httpListener": { + "id": "" + }, + "priority": 200, + "ruleType": "Basic" + } + }, + { + "name": "private4433-privateVmHttpSetting-privateVmHttpSetting", + "properties": { + "backendAddressPool": { + "id": "" + }, + "backendHttpSettings": { + "id": "" + }, + "httpListener": { + "id": "" + }, + "priority": 250, + "ruleType": "Basic" + } + }, + { + "name": "httpRedirect80-public443", + "properties": { + "httpListener": { + "id": "" + }, + "priority": 300, + "redirectConfiguration": { + "id": "" + }, + "ruleType": "Basic" + } + }, + { + "name": "httpRedirect8080-private4433", + "properties": { + "httpListener": { + "id": "" + }, + "priority": 350, + "redirectConfiguration": { + "id": "" + }, + "rewriteRuleSet": { + "id": "" + }, + "ruleType": "Basic" + } + } + ] + }, + "rewriteRuleSets": { + "value": [ + { + "id": "", + "name": "customRewrite", + "properties": { + "rewriteRules": [ + { + "actionSet": { + "requestHeaderConfigurations": [ + { + "headerName": "Content-Type", + "headerValue": "JSON" + }, + { + "headerName": "someheader" + } + ], + "responseHeaderConfigurations": [] + }, + "conditions": [], + "name": "NewRewrite", + "ruleSequence": 100 + } + ] + } + } + ] + }, + "sku": { + "value": "WAF_v2" + }, + "sslCertificates": { + "value": [ + { + "name": "az-apgw-x-001-ssl-certificate", + "properties": { + "keyVaultSecretId": "" + } + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + }, + "webApplicationFirewallConfiguration": { + "value": { + "enabled": true, + "fileUploadLimitInMb": 100, + "firewallMode": "Prevention", + "maxRequestBodySizeInKb": 128, + "requestBodyCheck": true, + "ruleSetType": "OWASP", + "ruleSetVersion": "3.0" + } + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Name of the Application Gateway. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`authenticationCertificates`](#parameter-authenticationcertificates) | array | Authentication certificates of the application gateway resource. | +| [`autoscaleMaxCapacity`](#parameter-autoscalemaxcapacity) | int | Upper bound on number of Application Gateway capacity. | +| [`autoscaleMinCapacity`](#parameter-autoscalemincapacity) | int | Lower bound on number of Application Gateway capacity. | +| [`backendAddressPools`](#parameter-backendaddresspools) | array | Backend address pool of the application gateway resource. | +| [`backendHttpSettingsCollection`](#parameter-backendhttpsettingscollection) | array | Backend http settings of the application gateway resource. | +| [`backendSettingsCollection`](#parameter-backendsettingscollection) | array | 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). | +| [`capacity`](#parameter-capacity) | int | The number of Application instances to be configured. | +| [`customErrorConfigurations`](#parameter-customerrorconfigurations) | array | Custom error configurations of the application gateway resource. | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`enableFips`](#parameter-enablefips) | bool | Whether FIPS is enabled on the application gateway resource. | +| [`enableHttp2`](#parameter-enablehttp2) | bool | Whether HTTP2 is enabled on the application gateway resource. | +| [`enableRequestBuffering`](#parameter-enablerequestbuffering) | bool | Enable request buffering. | +| [`enableResponseBuffering`](#parameter-enableresponsebuffering) | bool | Enable response buffering. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`firewallPolicyId`](#parameter-firewallpolicyid) | string | The resource ID of an associated firewall policy. Should be configured for security reasons. | +| [`frontendIPConfigurations`](#parameter-frontendipconfigurations) | array | Frontend IP addresses of the application gateway resource. | +| [`frontendPorts`](#parameter-frontendports) | array | Frontend ports of the application gateway resource. | +| [`gatewayIPConfigurations`](#parameter-gatewayipconfigurations) | array | Subnets of the application gateway resource. | +| [`httpListeners`](#parameter-httplisteners) | array | Http listeners of the application gateway resource. | +| [`listeners`](#parameter-listeners) | array | 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). | +| [`loadDistributionPolicies`](#parameter-loaddistributionpolicies) | array | Load distribution policies of the application gateway resource. | +| [`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. | +| [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| [`privateLinkConfigurations`](#parameter-privatelinkconfigurations) | array | PrivateLink configurations on application gateway. | +| [`probes`](#parameter-probes) | array | Probes of the application gateway resource. | +| [`redirectConfigurations`](#parameter-redirectconfigurations) | array | Redirect configurations of the application gateway resource. | +| [`requestRoutingRules`](#parameter-requestroutingrules) | array | Request routing rules of the application gateway resource. | +| [`rewriteRuleSets`](#parameter-rewriterulesets) | array | Rewrite rules for the application gateway resource. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`routingRules`](#parameter-routingrules) | array | Routing rules of the application gateway resource. | +| [`sku`](#parameter-sku) | string | The name of the SKU for the Application Gateway. | +| [`sslCertificates`](#parameter-sslcertificates) | array | SSL certificates of the application gateway resource. | +| [`sslPolicyCipherSuites`](#parameter-sslpolicyciphersuites) | array | Ssl cipher suites to be enabled in the specified order to application gateway. | +| [`sslPolicyMinProtocolVersion`](#parameter-sslpolicyminprotocolversion) | string | Ssl protocol enums. | +| [`sslPolicyName`](#parameter-sslpolicyname) | string | Ssl predefined policy name enums. | +| [`sslPolicyType`](#parameter-sslpolicytype) | string | Type of Ssl Policy. | +| [`sslProfiles`](#parameter-sslprofiles) | array | SSL profiles of the application gateway resource. | +| [`tags`](#parameter-tags) | object | Resource tags. | +| [`trustedClientCertificates`](#parameter-trustedclientcertificates) | array | Trusted client certificates of the application gateway resource. | +| [`trustedRootCertificates`](#parameter-trustedrootcertificates) | array | Trusted Root certificates of the application gateway resource. | +| [`urlPathMaps`](#parameter-urlpathmaps) | array | URL path map of the application gateway resource. | +| [`webApplicationFirewallConfiguration`](#parameter-webapplicationfirewallconfiguration) | object | Application gateway web application firewall configuration. Should be configured for security reasons. | +| [`zones`](#parameter-zones) | array | A list of availability zones denoting where the resource needs to come from. | + +### Parameter: `name` + +Name of the Application Gateway. + +- Required: Yes +- Type: string + +### Parameter: `authenticationCertificates` + +Authentication certificates of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `autoscaleMaxCapacity` + +Upper bound on number of Application Gateway capacity. + +- Required: No +- Type: int +- Default: `-1` + +### Parameter: `autoscaleMinCapacity` + +Lower bound on number of Application Gateway capacity. + +- Required: No +- Type: int +- Default: `-1` + +### Parameter: `backendAddressPools` + +Backend address pool of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `backendHttpSettingsCollection` + +Backend http settings of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `backendSettingsCollection` + +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). + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `capacity` + +The number of Application instances to be configured. + +- Required: No +- Type: int +- Default: `2` + +### Parameter: `customErrorConfigurations` + +Custom error configurations of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | 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`](#parameter-diagnosticsettingseventhubname) | string | 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. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`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. | +| [`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` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | +| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.metricCategories` + +The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingsmetriccategoriescategory) | string | Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enabled`](#parameter-diagnosticsettingsmetriccategoriesenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.metricCategories.category` + +Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. + +- Required: Yes +- Type: string + +### Parameter: `diagnosticSettings.metricCategories.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +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. + +- Required: No +- Type: string + +### 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. + +- Required: No +- Type: string + +### Parameter: `enableFips` + +Whether FIPS is enabled on the application gateway resource. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `enableHttp2` + +Whether HTTP2 is enabled on the application gateway resource. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `enableRequestBuffering` + +Enable request buffering. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `enableResponseBuffering` + +Enable response buffering. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `firewallPolicyId` + +The resource ID of an associated firewall policy. Should be configured for security reasons. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `frontendIPConfigurations` + +Frontend IP addresses of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `frontendPorts` + +Frontend ports of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `gatewayIPConfigurations` + +Subnets of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `httpListeners` + +Http listeners of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `listeners` + +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). + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `loadDistributionPolicies` + +Load distribution policies of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### 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. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. | + +### Parameter: `managedIdentities.userAssignedResourceIds` + +The resource ID(s) to assign to the resource. + +- Required: Yes +- Type: array + +### Parameter: `privateEndpoints` + +Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`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** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`applicationSecurityGroupResourceIds`](#parameter-privateendpointsapplicationsecuritygroupresourceids) | array | Application security groups in which the private endpoint IP configuration is included. | +| [`customDnsConfigs`](#parameter-privateendpointscustomdnsconfigs) | array | Custom DNS configurations. | +| [`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 | 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. | +| [`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 subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file". + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.subnetResourceId` + +Resource ID of the subnet where the endpoint needs to be created. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.applicationSecurityGroupResourceIds` + +Application security groups in which the private endpoint IP configuration is included. + +- Required: No +- Type: array + +### Parameter: `privateEndpoints.customDnsConfigs` + +Custom DNS configurations. + +- Required: No +- Type: array + +**Required parameters** + +| 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. | + +### Parameter: `privateEndpoints.customDnsConfigs.fqdn` + +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. + +- Required: Yes +- Type: array + +### Parameter: `privateEndpoints.customNetworkInterfaceName` + +The custom name of the network interface attached to the private endpoint. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool + +### Parameter: `privateEndpoints.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-privateendpointsipconfigurationsname) | string | The name of the resource that is unique within a resource group. | +| [`properties`](#parameter-privateendpointsipconfigurationsproperties) | object | Properties of private endpoint IP configurations. | + +### Parameter: `privateEndpoints.ipConfigurations.name` + +The name of the resource that is unique within a resource group. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.ipConfigurations.properties` + +Properties of private endpoint IP configurations. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`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. | + +### Parameter: `privateEndpoints.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: `privateEndpoints.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: `privateEndpoints.ipConfigurations.properties.privateIPAddress` + +A private IP address obtained from the private endpoint's subnet. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.isManualConnection` + +If Manual Private Link Connection is required. + +- Required: No +- Type: bool + +### Parameter: `privateEndpoints.location` + +The location to deploy the private endpoint to. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.lock` + +Specify the type of lock. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-privateendpointslockkind) | string | Specify the type of lock. | +| [`name`](#parameter-privateendpointslockname) | string | Specify the name of lock. | + +### Parameter: `privateEndpoints.lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `privateEndpoints.lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.manualConnectionRequestMessage` + +A message passed to the owner of the remote resource with the manual connection request. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.name` + +The name of the private endpoint. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroupName` + +The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneResourceIds` + +The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. + +- Required: No +- Type: array + +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- 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` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-privateendpointsroleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-privateendpointsroleassignmentsroledefinitionidorname) | 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-privateendpointsroleassignmentscondition) | 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-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. | +| [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `privateEndpoints.roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.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: `privateEndpoints.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: `privateEndpoints.roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `privateEndpoints.roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `privateEndpoints.tags` + +Tags to be applied on all resources/resource groups in this deployment. + +- Required: No +- Type: object + +### Parameter: `privateLinkConfigurations` + +PrivateLink configurations on application gateway. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `probes` + +Probes of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `redirectConfigurations` + +Redirect configurations of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `requestRoutingRules` + +Request routing rules of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `rewriteRuleSets` + +Rewrite rules for the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- 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. | +| [`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.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `routingRules` + +Routing rules of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `sku` + +The name of the SKU for the Application Gateway. + +- Required: No +- Type: string +- Default: `'WAF_v2'` +- Allowed: + ```Bicep + [ + 'Standard_Large' + 'Standard_Medium' + 'Standard_Small' + 'Standard_v2' + 'WAF_Large' + 'WAF_Medium' + 'WAF_v2' + ] + ``` + +### Parameter: `sslCertificates` + +SSL certificates of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `sslPolicyCipherSuites` + +Ssl cipher suites to be enabled in the specified order to application gateway. + +- Required: No +- Type: array +- Default: + ```Bicep + [ + 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' + 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384' + ] + ``` +- Allowed: + ```Bicep + [ + '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' + ] + ``` + +### Parameter: `sslPolicyMinProtocolVersion` + +Ssl protocol enums. + +- Required: No +- Type: string +- Default: `'TLSv1_2'` +- Allowed: + ```Bicep + [ + 'TLSv1_0' + 'TLSv1_1' + 'TLSv1_2' + 'TLSv1_3' + ] + ``` + +### Parameter: `sslPolicyName` + +Ssl predefined policy name enums. + +- Required: No +- Type: string +- Default: `''` +- Allowed: + ```Bicep + [ + '' + 'AppGwSslPolicy20150501' + 'AppGwSslPolicy20170401' + 'AppGwSslPolicy20170401S' + 'AppGwSslPolicy20220101' + 'AppGwSslPolicy20220101S' + ] + ``` + +### Parameter: `sslPolicyType` + +Type of Ssl Policy. + +- Required: No +- Type: string +- Default: `'Custom'` +- Allowed: + ```Bicep + [ + 'Custom' + 'CustomV2' + 'Predefined' + ] + ``` + +### Parameter: `sslProfiles` + +SSL profiles of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `tags` + +Resource tags. + +- Required: No +- Type: object + +### Parameter: `trustedClientCertificates` + +Trusted client certificates of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `trustedRootCertificates` + +Trusted Root certificates of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `urlPathMaps` + +URL path map of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `webApplicationFirewallConfiguration` + +Application gateway web application firewall configuration. Should be configured for security reasons. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `zones` + +A list of availability zones denoting where the resource needs to come from. + +- Required: No +- Type: array +- Default: `[]` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the application gateway. | +| `resourceGroupName` | string | The resource group the application gateway was deployed into. | +| `resourceId` | string | The resource ID of the application gateway. | + +## 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/network/private-endpoint:0.4.1` | 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/res/network/application-gateway/main.bicep b/avm/res/network/application-gateway/main.bicep new file mode 100644 index 0000000000..6aa9a00ea9 --- /dev/null +++ b/avm/res/network/application-gateway/main.bicep @@ -0,0 +1,616 @@ +metadata name = 'Network Application Gateways' +metadata description = 'This module deploys a Network Application Gateway.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Name of the Application Gateway.') +@maxLength(80) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. The managed identity definition for this resource.') +param managedIdentities managedIdentitiesType + +@description('Optional. Authentication certificates of the application gateway resource.') +param authenticationCertificates array = [] + +@description('Optional. Upper bound on number of Application Gateway capacity.') +param autoscaleMaxCapacity int = -1 + +@description('Optional. Lower bound on number of Application Gateway capacity.') +param autoscaleMinCapacity int = -1 + +@description('Optional. Backend address pool of the application gateway resource.') +param backendAddressPools array = [] + +@description('Optional. Backend http settings of the application gateway resource.') +param backendHttpSettingsCollection array = [] + +@description('Optional. Custom error configurations of the application gateway resource.') +param customErrorConfigurations array = [] + +@description('Optional. Whether FIPS is enabled on the application gateway resource.') +param enableFips bool = false + +@description('Optional. Whether HTTP2 is enabled on the application gateway resource.') +param enableHttp2 bool = false + +@description('Optional. The resource ID of an associated firewall policy. Should be configured for security reasons.') +param firewallPolicyId string = '' + +@description('Optional. Frontend IP addresses of the application gateway resource.') +param frontendIPConfigurations array = [] + +@description('Optional. Frontend ports of the application gateway resource.') +param frontendPorts array = [] + +@description('Optional. Subnets of the application gateway resource.') +param gatewayIPConfigurations array = [] + +@description('Optional. Enable request buffering.') +param enableRequestBuffering bool = false + +@description('Optional. Enable response buffering.') +param enableResponseBuffering bool = false + +@description('Optional. Http listeners of the application gateway resource.') +param httpListeners array = [] + +@description('Optional. Load distribution policies of the application gateway resource.') +param loadDistributionPolicies array = [] + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints privateEndpointType + +@description('Optional. PrivateLink configurations on application gateway.') +param privateLinkConfigurations array = [] + +@description('Optional. Probes of the application gateway resource.') +param probes array = [] + +@description('Optional. Redirect configurations of the application gateway resource.') +param redirectConfigurations array = [] + +@description('Optional. Request routing rules of the application gateway resource.') +param requestRoutingRules array = [] + +@description('Optional. Rewrite rules for the application gateway resource.') +param rewriteRuleSets array = [] + +@description('Optional. The name of the SKU for the Application Gateway.') +@allowed([ + 'Standard_Small' + 'Standard_Medium' + 'Standard_Large' + 'WAF_Medium' + 'WAF_Large' + 'Standard_v2' + 'WAF_v2' +]) +param sku string = 'WAF_v2' + +@description('Optional. The number of Application instances to be configured.') +@minValue(0) +@maxValue(10) +param capacity int = 2 + +@description('Optional. SSL certificates of the application gateway resource.') +param sslCertificates array = [] + +@description('Optional. Ssl cipher suites to be enabled in the specified order to application gateway.') +@allowed([ + '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' +]) +param sslPolicyCipherSuites array = [ + 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384' + 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' +] + +@description('Optional. Ssl protocol enums.') +@allowed([ + 'TLSv1_0' + 'TLSv1_1' + 'TLSv1_2' + 'TLSv1_3' +]) +param sslPolicyMinProtocolVersion string = 'TLSv1_2' + +@description('Optional. Ssl predefined policy name enums.') +@allowed([ + 'AppGwSslPolicy20150501' + 'AppGwSslPolicy20170401' + 'AppGwSslPolicy20170401S' + 'AppGwSslPolicy20220101' + 'AppGwSslPolicy20220101S' + '' +]) +param sslPolicyName string = '' + +@description('Optional. Type of Ssl Policy.') +@allowed([ + 'Custom' + 'CustomV2' + 'Predefined' +]) +param sslPolicyType string = 'Custom' + +@description('Optional. SSL profiles of the application gateway resource.') +param sslProfiles array = [] + +@description('Optional. Trusted client certificates of the application gateway resource.') +param trustedClientCertificates array = [] + +@description('Optional. Trusted Root certificates of the application gateway resource.') +param trustedRootCertificates array = [] + +@description('Optional. URL path map of the application gateway resource.') +param urlPathMaps array = [] + +@description('Optional. Application gateway web application firewall configuration. Should be configured for security reasons.') +param webApplicationFirewallConfiguration object = {} + +@description('Optional. A list of availability zones denoting where the resource needs to come from.') +param zones array = [] + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType + +var formattedUserAssignedIdentities = reduce( + map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), + {}, + (cur, next) => union(cur, next) +) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} } + +var identity = !empty(managedIdentities) + ? { + type: !empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : null + userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null + } + : null + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Optional. Resource tags.') +param tags object? + +@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).') +param backendSettingsCollection array = [] + +@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).') +param listeners array = [] + +@description('Optional. Routing rules of the application gateway resource.') +param routingRules array = [] + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +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( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.res.network-appgw.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } + } + +resource applicationGateway 'Microsoft.Network/applicationGateways@2023-04-01' = { + name: name + location: location + tags: tags + identity: identity + properties: union( + { + authenticationCertificates: authenticationCertificates + autoscaleConfiguration: autoscaleMaxCapacity > 0 && autoscaleMinCapacity >= 0 + ? { + maxCapacity: autoscaleMaxCapacity + minCapacity: autoscaleMinCapacity + } + : null + backendAddressPools: backendAddressPools + backendHttpSettingsCollection: backendHttpSettingsCollection + backendSettingsCollection: backendSettingsCollection + customErrorConfigurations: customErrorConfigurations + enableHttp2: enableHttp2 + firewallPolicy: !empty(firewallPolicyId) + ? { + id: firewallPolicyId + } + : null + forceFirewallPolicyAssociation: !empty(firewallPolicyId) + frontendIPConfigurations: frontendIPConfigurations + frontendPorts: frontendPorts + gatewayIPConfigurations: gatewayIPConfigurations + globalConfiguration: endsWith(sku, 'v2') + ? { + enableRequestBuffering: enableRequestBuffering + enableResponseBuffering: enableResponseBuffering + } + : null + httpListeners: httpListeners + loadDistributionPolicies: loadDistributionPolicies + listeners: listeners + privateLinkConfigurations: privateLinkConfigurations + probes: probes + redirectConfigurations: redirectConfigurations + requestRoutingRules: requestRoutingRules + routingRules: routingRules + rewriteRuleSets: rewriteRuleSets + sku: { + name: sku + tier: endsWith(sku, 'v2') ? sku : substring(sku, 0, indexOf(sku, '_')) + capacity: autoscaleMaxCapacity > 0 && autoscaleMinCapacity >= 0 ? null : capacity + } + sslCertificates: sslCertificates + sslPolicy: sslPolicyType != 'Predefined' + ? { + cipherSuites: sslPolicyCipherSuites + minProtocolVersion: sslPolicyMinProtocolVersion + policyName: empty(sslPolicyName) ? null : sslPolicyName + policyType: sslPolicyType + } + : { + policyName: empty(sslPolicyName) ? null : sslPolicyName + policyType: sslPolicyType + } + sslProfiles: sslProfiles + trustedClientCertificates: trustedClientCertificates + trustedRootCertificates: trustedRootCertificates + urlPathMaps: urlPathMaps + }, + (enableFips + ? { + enableFips: enableFips + } + : {}), + (!empty(webApplicationFirewallConfiguration) + ? { webApplicationFirewallConfiguration: webApplicationFirewallConfiguration } + : {}) + ) + zones: zones +} + +resource applicationGateway_lock 'Microsoft.Authorization/locks@2020-05-01' = + if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' + ? 'Cannot delete resource or child resources.' + : 'Cannot delete or modify the resource or child resources.' + } + scope: applicationGateway + } + +resource applicationGateway_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + metrics: [ + for group in (diagnosticSetting.?metricCategories ?? [{ category: 'AllMetrics' }]): { + category: group.category + enabled: group.?enabled ?? true + timeGrain: null + } + ] + logs: [ + for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' }]): { + categoryGroup: group.?categoryGroup + category: group.?category + enabled: group.?enabled ?? true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: applicationGateway + } +] + +module applicationGateway_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ + for (privateEndpoint, index) in (privateEndpoints ?? []): { + name: '${uniqueString(deployment().name, location)}-applicationGateway-PrivateEndpoint-${index}' + scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') + params: { + name: privateEndpoint.?name ?? 'pep-${last(split(applicationGateway.id, '/'))}-${privateEndpoint.service}-${index}' + privateLinkServiceConnections: privateEndpoint.?isManualConnection != true + ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(applicationGateway.id, '/'))}-${privateEndpoint.service}-${index}' + properties: { + privateLinkServiceId: applicationGateway.id + groupIds: [ + privateEndpoint.service + ] + } + } + ] + : null + manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true + ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(applicationGateway.id, '/'))}-${privateEndpoint.service}-${index}' + properties: { + privateLinkServiceId: applicationGateway.id + 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 + privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName + privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + roleAssignments: privateEndpoint.?roleAssignments + tags: privateEndpoint.?tags ?? tags + customDnsConfigs: privateEndpoint.?customDnsConfigs + ipConfigurations: privateEndpoint.?ipConfigurations + applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds + customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName + } + } +] + +resource applicationGateway_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(applicationGateway.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 + } + scope: applicationGateway + } +] + +@description('The name of the application gateway.') +output name string = applicationGateway.name + +@description('The resource ID of the application gateway.') +output resourceId string = applicationGateway.id + +@description('The resource group the application gateway was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = applicationGateway.location + +// =============== // +// Definitions // +// =============== // + +type managedIdentitiesType = { + @description('Optional. The resource ID(s) to assign to the resource.') + userAssignedResourceIds: string[] +}? + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type roleAssignmentType = { + @description('Required. The role to assign. You can provide either the display name of the 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 + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? + + @description('Optional. The description of the role assignment.') + description: string? + + @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +}[]? + +type privateEndpointType = { + @description('Optional. The name of the private endpoint.') + name: string? + + @description('Optional. The location to deploy the private endpoint to.') + location: string? + + @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 groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneResourceIds: 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.') + @maxLength(140) + manualConnectionRequestMessage: string? + + @description('Optional. Custom DNS configurations.') + customDnsConfigs: { + @description('Required. Fqdn that resolves to private endpoint IP address.') + fqdn: string? + + @description('Required. A list of private IP addresses of the private endpoint.') + ipAddresses: string[] + }[]? + + @description('Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints.') + ipConfigurations: { + @description('Required. The name of the resource that is unique within a resource group.') + name: string + + @description('Required. Properties of private endpoint IP configurations.') + properties: { + @description('Required. The ID of a group obtained from the remote resource that this private endpoint should connect to.') + groupId: string + + @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.') + privateIPAddress: string + } + }[]? + + @description('Optional. Application security groups in which the private endpoint IP configuration is included.') + applicationSecurityGroupResourceIds: string[]? + + @description('Optional. The custom name of the network interface attached to the private endpoint.') + customNetworkInterfaceName: string? + + @description('Optional. Specify the type of lock.') + lock: lockType + + @description('Optional. Array of role assignments to create.') + roleAssignments: roleAssignmentType + + @description('Optional. Tags to be applied on all resources/resource groups in this deployment.') + tags: object? + + @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 diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @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.') + categoryGroup: string? + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection.') + metricCategories: { + @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.') + category: string + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @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')? + + @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? + + @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.') + storageAccountResourceId: string? + + @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.') + eventHubAuthorizationRuleResourceId: string? + + @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.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +}[]? diff --git a/avm/res/network/application-gateway/main.json b/avm/res/network/application-gateway/main.json new file mode 100644 index 0000000000..d775da8b70 --- /dev/null +++ b/avm/res/network/application-gateway/main.json @@ -0,0 +1,1633 @@ +{ + "$schema": "https://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": "12167944734183128583" + }, + "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('-..--..-', '.', '-'), 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]" + } + } +} \ No newline at end of file diff --git a/avm/res/network/application-gateway/tests/e2e/defaults/dependencies.bicep b/avm/res/network/application-gateway/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..4f1950dc66 --- /dev/null +++ b/avm/res/network/application-gateway/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,60 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Public IP to create.') +param publicIPName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + +resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { + name: publicIPName + location: location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAllocationMethod: 'Static' + } + zones: [ + '1' + '2' + '3' + ] +} + +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) + } + } + { + name: 'privateLinkSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 1) + privateLinkServiceNetworkPolicies: 'Disabled' + } + } + ] + } +} + +@description('The resource ID of the created Virtual Network default subnet.') +output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Public IP.') +output publicIPResourceId string = publicIP.id diff --git a/avm/res/network/application-gateway/tests/e2e/defaults/main.test.bicep b/avm/res/network/application-gateway/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..0998d2c453 --- /dev/null +++ b/avm/res/network/application-gateway/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,146 @@ +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) +// e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' +param resourceGroupName string = 'dep-${namePrefix}-network.applicationgateway-${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 = 'nagmin' + +@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: { + publicIPName: 'dep-${namePrefix}-pip-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +var resourceName = '${namePrefix}${serviceShort}001' + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + // You parameters go here + name: resourceName + zones: [ + '1' + '2' + '3' + ] + location: resourceLocation + gatewayIPConfigurations: [ + { + name: 'publicIPConfig1' + properties: { + subnet: { + id: nestedDependencies.outputs.defaultSubnetResourceId + } + } + } + ] + frontendIPConfigurations: [ + { + name: 'frontendIPConfig1' + properties: { + publicIPAddress: { + id: nestedDependencies.outputs.publicIPResourceId + } + } + } + ] + frontendPorts: [ + { + name: 'frontendPort1' + properties: { + port: 80 + } + } + ] + backendAddressPools: [ + { + name: 'backendAddressPool1' + } + ] + backendHttpSettingsCollection: [ + { + name: 'backendHttpSettings1' + properties: { + port: 80 + protocol: 'Http' + cookieBasedAffinity: 'Disabled' + } + } + ] + httpListeners: [ + { + name: 'httpListener1' + properties: { + hostName: 'www.contoso.com' + protocol: 'Http' + frontendIPConfiguration: { + id: '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${resourceName}/frontendIPConfigurations/frontendIPConfig1' + } + frontendPort: { + id: '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${resourceName}/frontendPorts/frontendPort1' + } + } + } + ] + requestRoutingRules: [ + { + name: 'requestRoutingRule1' + properties: { + ruleType: 'Basic' + priority: 100 + httpListener: { + id: '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${resourceName}/httpListeners/httpListener1' + } + backendAddressPool: { + id: '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${resourceName}/backendAddressPools/backendAddressPool1' + } + backendHttpSettings: { + id: '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${resourceName}/backendHttpSettingsCollection/backendHttpSettings1' + } + } + } + ] + webApplicationFirewallConfiguration: { + enabled: false + } + } + } +] diff --git a/avm/res/network/application-gateway/tests/e2e/max/dependencies.bicep b/avm/res/network/application-gateway/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..2a9c2b0fae --- /dev/null +++ b/avm/res/network/application-gateway/tests/e2e/max/dependencies.bicep @@ -0,0 +1,154 @@ +@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 + +@description('Required. The name of the Public IP to create.') +param publicIPName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Deployment Script to create for the Certificate generation.') +param certDeploymentScriptName 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) + } + } + { + name: 'privateLinkSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 1) + privateLinkServiceNetworkPolicies: 'Disabled' + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.appgateway.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { + name: publicIPName + location: location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAllocationMethod: 'Static' + } + zones: [ + '1' + '2' + '3' + ] +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${managedIdentity.name}-KeyVault-Admin-RoleAssignment') + scope: keyVault + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '00482a5a-887f-4fb3-b363-3b7fe8e74483' + ) // Key Vault Administrator + principalType: 'ServicePrincipal' + } +} + +resource certDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: certDeploymentScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-KeyVaultName "${keyVault.name}" -CertName "applicationGatewaySslCertificate"' + scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Set-CertificateInKeyVault.ps1') + } +} + +@description('The resource ID of the created Virtual Network default subnet.') +output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Virtual Network private link subnet.') +output privateLinkSubnetResourceId string = virtualNetwork.properties.subnets[1].id + +@description('The resource ID of the created Public IP.') +output publicIPResourceId string = publicIP.id + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The URL of the created certificate.') +output certificateSecretUrl string = certDeploymentScript.properties.outputs.secretUrl + +@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/network/application-gateway/tests/e2e/max/main.test.bicep b/avm/res/network/application-gateway/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..7e67afba91 --- /dev/null +++ b/avm/res/network/application-gateway/tests/e2e/max/main.test.bicep @@ -0,0 +1,524 @@ +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}-network.applicationgateways-${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 = 'nagmax' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableTelemetry bool = true + +@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 +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + publicIPName: 'dep-${namePrefix}-pip-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + certDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-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 + } +} + +// ============== // +// Test Execution // +// ============== // + +var appGWName = '${namePrefix}${serviceShort}001' +var appGWExpectedResourceID = '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${appGWName}' +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + enableTelemetry: enableTelemetry + name: appGWName + zones: [ + '1' + '2' + '3' + ] + backendAddressPools: [ + { + name: 'appServiceBackendPool' + properties: { + backendAddresses: [ + { + fqdn: 'aghapp.azurewebsites.net' + } + ] + } + } + { + name: 'privateVmBackendPool' + properties: { + backendAddresses: [ + { + ipAddress: '10.0.0.4' + } + ] + } + } + ] + backendHttpSettingsCollection: [ + { + name: 'appServiceBackendHttpsSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: true + port: 443 + protocol: 'Https' + requestTimeout: 30 + } + } + { + name: 'privateVmHttpSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: false + port: 80 + probe: { + id: '${appGWExpectedResourceID}/probes/privateVmHttpSettingProbe' + } + protocol: 'Http' + requestTimeout: 30 + } + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + enableHttp2: true + privateLinkConfigurations: [ + { + name: 'pvtlink01' + id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01' + properties: { + ipConfigurations: [ + { + name: 'privateLinkIpConfig1' + id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01/ipConfigurations/privateLinkIpConfig1' + properties: { + privateIPAllocationMethod: 'Dynamic' + primary: false + subnet: { + id: nestedDependencies.outputs.privateLinkSubnetResourceId + } + } + } + ] + } + } + ] + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + service: 'public' + subnetResourceId: nestedDependencies.outputs.privateLinkSubnetResourceId + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + frontendIPConfigurations: [ + { + name: 'private' + properties: { + privateIPAddress: '10.0.0.20' + privateIPAllocationMethod: 'Static' + subnet: { + id: nestedDependencies.outputs.defaultSubnetResourceId + } + } + } + { + name: 'public' + properties: { + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: { + id: nestedDependencies.outputs.publicIPResourceId + } + privateLinkConfiguration: { + id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01' + } + } + } + ] + frontendPorts: [ + { + name: 'port443' + properties: { + port: 443 + } + } + { + name: 'port4433' + properties: { + port: 4433 + } + } + { + name: 'port80' + properties: { + port: 80 + } + } + { + name: 'port8080' + properties: { + port: 8080 + } + } + ] + gatewayIPConfigurations: [ + { + name: 'apw-ip-configuration' + properties: { + subnet: { + id: nestedDependencies.outputs.defaultSubnetResourceId + } + } + } + ] + httpListeners: [ + { + name: 'public443' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/public' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port443' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '${appGWExpectedResourceID}/sslCertificates/${namePrefix}-az-apgw-x-001-ssl-certificate' + } + } + } + { + name: 'private4433' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/private' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port4433' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '${appGWExpectedResourceID}/sslCertificates/${namePrefix}-az-apgw-x-001-ssl-certificate' + } + } + } + { + name: 'httpRedirect80' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/public' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port80' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + { + name: 'httpRedirect8080' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/private' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port8080' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + probes: [ + { + name: 'privateVmHttpSettingProbe' + properties: { + host: '10.0.0.4' + interval: 60 + match: { + statusCodes: [ + '200' + '401' + ] + } + minServers: 3 + path: '/' + pickHostNameFromBackendHttpSettings: false + protocol: 'Http' + timeout: 15 + unhealthyThreshold: 5 + } + } + ] + redirectConfigurations: [ + { + name: 'httpRedirect80' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '${appGWExpectedResourceID}/requestRoutingRules/httpRedirect80-public443' + } + ] + targetListener: { + id: '${appGWExpectedResourceID}/httpListeners/public443' + } + } + } + { + name: 'httpRedirect8080' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '${appGWExpectedResourceID}/requestRoutingRules/httpRedirect8080-private4433' + } + ] + targetListener: { + id: '${appGWExpectedResourceID}/httpListeners/private4433' + } + } + } + ] + requestRoutingRules: [ + { + name: 'public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting' + properties: { + backendAddressPool: { + id: '${appGWExpectedResourceID}/backendAddressPools/appServiceBackendPool' + } + backendHttpSettings: { + id: '${appGWExpectedResourceID}/backendHttpSettingsCollection/appServiceBackendHttpsSetting' + } + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/public443' + } + priority: 200 + ruleType: 'Basic' + } + } + { + name: 'private4433-privateVmHttpSetting-privateVmHttpSetting' + properties: { + backendAddressPool: { + id: '${appGWExpectedResourceID}/backendAddressPools/privateVmBackendPool' + } + backendHttpSettings: { + id: '${appGWExpectedResourceID}/backendHttpSettingsCollection/privateVmHttpSetting' + } + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/private4433' + } + priority: 250 + ruleType: 'Basic' + } + } + { + name: 'httpRedirect80-public443' + properties: { + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/httpRedirect80' + } + priority: 300 + redirectConfiguration: { + id: '${appGWExpectedResourceID}/redirectConfigurations/httpRedirect80' + } + ruleType: 'Basic' + } + } + { + name: 'httpRedirect8080-private4433' + properties: { + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/httpRedirect8080' + } + priority: 350 + redirectConfiguration: { + id: '${appGWExpectedResourceID}/redirectConfigurations/httpRedirect8080' + } + ruleType: 'Basic' + rewriteRuleSet: { + id: '${appGWExpectedResourceID}/rewriteRuleSets/customRewrite' + } + } + } + ] + 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' + } + ] + sku: 'WAF_v2' + sslCertificates: [ + { + name: '${namePrefix}-az-apgw-x-001-ssl-certificate' + properties: { + keyVaultSecretId: nestedDependencies.outputs.certificateSecretUrl + } + } + ] + managedIdentities: { + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + rewriteRuleSets: [ + { + name: 'customRewrite' + id: '${appGWExpectedResourceID}/rewriteRuleSets/customRewrite' + properties: { + rewriteRules: [ + { + ruleSequence: 100 + conditions: [] + name: 'NewRewrite' + actionSet: { + requestHeaderConfigurations: [ + { + headerName: 'Content-Type' + headerValue: 'JSON' + } + { + headerName: 'someheader' + } + ] + responseHeaderConfigurations: [] + } + } + ] + } + } + ] + webApplicationFirewallConfiguration: { + enabled: true + fileUploadLimitInMb: 100 + firewallMode: 'Detection' + maxRequestBodySizeInKb: 128 + requestBodyCheck: true + ruleSetType: 'OWASP' + ruleSetVersion: '3.0' + disabledRuleGroups: [ + { + ruleGroupName: 'Known-CVEs' + } + { + ruleGroupName: 'REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION' + } + { + ruleGroupName: 'REQUEST-941-APPLICATION-ATTACK-XSS' + } + ] + exclusions: [ + { + matchVariable: 'RequestHeaderNames' + selectorMatchOperator: 'StartsWith' + selector: 'hola' + } + ] + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +] diff --git a/avm/res/network/application-gateway/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/network/application-gateway/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..2a9c2b0fae --- /dev/null +++ b/avm/res/network/application-gateway/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,154 @@ +@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 + +@description('Required. The name of the Public IP to create.') +param publicIPName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Deployment Script to create for the Certificate generation.') +param certDeploymentScriptName 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) + } + } + { + name: 'privateLinkSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 1) + privateLinkServiceNetworkPolicies: 'Disabled' + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.appgateway.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { + name: publicIPName + location: location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAllocationMethod: 'Static' + } + zones: [ + '1' + '2' + '3' + ] +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${managedIdentity.name}-KeyVault-Admin-RoleAssignment') + scope: keyVault + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '00482a5a-887f-4fb3-b363-3b7fe8e74483' + ) // Key Vault Administrator + principalType: 'ServicePrincipal' + } +} + +resource certDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: certDeploymentScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-KeyVaultName "${keyVault.name}" -CertName "applicationGatewaySslCertificate"' + scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Set-CertificateInKeyVault.ps1') + } +} + +@description('The resource ID of the created Virtual Network default subnet.') +output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Virtual Network private link subnet.') +output privateLinkSubnetResourceId string = virtualNetwork.properties.subnets[1].id + +@description('The resource ID of the created Public IP.') +output publicIPResourceId string = publicIP.id + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The URL of the created certificate.') +output certificateSecretUrl string = certDeploymentScript.properties.outputs.secretUrl + +@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/network/application-gateway/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/application-gateway/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..c6f9dede4f --- /dev/null +++ b/avm/res/network/application-gateway/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,486 @@ +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}-network.applicationgateways-${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 = 'nagwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableTelemetry bool = true + +@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 +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + publicIPName: 'dep-${namePrefix}-pip-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + certDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-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 + } +} + +// ============== // +// Test Execution // +// ============== // + +var appGWName = '${namePrefix}${serviceShort}001' +var appGWExpectedResourceID = '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${appGWName}' +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + enableTelemetry: enableTelemetry + name: appGWName + zones: [ + '1' + '2' + '3' + ] + backendAddressPools: [ + { + name: 'appServiceBackendPool' + properties: { + backendAddresses: [ + { + fqdn: 'aghapp.azurewebsites.net' + } + ] + } + } + { + name: 'privateVmBackendPool' + properties: { + backendAddresses: [ + { + ipAddress: '10.0.0.4' + } + ] + } + } + ] + backendHttpSettingsCollection: [ + { + name: 'appServiceBackendHttpsSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: true + port: 443 + protocol: 'Https' + requestTimeout: 30 + } + } + { + name: 'privateVmHttpSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: false + port: 80 + probe: { + id: '${appGWExpectedResourceID}/probes/privateVmHttpSettingProbe' + } + protocol: 'Http' + requestTimeout: 30 + } + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + enableHttp2: true + privateLinkConfigurations: [ + { + name: 'pvtlink01' + id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01' + properties: { + ipConfigurations: [ + { + name: 'privateLinkIpConfig1' + id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01/ipConfigurations/privateLinkIpConfig1' + properties: { + privateIPAllocationMethod: 'Dynamic' + primary: false + subnet: { + id: nestedDependencies.outputs.privateLinkSubnetResourceId + } + } + } + ] + } + } + ] + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + service: 'public' + subnetResourceId: nestedDependencies.outputs.privateLinkSubnetResourceId + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + frontendIPConfigurations: [ + { + name: 'private' + properties: { + privateIPAddress: '10.0.0.20' + privateIPAllocationMethod: 'Static' + subnet: { + id: nestedDependencies.outputs.defaultSubnetResourceId + } + } + } + { + name: 'public' + properties: { + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: { + id: nestedDependencies.outputs.publicIPResourceId + } + privateLinkConfiguration: { + id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01' + } + } + } + ] + frontendPorts: [ + { + name: 'port443' + properties: { + port: 443 + } + } + { + name: 'port4433' + properties: { + port: 4433 + } + } + { + name: 'port80' + properties: { + port: 80 + } + } + { + name: 'port8080' + properties: { + port: 8080 + } + } + ] + gatewayIPConfigurations: [ + { + name: 'apw-ip-configuration' + properties: { + subnet: { + id: nestedDependencies.outputs.defaultSubnetResourceId + } + } + } + ] + httpListeners: [ + { + name: 'public443' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/public' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port443' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '${appGWExpectedResourceID}/sslCertificates/${namePrefix}-az-apgw-x-001-ssl-certificate' + } + } + } + { + name: 'private4433' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/private' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port4433' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '${appGWExpectedResourceID}/sslCertificates/${namePrefix}-az-apgw-x-001-ssl-certificate' + } + } + } + { + name: 'httpRedirect80' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/public' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port80' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + { + name: 'httpRedirect8080' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/private' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port8080' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + probes: [ + { + name: 'privateVmHttpSettingProbe' + properties: { + host: '10.0.0.4' + interval: 60 + match: { + statusCodes: [ + '200' + '401' + ] + } + minServers: 3 + path: '/' + pickHostNameFromBackendHttpSettings: false + protocol: 'Http' + timeout: 15 + unhealthyThreshold: 5 + } + } + ] + redirectConfigurations: [ + { + name: 'httpRedirect80' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '${appGWExpectedResourceID}/requestRoutingRules/httpRedirect80-public443' + } + ] + targetListener: { + id: '${appGWExpectedResourceID}/httpListeners/public443' + } + } + } + { + name: 'httpRedirect8080' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '${appGWExpectedResourceID}/requestRoutingRules/httpRedirect8080-private4433' + } + ] + targetListener: { + id: '${appGWExpectedResourceID}/httpListeners/private4433' + } + } + } + ] + requestRoutingRules: [ + { + name: 'public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting' + properties: { + backendAddressPool: { + id: '${appGWExpectedResourceID}/backendAddressPools/appServiceBackendPool' + } + backendHttpSettings: { + id: '${appGWExpectedResourceID}/backendHttpSettingsCollection/appServiceBackendHttpsSetting' + } + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/public443' + } + priority: 200 + ruleType: 'Basic' + } + } + { + name: 'private4433-privateVmHttpSetting-privateVmHttpSetting' + properties: { + backendAddressPool: { + id: '${appGWExpectedResourceID}/backendAddressPools/privateVmBackendPool' + } + backendHttpSettings: { + id: '${appGWExpectedResourceID}/backendHttpSettingsCollection/privateVmHttpSetting' + } + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/private4433' + } + priority: 250 + ruleType: 'Basic' + } + } + { + name: 'httpRedirect80-public443' + properties: { + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/httpRedirect80' + } + priority: 300 + redirectConfiguration: { + id: '${appGWExpectedResourceID}/redirectConfigurations/httpRedirect80' + } + ruleType: 'Basic' + } + } + { + name: 'httpRedirect8080-private4433' + properties: { + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/httpRedirect8080' + } + priority: 350 + redirectConfiguration: { + id: '${appGWExpectedResourceID}/redirectConfigurations/httpRedirect8080' + } + ruleType: 'Basic' + rewriteRuleSet: { + id: '${appGWExpectedResourceID}/rewriteRuleSets/customRewrite' + } + } + } + ] + sku: 'WAF_v2' + sslCertificates: [ + { + name: '${namePrefix}-az-apgw-x-001-ssl-certificate' + properties: { + keyVaultSecretId: nestedDependencies.outputs.certificateSecretUrl + } + } + ] + managedIdentities: { + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + rewriteRuleSets: [ + { + name: 'customRewrite' + id: '${appGWExpectedResourceID}/rewriteRuleSets/customRewrite' + properties: { + rewriteRules: [ + { + ruleSequence: 100 + conditions: [] + name: 'NewRewrite' + actionSet: { + requestHeaderConfigurations: [ + { + headerName: 'Content-Type' + headerValue: 'JSON' + } + { + headerName: 'someheader' + } + ] + responseHeaderConfigurations: [] + } + } + ] + } + } + ] + webApplicationFirewallConfiguration: { + enabled: true + fileUploadLimitInMb: 100 + firewallMode: 'Prevention' + maxRequestBodySizeInKb: 128 + requestBodyCheck: true + ruleSetType: 'OWASP' + ruleSetVersion: '3.0' + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +] diff --git a/avm/res/network/application-gateway/version.json b/avm/res/network/application-gateway/version.json new file mode 100644 index 0000000000..7fa401bdf7 --- /dev/null +++ b/avm/res/network/application-gateway/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/res/sql/instance-pool/README.md b/avm/res/sql/instance-pool/README.md new file mode 100644 index 0000000000..68e73a8f95 --- /dev/null +++ b/avm/res/sql/instance-pool/README.md @@ -0,0 +1,286 @@ +# SQL Server Instance Pool `[Microsoft.Sql/instancePools]` + +This module deploys an Azure SQL Server Instance Pool. + +## 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.Sql/instancePools` | [2023-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2023-05-01-preview/instancePools) | + +## 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/res/sql/instance-pool:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [WAF-aligned](#example-2-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module instancePool 'br/public:avm/res/sql/instance-pool:' = { + name: 'instancePoolDeployment' + params: { + // Required parameters + name: '' + subnetResourceId: '' + // 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 + "name": { + "value": "" + }, + "subnetResourceId": { + "value": "" + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

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

+ +via Bicep module + +```bicep +module instancePool 'br/public:avm/res/sql/instance-pool:' = { + name: 'instancePoolDeployment' + params: { + // Required parameters + name: '' + subnetResourceId: '' + // Non-required parameters + location: '' + skuName: 'GP_Gen8IM' + } +} +``` + +
+

+ +

+ +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": "" + }, + "subnetResourceId": { + "value": "" + }, + // Non-required parameters + "location": { + "value": "" + }, + "skuName": { + "value": "GP_Gen8IM" + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the instance pool. | +| [`subnetResourceId`](#parameter-subnetresourceid) | string | The subnet resource ID for the instance pool. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`licenseType`](#parameter-licensetype) | string | The license type to apply for this database. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`skuFamily`](#parameter-skufamily) | string | If the service has different generations of hardware, for the same SKU, then that can be captured here. | +| [`skuName`](#parameter-skuname) | string | The SKU name for the instance pool. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`tier`](#parameter-tier) | string | The vCore service tier for the instance pool. | +| [`vCores`](#parameter-vcores) | int | The number of vCores for the instance pool. | + +### Parameter: `name` + +The name of the instance pool. + +- Required: Yes +- Type: string + +### Parameter: `subnetResourceId` + +The subnet resource ID for the instance pool. + +- Required: Yes +- Type: string + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `licenseType` + +The license type to apply for this database. + +- Required: No +- Type: string +- Default: `'BasePrice'` +- Allowed: + ```Bicep + [ + 'BasePrice' + 'LicenseIncluded' + ] + ``` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `skuFamily` + +If the service has different generations of hardware, for the same SKU, then that can be captured here. + +- Required: No +- Type: string +- Default: `'Gen5'` + +### Parameter: `skuName` + +The SKU name for the instance pool. + +- Required: No +- Type: string +- Default: `'GP_Gen5'` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `tier` + +The vCore service tier for the instance pool. + +- Required: No +- Type: string +- Default: `'GeneralPurpose'` +- Allowed: + ```Bicep + [ + 'GeneralPurpose' + ] + ``` + +### Parameter: `vCores` + +The number of vCores for the instance pool. + +- Required: No +- Type: int +- Default: `8` +- Allowed: + ```Bicep + [ + 8 + 16 + 24 + 32 + 40 + 64 + 80 + 96 + 128 + 160 + 192 + 224 + 256 + ] + ``` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `instancePoolLocation` | string | The location of the SQL instance pool. | +| `name` | string | The name of the SQL instance pool. | +| `resourceGroupName` | string | The resource group name of the SQL instance pool. | +| `resourceId` | string | The ID of the SQL instance pool. | + +## 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/sql/instance-pool/main.bicep b/avm/res/sql/instance-pool/main.bicep new file mode 100644 index 0000000000..088cd87ada --- /dev/null +++ b/avm/res/sql/instance-pool/main.bicep @@ -0,0 +1,102 @@ +metadata name = 'SQL Server Instance Pool' +metadata description = 'This module deploys an Azure SQL Server Instance Pool.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the instance pool.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Required. The subnet resource ID for the instance pool.') +param subnetResourceId string + +@description('Optional. The license type to apply for this database.') +@allowed([ + 'BasePrice' + 'LicenseIncluded' +]) +param licenseType string = 'BasePrice' + +@description('Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here.') +param skuFamily string = 'Gen5' + +@description('Optional. The number of vCores for the instance pool.') +@allowed([ + 8 + 16 + 24 + 32 + 40 + 64 + 80 + 96 + 128 + 160 + 192 + 224 + 256 +]) +param vCores int = 8 + +@description('Optional. The vCore service tier for the instance pool.') +@allowed([ + 'GeneralPurpose' +]) +param tier string = 'GeneralPurpose' + +@description('Optional. The SKU name for the instance pool.') +param skuName string = 'GP_Gen5' + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +resource instancePool 'Microsoft.Sql/instancePools@2023-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + family: skuFamily + name: skuName + tier: tier + } + properties: { + licenseType: licenseType + subnetId: subnetResourceId + vCores: vCores + } +} + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.res.sql-instancepool.${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' + } + } + } + } + } + +@description('The ID of the SQL instance pool.') +output resourceId string = instancePool.id + +@description('The name of the SQL instance pool.') +output name string = instancePool.name + +@description('The location of the SQL instance pool.') +output instancePoolLocation string = instancePool.location + +@description('The resource group name of the SQL instance pool.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/instance-pool/main.json b/avm/res/sql/instance-pool/main.json new file mode 100644 index 0000000000..684f086ed4 --- /dev/null +++ b/avm/res/sql/instance-pool/main.json @@ -0,0 +1,176 @@ +{ + "$schema": "https://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": "1210707271653756300" + }, + "name": "SQL Server Instance Pool", + "description": "This module deploys an Azure SQL Server Instance Pool.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the instance pool." + } + }, + "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." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The subnet resource ID for the instance pool." + } + }, + "licenseType": { + "type": "string", + "defaultValue": "BasePrice", + "allowedValues": [ + "BasePrice", + "LicenseIncluded" + ], + "metadata": { + "description": "Optional. The license type to apply for this database." + } + }, + "skuFamily": { + "type": "string", + "defaultValue": "Gen5", + "metadata": { + "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." + } + }, + "vCores": { + "type": "int", + "defaultValue": 8, + "allowedValues": [ + 8, + 16, + 24, + 32, + 40, + 64, + 80, + 96, + 128, + 160, + 192, + 224, + 256 + ], + "metadata": { + "description": "Optional. The number of vCores for the instance pool." + } + }, + "tier": { + "type": "string", + "defaultValue": "GeneralPurpose", + "allowedValues": [ + "GeneralPurpose" + ], + "metadata": { + "description": "Optional. The vCore service tier for the instance pool." + } + }, + "skuName": { + "type": "string", + "defaultValue": "GP_Gen5", + "metadata": { + "description": "Optional. The SKU name for the instance pool." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "resources": { + "instancePool": { + "type": "Microsoft.Sql/instancePools", + "apiVersion": "2023-05-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "family": "[parameters('skuFamily')]", + "name": "[parameters('skuName')]", + "tier": "[parameters('tier')]" + }, + "properties": { + "licenseType": "[parameters('licenseType')]", + "subnetId": "[parameters('subnetResourceId')]", + "vCores": "[parameters('vCores')]" + } + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.sql-instancepool.{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" + } + } + } + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The ID of the SQL instance pool." + }, + "value": "[resourceId('Microsoft.Sql/instancePools', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the SQL instance pool." + }, + "value": "[parameters('name')]" + }, + "instancePoolLocation": { + "type": "string", + "metadata": { + "description": "The location of the SQL instance pool." + }, + "value": "[reference('instancePool', '2023-05-01-preview', 'full').location]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group name of the SQL instance pool." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/instance-pool/tests/e2e/defaults/dependencies.bicep b/avm/res/sql/instance-pool/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..772bd070fc --- /dev/null +++ b/avm/res/sql/instance-pool/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,286 @@ +@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 Deployment Script to create to get the paired region name.') +param pairedRegionScriptName string + +@description('Required. The name of the SQL Instance Pool.') +param sqlInstancePoolName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Optional. The name of the Subnet to create.') +param subnetName string = 'sql-instancepool-subnet' + +@description('Required. The name of the NSG to create.') +param nsgName string + +@description('Required. The name of the route table to create.') +param routeTableName string + +var addressPrefix = '10.0.0.0/16' +var subnetAddressPrefix = cidrSubnet(addressPrefix, 24, 0) + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: managedIdentityName + location: location +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${location}-${managedIdentity.id}-Reader-RoleAssignment') + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) // Reader + principalType: 'ServicePrincipal' + } +} + +resource getPairedRegionScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: pairedRegionScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-Location \\"${location}\\"' + scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Get-PairedRegion.ps1') + } + dependsOn: [ + roleAssignment + ] +} + +resource nsg 'Microsoft.Network/networkSecurityGroups@2020-06-01' = { + name: nsgName + location: location + properties: { + securityRules: [ + { + name: 'sqlmi-healthprobe-in' + properties: { + description: 'Allow Azure Load Balancer inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: subnetAddressPrefix + access: 'Allow' + priority: 100 + direction: 'Inbound' + } + } + { + name: 'sqlmi-internal-in' + properties: { + description: 'Allow MI internal inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: subnetAddressPrefix + access: 'Allow' + priority: 101 + direction: 'Inbound' + } + } + { + name: 'sqlmi-aad-out' + properties: { + description: 'Allow communication with Azure Active Directory over https' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'AzureActiveDirectory' + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + { + name: 'sqlmi-onedsc-out' + properties: { + description: 'Allow communication with the One DS Collector over https' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'OneDsCollector' + access: 'Allow' + priority: 102 + direction: 'Outbound' + } + } + { + name: 'sqlmi-internal-out' + properties: { + description: 'Allow MI internal outbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: subnetAddressPrefix + access: 'Allow' + priority: 103 + direction: 'Outbound' + } + } + { + name: 'sqlmi-strg-p-out' + properties: { + description: 'Allow outbound communication with storage over HTTPS' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'Storage.${location}' + access: 'Allow' + priority: 104 + direction: 'Outbound' + } + } + { + name: 'sqlmi-strg-s-out' + properties: { + description: 'Allow outbound communication with storage over HTTPS' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'Storage.${getPairedRegionScript.properties.outputs.pairedRegionName}' + access: 'Allow' + priority: 105 + direction: 'Outbound' + } + } + { + name: 'sqlmi-optional-azure-out' + properties: { + description: 'Allow AzureCloud outbound https traffic' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 100 + direction: 'Outbound' + } + } + ] + } +} + +resource routeTable 'Microsoft.Network/routeTables@2020-06-01' = { + name: routeTableName + location: location + properties: { + disableBgpRoutePropagation: false + routes: [ + { + name: 'sqlmi_subnet-to-vnetlocal' + properties: { + addressPrefix: subnetAddressPrefix + nextHopType: 'VnetLocal' + } + } + { + name: 'sqlmi-aad' + properties: { + addressPrefix: 'AzureActiveDirectory' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-odscollector' + properties: { + addressPrefix: 'OneDsCollector' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-stg-p' + properties: { + addressPrefix: 'Storage.${location}' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-stg-s' + properties: { + addressPrefix: 'Storage.${getPairedRegionScript.properties.outputs.pairedRegionName}' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-optional-azure-p' + properties: { + addressPrefix: 'AzureCloud.${location}' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-optional-azurecloud-s' + properties: { + addressPrefix: 'AzureCloud.${getPairedRegionScript.properties.outputs.pairedRegionName}' + nextHopType: 'Internet' + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: subnetAddressPrefix + networkSecurityGroup: { + id: nsg.id + } + routeTable: { + id: routeTable.id + } + delegations: [ + { + name: 'ip-delegations-${sqlInstancePoolName}' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } + ] + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } + } + ] + } +} + +@description('The subnetId required for the instance pool creation.') +output subnetId string = virtualNetwork.properties.subnets[0].id + +@description('The name of the sql instnce pool to be created.') +output sqlInstancePoolName string = sqlInstancePoolName diff --git a/avm/res/sql/instance-pool/tests/e2e/defaults/main.test.bicep b/avm/res/sql/instance-pool/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..677a80dbe1 --- /dev/null +++ b/avm/res/sql/instance-pool/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,61 @@ +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}-sql.instancepool-${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 = 'sipmin' + +@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@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}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + pairedRegionScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + nsgName: 'dep-${namePrefix}-nsg-${serviceShort}' + routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' + sqlInstancePoolName: '${namePrefix}${serviceShort}001' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: nestedDependencies.outputs.sqlInstancePoolName + location: resourceLocation + subnetResourceId: nestedDependencies.outputs.subnetId + } +} diff --git a/avm/res/sql/instance-pool/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/sql/instance-pool/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..772bd070fc --- /dev/null +++ b/avm/res/sql/instance-pool/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,286 @@ +@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 Deployment Script to create to get the paired region name.') +param pairedRegionScriptName string + +@description('Required. The name of the SQL Instance Pool.') +param sqlInstancePoolName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Optional. The name of the Subnet to create.') +param subnetName string = 'sql-instancepool-subnet' + +@description('Required. The name of the NSG to create.') +param nsgName string + +@description('Required. The name of the route table to create.') +param routeTableName string + +var addressPrefix = '10.0.0.0/16' +var subnetAddressPrefix = cidrSubnet(addressPrefix, 24, 0) + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: managedIdentityName + location: location +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${location}-${managedIdentity.id}-Reader-RoleAssignment') + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) // Reader + principalType: 'ServicePrincipal' + } +} + +resource getPairedRegionScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: pairedRegionScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-Location \\"${location}\\"' + scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Get-PairedRegion.ps1') + } + dependsOn: [ + roleAssignment + ] +} + +resource nsg 'Microsoft.Network/networkSecurityGroups@2020-06-01' = { + name: nsgName + location: location + properties: { + securityRules: [ + { + name: 'sqlmi-healthprobe-in' + properties: { + description: 'Allow Azure Load Balancer inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: subnetAddressPrefix + access: 'Allow' + priority: 100 + direction: 'Inbound' + } + } + { + name: 'sqlmi-internal-in' + properties: { + description: 'Allow MI internal inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: subnetAddressPrefix + access: 'Allow' + priority: 101 + direction: 'Inbound' + } + } + { + name: 'sqlmi-aad-out' + properties: { + description: 'Allow communication with Azure Active Directory over https' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'AzureActiveDirectory' + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + { + name: 'sqlmi-onedsc-out' + properties: { + description: 'Allow communication with the One DS Collector over https' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'OneDsCollector' + access: 'Allow' + priority: 102 + direction: 'Outbound' + } + } + { + name: 'sqlmi-internal-out' + properties: { + description: 'Allow MI internal outbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: subnetAddressPrefix + access: 'Allow' + priority: 103 + direction: 'Outbound' + } + } + { + name: 'sqlmi-strg-p-out' + properties: { + description: 'Allow outbound communication with storage over HTTPS' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'Storage.${location}' + access: 'Allow' + priority: 104 + direction: 'Outbound' + } + } + { + name: 'sqlmi-strg-s-out' + properties: { + description: 'Allow outbound communication with storage over HTTPS' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'Storage.${getPairedRegionScript.properties.outputs.pairedRegionName}' + access: 'Allow' + priority: 105 + direction: 'Outbound' + } + } + { + name: 'sqlmi-optional-azure-out' + properties: { + description: 'Allow AzureCloud outbound https traffic' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 100 + direction: 'Outbound' + } + } + ] + } +} + +resource routeTable 'Microsoft.Network/routeTables@2020-06-01' = { + name: routeTableName + location: location + properties: { + disableBgpRoutePropagation: false + routes: [ + { + name: 'sqlmi_subnet-to-vnetlocal' + properties: { + addressPrefix: subnetAddressPrefix + nextHopType: 'VnetLocal' + } + } + { + name: 'sqlmi-aad' + properties: { + addressPrefix: 'AzureActiveDirectory' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-odscollector' + properties: { + addressPrefix: 'OneDsCollector' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-stg-p' + properties: { + addressPrefix: 'Storage.${location}' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-stg-s' + properties: { + addressPrefix: 'Storage.${getPairedRegionScript.properties.outputs.pairedRegionName}' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-optional-azure-p' + properties: { + addressPrefix: 'AzureCloud.${location}' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-optional-azurecloud-s' + properties: { + addressPrefix: 'AzureCloud.${getPairedRegionScript.properties.outputs.pairedRegionName}' + nextHopType: 'Internet' + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: subnetAddressPrefix + networkSecurityGroup: { + id: nsg.id + } + routeTable: { + id: routeTable.id + } + delegations: [ + { + name: 'ip-delegations-${sqlInstancePoolName}' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } + ] + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } + } + ] + } +} + +@description('The subnetId required for the instance pool creation.') +output subnetId string = virtualNetwork.properties.subnets[0].id + +@description('The name of the sql instnce pool to be created.') +output sqlInstancePoolName string = sqlInstancePoolName diff --git a/avm/res/sql/instance-pool/tests/e2e/waf-aligned/main.test.bicep b/avm/res/sql/instance-pool/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..ffaa207271 --- /dev/null +++ b/avm/res/sql/instance-pool/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,62 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-sql.instancepool-${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 = 'sipwaf' + +@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@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}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + pairedRegionScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + nsgName: 'dep-${namePrefix}-nsg-${serviceShort}' + routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' + sqlInstancePoolName: '${namePrefix}${serviceShort}001' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: nestedDependencies.outputs.sqlInstancePoolName + location: resourceLocation + skuName: 'GP_Gen8IM' + subnetResourceId: nestedDependencies.outputs.subnetId + } +} diff --git a/avm/res/sql/instance-pool/version.json b/avm/res/sql/instance-pool/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/res/sql/instance-pool/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/res/sql/managed-instance/README.md b/avm/res/sql/managed-instance/README.md new file mode 100644 index 0000000000..1d06c5a612 --- /dev/null +++ b/avm/res/sql/managed-instance/README.md @@ -0,0 +1,1485 @@ +# SQL Managed Instances `[Microsoft.Sql/managedInstances]` + +This module deploys a SQL Managed Instance. + +## 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.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Sql/managedInstances` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances) | +| `Microsoft.Sql/managedInstances/administrators` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/administrators) | +| `Microsoft.Sql/managedInstances/databases` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/databases) | +| `Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies` | [2022-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/managedInstances/databases/backupLongTermRetentionPolicies) | +| `Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies) | +| `Microsoft.Sql/managedInstances/encryptionProtector` | [2022-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/managedInstances/encryptionProtector) | +| `Microsoft.Sql/managedInstances/keys` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/keys) | +| `Microsoft.Sql/managedInstances/securityAlertPolicies` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/securityAlertPolicies) | +| `Microsoft.Sql/managedInstances/vulnerabilityAssessments` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/vulnerabilityAssessments) | + +## 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/res/sql/managed-instance:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [With vulnerability assessment](#example-3-with-vulnerability-assessment) +- [WAF-aligned](#example-4-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module managedInstance 'br/public:avm/res/sql/managed-instance:' = { + name: 'managedInstanceDeployment' + params: { + // Required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: '' + name: 'sqlmimin' + subnetResourceId: '' + // 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 + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "value": "" + }, + "name": { + "value": "sqlmimin" + }, + "subnetResourceId": { + "value": "" + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

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

+ +via Bicep module + +```bicep +module managedInstance 'br/public:avm/res/sql/managed-instance:' = { + name: 'managedInstanceDeployment' + params: { + // Required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: '' + name: 'sqlmimax' + subnetResourceId: '' + // Non-required parameters + collation: 'SQL_Latin1_General_CP1_CI_AS' + databases: [ + { + backupLongTermRetentionPolicies: { + name: 'default' + } + backupShortTermRetentionPolicies: { + name: 'default' + } + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + name: 'sqlmimax-db-001' + } + ] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + dnsZonePartner: '' + encryptionProtectorObj: { + serverKeyName: '' + serverKeyType: 'AzureKeyVault' + } + hardwareFamily: 'Gen5' + keys: [ + { + name: '' + serverKeyType: 'AzureKeyVault' + uri: '' + } + ] + licenseType: 'LicenseIncluded' + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentities: { + systemAssigned: true + userAssignedResourceIds: [ + '' + ] + } + primaryUserAssignedIdentityId: '' + proxyOverride: 'Proxy' + publicDataEndpointEnabled: false + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' + } + ] + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + servicePrincipal: 'SystemAssigned' + skuName: 'GP_Gen5' + skuTier: 'GeneralPurpose' + storageSizeInGB: 32 + timezoneId: 'UTC' + vCores: 4 + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: '' + 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": { + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "value": "" + }, + "name": { + "value": "sqlmimax" + }, + "subnetResourceId": { + "value": "" + }, + "collation": { + "value": "SQL_Latin1_General_CP1_CI_AS" + }, + "databases": { + "value": [ + { + "backupLongTermRetentionPolicies": { + "name": "default" + }, + "backupShortTermRetentionPolicies": { + "name": "default" + }, + "diagnosticSettings": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ], + "name": "sqlmimax-db-001" + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "logCategoriesAndGroups": [ + { + "categoryGroup": "allLogs" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "dnsZonePartner": { + "value": "" + }, + "encryptionProtectorObj": { + "value": { + "serverKeyName": "", + "serverKeyType": "AzureKeyVault" + } + }, + "hardwareFamily": { + "value": "Gen5" + }, + "keys": { + "value": [ + { + "name": "", + "serverKeyType": "AzureKeyVault", + "uri": "" + } + ] + }, + "licenseType": { + "value": "LicenseIncluded" + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "managedIdentities": { + "value": { + "systemAssigned": true, + "userAssignedResourceIds": [ + "" + ] + } + }, + "primaryUserAssignedIdentityId": { + "value": "" + }, + "proxyOverride": { + "value": "Proxy" + }, + "publicDataEndpointEnabled": { + "value": false + }, + "roleAssignments": { + "value": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" + } + ] + }, + "securityAlertPoliciesObj": { + "value": { + "emailAccountAdmins": true, + "name": "default", + "state": "Enabled" + } + }, + "servicePrincipal": { + "value": "SystemAssigned" + }, + "skuName": { + "value": "GP_Gen5" + }, + "skuTier": { + "value": "GeneralPurpose" + }, + "storageSizeInGB": { + "value": 32 + }, + "timezoneId": { + "value": "UTC" + }, + "vCores": { + "value": 4 + }, + "vulnerabilityAssessmentsObj": { + "value": { + "emailSubscriptionAdmins": true, + "name": "default", + "recurringScansEmails": [ + "test1@contoso.com", + "test2@contoso.com" + ], + "recurringScansIsEnabled": true, + "storageAccountResourceId": "", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } + } +} +``` + +
+

+ +### Example 3: _With vulnerability assessment_ + +This instance deploys the module with a vulnerability assessment. + + +

+ +via Bicep module + +```bicep +module managedInstance 'br/public:avm/res/sql/managed-instance:' = { + name: 'managedInstanceDeployment' + params: { + // Required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: '' + name: 'sqlmivln' + subnetResourceId: '' + // Non-required parameters + location: '' + managedIdentities: { + systemAssigned: true + } + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + vulnerabilityAssessmentsObj: { + createStorageRoleAssignment: true + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + useStorageAccountAccessKey: 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 + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "value": "" + }, + "name": { + "value": "sqlmivln" + }, + "subnetResourceId": { + "value": "" + }, + // Non-required parameters + "location": { + "value": "" + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "securityAlertPoliciesObj": { + "value": { + "emailAccountAdmins": true, + "name": "default", + "state": "Enabled" + } + }, + "vulnerabilityAssessmentsObj": { + "value": { + "createStorageRoleAssignment": true, + "emailSubscriptionAdmins": true, + "name": "default", + "recurringScansEmails": [ + "test1@contoso.com", + "test2@contoso.com" + ], + "recurringScansIsEnabled": true, + "storageAccountResourceId": "", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + }, + "useStorageAccountAccessKey": false + } + } + } +} +``` + +
+

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

+ +via Bicep module + +```bicep +module managedInstance 'br/public:avm/res/sql/managed-instance:' = { + name: 'managedInstanceDeployment' + params: { + // Required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: '' + name: 'sqlmiwaf' + subnetResourceId: '' + // Non-required parameters + collation: 'SQL_Latin1_General_CP1_CI_AS' + databases: [ + { + backupLongTermRetentionPolicies: { + name: 'default' + } + backupShortTermRetentionPolicies: { + name: 'default' + } + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + name: 'sqlmiwaf-db-001' + } + ] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + dnsZonePartner: '' + encryptionProtectorObj: { + serverKeyName: '' + serverKeyType: 'AzureKeyVault' + } + hardwareFamily: 'Gen5' + keys: [ + { + name: '' + serverKeyType: 'AzureKeyVault' + uri: '' + } + ] + licenseType: 'LicenseIncluded' + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentities: { + systemAssigned: true + userAssignedResourceIds: [ + '' + ] + } + primaryUserAssignedIdentityId: '' + proxyOverride: 'Proxy' + publicDataEndpointEnabled: false + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + servicePrincipal: 'SystemAssigned' + skuName: 'GP_Gen5' + skuTier: 'GeneralPurpose' + storageSizeInGB: 32 + timezoneId: 'UTC' + vCores: 4 + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: '' + 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": { + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "value": "" + }, + "name": { + "value": "sqlmiwaf" + }, + "subnetResourceId": { + "value": "" + }, + "collation": { + "value": "SQL_Latin1_General_CP1_CI_AS" + }, + "databases": { + "value": [ + { + "backupLongTermRetentionPolicies": { + "name": "default" + }, + "backupShortTermRetentionPolicies": { + "name": "default" + }, + "diagnosticSettings": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ], + "name": "sqlmiwaf-db-001" + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "logCategoriesAndGroups": [ + { + "categoryGroup": "allLogs" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "dnsZonePartner": { + "value": "" + }, + "encryptionProtectorObj": { + "value": { + "serverKeyName": "", + "serverKeyType": "AzureKeyVault" + } + }, + "hardwareFamily": { + "value": "Gen5" + }, + "keys": { + "value": [ + { + "name": "", + "serverKeyType": "AzureKeyVault", + "uri": "" + } + ] + }, + "licenseType": { + "value": "LicenseIncluded" + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "managedIdentities": { + "value": { + "systemAssigned": true, + "userAssignedResourceIds": [ + "" + ] + } + }, + "primaryUserAssignedIdentityId": { + "value": "" + }, + "proxyOverride": { + "value": "Proxy" + }, + "publicDataEndpointEnabled": { + "value": false + }, + "securityAlertPoliciesObj": { + "value": { + "emailAccountAdmins": true, + "name": "default", + "state": "Enabled" + } + }, + "servicePrincipal": { + "value": "SystemAssigned" + }, + "skuName": { + "value": "GP_Gen5" + }, + "skuTier": { + "value": "GeneralPurpose" + }, + "storageSizeInGB": { + "value": 32 + }, + "timezoneId": { + "value": "UTC" + }, + "vCores": { + "value": 4 + }, + "vulnerabilityAssessmentsObj": { + "value": { + "emailSubscriptionAdmins": true, + "name": "default", + "recurringScansEmails": [ + "test1@contoso.com", + "test2@contoso.com" + ], + "recurringScansIsEnabled": true, + "storageAccountResourceId": "", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`administratorLogin`](#parameter-administratorlogin) | string | The username used to establish jumpbox VMs. | +| [`administratorLoginPassword`](#parameter-administratorloginpassword) | securestring | The password given to the admin user. | +| [`name`](#parameter-name) | string | The name of the SQL managed instance. | +| [`subnetResourceId`](#parameter-subnetresourceid) | string | The fully qualified resource ID of the subnet on which the SQL managed instance will be placed. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`primaryUserAssignedIdentityId`](#parameter-primaryuserassignedidentityid) | string | The resource ID of a user assigned identity to be used by default. Required if "userAssignedIdentities" is not empty. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`administratorsObj`](#parameter-administratorsobj) | object | The administrator configuration. | +| [`collation`](#parameter-collation) | string | Collation of the managed instance. | +| [`databases`](#parameter-databases) | array | Databases to create in this server. | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`dnsZonePartner`](#parameter-dnszonepartner) | string | The resource ID of another managed instance whose DNS zone this managed instance will share after creation. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`encryptionProtectorObj`](#parameter-encryptionprotectorobj) | object | The encryption protection configuration. | +| [`hardwareFamily`](#parameter-hardwarefamily) | string | If the service has different generations of hardware, for the same SKU, then that can be captured here. | +| [`instancePoolResourceId`](#parameter-instancepoolresourceid) | string | The resource ID of the instance pool this managed server belongs to. | +| [`keys`](#parameter-keys) | array | The keys to configure. | +| [`licenseType`](#parameter-licensetype) | string | The license type. Possible values are 'LicenseIncluded' (regular price inclusive of a new SQL license) and 'BasePrice' (discounted AHB price for bringing your own SQL licenses). | +| [`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. | +| [`managedInstanceCreateMode`](#parameter-managedinstancecreatemode) | string | Specifies the mode of database creation. Default: Regular instance creation. Restore: Creates an instance by restoring a set of backups to specific point in time. RestorePointInTime and SourceManagedInstanceId must be specified. | +| [`minimalTlsVersion`](#parameter-minimaltlsversion) | string | Minimal TLS version allowed. | +| [`proxyOverride`](#parameter-proxyoverride) | string | Connection type used for connecting to the instance. | +| [`publicDataEndpointEnabled`](#parameter-publicdataendpointenabled) | bool | Whether or not the public data endpoint is enabled. | +| [`requestedBackupStorageRedundancy`](#parameter-requestedbackupstorageredundancy) | string | The storage account type used to store backups for this database. | +| [`restorePointInTime`](#parameter-restorepointintime) | string | Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`securityAlertPoliciesObj`](#parameter-securityalertpoliciesobj) | object | The security alert policy configuration. | +| [`servicePrincipal`](#parameter-serviceprincipal) | string | Service principal type. If using AD Authentication and applying Admin, must be set to `SystemAssigned`. Then Global Admin must allow Reader access to Azure AD for the Service Principal. | +| [`skuName`](#parameter-skuname) | string | The name of the SKU, typically, a letter + Number code, e.g. P3. | +| [`skuTier`](#parameter-skutier) | string | The tier or edition of the particular SKU, e.g. Basic, Premium. | +| [`sourceManagedInstanceId`](#parameter-sourcemanagedinstanceid) | string | The resource identifier of the source managed instance associated with create operation of this instance. | +| [`storageSizeInGB`](#parameter-storagesizeingb) | int | Storage size in GB. Minimum value: 32. Maximum value: 8192. Increments of 32 GB allowed only. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`timezoneId`](#parameter-timezoneid) | string | ID of the timezone. Allowed values are timezones supported by Windows. | +| [`vCores`](#parameter-vcores) | int | The number of vCores. Allowed values: 8, 16, 24, 32, 40, 64, 80. | +| [`vulnerabilityAssessmentsObj`](#parameter-vulnerabilityassessmentsobj) | object | The vulnerability assessment configuration. | +| [`zoneRedundant`](#parameter-zoneredundant) | bool | Whether or not multi-az is enabled. | + +### Parameter: `administratorLogin` + +The username used to establish jumpbox VMs. + +- Required: Yes +- Type: string + +### Parameter: `administratorLoginPassword` + +The password given to the admin user. + +- Required: Yes +- Type: securestring + +### Parameter: `name` + +The name of the SQL managed instance. + +- Required: Yes +- Type: string + +### Parameter: `subnetResourceId` + +The fully qualified resource ID of the subnet on which the SQL managed instance will be placed. + +- Required: Yes +- Type: string + +### Parameter: `primaryUserAssignedIdentityId` + +The resource ID of a user assigned identity to be used by default. Required if "userAssignedIdentities" is not empty. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `administratorsObj` + +The administrator configuration. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `collation` + +Collation of the managed instance. + +- Required: No +- Type: string +- Default: `'SQL_Latin1_General_CP1_CI_AS'` + +### Parameter: `databases` + +Databases to create in this server. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | 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`](#parameter-diagnosticsettingseventhubname) | string | 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. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`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. | +| [`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` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | +| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.metricCategories` + +The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingsmetriccategoriescategory) | string | Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enabled`](#parameter-diagnosticsettingsmetriccategoriesenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.metricCategories.category` + +Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. + +- Required: Yes +- Type: string + +### Parameter: `diagnosticSettings.metricCategories.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +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. + +- Required: No +- Type: string + +### 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. + +- Required: No +- Type: string + +### Parameter: `dnsZonePartner` + +The resource ID of another managed instance whose DNS zone this managed instance will share after creation. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `encryptionProtectorObj` + +The encryption protection configuration. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `hardwareFamily` + +If the service has different generations of hardware, for the same SKU, then that can be captured here. + +- Required: No +- Type: string +- Default: `'Gen5'` + +### Parameter: `instancePoolResourceId` + +The resource ID of the instance pool this managed server belongs to. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `keys` + +The keys to configure. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `licenseType` + +The license type. Possible values are 'LicenseIncluded' (regular price inclusive of a new SQL license) and 'BasePrice' (discounted AHB price for bringing your own SQL licenses). + +- Required: No +- Type: string +- Default: `'LicenseIncluded'` +- Allowed: + ```Bicep + [ + 'BasePrice' + 'LicenseIncluded' + ] + ``` + +### 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. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. | + +### Parameter: `managedIdentities.systemAssigned` + +Enables system assigned managed identity on the resource. + +- Required: No +- Type: bool + +### Parameter: `managedIdentities.userAssignedResourceIds` + +The resource ID(s) to assign to the resource. + +- Required: No +- Type: array + +### Parameter: `managedInstanceCreateMode` + +Specifies the mode of database creation. Default: Regular instance creation. Restore: Creates an instance by restoring a set of backups to specific point in time. RestorePointInTime and SourceManagedInstanceId must be specified. + +- Required: No +- Type: string +- Default: `'Default'` +- Allowed: + ```Bicep + [ + 'Default' + 'PointInTimeRestore' + ] + ``` + +### Parameter: `minimalTlsVersion` + +Minimal TLS version allowed. + +- Required: No +- Type: string +- Default: `'1.2'` +- Allowed: + ```Bicep + [ + '1.0' + '1.1' + '1.2' + 'None' + ] + ``` + +### Parameter: `proxyOverride` + +Connection type used for connecting to the instance. + +- Required: No +- Type: string +- Default: `'Proxy'` +- Allowed: + ```Bicep + [ + 'Default' + 'Proxy' + 'Redirect' + ] + ``` + +### Parameter: `publicDataEndpointEnabled` + +Whether or not the public data endpoint is enabled. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `requestedBackupStorageRedundancy` + +The storage account type used to store backups for this database. + +- Required: No +- Type: string +- Default: `'Geo'` +- Allowed: + ```Bicep + [ + 'Geo' + 'GeoZone' + 'Local' + 'Zone' + ] + ``` + +### Parameter: `restorePointInTime` + +Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- 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. | +| [`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.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `securityAlertPoliciesObj` + +The security alert policy configuration. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `servicePrincipal` + +Service principal type. If using AD Authentication and applying Admin, must be set to `SystemAssigned`. Then Global Admin must allow Reader access to Azure AD for the Service Principal. + +- Required: No +- Type: string +- Default: `'None'` +- Allowed: + ```Bicep + [ + 'None' + 'SystemAssigned' + ] + ``` + +### Parameter: `skuName` + +The name of the SKU, typically, a letter + Number code, e.g. P3. + +- Required: No +- Type: string +- Default: `'GP_Gen5'` + +### Parameter: `skuTier` + +The tier or edition of the particular SKU, e.g. Basic, Premium. + +- Required: No +- Type: string +- Default: `'GeneralPurpose'` + +### Parameter: `sourceManagedInstanceId` + +The resource identifier of the source managed instance associated with create operation of this instance. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `storageSizeInGB` + +Storage size in GB. Minimum value: 32. Maximum value: 8192. Increments of 32 GB allowed only. + +- Required: No +- Type: int +- Default: `32` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `timezoneId` + +ID of the timezone. Allowed values are timezones supported by Windows. + +- Required: No +- Type: string +- Default: `'UTC'` + +### Parameter: `vCores` + +The number of vCores. Allowed values: 8, 16, 24, 32, 40, 64, 80. + +- Required: No +- Type: int +- Default: `4` + +### Parameter: `vulnerabilityAssessmentsObj` + +The vulnerability assessment configuration. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `zoneRedundant` + +Whether or not multi-az is enabled. + +- Required: No +- Type: bool +- Default: `False` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed managed instance. | +| `resourceGroupName` | string | The resource group of the deployed managed instance. | +| `resourceId` | string | The resource ID of the deployed managed instance. | +| `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/sql/managed-instance/administrator/README.md b/avm/res/sql/managed-instance/administrator/README.md new file mode 100644 index 0000000000..87b2ba4cd3 --- /dev/null +++ b/avm/res/sql/managed-instance/administrator/README.md @@ -0,0 +1,84 @@ +# SQL Managed Instances Administrator `[Microsoft.Sql/managedInstances/administrators]` + +This module deploys a SQL Managed Instance Administrator. + +## 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.Sql/managedInstances/administrators` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/administrators) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`login`](#parameter-login) | string | Login name of the managed instance administrator. | +| [`sid`](#parameter-sid) | string | SID (object ID) of the managed instance administrator. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`tenantId`](#parameter-tenantid) | string | Tenant ID of the managed instance administrator. | + +### Parameter: `login` + +Login name of the managed instance administrator. + +- Required: Yes +- Type: string + +### Parameter: `sid` + +SID (object ID) of the managed instance administrator. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `tenantId` + +Tenant ID of the managed instance administrator. + +- Required: No +- Type: string +- Default: `''` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed managed instance administrator. | +| `resourceGroupName` | string | The resource group of the deployed managed instance administrator. | +| `resourceId` | string | The resource ID of the deployed managed instance administrator. | + +## 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/sql/managed-instance/administrator/main.bicep b/avm/res/sql/managed-instance/administrator/main.bicep new file mode 100644 index 0000000000..2b6fcc3275 --- /dev/null +++ b/avm/res/sql/managed-instance/administrator/main.bicep @@ -0,0 +1,39 @@ +metadata name = 'SQL Managed Instances Administrator' +metadata description = 'This module deploys a SQL Managed Instance Administrator.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Required. Login name of the managed instance administrator.') +param login string + +@description('Required. SID (object ID) of the managed instance administrator.') +param sid string + +@description('Optional. Tenant ID of the managed instance administrator.') +param tenantId string = '' + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName +} + +resource administrator 'Microsoft.Sql/managedInstances/administrators@2023-08-01-preview' = { + name: 'ActiveDirectory' + parent: managedInstance + properties: { + administratorType: 'ActiveDirectory' + login: login + sid: sid + tenantId: tenantId + } +} + +@description('The name of the deployed managed instance administrator.') +output name string = administrator.name + +@description('The resource ID of the deployed managed instance administrator.') +output resourceId string = administrator.id + +@description('The resource group of the deployed managed instance administrator.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/administrator/main.json b/avm/res/sql/managed-instance/administrator/main.json new file mode 100644 index 0000000000..bda17b0156 --- /dev/null +++ b/avm/res/sql/managed-instance/administrator/main.json @@ -0,0 +1,77 @@ +{ + "$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": "14195697460339552085" + }, + "name": "SQL Managed Instances Administrator", + "description": "This module deploys a SQL Managed Instance Administrator.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "login": { + "type": "string", + "metadata": { + "description": "Required. Login name of the managed instance administrator." + } + }, + "sid": { + "type": "string", + "metadata": { + "description": "Required. SID (object ID) of the managed instance administrator." + } + }, + "tenantId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Tenant ID of the managed instance administrator." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/administrators", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), 'ActiveDirectory')]", + "properties": { + "administratorType": "ActiveDirectory", + "login": "[parameters('login')]", + "sid": "[parameters('sid')]", + "tenantId": "[parameters('tenantId')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance administrator." + }, + "value": "ActiveDirectory" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance administrator." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/administrators', parameters('managedInstanceName'), 'ActiveDirectory')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance administrator." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/database/README.md b/avm/res/sql/managed-instance/database/README.md new file mode 100644 index 0000000000..fcf9c4ed6c --- /dev/null +++ b/avm/res/sql/managed-instance/database/README.md @@ -0,0 +1,349 @@ +# SQL Managed Instance Databases `[Microsoft.Sql/managedInstances/databases]` + +This module deploys a SQL Managed Instance Database. + +## 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.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Sql/managedInstances/databases` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/databases) | +| `Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies` | [2022-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/managedInstances/databases/backupLongTermRetentionPolicies) | +| `Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the SQL managed instance database. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`longTermRetentionBackupResourceId`](#parameter-longtermretentionbackupresourceid) | string | The resource ID of the Long Term Retention backup to be used for restore of this managed database. Required if createMode is RestoreLongTermRetentionBackup. | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | +| [`recoverableDatabaseId`](#parameter-recoverabledatabaseid) | string | The resource identifier of the recoverable database associated with create operation of this database. Required if createMode is Recovery. | +| [`restorePointInTime`](#parameter-restorepointintime) | string | Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. Required if createMode is PointInTimeRestore. | +| [`sourceDatabaseId`](#parameter-sourcedatabaseid) | string | The resource identifier of the source database associated with create operation of this database. Required if createMode is PointInTimeRestore. | +| [`storageContainerSasToken`](#parameter-storagecontainersastoken) | securestring | Specifies the storage container sas token. Required if createMode is RestoreExternalBackup. | +| [`storageContainerUri`](#parameter-storagecontaineruri) | string | Specifies the uri of the storage container where backups for this restore are stored. Required if createMode is RestoreExternalBackup. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`backupLongTermRetentionPoliciesObj`](#parameter-backuplongtermretentionpoliciesobj) | object | The configuration for the backup long term retention policy definition. | +| [`backupShortTermRetentionPoliciesObj`](#parameter-backupshorttermretentionpoliciesobj) | object | The configuration for the backup short term retention policy definition. | +| [`catalogCollation`](#parameter-catalogcollation) | string | Collation of the managed instance. | +| [`collation`](#parameter-collation) | string | Collation of the managed instance database. | +| [`createMode`](#parameter-createmode) | string | Managed database create mode. PointInTimeRestore: Create a database by restoring a point in time backup of an existing database. SourceDatabaseName, SourceManagedInstanceName and PointInTime must be specified. RestoreExternalBackup: Create a database by restoring from external backup files. Collation, StorageContainerUri and StorageContainerSasToken must be specified. Recovery: Creates a database by restoring a geo-replicated backup. RecoverableDatabaseId must be specified as the recoverable database resource ID to restore. RestoreLongTermRetentionBackup: Create a database by restoring from a long term retention backup (longTermRetentionBackupResourceId required). | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`restorableDroppedDatabaseId`](#parameter-restorabledroppeddatabaseid) | string | The restorable dropped database resource ID to restore when creating this database. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | + +### Parameter: `name` + +The name of the SQL managed instance database. + +- Required: Yes +- Type: string + +### Parameter: `longTermRetentionBackupResourceId` + +The resource ID of the Long Term Retention backup to be used for restore of this managed database. Required if createMode is RestoreLongTermRetentionBackup. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `recoverableDatabaseId` + +The resource identifier of the recoverable database associated with create operation of this database. Required if createMode is Recovery. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `restorePointInTime` + +Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. Required if createMode is PointInTimeRestore. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `sourceDatabaseId` + +The resource identifier of the source database associated with create operation of this database. Required if createMode is PointInTimeRestore. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `storageContainerSasToken` + +Specifies the storage container sas token. Required if createMode is RestoreExternalBackup. + +- Required: No +- Type: securestring +- Default: `''` + +### Parameter: `storageContainerUri` + +Specifies the uri of the storage container where backups for this restore are stored. Required if createMode is RestoreExternalBackup. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `backupLongTermRetentionPoliciesObj` + +The configuration for the backup long term retention policy definition. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `backupShortTermRetentionPoliciesObj` + +The configuration for the backup short term retention policy definition. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `catalogCollation` + +Collation of the managed instance. + +- Required: No +- Type: string +- Default: `'SQL_Latin1_General_CP1_CI_AS'` + +### Parameter: `collation` + +Collation of the managed instance database. + +- Required: No +- Type: string +- Default: `'SQL_Latin1_General_CP1_CI_AS'` + +### Parameter: `createMode` + +Managed database create mode. PointInTimeRestore: Create a database by restoring a point in time backup of an existing database. SourceDatabaseName, SourceManagedInstanceName and PointInTime must be specified. RestoreExternalBackup: Create a database by restoring from external backup files. Collation, StorageContainerUri and StorageContainerSasToken must be specified. Recovery: Creates a database by restoring a geo-replicated backup. RecoverableDatabaseId must be specified as the recoverable database resource ID to restore. RestoreLongTermRetentionBackup: Create a database by restoring from a long term retention backup (longTermRetentionBackupResourceId required). + +- Required: No +- Type: string +- Default: `'Default'` +- Allowed: + ```Bicep + [ + 'Default' + 'PointInTimeRestore' + 'Recovery' + 'RestoreExternalBackup' + 'RestoreLongTermRetentionBackup' + ] + ``` + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | 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`](#parameter-diagnosticsettingseventhubname) | string | 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. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to '' to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`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. | +| [`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` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +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. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to '' to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +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. + +- Required: No +- Type: string + +### 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. + +- Required: No +- Type: string + +### 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: `restorableDroppedDatabaseId` + +The restorable dropped database resource ID to restore when creating this database. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed database. | +| `resourceGroupName` | string | The resource group the database was deployed into. | +| `resourceId` | string | The resource ID of the deployed database. | + +## 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/sql/managed-instance/database/backup-long-term-retention-policy/README.md b/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/README.md new file mode 100644 index 0000000000..9bd34cadb3 --- /dev/null +++ b/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/README.md @@ -0,0 +1,111 @@ +# SQL Managed Instance Database Backup Long-Term Retention Policies `[Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies]` + +This module deploys a SQL Managed Instance Database Backup Long-Term Retention Policy. + +## 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.Sql/managedInstances/databases/backupLongTermRetentionPolicies` | [2022-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/managedInstances/databases/backupLongTermRetentionPolicies) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the Long Term Retention backup policy. For example "default". | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`databaseName`](#parameter-databasename) | string | The name of the parent managed instance database. Required if the template is used in a standalone deployment. | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`monthlyRetention`](#parameter-monthlyretention) | string | The monthly retention policy for an LTR backup in an ISO 8601 format. | +| [`weeklyRetention`](#parameter-weeklyretention) | string | The weekly retention policy for an LTR backup in an ISO 8601 format. | +| [`weekOfYear`](#parameter-weekofyear) | int | The week of year to take the yearly backup in an ISO 8601 format. | +| [`yearlyRetention`](#parameter-yearlyretention) | string | The yearly retention policy for an LTR backup in an ISO 8601 format. | + +### Parameter: `name` + +The name of the Long Term Retention backup policy. For example "default". + +- Required: Yes +- Type: string + +### Parameter: `databaseName` + +The name of the parent managed instance database. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `monthlyRetention` + +The monthly retention policy for an LTR backup in an ISO 8601 format. + +- Required: No +- Type: string +- Default: `'P1Y'` + +### Parameter: `weeklyRetention` + +The weekly retention policy for an LTR backup in an ISO 8601 format. + +- Required: No +- Type: string +- Default: `'P1M'` + +### Parameter: `weekOfYear` + +The week of year to take the yearly backup in an ISO 8601 format. + +- Required: No +- Type: int +- Default: `5` + +### Parameter: `yearlyRetention` + +The yearly retention policy for an LTR backup in an ISO 8601 format. + +- Required: No +- Type: string +- Default: `'P5Y'` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed database backup long-term retention policy. | +| `resourceGroupName` | string | The resource group of the deployed database backup long-term retention policy. | +| `resourceId` | string | The resource ID of the deployed database backup long-term retention policy. | + +## 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/sql/managed-instance/database/backup-long-term-retention-policy/main.bicep b/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.bicep new file mode 100644 index 0000000000..52ce6d2b23 --- /dev/null +++ b/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.bicep @@ -0,0 +1,52 @@ +metadata name = 'SQL Managed Instance Database Backup Long-Term Retention Policies' +metadata description = 'This module deploys a SQL Managed Instance Database Backup Long-Term Retention Policy.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the Long Term Retention backup policy. For example "default".') +param name string + +@description('Conditional. The name of the parent managed instance database. Required if the template is used in a standalone deployment.') +param databaseName string + +@description('Conditional. The name of the parent managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. The week of year to take the yearly backup in an ISO 8601 format.') +param weekOfYear int = 5 + +@description('Optional. The weekly retention policy for an LTR backup in an ISO 8601 format.') +param weeklyRetention string = 'P1M' + +@description('Optional. The monthly retention policy for an LTR backup in an ISO 8601 format.') +param monthlyRetention string = 'P1Y' + +@description('Optional. The yearly retention policy for an LTR backup in an ISO 8601 format.') +param yearlyRetention string = 'P5Y' + +resource managedInstance 'Microsoft.Sql/managedInstances@2022-05-01-preview' existing = { + name: managedInstanceName + + resource managedInstaceDatabase 'databases@2022-05-01-preview' existing = { + name: databaseName + } +} + +resource backupLongTermRetentionPolicy 'Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies@2022-05-01-preview' = { + name: name + parent: managedInstance::managedInstaceDatabase + properties: { + monthlyRetention: monthlyRetention + weeklyRetention: weeklyRetention + weekOfYear: weekOfYear + yearlyRetention: yearlyRetention + } +} + +@description('The name of the deployed database backup long-term retention policy.') +output name string = backupLongTermRetentionPolicy.name + +@description('The resource ID of the deployed database backup long-term retention policy.') +output resourceId string = backupLongTermRetentionPolicy.id + +@description('The resource group of the deployed database backup long-term retention policy.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.json b/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.json new file mode 100644 index 0000000000..3edd496f59 --- /dev/null +++ b/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.json @@ -0,0 +1,98 @@ +{ + "$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": "2315344113299493188" + }, + "name": "SQL Managed Instance Database Backup Long-Term Retention Policies", + "description": "This module deploys a SQL Managed Instance Database Backup Long-Term Retention Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Long Term Retention backup policy. For example \"default\"." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed instance database. Required if the template is used in a standalone deployment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed instance. Required if the template is used in a standalone deployment." + } + }, + "weekOfYear": { + "type": "int", + "defaultValue": 5, + "metadata": { + "description": "Optional. The week of year to take the yearly backup in an ISO 8601 format." + } + }, + "weeklyRetention": { + "type": "string", + "defaultValue": "P1M", + "metadata": { + "description": "Optional. The weekly retention policy for an LTR backup in an ISO 8601 format." + } + }, + "monthlyRetention": { + "type": "string", + "defaultValue": "P1Y", + "metadata": { + "description": "Optional. The monthly retention policy for an LTR backup in an ISO 8601 format." + } + }, + "yearlyRetention": { + "type": "string", + "defaultValue": "P5Y", + "metadata": { + "description": "Optional. The yearly retention policy for an LTR backup in an ISO 8601 format." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies", + "apiVersion": "2022-05-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]", + "properties": { + "monthlyRetention": "[parameters('monthlyRetention')]", + "weeklyRetention": "[parameters('weeklyRetention')]", + "weekOfYear": "[parameters('weekOfYear')]", + "yearlyRetention": "[parameters('yearlyRetention')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database backup long-term retention policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database backup long-term retention policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database backup long-term retention policy." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/README.md b/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/README.md new file mode 100644 index 0000000000..623130d636 --- /dev/null +++ b/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/README.md @@ -0,0 +1,84 @@ +# SQL Managed Instance Database Backup Short-Term Retention Policies `[Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies]` + +This module deploys a SQL Managed Instance Database Backup Short-Term Retention Policy. + +## 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.Sql/managedInstances/databases/backupShortTermRetentionPolicies` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the Short Term Retention backup policy. For example "default". | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`databaseName`](#parameter-databasename) | string | The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment. | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`retentionDays`](#parameter-retentiondays) | int | The backup retention period in days. This is how many days Point-in-Time Restore will be supported. | + +### Parameter: `name` + +The name of the Short Term Retention backup policy. For example "default". + +- Required: Yes +- Type: string + +### Parameter: `databaseName` + +The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `retentionDays` + +The backup retention period in days. This is how many days Point-in-Time Restore will be supported. + +- Required: No +- Type: int +- Default: `35` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed database backup short-term retention policy. | +| `resourceGroupName` | string | The resource group of the deployed database backup short-term retention policy. | +| `resourceId` | string | The resource ID of the deployed database backup short-term retention policy. | + +## 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/sql/managed-instance/database/backup-short-term-retention-policy/main.bicep b/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.bicep new file mode 100644 index 0000000000..207d0d314e --- /dev/null +++ b/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.bicep @@ -0,0 +1,40 @@ +metadata name = 'SQL Managed Instance Database Backup Short-Term Retention Policies' +metadata description = 'This module deploys a SQL Managed Instance Database Backup Short-Term Retention Policy.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the Short Term Retention backup policy. For example "default".') +param name string + +@description('Conditional. The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment.') +param databaseName string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. The backup retention period in days. This is how many days Point-in-Time Restore will be supported.') +param retentionDays int = 35 + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName + + resource managedInstaceDatabase 'databases@2023-08-01-preview' existing = { + name: databaseName + } +} + +resource backupShortTermRetentionPolicy 'Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies@2023-08-01-preview' = { + name: name + parent: managedInstance::managedInstaceDatabase + properties: { + retentionDays: retentionDays + } +} + +@description('The name of the deployed database backup short-term retention policy.') +output name string = backupShortTermRetentionPolicy.name + +@description('The resource ID of the deployed database backup short-term retention policy.') +output resourceId string = backupShortTermRetentionPolicy.id + +@description('The resource group of the deployed database backup short-term retention policy.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.json b/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.json new file mode 100644 index 0000000000..4cee96ffa9 --- /dev/null +++ b/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/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.26.170.59819", + "templateHash": "4816864047763535590" + }, + "name": "SQL Managed Instance Database Backup Short-Term Retention Policies", + "description": "This module deploys a SQL Managed Instance Database Backup Short-Term Retention Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Short Term Retention backup policy. For example \"default\"." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "retentionDays": { + "type": "int", + "defaultValue": 35, + "metadata": { + "description": "Optional. The backup retention period in days. This is how many days Point-in-Time Restore will be supported." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]", + "properties": { + "retentionDays": "[parameters('retentionDays')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database backup short-term retention policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database backup short-term retention policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database backup short-term retention policy." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/database/main.bicep b/avm/res/sql/managed-instance/database/main.bicep new file mode 100644 index 0000000000..a5180004e9 --- /dev/null +++ b/avm/res/sql/managed-instance/database/main.bicep @@ -0,0 +1,198 @@ +metadata name = 'SQL Managed Instance Databases' +metadata description = 'This module deploys a SQL Managed Instance Database.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the SQL managed instance database.') +param name string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Collation of the managed instance database.') +param collation string = 'SQL_Latin1_General_CP1_CI_AS' + +@description('Optional. Collation of the managed instance.') +param catalogCollation string = 'SQL_Latin1_General_CP1_CI_AS' + +@description('Optional. Managed database create mode. PointInTimeRestore: Create a database by restoring a point in time backup of an existing database. SourceDatabaseName, SourceManagedInstanceName and PointInTime must be specified. RestoreExternalBackup: Create a database by restoring from external backup files. Collation, StorageContainerUri and StorageContainerSasToken must be specified. Recovery: Creates a database by restoring a geo-replicated backup. RecoverableDatabaseId must be specified as the recoverable database resource ID to restore. RestoreLongTermRetentionBackup: Create a database by restoring from a long term retention backup (longTermRetentionBackupResourceId required).') +@allowed([ + 'Default' + 'RestoreExternalBackup' + 'PointInTimeRestore' + 'Recovery' + 'RestoreLongTermRetentionBackup' +]) +param createMode string = 'Default' + +@description('Conditional. The resource identifier of the source database associated with create operation of this database. Required if createMode is PointInTimeRestore.') +param sourceDatabaseId string = '' + +@description('Conditional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. Required if createMode is PointInTimeRestore.') +param restorePointInTime string = '' + +@description('Optional. The restorable dropped database resource ID to restore when creating this database.') +param restorableDroppedDatabaseId string = '' + +@description('Conditional. Specifies the uri of the storage container where backups for this restore are stored. Required if createMode is RestoreExternalBackup.') +param storageContainerUri string = '' + +@description('Conditional. Specifies the storage container sas token. Required if createMode is RestoreExternalBackup.') +@secure() +param storageContainerSasToken string = '' + +@description('Conditional. The resource identifier of the recoverable database associated with create operation of this database. Required if createMode is Recovery.') +param recoverableDatabaseId string = '' + +@description('Conditional. The resource ID of the Long Term Retention backup to be used for restore of this managed database. Required if createMode is RestoreLongTermRetentionBackup.') +param longTermRetentionBackupResourceId string = '' + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. The configuration for the backup short term retention policy definition.') +param backupShortTermRetentionPoliciesObj object = {} + +@description('Optional. The configuration for the backup long term retention policy definition.') +param backupLongTermRetentionPoliciesObj object = {} + +@description('Optional. Tags of the resource.') +param tags object? + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName +} + +resource database 'Microsoft.Sql/managedInstances/databases@2023-08-01-preview' = { + name: name + parent: managedInstance + location: location + tags: tags + properties: { + collation: empty(collation) ? null : collation + restorePointInTime: empty(restorePointInTime) ? null : restorePointInTime + catalogCollation: empty(catalogCollation) ? null : catalogCollation + createMode: empty(createMode) ? null : createMode + storageContainerUri: empty(storageContainerUri) ? null : storageContainerUri + sourceDatabaseId: empty(sourceDatabaseId) ? null : sourceDatabaseId + restorableDroppedDatabaseId: empty(restorableDroppedDatabaseId) ? null : restorableDroppedDatabaseId + storageContainerSasToken: empty(storageContainerSasToken) ? null : storageContainerSasToken + recoverableDatabaseId: empty(recoverableDatabaseId) ? null : recoverableDatabaseId + longTermRetentionBackupResourceId: empty(longTermRetentionBackupResourceId) ? null : longTermRetentionBackupResourceId + } +} + +resource database_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot delete or modify the resource or child resources.' + } + scope: database +} + +resource database_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + logs: [ + for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' } ]): { + categoryGroup: group.?categoryGroup + category: group.?category + enabled: group.?enabled ?? true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: database + } +] + +module database_backupShortTermRetentionPolicy 'backup-short-term-retention-policy/main.bicep' = if (!empty(backupShortTermRetentionPoliciesObj)) { + name: '${deployment().name}-BackupShortTRetPol' + params: { + managedInstanceName: managedInstanceName + databaseName: last(split(database.name, '/'))! + name: backupShortTermRetentionPoliciesObj.name + retentionDays: contains(backupShortTermRetentionPoliciesObj, 'retentionDays') ? backupShortTermRetentionPoliciesObj.retentionDays : 35 + } +} + +module database_backupLongTermRetentionPolicy 'backup-long-term-retention-policy/main.bicep' = if (!empty(backupLongTermRetentionPoliciesObj)) { + name: '${deployment().name}-BackupLongTRetPol' + params: { + managedInstanceName: managedInstanceName + databaseName: last(split(database.name, '/'))! + name: backupLongTermRetentionPoliciesObj.name + weekOfYear: contains(backupLongTermRetentionPoliciesObj, 'weekOfYear') ? backupLongTermRetentionPoliciesObj.weekOfYear : 5 + weeklyRetention: contains(backupLongTermRetentionPoliciesObj, 'weeklyRetention') ? backupLongTermRetentionPoliciesObj.weeklyRetention : 'P1M' + monthlyRetention: contains(backupLongTermRetentionPoliciesObj, 'monthlyRetention') ? backupLongTermRetentionPoliciesObj.monthlyRetention : 'P1Y' + yearlyRetention: contains(backupLongTermRetentionPoliciesObj, 'yearlyRetention') ? backupLongTermRetentionPoliciesObj.yearlyRetention : 'P5Y' + } +} + +@description('The name of the deployed database.') +output name string = database.name + +@description('The resource ID of the deployed database.') +output resourceId string = database.id + +@description('The resource group the database was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = database.location + +// =============== // +// Definitions // +// =============== // + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to \'\' to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @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.') + categoryGroup: string? + }[]? + + @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')? + + @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? + + @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.') + storageAccountResourceId: string? + + @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.') + eventHubAuthorizationRuleResourceId: string? + + @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.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +}[]? diff --git a/avm/res/sql/managed-instance/database/main.json b/avm/res/sql/managed-instance/database/main.json new file mode 100644 index 0000000000..8ae32849aa --- /dev/null +++ b/avm/res/sql/managed-instance/database/main.json @@ -0,0 +1,596 @@ +{ + "$schema": "https://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": "15831945430809011826" + }, + "name": "SQL Managed Instance Databases", + "description": "This module deploys a SQL Managed Instance Database.", + "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 + }, + "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." + } + } + } + }, + "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. The name of the SQL managed instance database." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "collation": { + "type": "string", + "defaultValue": "SQL_Latin1_General_CP1_CI_AS", + "metadata": { + "description": "Optional. Collation of the managed instance database." + } + }, + "catalogCollation": { + "type": "string", + "defaultValue": "SQL_Latin1_General_CP1_CI_AS", + "metadata": { + "description": "Optional. Collation of the managed instance." + } + }, + "createMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "RestoreExternalBackup", + "PointInTimeRestore", + "Recovery", + "RestoreLongTermRetentionBackup" + ], + "metadata": { + "description": "Optional. Managed database create mode. PointInTimeRestore: Create a database by restoring a point in time backup of an existing database. SourceDatabaseName, SourceManagedInstanceName and PointInTime must be specified. RestoreExternalBackup: Create a database by restoring from external backup files. Collation, StorageContainerUri and StorageContainerSasToken must be specified. Recovery: Creates a database by restoring a geo-replicated backup. RecoverableDatabaseId must be specified as the recoverable database resource ID to restore. RestoreLongTermRetentionBackup: Create a database by restoring from a long term retention backup (longTermRetentionBackupResourceId required)." + } + }, + "sourceDatabaseId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource identifier of the source database associated with create operation of this database. Required if createMode is PointInTimeRestore." + } + }, + "restorePointInTime": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. Required if createMode is PointInTimeRestore." + } + }, + "restorableDroppedDatabaseId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The restorable dropped database resource ID to restore when creating this database." + } + }, + "storageContainerUri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. Specifies the uri of the storage container where backups for this restore are stored. Required if createMode is RestoreExternalBackup." + } + }, + "storageContainerSasToken": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Conditional. Specifies the storage container sas token. Required if createMode is RestoreExternalBackup." + } + }, + "recoverableDatabaseId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource identifier of the recoverable database associated with create operation of this database. Required if createMode is Recovery." + } + }, + "longTermRetentionBackupResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of the Long Term Retention backup to be used for restore of this managed database. Required if createMode is RestoreLongTermRetentionBackup." + } + }, + "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." + } + }, + "backupShortTermRetentionPoliciesObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The configuration for the backup short term retention policy definition." + } + }, + "backupLongTermRetentionPoliciesObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The configuration for the backup long term retention policy definition." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "managedInstance": { + "existing": true, + "type": "Microsoft.Sql/managedInstances", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('managedInstanceName')]" + }, + "database": { + "type": "Microsoft.Sql/managedInstances/databases", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "collation": "[if(empty(parameters('collation')), null(), parameters('collation'))]", + "restorePointInTime": "[if(empty(parameters('restorePointInTime')), null(), parameters('restorePointInTime'))]", + "catalogCollation": "[if(empty(parameters('catalogCollation')), null(), parameters('catalogCollation'))]", + "createMode": "[if(empty(parameters('createMode')), null(), parameters('createMode'))]", + "storageContainerUri": "[if(empty(parameters('storageContainerUri')), null(), parameters('storageContainerUri'))]", + "sourceDatabaseId": "[if(empty(parameters('sourceDatabaseId')), null(), parameters('sourceDatabaseId'))]", + "restorableDroppedDatabaseId": "[if(empty(parameters('restorableDroppedDatabaseId')), null(), parameters('restorableDroppedDatabaseId'))]", + "storageContainerSasToken": "[if(empty(parameters('storageContainerSasToken')), null(), parameters('storageContainerSasToken'))]", + "recoverableDatabaseId": "[if(empty(parameters('recoverableDatabaseId')), null(), parameters('recoverableDatabaseId'))]", + "longTermRetentionBackupResourceId": "[if(empty(parameters('longTermRetentionBackupResourceId')), null(), parameters('longTermRetentionBackupResourceId'))]" + }, + "dependsOn": [ + "managedInstance" + ] + }, + "database_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Sql/managedInstances/{0}/databases/{1}', parameters('managedInstanceName'), 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": [ + "database" + ] + }, + "database_diagnosticSettings": { + "copy": { + "name": "database_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Sql/managedInstances/{0}/databases/{1}', parameters('managedInstanceName'), 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": [ + "database" + ] + }, + "database_backupShortTermRetentionPolicy": { + "condition": "[not(empty(parameters('backupShortTermRetentionPoliciesObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-BackupShortTRetPol', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('managedInstanceName')]" + }, + "databaseName": { + "value": "[last(split(parameters('name'), '/'))]" + }, + "name": { + "value": "[parameters('backupShortTermRetentionPoliciesObj').name]" + }, + "retentionDays": "[if(contains(parameters('backupShortTermRetentionPoliciesObj'), 'retentionDays'), createObject('value', parameters('backupShortTermRetentionPoliciesObj').retentionDays), createObject('value', 35))]" + }, + "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": "4816864047763535590" + }, + "name": "SQL Managed Instance Database Backup Short-Term Retention Policies", + "description": "This module deploys a SQL Managed Instance Database Backup Short-Term Retention Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Short Term Retention backup policy. For example \"default\"." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "retentionDays": { + "type": "int", + "defaultValue": 35, + "metadata": { + "description": "Optional. The backup retention period in days. This is how many days Point-in-Time Restore will be supported." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]", + "properties": { + "retentionDays": "[parameters('retentionDays')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database backup short-term retention policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database backup short-term retention policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database backup short-term retention policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "database" + ] + }, + "database_backupLongTermRetentionPolicy": { + "condition": "[not(empty(parameters('backupLongTermRetentionPoliciesObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-BackupLongTRetPol', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('managedInstanceName')]" + }, + "databaseName": { + "value": "[last(split(parameters('name'), '/'))]" + }, + "name": { + "value": "[parameters('backupLongTermRetentionPoliciesObj').name]" + }, + "weekOfYear": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'weekOfYear'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').weekOfYear), createObject('value', 5))]", + "weeklyRetention": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'weeklyRetention'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').weeklyRetention), createObject('value', 'P1M'))]", + "monthlyRetention": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'monthlyRetention'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').monthlyRetention), createObject('value', 'P1Y'))]", + "yearlyRetention": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'yearlyRetention'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').yearlyRetention), createObject('value', 'P5Y'))]" + }, + "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": "2315344113299493188" + }, + "name": "SQL Managed Instance Database Backup Long-Term Retention Policies", + "description": "This module deploys a SQL Managed Instance Database Backup Long-Term Retention Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Long Term Retention backup policy. For example \"default\"." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed instance database. Required if the template is used in a standalone deployment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed instance. Required if the template is used in a standalone deployment." + } + }, + "weekOfYear": { + "type": "int", + "defaultValue": 5, + "metadata": { + "description": "Optional. The week of year to take the yearly backup in an ISO 8601 format." + } + }, + "weeklyRetention": { + "type": "string", + "defaultValue": "P1M", + "metadata": { + "description": "Optional. The weekly retention policy for an LTR backup in an ISO 8601 format." + } + }, + "monthlyRetention": { + "type": "string", + "defaultValue": "P1Y", + "metadata": { + "description": "Optional. The monthly retention policy for an LTR backup in an ISO 8601 format." + } + }, + "yearlyRetention": { + "type": "string", + "defaultValue": "P5Y", + "metadata": { + "description": "Optional. The yearly retention policy for an LTR backup in an ISO 8601 format." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies", + "apiVersion": "2022-05-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]", + "properties": { + "monthlyRetention": "[parameters('monthlyRetention')]", + "weeklyRetention": "[parameters('weeklyRetention')]", + "weekOfYear": "[parameters('weekOfYear')]", + "yearlyRetention": "[parameters('yearlyRetention')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database backup long-term retention policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database backup long-term retention policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database backup long-term retention policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "database" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases', parameters('managedInstanceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the database was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('database', '2023-08-01-preview', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/encryption-protector/README.md b/avm/res/sql/managed-instance/encryption-protector/README.md new file mode 100644 index 0000000000..18c0a5d1f8 --- /dev/null +++ b/avm/res/sql/managed-instance/encryption-protector/README.md @@ -0,0 +1,92 @@ +# SQL Managed Instance Encryption Protector `[Microsoft.Sql/managedInstances/encryptionProtector]` + +This module deploys a SQL Managed Instance Encryption Protector. + +## 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.Sql/managedInstances/encryptionProtector` | [2022-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/managedInstances/encryptionProtector) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`serverKeyName`](#parameter-serverkeyname) | string | The name of the SQL managed instance key. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`autoRotationEnabled`](#parameter-autorotationenabled) | bool | Key auto rotation opt-in flag. | +| [`serverKeyType`](#parameter-serverkeytype) | string | The encryption protector type like "ServiceManaged", "AzureKeyVault". | + +### Parameter: `serverKeyName` + +The name of the SQL managed instance key. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `autoRotationEnabled` + +Key auto rotation opt-in flag. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `serverKeyType` + +The encryption protector type like "ServiceManaged", "AzureKeyVault". + +- Required: No +- Type: string +- Default: `'ServiceManaged'` +- Allowed: + ```Bicep + [ + 'AzureKeyVault' + 'ServiceManaged' + ] + ``` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed managed instance encryption protector. | +| `resourceGroupName` | string | The resource group of the deployed managed instance encryption protector. | +| `resourceId` | string | The resource ID of the deployed managed instance encryption protector. | + +## 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/sql/managed-instance/encryption-protector/main.bicep b/avm/res/sql/managed-instance/encryption-protector/main.bicep new file mode 100644 index 0000000000..d3b89a6f22 --- /dev/null +++ b/avm/res/sql/managed-instance/encryption-protector/main.bicep @@ -0,0 +1,42 @@ +metadata name = 'SQL Managed Instance Encryption Protector' +metadata description = 'This module deploys a SQL Managed Instance Encryption Protector.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Required. The name of the SQL managed instance key.') +param serverKeyName string + +@description('Optional. The encryption protector type like "ServiceManaged", "AzureKeyVault".') +@allowed([ + 'AzureKeyVault' + 'ServiceManaged' +]) +param serverKeyType string = 'ServiceManaged' + +@description('Optional. Key auto rotation opt-in flag.') +param autoRotationEnabled bool = false + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName +} + +resource encryptionProtector 'Microsoft.Sql/managedInstances/encryptionProtector@2022-05-01-preview' = { + name: 'current' + parent: managedInstance + properties: { + autoRotationEnabled: autoRotationEnabled + serverKeyName: serverKeyName + serverKeyType: serverKeyType + } +} + +@description('The name of the deployed managed instance encryption protector.') +output name string = encryptionProtector.name + +@description('The resource ID of the deployed managed instance encryption protector.') +output resourceId string = encryptionProtector.id + +@description('The resource group of the deployed managed instance encryption protector.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/encryption-protector/main.json b/avm/res/sql/managed-instance/encryption-protector/main.json new file mode 100644 index 0000000000..54edff7908 --- /dev/null +++ b/avm/res/sql/managed-instance/encryption-protector/main.json @@ -0,0 +1,81 @@ +{ + "$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": "13463643567330956322" + }, + "name": "SQL Managed Instance Encryption Protector", + "description": "This module deploys a SQL Managed Instance Encryption Protector.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "serverKeyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the SQL managed instance key." + } + }, + "serverKeyType": { + "type": "string", + "defaultValue": "ServiceManaged", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], + "metadata": { + "description": "Optional. The encryption protector type like \"ServiceManaged\", \"AzureKeyVault\"." + } + }, + "autoRotationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Key auto rotation opt-in flag." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/encryptionProtector", + "apiVersion": "2022-05-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), 'current')]", + "properties": { + "autoRotationEnabled": "[parameters('autoRotationEnabled')]", + "serverKeyName": "[parameters('serverKeyName')]", + "serverKeyType": "[parameters('serverKeyType')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance encryption protector." + }, + "value": "current" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance encryption protector." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/encryptionProtector', parameters('managedInstanceName'), 'current')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance encryption protector." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/key/README.md b/avm/res/sql/managed-instance/key/README.md new file mode 100644 index 0000000000..3cc7f5cf6e --- /dev/null +++ b/avm/res/sql/managed-instance/key/README.md @@ -0,0 +1,92 @@ +# SQL Managed Instance Keys `[Microsoft.Sql/managedInstances/keys]` + +This module deploys a SQL Managed Instance Key. + +## 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.Sql/managedInstances/keys` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/keys) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the key. Must follow the [__] pattern. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`serverKeyType`](#parameter-serverkeytype) | string | The encryption protector type like "ServiceManaged", "AzureKeyVault". | +| [`uri`](#parameter-uri) | string | The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required. | + +### Parameter: `name` + +The name of the key. Must follow the [__] pattern. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `serverKeyType` + +The encryption protector type like "ServiceManaged", "AzureKeyVault". + +- Required: No +- Type: string +- Default: `'ServiceManaged'` +- Allowed: + ```Bicep + [ + 'AzureKeyVault' + 'ServiceManaged' + ] + ``` + +### Parameter: `uri` + +The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required. + +- Required: No +- Type: string +- Default: `''` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed managed instance key. | +| `resourceGroupName` | string | The resource group of the deployed managed instance key. | +| `resourceId` | string | The resource ID of the deployed managed instance key. | + +## 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/sql/managed-instance/key/main.bicep b/avm/res/sql/managed-instance/key/main.bicep new file mode 100644 index 0000000000..d95ac84f4b --- /dev/null +++ b/avm/res/sql/managed-instance/key/main.bicep @@ -0,0 +1,47 @@ +metadata name = 'SQL Managed Instance Keys' +metadata description = 'This module deploys a SQL Managed Instance Key.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the key. Must follow the [__] pattern.') +param name string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. The encryption protector type like "ServiceManaged", "AzureKeyVault".') +@allowed([ + 'AzureKeyVault' + 'ServiceManaged' +]) +param serverKeyType string = 'ServiceManaged' + +@description('Optional. The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required.') +param uri string = '' + +var splittedKeyUri = split(uri, '/') + +// if serverManaged, use serverManaged, if uri provided use concated uri value +// MUST match the pattern '__' +var serverKeyName = empty(uri) ? 'ServiceManaged' : '${split(splittedKeyUri[2], '.')[0]}_${splittedKeyUri[4]}_${splittedKeyUri[5]}' + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName +} + +resource key 'Microsoft.Sql/managedInstances/keys@2023-08-01-preview' = { + name: !empty(name) ? name : serverKeyName + parent: managedInstance + properties: { + serverKeyType: serverKeyType + uri: uri + } +} + +@description('The name of the deployed managed instance key.') +output name string = key.name + +@description('The resource ID of the deployed managed instance key.') +output resourceId string = key.id + +@description('The resource group of the deployed managed instance key.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/key/main.json b/avm/res/sql/managed-instance/key/main.json new file mode 100644 index 0000000000..403f395866 --- /dev/null +++ b/avm/res/sql/managed-instance/key/main.json @@ -0,0 +1,84 @@ +{ + "$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": "101283708334468532" + }, + "name": "SQL Managed Instance Keys", + "description": "This module deploys a SQL Managed Instance Key.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the key. Must follow the [__] pattern." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "serverKeyType": { + "type": "string", + "defaultValue": "ServiceManaged", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], + "metadata": { + "description": "Optional. The encryption protector type like \"ServiceManaged\", \"AzureKeyVault\"." + } + }, + "uri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required." + } + } + }, + "variables": { + "splittedKeyUri": "[split(parameters('uri'), '/')]", + "serverKeyName": "[if(empty(parameters('uri')), 'ServiceManaged', format('{0}_{1}_{2}', split(variables('splittedKeyUri')[2], '.')[0], variables('splittedKeyUri')[4], variables('splittedKeyUri')[5]))]" + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/keys", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), if(not(empty(parameters('name'))), parameters('name'), variables('serverKeyName')))]", + "properties": { + "serverKeyType": "[parameters('serverKeyType')]", + "uri": "[parameters('uri')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance key." + }, + "value": "[if(not(empty(parameters('name'))), parameters('name'), variables('serverKeyName'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance key." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/keys', parameters('managedInstanceName'), if(not(empty(parameters('name'))), parameters('name'), variables('serverKeyName')))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance key." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/main.bicep b/avm/res/sql/managed-instance/main.bicep new file mode 100644 index 0000000000..c180440c4d --- /dev/null +++ b/avm/res/sql/managed-instance/main.bicep @@ -0,0 +1,460 @@ +metadata name = 'SQL Managed Instances' +metadata description = 'This module deploys a SQL Managed Instance.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the SQL managed instance.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. The username used to establish jumpbox VMs.') +param administratorLogin string + +@description('Required. The password given to the admin user.') +@secure() +param administratorLoginPassword string + +@description('Required. The fully qualified resource ID of the subnet on which the SQL managed instance will be placed.') +param subnetResourceId string + +@description('Optional. The name of the SKU, typically, a letter + Number code, e.g. P3.') +param skuName string = 'GP_Gen5' + +@description('Optional. The tier or edition of the particular SKU, e.g. Basic, Premium.') +param skuTier string = 'GeneralPurpose' + +@description('Optional. Storage size in GB. Minimum value: 32. Maximum value: 8192. Increments of 32 GB allowed only.') +param storageSizeInGB int = 32 + +@description('Optional. The number of vCores. Allowed values: 8, 16, 24, 32, 40, 64, 80.') +param vCores int = 4 + +@description('Optional. The license type. Possible values are \'LicenseIncluded\' (regular price inclusive of a new SQL license) and \'BasePrice\' (discounted AHB price for bringing your own SQL licenses).') +@allowed([ + 'LicenseIncluded' + 'BasePrice' +]) +param licenseType string = 'LicenseIncluded' + +@description('Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here.') +param hardwareFamily string = 'Gen5' + +@description('Optional. Whether or not multi-az is enabled.') +param zoneRedundant bool = false + +@description('Optional. Service principal type. If using AD Authentication and applying Admin, must be set to `SystemAssigned`. Then Global Admin must allow Reader access to Azure AD for the Service Principal.') +@allowed([ + 'None' + 'SystemAssigned' +]) +param servicePrincipal string = 'None' + +@description('Optional. Specifies the mode of database creation. Default: Regular instance creation. Restore: Creates an instance by restoring a set of backups to specific point in time. RestorePointInTime and SourceManagedInstanceId must be specified.') +@allowed([ + 'Default' + 'PointInTimeRestore' +]) +param managedInstanceCreateMode string = 'Default' + +@description('Optional. The resource ID of another managed instance whose DNS zone this managed instance will share after creation.') +param dnsZonePartner string = '' + +@description('Optional. Collation of the managed instance.') +param collation string = 'SQL_Latin1_General_CP1_CI_AS' + +@description('Optional. Connection type used for connecting to the instance.') +@allowed([ + 'Proxy' + 'Redirect' + 'Default' +]) +param proxyOverride string = 'Proxy' + +@description('Optional. Whether or not the public data endpoint is enabled.') +param publicDataEndpointEnabled bool = false + +@description('Optional. ID of the timezone. Allowed values are timezones supported by Windows.') +param timezoneId string = 'UTC' + +@description('Optional. The resource ID of the instance pool this managed server belongs to.') +param instancePoolResourceId string = '' + +@description('Optional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database.') +param restorePointInTime string = '' + +@description('Optional. The resource identifier of the source managed instance associated with create operation of this instance.') +param sourceManagedInstanceId string = '' + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. The managed identity definition for this resource.') +param managedIdentities managedIdentitiesType + +@description('Conditional. The resource ID of a user assigned identity to be used by default. Required if "userAssignedIdentities" is not empty.') +param primaryUserAssignedIdentityId string = '' + +@description('Optional. Databases to create in this server.') +param databases array = [] + +@description('Optional. The vulnerability assessment configuration.') +param vulnerabilityAssessmentsObj object = {} + +@description('Optional. The security alert policy configuration.') +param securityAlertPoliciesObj object = {} + +@description('Optional. The keys to configure.') +param keys array = [] + +@description('Optional. The encryption protection configuration.') +param encryptionProtectorObj object = {} + +@description('Optional. The administrator configuration.') +param administratorsObj object = {} + +@description('Optional. Minimal TLS version allowed.') +@allowed([ + 'None' + '1.0' + '1.1' + '1.2' +]) +param minimalTlsVersion string = '1.2' + +@description('Optional. The storage account type used to store backups for this database.') +@allowed([ + 'Geo' + 'GeoZone' + 'Local' + 'Zone' +]) +param requestedBackupStorageRedundancy string = 'Geo' + +var formattedUserAssignedIdentities = reduce(map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, (cur, next) => union(cur, next)) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} } + +var identity = !empty(managedIdentities) ? { + type: (managedIdentities.?systemAssigned ?? false) ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : null) + userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null +} : null + + +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') + 'Reservation Purchaser': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'SQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9b7fa17d-e63e-47b0-bb0a-15c516ac86ec') + 'SQL Managed Instance Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d') + 'SQL Security Manager': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3') + 'SQL Server Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437') + 'SqlDb Migration Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '189207d4-bb67-4208-a635-b06afe8b2c57') + 'SqlMI Migration Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1d335eef-eee1-47fe-a9e0-53214eba8872') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: '46d3xbcp.res.sql-managedinstance.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' = { + name: name + location: location + tags: tags + identity: identity + sku: { + name: skuName + tier: skuTier + family: hardwareFamily + } + + properties: { + managedInstanceCreateMode: managedInstanceCreateMode + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + subnetId: subnetResourceId + licenseType: licenseType + vCores: vCores + storageSizeInGB: storageSizeInGB + collation: collation + dnsZonePartner: !empty(dnsZonePartner) ? dnsZonePartner : null + publicDataEndpointEnabled: publicDataEndpointEnabled + sourceManagedInstanceId: !empty(sourceManagedInstanceId) ? sourceManagedInstanceId : null + restorePointInTime: !empty(restorePointInTime) ? restorePointInTime : null + proxyOverride: proxyOverride + timezoneId: timezoneId + instancePoolId: !empty(instancePoolResourceId) ? instancePoolResourceId : null + primaryUserAssignedIdentityId: !empty(primaryUserAssignedIdentityId) ? primaryUserAssignedIdentityId : null + requestedBackupStorageRedundancy: requestedBackupStorageRedundancy + zoneRedundant: zoneRedundant + servicePrincipal: { + type: servicePrincipal + } + minimalTlsVersion: minimalTlsVersion + } +} + +resource managedInstance_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot delete or modify the resource or child resources.' + } + scope: managedInstance +} + +resource managedInstance_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + logs: [ + for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' }]): { + categoryGroup: group.?categoryGroup + category: group.?category + enabled: group.?enabled ?? true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: managedInstance + } +] + +resource managedInstance_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(managedInstance.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 + } + scope: managedInstance + } +] + +module managedInstance_databases 'database/main.bicep' = [for (database, index) in databases: { + name: '${uniqueString(deployment().name, location)}-SqlMi-DB-${index}' + params: { + name: database.name + managedInstanceName: managedInstance.name + catalogCollation: contains(database, 'catalogCollation') ? database.catalogCollation : 'SQL_Latin1_General_CP1_CI_AS' + collation: contains(database, 'collation') ? database.collation : 'SQL_Latin1_General_CP1_CI_AS' + createMode: contains(database, 'createMode') ? database.createMode : 'Default' + diagnosticSettings: database.?diagnosticSettings + location: contains(database, 'location') ? database.location : managedInstance.location + lock: database.?lock ?? lock + longTermRetentionBackupResourceId: contains(database, 'longTermRetentionBackupResourceId') ? database.longTermRetentionBackupResourceId : '' + recoverableDatabaseId: contains(database, 'recoverableDatabaseId') ? database.recoverableDatabaseId : '' + restorableDroppedDatabaseId: contains(database, 'restorableDroppedDatabaseId') ? database.restorableDroppedDatabaseId : '' + restorePointInTime: contains(database, 'restorePointInTime') ? database.restorePointInTime : '' + sourceDatabaseId: contains(database, 'sourceDatabaseId') ? database.sourceDatabaseId : '' + storageContainerSasToken: contains(database, 'storageContainerSasToken') ? database.storageContainerSasToken : '' + storageContainerUri: contains(database, 'storageContainerUri') ? database.storageContainerUri : '' + tags: database.?tags ?? tags + backupShortTermRetentionPoliciesObj: contains(database, 'backupShortTermRetentionPolicies') ? database.backupShortTermRetentionPolicies : {} + backupLongTermRetentionPoliciesObj: contains(database, 'backupLongTermRetentionPolicies') ? database.backupLongTermRetentionPolicies : {} + } +}] + +module managedInstance_securityAlertPolicy 'security-alert-policy/main.bicep' = if (!empty(securityAlertPoliciesObj)) { + name: '${uniqueString(deployment().name, location)}-SqlMi-SecAlertPol' + params: { + managedInstanceName: managedInstance.name + name: securityAlertPoliciesObj.name + emailAccountAdmins: contains(securityAlertPoliciesObj, 'emailAccountAdmins') ? securityAlertPoliciesObj.emailAccountAdmins : false + state: contains(securityAlertPoliciesObj, 'state') ? securityAlertPoliciesObj.state : 'Disabled' + } +} + +module managedInstance_vulnerabilityAssessment 'vulnerability-assessment/main.bicep' = if (!empty(vulnerabilityAssessmentsObj) && (managedIdentities.?systemAssigned ?? false)) { + name: '${uniqueString(deployment().name, location)}-SqlMi-VulnAssessm' + params: { + managedInstanceName: managedInstance.name + name: vulnerabilityAssessmentsObj.name + recurringScansEmails: contains(vulnerabilityAssessmentsObj, 'recurringScansEmails') ? vulnerabilityAssessmentsObj.recurringScansEmails : [] + recurringScansEmailSubscriptionAdmins: contains(vulnerabilityAssessmentsObj, 'recurringScansEmailSubscriptionAdmins') ? vulnerabilityAssessmentsObj.recurringScansEmailSubscriptionAdmins : false + recurringScansIsEnabled: contains(vulnerabilityAssessmentsObj, 'recurringScansIsEnabled') ? vulnerabilityAssessmentsObj.recurringScansIsEnabled : false + storageAccountResourceId: vulnerabilityAssessmentsObj.storageAccountResourceId + useStorageAccountAccessKey: contains(vulnerabilityAssessmentsObj, 'useStorageAccountAccessKey') ? vulnerabilityAssessmentsObj.useStorageAccountAccessKey : false + createStorageRoleAssignment: contains(vulnerabilityAssessmentsObj, 'createStorageRoleAssignment') ? vulnerabilityAssessmentsObj.createStorageRoleAssignment : true + } + dependsOn: [ + managedInstance_securityAlertPolicy + ] +} + +module managedInstance_keys 'key/main.bicep' = [for (key, index) in keys: { + name: '${uniqueString(deployment().name, location)}-SqlMi-Key-${index}' + params: { + name: key.name + managedInstanceName: managedInstance.name + serverKeyType: contains(key, 'serverKeyType') ? key.serverKeyType : 'ServiceManaged' + uri: contains(key, 'uri') ? key.uri : '' + } +}] + +module managedInstance_encryptionProtector 'encryption-protector/main.bicep' = if (!empty(encryptionProtectorObj)) { + name: '${uniqueString(deployment().name, location)}-SqlMi-EncryProtector' + params: { + managedInstanceName: managedInstance.name + serverKeyName: encryptionProtectorObj.serverKeyName + serverKeyType: contains(encryptionProtectorObj, 'serverKeyType') ? encryptionProtectorObj.serverKeyType : 'ServiceManaged' + autoRotationEnabled: contains(encryptionProtectorObj, 'autoRotationEnabled') ? encryptionProtectorObj.autoRotationEnabled : true + } + dependsOn: [ + managedInstance_keys + ] +} + +module managedInstance_administrator 'administrator/main.bicep' = if (!empty(administratorsObj)) { + name: '${uniqueString(deployment().name, location)}-SqlMi-Admin' + params: { + managedInstanceName: managedInstance.name + login: administratorsObj.name + sid: administratorsObj.sid + tenantId: contains(administratorsObj, 'tenantId') ? administratorsObj.tenantId : '' + } +} + +@description('The name of the deployed managed instance.') +output name string = managedInstance.name + +@description('The resource ID of the deployed managed instance.') +output resourceId string = managedInstance.id + +@description('The resource group of the deployed managed instance.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedMIPrincipalId string = managedInstance.?identity.?principalId ?? '' + +@description('The location the resource was deployed into.') +output location string = managedInstance.location + +// =============== // +// Definitions // +// =============== // + +type managedIdentitiesType = { + @description('Optional. Enables system assigned managed identity on the resource.') + systemAssigned: bool? + + @description('Optional. The resource ID(s) to assign to the resource.') + userAssignedResourceIds: string[]? +}? + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type roleAssignmentType = { + @description('Required. The role to assign. You can provide either the display name of the 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 + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? + + @description('Optional. The description of the role assignment.') + description: string? + + @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +}[]? + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @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.') + categoryGroup: string? + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection.') + metricCategories: { + @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.') + category: string + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @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')? + + @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? + + @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.') + storageAccountResourceId: string? + + @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.') + eventHubAuthorizationRuleResourceId: string? + + @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.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +}[]? diff --git a/avm/res/sql/managed-instance/main.json b/avm/res/sql/managed-instance/main.json new file mode 100644 index 0000000000..1786333be1 --- /dev/null +++ b/avm/res/sql/managed-instance/main.json @@ -0,0 +1,1986 @@ +{ + "$schema": "https://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": "9634979761136417752" + }, + "name": "SQL Managed Instances", + "description": "This module deploys a SQL Managed Instance.", + "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. The name of the SQL managed instance." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "administratorLogin": { + "type": "string", + "metadata": { + "description": "Required. The username used to establish jumpbox VMs." + } + }, + "administratorLoginPassword": { + "type": "securestring", + "metadata": { + "description": "Required. The password given to the admin user." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The fully qualified resource ID of the subnet on which the SQL managed instance will be placed." + } + }, + "skuName": { + "type": "string", + "defaultValue": "GP_Gen5", + "metadata": { + "description": "Optional. The name of the SKU, typically, a letter + Number code, e.g. P3." + } + }, + "skuTier": { + "type": "string", + "defaultValue": "GeneralPurpose", + "metadata": { + "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." + } + }, + "storageSizeInGB": { + "type": "int", + "defaultValue": 32, + "metadata": { + "description": "Optional. Storage size in GB. Minimum value: 32. Maximum value: 8192. Increments of 32 GB allowed only." + } + }, + "vCores": { + "type": "int", + "defaultValue": 4, + "metadata": { + "description": "Optional. The number of vCores. Allowed values: 8, 16, 24, 32, 40, 64, 80." + } + }, + "licenseType": { + "type": "string", + "defaultValue": "LicenseIncluded", + "allowedValues": [ + "LicenseIncluded", + "BasePrice" + ], + "metadata": { + "description": "Optional. The license type. Possible values are 'LicenseIncluded' (regular price inclusive of a new SQL license) and 'BasePrice' (discounted AHB price for bringing your own SQL licenses)." + } + }, + "hardwareFamily": { + "type": "string", + "defaultValue": "Gen5", + "metadata": { + "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." + } + }, + "zoneRedundant": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether or not multi-az is enabled." + } + }, + "servicePrincipal": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned" + ], + "metadata": { + "description": "Optional. Service principal type. If using AD Authentication and applying Admin, must be set to `SystemAssigned`. Then Global Admin must allow Reader access to Azure AD for the Service Principal." + } + }, + "managedInstanceCreateMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "PointInTimeRestore" + ], + "metadata": { + "description": "Optional. Specifies the mode of database creation. Default: Regular instance creation. Restore: Creates an instance by restoring a set of backups to specific point in time. RestorePointInTime and SourceManagedInstanceId must be specified." + } + }, + "dnsZonePartner": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of another managed instance whose DNS zone this managed instance will share after creation." + } + }, + "collation": { + "type": "string", + "defaultValue": "SQL_Latin1_General_CP1_CI_AS", + "metadata": { + "description": "Optional. Collation of the managed instance." + } + }, + "proxyOverride": { + "type": "string", + "defaultValue": "Proxy", + "allowedValues": [ + "Proxy", + "Redirect", + "Default" + ], + "metadata": { + "description": "Optional. Connection type used for connecting to the instance." + } + }, + "publicDataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether or not the public data endpoint is enabled." + } + }, + "timezoneId": { + "type": "string", + "defaultValue": "UTC", + "metadata": { + "description": "Optional. ID of the timezone. Allowed values are timezones supported by Windows." + } + }, + "instancePoolResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the instance pool this managed server belongs to." + } + }, + "restorePointInTime": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database." + } + }, + "sourceManagedInstanceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource identifier of the source managed instance associated with create operation of this instance." + } + }, + "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." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "primaryUserAssignedIdentityId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of a user assigned identity to be used by default. Required if \"userAssignedIdentities\" is not empty." + } + }, + "databases": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Databases to create in this server." + } + }, + "vulnerabilityAssessmentsObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The vulnerability assessment configuration." + } + }, + "securityAlertPoliciesObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The security alert policy configuration." + } + }, + "keys": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The keys to configure." + } + }, + "encryptionProtectorObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The encryption protection configuration." + } + }, + "administratorsObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The administrator configuration." + } + }, + "minimalTlsVersion": { + "type": "string", + "defaultValue": "1.2", + "allowedValues": [ + "None", + "1.0", + "1.1", + "1.2" + ], + "metadata": { + "description": "Optional. Minimal TLS version allowed." + } + }, + "requestedBackupStorageRedundancy": { + "type": "string", + "defaultValue": "Geo", + "allowedValues": [ + "Geo", + "GeoZone", + "Local", + "Zone" + ], + "metadata": { + "description": "Optional. The storage account type used to store backups for this database." + } + } + }, + "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())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reservation Purchaser": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "SQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9b7fa17d-e63e-47b0-bb0a-15c516ac86ec')]", + "SQL Managed Instance Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d')]", + "SQL Security Manager": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3')]", + "SQL Server Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437')]", + "SqlDb Migration Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '189207d4-bb67-4208-a635-b06afe8b2c57')]", + "SqlMI Migration Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1d335eef-eee1-47fe-a9e0-53214eba8872')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.sql-managedinstance.{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" + } + } + } + } + }, + "managedInstance": { + "type": "Microsoft.Sql/managedInstances", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "sku": { + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]", + "family": "[parameters('hardwareFamily')]" + }, + "properties": { + "managedInstanceCreateMode": "[parameters('managedInstanceCreateMode')]", + "administratorLogin": "[parameters('administratorLogin')]", + "administratorLoginPassword": "[parameters('administratorLoginPassword')]", + "subnetId": "[parameters('subnetResourceId')]", + "licenseType": "[parameters('licenseType')]", + "vCores": "[parameters('vCores')]", + "storageSizeInGB": "[parameters('storageSizeInGB')]", + "collation": "[parameters('collation')]", + "dnsZonePartner": "[if(not(empty(parameters('dnsZonePartner'))), parameters('dnsZonePartner'), null())]", + "publicDataEndpointEnabled": "[parameters('publicDataEndpointEnabled')]", + "sourceManagedInstanceId": "[if(not(empty(parameters('sourceManagedInstanceId'))), parameters('sourceManagedInstanceId'), null())]", + "restorePointInTime": "[if(not(empty(parameters('restorePointInTime'))), parameters('restorePointInTime'), null())]", + "proxyOverride": "[parameters('proxyOverride')]", + "timezoneId": "[parameters('timezoneId')]", + "instancePoolId": "[if(not(empty(parameters('instancePoolResourceId'))), parameters('instancePoolResourceId'), null())]", + "primaryUserAssignedIdentityId": "[if(not(empty(parameters('primaryUserAssignedIdentityId'))), parameters('primaryUserAssignedIdentityId'), null())]", + "requestedBackupStorageRedundancy": "[parameters('requestedBackupStorageRedundancy')]", + "zoneRedundant": "[parameters('zoneRedundant')]", + "servicePrincipal": { + "type": "[parameters('servicePrincipal')]" + }, + "minimalTlsVersion": "[parameters('minimalTlsVersion')]" + } + }, + "managedInstance_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Sql/managedInstances/{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": [ + "managedInstance" + ] + }, + "managedInstance_diagnosticSettings": { + "copy": { + "name": "managedInstance_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Sql/managedInstances/{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": [ + "managedInstance" + ] + }, + "managedInstance_roleAssignments": { + "copy": { + "name": "managedInstance_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Sql/managedInstances/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Sql/managedInstances', 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": [ + "managedInstance" + ] + }, + "managedInstance_databases": { + "copy": { + "name": "managedInstance_databases", + "count": "[length(parameters('databases'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SqlMi-DB-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('databases')[copyIndex()].name]" + }, + "managedInstanceName": { + "value": "[parameters('name')]" + }, + "catalogCollation": "[if(contains(parameters('databases')[copyIndex()], 'catalogCollation'), createObject('value', parameters('databases')[copyIndex()].catalogCollation), createObject('value', 'SQL_Latin1_General_CP1_CI_AS'))]", + "collation": "[if(contains(parameters('databases')[copyIndex()], 'collation'), createObject('value', parameters('databases')[copyIndex()].collation), createObject('value', 'SQL_Latin1_General_CP1_CI_AS'))]", + "createMode": "[if(contains(parameters('databases')[copyIndex()], 'createMode'), createObject('value', parameters('databases')[copyIndex()].createMode), createObject('value', 'Default'))]", + "diagnosticSettings": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'diagnosticSettings')]" + }, + "location": "[if(contains(parameters('databases')[copyIndex()], 'location'), createObject('value', parameters('databases')[copyIndex()].location), createObject('value', reference('managedInstance', '2023-08-01-preview', 'full').location))]", + "lock": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'lock'), parameters('lock'))]" + }, + "longTermRetentionBackupResourceId": "[if(contains(parameters('databases')[copyIndex()], 'longTermRetentionBackupResourceId'), createObject('value', parameters('databases')[copyIndex()].longTermRetentionBackupResourceId), createObject('value', ''))]", + "recoverableDatabaseId": "[if(contains(parameters('databases')[copyIndex()], 'recoverableDatabaseId'), createObject('value', parameters('databases')[copyIndex()].recoverableDatabaseId), createObject('value', ''))]", + "restorableDroppedDatabaseId": "[if(contains(parameters('databases')[copyIndex()], 'restorableDroppedDatabaseId'), createObject('value', parameters('databases')[copyIndex()].restorableDroppedDatabaseId), createObject('value', ''))]", + "restorePointInTime": "[if(contains(parameters('databases')[copyIndex()], 'restorePointInTime'), createObject('value', parameters('databases')[copyIndex()].restorePointInTime), createObject('value', ''))]", + "sourceDatabaseId": "[if(contains(parameters('databases')[copyIndex()], 'sourceDatabaseId'), createObject('value', parameters('databases')[copyIndex()].sourceDatabaseId), createObject('value', ''))]", + "storageContainerSasToken": "[if(contains(parameters('databases')[copyIndex()], 'storageContainerSasToken'), createObject('value', parameters('databases')[copyIndex()].storageContainerSasToken), createObject('value', ''))]", + "storageContainerUri": "[if(contains(parameters('databases')[copyIndex()], 'storageContainerUri'), createObject('value', parameters('databases')[copyIndex()].storageContainerUri), createObject('value', ''))]", + "tags": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'tags'), parameters('tags'))]" + }, + "backupShortTermRetentionPoliciesObj": "[if(contains(parameters('databases')[copyIndex()], 'backupShortTermRetentionPolicies'), createObject('value', parameters('databases')[copyIndex()].backupShortTermRetentionPolicies), createObject('value', createObject()))]", + "backupLongTermRetentionPoliciesObj": "[if(contains(parameters('databases')[copyIndex()], 'backupLongTermRetentionPolicies'), createObject('value', parameters('databases')[copyIndex()].backupLongTermRetentionPolicies), 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.26.170.59819", + "templateHash": "15831945430809011826" + }, + "name": "SQL Managed Instance Databases", + "description": "This module deploys a SQL Managed Instance Database.", + "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 + }, + "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." + } + } + } + }, + "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. The name of the SQL managed instance database." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "collation": { + "type": "string", + "defaultValue": "SQL_Latin1_General_CP1_CI_AS", + "metadata": { + "description": "Optional. Collation of the managed instance database." + } + }, + "catalogCollation": { + "type": "string", + "defaultValue": "SQL_Latin1_General_CP1_CI_AS", + "metadata": { + "description": "Optional. Collation of the managed instance." + } + }, + "createMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "RestoreExternalBackup", + "PointInTimeRestore", + "Recovery", + "RestoreLongTermRetentionBackup" + ], + "metadata": { + "description": "Optional. Managed database create mode. PointInTimeRestore: Create a database by restoring a point in time backup of an existing database. SourceDatabaseName, SourceManagedInstanceName and PointInTime must be specified. RestoreExternalBackup: Create a database by restoring from external backup files. Collation, StorageContainerUri and StorageContainerSasToken must be specified. Recovery: Creates a database by restoring a geo-replicated backup. RecoverableDatabaseId must be specified as the recoverable database resource ID to restore. RestoreLongTermRetentionBackup: Create a database by restoring from a long term retention backup (longTermRetentionBackupResourceId required)." + } + }, + "sourceDatabaseId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource identifier of the source database associated with create operation of this database. Required if createMode is PointInTimeRestore." + } + }, + "restorePointInTime": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. Required if createMode is PointInTimeRestore." + } + }, + "restorableDroppedDatabaseId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The restorable dropped database resource ID to restore when creating this database." + } + }, + "storageContainerUri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. Specifies the uri of the storage container where backups for this restore are stored. Required if createMode is RestoreExternalBackup." + } + }, + "storageContainerSasToken": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Conditional. Specifies the storage container sas token. Required if createMode is RestoreExternalBackup." + } + }, + "recoverableDatabaseId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource identifier of the recoverable database associated with create operation of this database. Required if createMode is Recovery." + } + }, + "longTermRetentionBackupResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of the Long Term Retention backup to be used for restore of this managed database. Required if createMode is RestoreLongTermRetentionBackup." + } + }, + "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." + } + }, + "backupShortTermRetentionPoliciesObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The configuration for the backup short term retention policy definition." + } + }, + "backupLongTermRetentionPoliciesObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The configuration for the backup long term retention policy definition." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "managedInstance": { + "existing": true, + "type": "Microsoft.Sql/managedInstances", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('managedInstanceName')]" + }, + "database": { + "type": "Microsoft.Sql/managedInstances/databases", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "collation": "[if(empty(parameters('collation')), null(), parameters('collation'))]", + "restorePointInTime": "[if(empty(parameters('restorePointInTime')), null(), parameters('restorePointInTime'))]", + "catalogCollation": "[if(empty(parameters('catalogCollation')), null(), parameters('catalogCollation'))]", + "createMode": "[if(empty(parameters('createMode')), null(), parameters('createMode'))]", + "storageContainerUri": "[if(empty(parameters('storageContainerUri')), null(), parameters('storageContainerUri'))]", + "sourceDatabaseId": "[if(empty(parameters('sourceDatabaseId')), null(), parameters('sourceDatabaseId'))]", + "restorableDroppedDatabaseId": "[if(empty(parameters('restorableDroppedDatabaseId')), null(), parameters('restorableDroppedDatabaseId'))]", + "storageContainerSasToken": "[if(empty(parameters('storageContainerSasToken')), null(), parameters('storageContainerSasToken'))]", + "recoverableDatabaseId": "[if(empty(parameters('recoverableDatabaseId')), null(), parameters('recoverableDatabaseId'))]", + "longTermRetentionBackupResourceId": "[if(empty(parameters('longTermRetentionBackupResourceId')), null(), parameters('longTermRetentionBackupResourceId'))]" + }, + "dependsOn": [ + "managedInstance" + ] + }, + "database_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Sql/managedInstances/{0}/databases/{1}', parameters('managedInstanceName'), 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": [ + "database" + ] + }, + "database_diagnosticSettings": { + "copy": { + "name": "database_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Sql/managedInstances/{0}/databases/{1}', parameters('managedInstanceName'), 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": [ + "database" + ] + }, + "database_backupShortTermRetentionPolicy": { + "condition": "[not(empty(parameters('backupShortTermRetentionPoliciesObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-BackupShortTRetPol', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('managedInstanceName')]" + }, + "databaseName": { + "value": "[last(split(parameters('name'), '/'))]" + }, + "name": { + "value": "[parameters('backupShortTermRetentionPoliciesObj').name]" + }, + "retentionDays": "[if(contains(parameters('backupShortTermRetentionPoliciesObj'), 'retentionDays'), createObject('value', parameters('backupShortTermRetentionPoliciesObj').retentionDays), createObject('value', 35))]" + }, + "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": "4816864047763535590" + }, + "name": "SQL Managed Instance Database Backup Short-Term Retention Policies", + "description": "This module deploys a SQL Managed Instance Database Backup Short-Term Retention Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Short Term Retention backup policy. For example \"default\"." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "retentionDays": { + "type": "int", + "defaultValue": 35, + "metadata": { + "description": "Optional. The backup retention period in days. This is how many days Point-in-Time Restore will be supported." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]", + "properties": { + "retentionDays": "[parameters('retentionDays')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database backup short-term retention policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database backup short-term retention policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database backup short-term retention policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "database" + ] + }, + "database_backupLongTermRetentionPolicy": { + "condition": "[not(empty(parameters('backupLongTermRetentionPoliciesObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-BackupLongTRetPol', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('managedInstanceName')]" + }, + "databaseName": { + "value": "[last(split(parameters('name'), '/'))]" + }, + "name": { + "value": "[parameters('backupLongTermRetentionPoliciesObj').name]" + }, + "weekOfYear": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'weekOfYear'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').weekOfYear), createObject('value', 5))]", + "weeklyRetention": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'weeklyRetention'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').weeklyRetention), createObject('value', 'P1M'))]", + "monthlyRetention": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'monthlyRetention'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').monthlyRetention), createObject('value', 'P1Y'))]", + "yearlyRetention": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'yearlyRetention'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').yearlyRetention), createObject('value', 'P5Y'))]" + }, + "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": "2315344113299493188" + }, + "name": "SQL Managed Instance Database Backup Long-Term Retention Policies", + "description": "This module deploys a SQL Managed Instance Database Backup Long-Term Retention Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Long Term Retention backup policy. For example \"default\"." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed instance database. Required if the template is used in a standalone deployment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed instance. Required if the template is used in a standalone deployment." + } + }, + "weekOfYear": { + "type": "int", + "defaultValue": 5, + "metadata": { + "description": "Optional. The week of year to take the yearly backup in an ISO 8601 format." + } + }, + "weeklyRetention": { + "type": "string", + "defaultValue": "P1M", + "metadata": { + "description": "Optional. The weekly retention policy for an LTR backup in an ISO 8601 format." + } + }, + "monthlyRetention": { + "type": "string", + "defaultValue": "P1Y", + "metadata": { + "description": "Optional. The monthly retention policy for an LTR backup in an ISO 8601 format." + } + }, + "yearlyRetention": { + "type": "string", + "defaultValue": "P5Y", + "metadata": { + "description": "Optional. The yearly retention policy for an LTR backup in an ISO 8601 format." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies", + "apiVersion": "2022-05-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]", + "properties": { + "monthlyRetention": "[parameters('monthlyRetention')]", + "weeklyRetention": "[parameters('weeklyRetention')]", + "weekOfYear": "[parameters('weekOfYear')]", + "yearlyRetention": "[parameters('yearlyRetention')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database backup long-term retention policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database backup long-term retention policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database backup long-term retention policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "database" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases', parameters('managedInstanceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the database was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('database', '2023-08-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "managedInstance" + ] + }, + "managedInstance_securityAlertPolicy": { + "condition": "[not(empty(parameters('securityAlertPoliciesObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SqlMi-SecAlertPol', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('securityAlertPoliciesObj').name]" + }, + "emailAccountAdmins": "[if(contains(parameters('securityAlertPoliciesObj'), 'emailAccountAdmins'), createObject('value', parameters('securityAlertPoliciesObj').emailAccountAdmins), createObject('value', false()))]", + "state": "[if(contains(parameters('securityAlertPoliciesObj'), 'state'), createObject('value', parameters('securityAlertPoliciesObj').state), createObject('value', 'Disabled'))]" + }, + "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": "16936228680401372562" + }, + "name": "SQL Managed Instance Security Alert Policies", + "description": "This module deploys a SQL Managed Instance Security Alert Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security alert policy." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "state": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Enables advanced data security features, like recuring vulnerability assesment scans and ATP. If enabled, storage account must be provided." + } + }, + "emailAccountAdmins": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/securityAlertPolicies", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), parameters('name'))]", + "properties": { + "state": "[parameters('state')]", + "emailAccountAdmins": "[parameters('emailAccountAdmins')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed security alert policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed security alert policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/securityAlertPolicies', parameters('managedInstanceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed security alert policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedInstance" + ] + }, + "managedInstance_vulnerabilityAssessment": { + "condition": "[and(not(empty(parameters('vulnerabilityAssessmentsObj'))), coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SqlMi-VulnAssessm', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('vulnerabilityAssessmentsObj').name]" + }, + "recurringScansEmails": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'recurringScansEmails'), createObject('value', parameters('vulnerabilityAssessmentsObj').recurringScansEmails), createObject('value', createArray()))]", + "recurringScansEmailSubscriptionAdmins": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'recurringScansEmailSubscriptionAdmins'), createObject('value', parameters('vulnerabilityAssessmentsObj').recurringScansEmailSubscriptionAdmins), createObject('value', false()))]", + "recurringScansIsEnabled": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'recurringScansIsEnabled'), createObject('value', parameters('vulnerabilityAssessmentsObj').recurringScansIsEnabled), createObject('value', false()))]", + "storageAccountResourceId": { + "value": "[parameters('vulnerabilityAssessmentsObj').storageAccountResourceId]" + }, + "useStorageAccountAccessKey": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'useStorageAccountAccessKey'), createObject('value', parameters('vulnerabilityAssessmentsObj').useStorageAccountAccessKey), createObject('value', false()))]", + "createStorageRoleAssignment": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'createStorageRoleAssignment'), createObject('value', parameters('vulnerabilityAssessmentsObj').createStorageRoleAssignment), createObject('value', true()))]" + }, + "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": "15455405847587423486" + }, + "name": "SQL Managed Instance Vulnerability Assessments", + "description": "This module deploys a SQL Managed Instance Vulnerability Assessment.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the vulnerability assessment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "recurringScansIsEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Recurring scans state." + } + }, + "recurringScansEmailSubscriptionAdmins": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators." + } + }, + "recurringScansEmails": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies an array of email addresses to which the scan notification is sent." + } + }, + "storageAccountResourceId": { + "type": "string", + "metadata": { + "description": "Required. A blob storage to hold the scan results." + } + }, + "useStorageAccountAccessKey": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL MI system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account." + } + }, + "createStorageRoleAssignment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/vulnerabilityAssessments", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), parameters('name'))]", + "properties": { + "storageContainerPath": "[format('https://{0}.blob.{1}/vulnerability-assessment/', last(split(parameters('storageAccountResourceId'), '/')), environment().suffixes.storage)]", + "storageAccountAccessKey": "[if(parameters('useStorageAccountAccessKey'), listKeys(parameters('storageAccountResourceId'), '2019-06-01').keys[0].value, null())]", + "recurringScans": { + "isEnabled": "[parameters('recurringScansIsEnabled')]", + "emailSubscriptionAdmins": "[parameters('recurringScansEmailSubscriptionAdmins')]", + "emails": "[parameters('recurringScansEmails')]" + } + } + }, + { + "condition": "[and(not(parameters('useStorageAccountAccessKey')), parameters('createStorageRoleAssignment'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sbdc-rbac', parameters('managedInstanceName'))]", + "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[last(split(parameters('storageAccountResourceId'), '/'))]" + }, + "managedInstanceIdentityPrincipalId": { + "value": "[reference(resourceId('Microsoft.Sql/managedInstances', parameters('managedInstanceName')), '2023-08-01-preview', 'full').identity.principalId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "18021215853157074333" + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Required. Storage account name." + } + }, + "managedInstanceIdentityPrincipalId": { + "type": "string", + "metadata": { + "description": "Required. Managed Identity Principal ID." + } + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", + "name": "[guid(format('{0}-{1}-Storage-Blob-Data-Contributor', resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('managedInstanceIdentityPrincipalId')))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "principalId": "[parameters('managedInstanceIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + } + } + ] + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed vulnerability assessment." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed vulnerability assessment." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/vulnerabilityAssessments', parameters('managedInstanceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed vulnerability assessment." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedInstance", + "managedInstance_securityAlertPolicy" + ] + }, + "managedInstance_keys": { + "copy": { + "name": "managedInstance_keys", + "count": "[length(parameters('keys'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SqlMi-Key-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('keys')[copyIndex()].name]" + }, + "managedInstanceName": { + "value": "[parameters('name')]" + }, + "serverKeyType": "[if(contains(parameters('keys')[copyIndex()], 'serverKeyType'), createObject('value', parameters('keys')[copyIndex()].serverKeyType), createObject('value', 'ServiceManaged'))]", + "uri": "[if(contains(parameters('keys')[copyIndex()], 'uri'), createObject('value', parameters('keys')[copyIndex()].uri), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "101283708334468532" + }, + "name": "SQL Managed Instance Keys", + "description": "This module deploys a SQL Managed Instance Key.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the key. Must follow the [__] pattern." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "serverKeyType": { + "type": "string", + "defaultValue": "ServiceManaged", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], + "metadata": { + "description": "Optional. The encryption protector type like \"ServiceManaged\", \"AzureKeyVault\"." + } + }, + "uri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required." + } + } + }, + "variables": { + "splittedKeyUri": "[split(parameters('uri'), '/')]", + "serverKeyName": "[if(empty(parameters('uri')), 'ServiceManaged', format('{0}_{1}_{2}', split(variables('splittedKeyUri')[2], '.')[0], variables('splittedKeyUri')[4], variables('splittedKeyUri')[5]))]" + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/keys", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), if(not(empty(parameters('name'))), parameters('name'), variables('serverKeyName')))]", + "properties": { + "serverKeyType": "[parameters('serverKeyType')]", + "uri": "[parameters('uri')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance key." + }, + "value": "[if(not(empty(parameters('name'))), parameters('name'), variables('serverKeyName'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance key." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/keys', parameters('managedInstanceName'), if(not(empty(parameters('name'))), parameters('name'), variables('serverKeyName')))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance key." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedInstance" + ] + }, + "managedInstance_encryptionProtector": { + "condition": "[not(empty(parameters('encryptionProtectorObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SqlMi-EncryProtector', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('name')]" + }, + "serverKeyName": { + "value": "[parameters('encryptionProtectorObj').serverKeyName]" + }, + "serverKeyType": "[if(contains(parameters('encryptionProtectorObj'), 'serverKeyType'), createObject('value', parameters('encryptionProtectorObj').serverKeyType), createObject('value', 'ServiceManaged'))]", + "autoRotationEnabled": "[if(contains(parameters('encryptionProtectorObj'), 'autoRotationEnabled'), createObject('value', parameters('encryptionProtectorObj').autoRotationEnabled), createObject('value', true()))]" + }, + "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": "13463643567330956322" + }, + "name": "SQL Managed Instance Encryption Protector", + "description": "This module deploys a SQL Managed Instance Encryption Protector.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "serverKeyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the SQL managed instance key." + } + }, + "serverKeyType": { + "type": "string", + "defaultValue": "ServiceManaged", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], + "metadata": { + "description": "Optional. The encryption protector type like \"ServiceManaged\", \"AzureKeyVault\"." + } + }, + "autoRotationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Key auto rotation opt-in flag." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/encryptionProtector", + "apiVersion": "2022-05-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), 'current')]", + "properties": { + "autoRotationEnabled": "[parameters('autoRotationEnabled')]", + "serverKeyName": "[parameters('serverKeyName')]", + "serverKeyType": "[parameters('serverKeyType')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance encryption protector." + }, + "value": "current" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance encryption protector." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/encryptionProtector', parameters('managedInstanceName'), 'current')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance encryption protector." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedInstance", + "managedInstance_keys" + ] + }, + "managedInstance_administrator": { + "condition": "[not(empty(parameters('administratorsObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SqlMi-Admin', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('name')]" + }, + "login": { + "value": "[parameters('administratorsObj').name]" + }, + "sid": { + "value": "[parameters('administratorsObj').sid]" + }, + "tenantId": "[if(contains(parameters('administratorsObj'), 'tenantId'), createObject('value', parameters('administratorsObj').tenantId), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "14195697460339552085" + }, + "name": "SQL Managed Instances Administrator", + "description": "This module deploys a SQL Managed Instance Administrator.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "login": { + "type": "string", + "metadata": { + "description": "Required. Login name of the managed instance administrator." + } + }, + "sid": { + "type": "string", + "metadata": { + "description": "Required. SID (object ID) of the managed instance administrator." + } + }, + "tenantId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Tenant ID of the managed instance administrator." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/administrators", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), 'ActiveDirectory')]", + "properties": { + "administratorType": "ActiveDirectory", + "login": "[parameters('login')]", + "sid": "[parameters('sid')]", + "tenantId": "[parameters('tenantId')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance administrator." + }, + "value": "ActiveDirectory" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance administrator." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/administrators', parameters('managedInstanceName'), 'ActiveDirectory')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance administrator." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedInstance" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('managedInstance', '2023-08-01-preview', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('managedInstance', '2023-08-01-preview', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/security-alert-policy/README.md b/avm/res/sql/managed-instance/security-alert-policy/README.md new file mode 100644 index 0000000000..29b532c3a8 --- /dev/null +++ b/avm/res/sql/managed-instance/security-alert-policy/README.md @@ -0,0 +1,92 @@ +# SQL Managed Instance Security Alert Policies `[Microsoft.Sql/managedInstances/securityAlertPolicies]` + +This module deploys a SQL Managed Instance Security Alert Policy. + +## 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.Sql/managedInstances/securityAlertPolicies` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/securityAlertPolicies) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the security alert policy. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`emailAccountAdmins`](#parameter-emailaccountadmins) | bool | Specifies that the schedule scan notification will be is sent to the subscription administrators. | +| [`state`](#parameter-state) | string | Enables advanced data security features, like recuring vulnerability assesment scans and ATP. If enabled, storage account must be provided. | + +### Parameter: `name` + +The name of the security alert policy. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `emailAccountAdmins` + +Specifies that the schedule scan notification will be is sent to the subscription administrators. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `state` + +Enables advanced data security features, like recuring vulnerability assesment scans and ATP. If enabled, storage account must be provided. + +- Required: No +- Type: string +- Default: `'Disabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed security alert policy. | +| `resourceGroupName` | string | The resource group of the deployed security alert policy. | +| `resourceId` | string | The resource ID of the deployed security alert policy. | + +## 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/sql/managed-instance/security-alert-policy/main.bicep b/avm/res/sql/managed-instance/security-alert-policy/main.bicep new file mode 100644 index 0000000000..1928a343fe --- /dev/null +++ b/avm/res/sql/managed-instance/security-alert-policy/main.bicep @@ -0,0 +1,41 @@ +metadata name = 'SQL Managed Instance Security Alert Policies' +metadata description = 'This module deploys a SQL Managed Instance Security Alert Policy.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the security alert policy.') +param name string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. Enables advanced data security features, like recuring vulnerability assesment scans and ATP. If enabled, storage account must be provided.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param state string = 'Disabled' + +@description('Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators.') +param emailAccountAdmins bool = false + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName +} + +resource securityAlertPolicy 'Microsoft.Sql/managedInstances/securityAlertPolicies@2023-08-01-preview' = { + name: name + parent: managedInstance + properties: { + state: state + emailAccountAdmins: emailAccountAdmins + } +} + +@description('The name of the deployed security alert policy.') +output name string = securityAlertPolicy.name + +@description('The resource ID of the deployed security alert policy.') +output resourceId string = securityAlertPolicy.id + +@description('The resource group of the deployed security alert policy.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/security-alert-policy/main.json b/avm/res/sql/managed-instance/security-alert-policy/main.json new file mode 100644 index 0000000000..021521bfa3 --- /dev/null +++ b/avm/res/sql/managed-instance/security-alert-policy/main.json @@ -0,0 +1,80 @@ +{ + "$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": "16936228680401372562" + }, + "name": "SQL Managed Instance Security Alert Policies", + "description": "This module deploys a SQL Managed Instance Security Alert Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security alert policy." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "state": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Enables advanced data security features, like recuring vulnerability assesment scans and ATP. If enabled, storage account must be provided." + } + }, + "emailAccountAdmins": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/securityAlertPolicies", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), parameters('name'))]", + "properties": { + "state": "[parameters('state')]", + "emailAccountAdmins": "[parameters('emailAccountAdmins')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed security alert policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed security alert policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/securityAlertPolicies', parameters('managedInstanceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed security alert policy." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/tests/e2e/defaults/dependencies.bicep b/avm/res/sql/managed-instance/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..b1f97e3ddf --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,288 @@ +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Network Security Group to create.') +param networkSecurityGroupName string + +@description('Required. The name of the Route Table to create.') +param routeTableName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +var addressPrefix = '10.0.0.0/16' +var addressPrefixString = replace(replace(addressPrefix, '.', '-'), '/', '-') + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: networkSecurityGroupName + location: location + properties: { + securityRules: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-sqlmgmt-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI provisioning Control Plane Deployment and Authentication Service' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'SqlManagement' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 100 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1438' + '1440' + '1452' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corpsaw-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetSaw' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1440' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corppublic-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability through Corpnet ranges' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetPublic' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 102 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-healthprobe-in-${addressPrefixString}-v10' + properties: { + description: 'Allow Azure Load Balancer inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 103 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 104 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-services-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI services outbound traffic over https' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 100 + direction: 'Outbound' + destinationPortRanges: [ + '443' + '12000' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal outbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + ] + } +} + +resource routeTable 'Microsoft.Network/routeTables@2023-04-01' = { + name: routeTableName + location: location + properties: { + disableBgpRoutePropagation: false + routes: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_subnet-${addressPrefixString}-to-vnetlocal' + properties: { + addressPrefix: addressPrefix + nextHopType: 'VnetLocal' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage' + properties: { + addressPrefix: 'Storage' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-SqlManagement' + properties: { + addressPrefix: 'SqlManagement' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureMonitor' + properties: { + addressPrefix: 'AzureMonitor' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetSaw' + properties: { + addressPrefix: 'CorpNetSaw' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetPublic' + properties: { + addressPrefix: 'CorpNetPublic' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureActiveDirectory' + properties: { + addressPrefix: 'AzureActiveDirectory' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.westeurope' + properties: { + addressPrefix: 'AzureCloud.westeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.northeurope' + properties: { + addressPrefix: 'AzureCloud.northeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.westeurope' + properties: { + addressPrefix: 'Storage.westeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.northeurope' + properties: { + addressPrefix: 'Storage.northeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.westeurope' + properties: { + addressPrefix: 'EventHub.westeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.northeurope' + properties: { + addressPrefix: 'EventHub.northeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'ManagedInstance' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + routeTable: { + id: routeTable.id + } + networkSecurityGroup: { + id: networkSecurityGroup.id + } + delegations: [ + { + name: 'managedInstanceDelegation' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } + ] + } + } + ] + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id diff --git a/avm/res/sql/managed-instance/tests/e2e/defaults/main.test.bicep b/avm/res/sql/managed-instance/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..e6eda07e9e --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,64 @@ +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}-sql.managedinstances-${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 = 'sqlmimin' + +@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_#' + +// ============ // +// 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}' + networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' + routeTableName: 'dep-${namePrefix}-rt-${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}' + location: resourceLocation + administratorLogin: 'adminUserName' + administratorLoginPassword: password + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } +}] diff --git a/avm/res/sql/managed-instance/tests/e2e/max/dependencies.bicep b/avm/res/sql/managed-instance/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..20398fd372 --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/max/dependencies.bicep @@ -0,0 +1,326 @@ +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Network Security Group to create.') +param networkSecurityGroupName string + +@description('Required. The name of the Route Table to create.') +param routeTableName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +var addressPrefix = '10.0.0.0/16' +var addressPrefixString = replace(replace(addressPrefix, '.', '-'), '/', '-') + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: networkSecurityGroupName + location: location + properties: { + securityRules: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-sqlmgmt-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI provisioning Control Plane Deployment and Authentication Service' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'SqlManagement' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 100 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1438' + '1440' + '1452' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corpsaw-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetSaw' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1440' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corppublic-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability through Corpnet ranges' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetPublic' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 102 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-healthprobe-in-${addressPrefixString}-v10' + properties: { + description: 'Allow Azure Load Balancer inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 103 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 104 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-services-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI services outbound traffic over https' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 100 + direction: 'Outbound' + destinationPortRanges: [ + '443' + '12000' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal outbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + ] + } +} + +resource routeTable 'Microsoft.Network/routeTables@2023-04-01' = { + name: routeTableName + location: location + properties: { + disableBgpRoutePropagation: false + routes: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_subnet-${addressPrefixString}-to-vnetlocal' + properties: { + addressPrefix: addressPrefix + nextHopType: 'VnetLocal' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage' + properties: { + addressPrefix: 'Storage' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-SqlManagement' + properties: { + addressPrefix: 'SqlManagement' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureMonitor' + properties: { + addressPrefix: 'AzureMonitor' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetSaw' + properties: { + addressPrefix: 'CorpNetSaw' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetPublic' + properties: { + addressPrefix: 'CorpNetPublic' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureActiveDirectory' + properties: { + addressPrefix: 'AzureActiveDirectory' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.${location}' + properties: { + addressPrefix: 'AzureCloud.${location}' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.${location}' + properties: { + addressPrefix: 'Storage.${location}' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.${location}' + properties: { + addressPrefix: 'EventHub.${location}' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'ManagedInstance' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + routeTable: { + id: routeTable.id + } + networkSecurityGroup: { + id: networkSecurityGroup.id + } + delegations: [ + { + name: 'managedInstanceDelegation' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } + ] + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' + } + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Reader-RoleAssignment') + scope: keyVault::key + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') // Key Vault Crypto Service Encryption User + principalType: 'ServicePrincipal' + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@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 URL of the created Key Vault Encryption Key.') +output keyVaultEncryptionKeyUrl string = keyVault::key.properties.keyUriWithVersion + +@description('The name of the created Key Vault Encryption Key.') +output keyVaultKeyName string = keyVault::key.name + +@description('The name of the created Key Vault.') +output keyVaultName string = keyVault.name diff --git a/avm/res/sql/managed-instance/tests/e2e/max/main.test.bicep b/avm/res/sql/managed-instance/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..b45ce3cbb7 --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/max/main.test.bicep @@ -0,0 +1,189 @@ +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}-sql.managedinstances-${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 = 'sqlmimax' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@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_#' + +// ============ // +// 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) + keyVaultName: 'dep${namePrefix}kv${serviceShort}${substring(uniqueString(baseTime), 0, 3)}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' + routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' + location: resourceLocation + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: toLower('dep${namePrefix}x${serviceShort}01') + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${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: { + location: resourceLocation + name: '${namePrefix}-${serviceShort}' + administratorLogin: 'adminUserName' + administratorLoginPassword: password + subnetResourceId: nestedDependencies.outputs.subnetResourceId + collation: 'SQL_Latin1_General_CP1_CI_AS' + databases: [ + { + backupLongTermRetentionPolicies: { + name: 'default' + } + backupShortTermRetentionPolicies: { + name: 'default' + } + name: '${namePrefix}-${serviceShort}-db-001' + diagnosticSettings: [ + { + name: 'customSetting' + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + logCategoriesAndGroups:[ + { + categoryGroup: 'allLogs' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + dnsZonePartner: '' + encryptionProtectorObj: { + serverKeyName: '${nestedDependencies.outputs.keyVaultName}_${nestedDependencies.outputs.keyVaultKeyName}_${last(split(nestedDependencies.outputs.keyVaultEncryptionKeyUrl, '/'))}' + serverKeyType: 'AzureKeyVault' + } + hardwareFamily: 'Gen5' + keys: [ + { + name: '${nestedDependencies.outputs.keyVaultName}_${nestedDependencies.outputs.keyVaultKeyName}_${last(split(nestedDependencies.outputs.keyVaultEncryptionKeyUrl, '/'))}' + serverKeyType: 'AzureKeyVault' + uri: nestedDependencies.outputs.keyVaultEncryptionKeyUrl + } + ] + licenseType: 'LicenseIncluded' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + primaryUserAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId + proxyOverride: 'Proxy' + publicDataEndpointEnabled: false + 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' + } + ] + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + servicePrincipal: 'SystemAssigned' + skuName: 'GP_Gen5' + skuTier: 'GeneralPurpose' + storageSizeInGB: 32 + managedIdentities: { + systemAssigned: true + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + timezoneId: 'UTC' + vCores: 4 + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +}] diff --git a/avm/res/sql/managed-instance/tests/e2e/vulnAssm/dependencies.bicep b/avm/res/sql/managed-instance/tests/e2e/vulnAssm/dependencies.bicep new file mode 100644 index 0000000000..d06ccfa76e --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/vulnAssm/dependencies.bicep @@ -0,0 +1,386 @@ +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Network Security Group to create.') +param networkSecurityGroupName string + +@description('Required. The name of the Route Table to create.') +param routeTableName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +var addressPrefix = '10.0.0.0/16' +var addressPrefixString = replace(replace(addressPrefix, '.', '-'), '/', '-') + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: networkSecurityGroupName + location: location + properties: { + securityRules: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-sqlmgmt-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI provisioning Control Plane Deployment and Authentication Service' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'SqlManagement' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 100 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1438' + '1440' + '1452' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corpsaw-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetSaw' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1440' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corppublic-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability through Corpnet ranges' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetPublic' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 102 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-healthprobe-in-${addressPrefixString}-v10' + properties: { + description: 'Allow Azure Load Balancer inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 103 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 104 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-aad-out-${addressPrefixString}-v11' + properties: { + description: 'Allow communication with Azure Active Directory over https' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'AzureActiveDirectory' + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-onedsc-out-${addressPrefixString}-v11' + properties: { + description: 'Allow communication with the One DS Collector over https' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'OneDsCollector' + access: 'Allow' + priority: 102 + direction: 'Outbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-services-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI services outbound traffic over https' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 100 + direction: 'Outbound' + destinationPortRanges: [ + '443' + '12000' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal outbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 103 + direction: 'Outbound' + } + } + { + name: 'mi-strg-p-out-${addressPrefixString}-v11' + properties: { + description: 'Allow outbound communication with storage over HTTPS' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'Storage.eastus' + access: 'Allow' + priority: 104 + direction: 'Outbound' + } + } + { + name: 'mi-strg-s-out-${addressPrefixString}-v11' + properties: { + description: 'Allow outbound communication with storage over HTTPS' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'Storage.westus' + access: 'Allow' + priority: 105 + direction: 'Outbound' + } + } + ] + } +} + +resource routeTable 'Microsoft.Network/routeTables@2023-04-01' = { + name: routeTableName + location: location + properties: { + disableBgpRoutePropagation: false + routes: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_subnet-${addressPrefixString}-to-vnetlocal' + properties: { + addressPrefix: addressPrefix + nextHopType: 'VnetLocal' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage' + properties: { + addressPrefix: 'Storage' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.eastus' + properties: { + addressPrefix: 'Storage.eastus' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.westus' + properties: { + addressPrefix: 'Storage.westus' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-SqlManagement' + properties: { + addressPrefix: 'SqlManagement' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureMonitor' + properties: { + addressPrefix: 'AzureMonitor' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetSaw' + properties: { + addressPrefix: 'CorpNetSaw' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetPublic' + properties: { + addressPrefix: 'CorpNetPublic' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureActiveDirectory' + properties: { + addressPrefix: 'AzureActiveDirectory' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-OneDsCollector' + properties: { + addressPrefix: 'OneDsCollector' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.westeurope' + properties: { + addressPrefix: 'AzureCloud.westeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.northeurope' + properties: { + addressPrefix: 'AzureCloud.northeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.westeurope' + properties: { + addressPrefix: 'Storage.westeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.northeurope' + properties: { + addressPrefix: 'Storage.northeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.westeurope' + properties: { + addressPrefix: 'EventHub.westeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.northeurope' + properties: { + addressPrefix: 'EventHub.northeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'ManagedInstance' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + routeTable: { + id: routeTable.id + } + networkSecurityGroup: { + id: networkSecurityGroup.id + } + delegations: [ + { + name: 'managedInstanceDelegation' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } + ] + } + } + ] + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = { + name: storageAccountName + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_LRS' + } + properties: { + allowBlobPublicAccess: false + } +} + +@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/sql/managed-instance/tests/e2e/vulnAssm/main.test.bicep b/avm/res/sql/managed-instance/tests/e2e/vulnAssm/main.test.bicep new file mode 100644 index 0000000000..a51971ca80 --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/vulnAssm/main.test.bicep @@ -0,0 +1,90 @@ +targetScope = 'subscription' + +metadata name = 'With vulnerability assessment' +metadata description = 'This instance deploys the module with a vulnerability assessment.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-sql.managedinstances-${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 = 'sqlmivln' + +@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_#' + +// ============ // +// 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}' + networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' + routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' + location: resourceLocation + storageAccountName: toLower('dep${namePrefix}v${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: { + location: resourceLocation + name: '${namePrefix}-${serviceShort}' + administratorLogin: 'adminUserName' + administratorLoginPassword: password + subnetResourceId: nestedDependencies.outputs.subnetResourceId + managedIdentities: { + systemAssigned: true + } + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId + useStorageAccountAccessKey: false + createStorageRoleAssignment: true + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +}] diff --git a/avm/res/sql/managed-instance/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/sql/managed-instance/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..20398fd372 --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,326 @@ +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Network Security Group to create.') +param networkSecurityGroupName string + +@description('Required. The name of the Route Table to create.') +param routeTableName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +var addressPrefix = '10.0.0.0/16' +var addressPrefixString = replace(replace(addressPrefix, '.', '-'), '/', '-') + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: networkSecurityGroupName + location: location + properties: { + securityRules: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-sqlmgmt-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI provisioning Control Plane Deployment and Authentication Service' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'SqlManagement' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 100 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1438' + '1440' + '1452' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corpsaw-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetSaw' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1440' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corppublic-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability through Corpnet ranges' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetPublic' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 102 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-healthprobe-in-${addressPrefixString}-v10' + properties: { + description: 'Allow Azure Load Balancer inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 103 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 104 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-services-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI services outbound traffic over https' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 100 + direction: 'Outbound' + destinationPortRanges: [ + '443' + '12000' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal outbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + ] + } +} + +resource routeTable 'Microsoft.Network/routeTables@2023-04-01' = { + name: routeTableName + location: location + properties: { + disableBgpRoutePropagation: false + routes: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_subnet-${addressPrefixString}-to-vnetlocal' + properties: { + addressPrefix: addressPrefix + nextHopType: 'VnetLocal' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage' + properties: { + addressPrefix: 'Storage' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-SqlManagement' + properties: { + addressPrefix: 'SqlManagement' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureMonitor' + properties: { + addressPrefix: 'AzureMonitor' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetSaw' + properties: { + addressPrefix: 'CorpNetSaw' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetPublic' + properties: { + addressPrefix: 'CorpNetPublic' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureActiveDirectory' + properties: { + addressPrefix: 'AzureActiveDirectory' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.${location}' + properties: { + addressPrefix: 'AzureCloud.${location}' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.${location}' + properties: { + addressPrefix: 'Storage.${location}' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.${location}' + properties: { + addressPrefix: 'EventHub.${location}' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'ManagedInstance' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + routeTable: { + id: routeTable.id + } + networkSecurityGroup: { + id: networkSecurityGroup.id + } + delegations: [ + { + name: 'managedInstanceDelegation' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } + ] + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' + } + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Reader-RoleAssignment') + scope: keyVault::key + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') // Key Vault Crypto Service Encryption User + principalType: 'ServicePrincipal' + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@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 URL of the created Key Vault Encryption Key.') +output keyVaultEncryptionKeyUrl string = keyVault::key.properties.keyUriWithVersion + +@description('The name of the created Key Vault Encryption Key.') +output keyVaultKeyName string = keyVault::key.name + +@description('The name of the created Key Vault.') +output keyVaultName string = keyVault.name diff --git a/avm/res/sql/managed-instance/tests/e2e/waf-aligned/main.test.bicep b/avm/res/sql/managed-instance/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..7cbd5f19b5 --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,172 @@ +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}-sql.managedinstances-${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 = 'sqlmiwaf' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@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_#' + +// ============ // +// 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) + keyVaultName: 'dep${namePrefix}kv${serviceShort}${substring(uniqueString(baseTime), 0, 3)}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' + routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' + location: resourceLocation + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: toLower('dep${namePrefix}wf${serviceShort}01') + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${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: { + location: resourceLocation + name: '${namePrefix}-${serviceShort}' + administratorLogin: 'adminUserName' + administratorLoginPassword: password + subnetResourceId: nestedDependencies.outputs.subnetResourceId + collation: 'SQL_Latin1_General_CP1_CI_AS' + databases: [ + { + backupLongTermRetentionPolicies: { + name: 'default' + } + backupShortTermRetentionPolicies: { + name: 'default' + } + name: '${namePrefix}-${serviceShort}-db-001' + diagnosticSettings: [ + { + name: 'customSetting' + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + dnsZonePartner: '' + encryptionProtectorObj: { + serverKeyName: '${nestedDependencies.outputs.keyVaultName}_${nestedDependencies.outputs.keyVaultKeyName}_${last(split(nestedDependencies.outputs.keyVaultEncryptionKeyUrl, '/'))}' + serverKeyType: 'AzureKeyVault' + } + hardwareFamily: 'Gen5' + keys: [ + { + name: '${nestedDependencies.outputs.keyVaultName}_${nestedDependencies.outputs.keyVaultKeyName}_${last(split(nestedDependencies.outputs.keyVaultEncryptionKeyUrl, '/'))}' + serverKeyType: 'AzureKeyVault' + uri: nestedDependencies.outputs.keyVaultEncryptionKeyUrl + } + ] + licenseType: 'LicenseIncluded' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + primaryUserAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId + proxyOverride: 'Proxy' + publicDataEndpointEnabled: false + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + servicePrincipal: 'SystemAssigned' + skuName: 'GP_Gen5' + skuTier: 'GeneralPurpose' + storageSizeInGB: 32 + managedIdentities: { + systemAssigned: true + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + timezoneId: 'UTC' + vCores: 4 + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +}] diff --git a/avm/res/sql/managed-instance/version.json b/avm/res/sql/managed-instance/version.json new file mode 100644 index 0000000000..7fa401bdf7 --- /dev/null +++ b/avm/res/sql/managed-instance/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/res/sql/managed-instance/vulnerability-assessment/README.md b/avm/res/sql/managed-instance/vulnerability-assessment/README.md new file mode 100644 index 0000000000..62c9a59da2 --- /dev/null +++ b/avm/res/sql/managed-instance/vulnerability-assessment/README.md @@ -0,0 +1,121 @@ +# SQL Managed Instance Vulnerability Assessments `[Microsoft.Sql/managedInstances/vulnerabilityAssessments]` + +This module deploys a SQL Managed Instance Vulnerability Assessment. + +## 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.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Sql/managedInstances/vulnerabilityAssessments` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/vulnerabilityAssessments) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the vulnerability assessment. | +| [`storageAccountResourceId`](#parameter-storageaccountresourceid) | string | A blob storage to hold the scan results. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`createStorageRoleAssignment`](#parameter-createstorageroleassignment) | bool | Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account. | +| [`recurringScansEmails`](#parameter-recurringscansemails) | array | Specifies an array of email addresses to which the scan notification is sent. | +| [`recurringScansEmailSubscriptionAdmins`](#parameter-recurringscansemailsubscriptionadmins) | bool | Specifies that the schedule scan notification will be is sent to the subscription administrators. | +| [`recurringScansIsEnabled`](#parameter-recurringscansisenabled) | bool | Recurring scans state. | +| [`useStorageAccountAccessKey`](#parameter-usestorageaccountaccesskey) | bool | Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL MI system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account. | + +### Parameter: `name` + +The name of the vulnerability assessment. + +- Required: Yes +- Type: string + +### Parameter: `storageAccountResourceId` + +A blob storage to hold the scan results. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `createStorageRoleAssignment` + +Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `recurringScansEmails` + +Specifies an array of email addresses to which the scan notification is sent. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `recurringScansEmailSubscriptionAdmins` + +Specifies that the schedule scan notification will be is sent to the subscription administrators. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `recurringScansIsEnabled` + +Recurring scans state. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `useStorageAccountAccessKey` + +Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL MI system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account. + +- Required: No +- Type: bool +- Default: `False` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed vulnerability assessment. | +| `resourceGroupName` | string | The resource group of the deployed vulnerability assessment. | +| `resourceId` | string | The resource ID of the deployed vulnerability assessment. | + +## 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/sql/managed-instance/vulnerability-assessment/main.bicep b/avm/res/sql/managed-instance/vulnerability-assessment/main.bicep new file mode 100644 index 0000000000..4bac1770f6 --- /dev/null +++ b/avm/res/sql/managed-instance/vulnerability-assessment/main.bicep @@ -0,0 +1,64 @@ +metadata name = 'SQL Managed Instance Vulnerability Assessments' +metadata description = 'This module deploys a SQL Managed Instance Vulnerability Assessment.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the vulnerability assessment.') +param name string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. Recurring scans state.') +param recurringScansIsEnabled bool = false + +@description('Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators.') +param recurringScansEmailSubscriptionAdmins bool = false + +@description('Optional. Specifies an array of email addresses to which the scan notification is sent.') +param recurringScansEmails array = [] + +@description('Required. A blob storage to hold the scan results.') +param storageAccountResourceId string + +@description('Optional. Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL MI system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account.') +param useStorageAccountAccessKey bool = false + +@description('Optional. Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account.') +param createStorageRoleAssignment bool = true + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName +} + +// Assign SQL MI MSI access to storage account +module storageAccount_sbdc_rbac 'modules/nested_storageRoleAssignment.bicep' = if (!useStorageAccountAccessKey && createStorageRoleAssignment) { + name: '${managedInstance.name}-sbdc-rbac' + scope: resourceGroup(split(storageAccountResourceId, '/')[4]) + params: { + storageAccountName: last(split(storageAccountResourceId, '/')) + managedInstanceIdentityPrincipalId: managedInstance.identity.principalId + } +} + +resource vulnerabilityAssessment 'Microsoft.Sql/managedInstances/vulnerabilityAssessments@2023-08-01-preview' = { + name: name + parent: managedInstance + properties: { + storageContainerPath: 'https://${last(split(storageAccountResourceId, '/'))}.blob.${environment().suffixes.storage}/vulnerability-assessment/' + storageAccountAccessKey: useStorageAccountAccessKey ? listKeys(storageAccountResourceId, '2019-06-01').keys[0].value : any(null) + recurringScans: { + isEnabled: recurringScansIsEnabled + emailSubscriptionAdmins: recurringScansEmailSubscriptionAdmins + emails: recurringScansEmails + } + } +} + +@description('The name of the deployed vulnerability assessment.') +output name string = vulnerabilityAssessment.name + +@description('The resource ID of the deployed vulnerability assessment.') +output resourceId string = vulnerabilityAssessment.id + +@description('The resource group of the deployed vulnerability assessment.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/vulnerability-assessment/main.json b/avm/res/sql/managed-instance/vulnerability-assessment/main.json new file mode 100644 index 0000000000..8d0fef695c --- /dev/null +++ b/avm/res/sql/managed-instance/vulnerability-assessment/main.json @@ -0,0 +1,167 @@ +{ + "$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": "15455405847587423486" + }, + "name": "SQL Managed Instance Vulnerability Assessments", + "description": "This module deploys a SQL Managed Instance Vulnerability Assessment.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the vulnerability assessment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "recurringScansIsEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Recurring scans state." + } + }, + "recurringScansEmailSubscriptionAdmins": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators." + } + }, + "recurringScansEmails": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies an array of email addresses to which the scan notification is sent." + } + }, + "storageAccountResourceId": { + "type": "string", + "metadata": { + "description": "Required. A blob storage to hold the scan results." + } + }, + "useStorageAccountAccessKey": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL MI system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account." + } + }, + "createStorageRoleAssignment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/vulnerabilityAssessments", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), parameters('name'))]", + "properties": { + "storageContainerPath": "[format('https://{0}.blob.{1}/vulnerability-assessment/', last(split(parameters('storageAccountResourceId'), '/')), environment().suffixes.storage)]", + "storageAccountAccessKey": "[if(parameters('useStorageAccountAccessKey'), listKeys(parameters('storageAccountResourceId'), '2019-06-01').keys[0].value, null())]", + "recurringScans": { + "isEnabled": "[parameters('recurringScansIsEnabled')]", + "emailSubscriptionAdmins": "[parameters('recurringScansEmailSubscriptionAdmins')]", + "emails": "[parameters('recurringScansEmails')]" + } + } + }, + { + "condition": "[and(not(parameters('useStorageAccountAccessKey')), parameters('createStorageRoleAssignment'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sbdc-rbac', parameters('managedInstanceName'))]", + "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[last(split(parameters('storageAccountResourceId'), '/'))]" + }, + "managedInstanceIdentityPrincipalId": { + "value": "[reference(resourceId('Microsoft.Sql/managedInstances', parameters('managedInstanceName')), '2023-08-01-preview', 'full').identity.principalId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "18021215853157074333" + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Required. Storage account name." + } + }, + "managedInstanceIdentityPrincipalId": { + "type": "string", + "metadata": { + "description": "Required. Managed Identity Principal ID." + } + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", + "name": "[guid(format('{0}-{1}-Storage-Blob-Data-Contributor', resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('managedInstanceIdentityPrincipalId')))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "principalId": "[parameters('managedInstanceIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + } + } + ] + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed vulnerability assessment." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed vulnerability assessment." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/vulnerabilityAssessments', parameters('managedInstanceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed vulnerability assessment." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/vulnerability-assessment/modules/nested_storageRoleAssignment.bicep b/avm/res/sql/managed-instance/vulnerability-assessment/modules/nested_storageRoleAssignment.bicep new file mode 100644 index 0000000000..7ef3f28a53 --- /dev/null +++ b/avm/res/sql/managed-instance/vulnerability-assessment/modules/nested_storageRoleAssignment.bicep @@ -0,0 +1,20 @@ +@description('Required. Storage account name.') +param storageAccountName string + +@description('Required. Managed Identity Principal ID.') +param managedInstanceIdentityPrincipalId string + +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = { + name: storageAccountName +} + +// Assign Storage Blob Data Contributor RBAC role +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('${storageAccount.id}-${managedInstanceIdentityPrincipalId}-Storage-Blob-Data-Contributor') + scope: storageAccount + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + principalId: managedInstanceIdentityPrincipalId + principalType: 'ServicePrincipal' + } +} diff --git a/avm/res/storage/storage-account/README.md b/avm/res/storage/storage-account/README.md index 4bdbae8b8e..83fba9bd49 100644 --- a/avm/res/storage/storage-account/README.md +++ b/avm/res/storage/storage-account/README.md @@ -181,6 +181,11 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { name: 'ssamin001' // Non-required parameters allowBlobPublicAccess: false + location: '' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + } } } ``` @@ -204,6 +209,15 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { // Non-required parameters "allowBlobPublicAccess": { "value": false + }, + "location": { + "value": "" + }, + "networkAcls": { + "value": { + "bypass": "AzureServices", + "defaultAction": "Deny" + } } } } 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 91a31edc0d..a45e614327 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.26.54.24096", - "templateHash": "2429139364933838439" + "version": "0.26.170.59819", + "templateHash": "12849754295459852309" }, "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.json b/avm/res/storage/storage-account/blob-service/container/main.json index a41cc7b8ce..0d487d2827 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.26.54.24096", - "templateHash": "3297137219061666309" + "version": "0.26.170.59819", + "templateHash": "3805384021483033369" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -274,8 +274,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2429139364933838439" + "version": "0.26.170.59819", + "templateHash": "12849754295459852309" }, "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 2ac6992281..1ffa10179f 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.26.54.24096", - "templateHash": "12175020972967228537" + "version": "0.26.170.59819", + "templateHash": "7278814029590745003" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -403,8 +403,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3297137219061666309" + "version": "0.26.170.59819", + "templateHash": "3805384021483033369" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -672,8 +672,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2429139364933838439" + "version": "0.26.170.59819", + "templateHash": "12849754295459852309" }, "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 38dcbcb7c7..31a38bc821 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.26.54.24096", - "templateHash": "13860799899517512764" + "version": "0.26.170.59819", + "templateHash": "4306536926065797375" }, "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.26.54.24096", - "templateHash": "18405917853511220559" + "version": "0.26.170.59819", + "templateHash": "13618261904162439978" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -486,8 +486,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16221226236637657314" + "version": "0.26.170.59819", + "templateHash": "6057169747302051267" } }, "parameters": { 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 813cd74fbd..5e5f8ff5c6 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.26.54.24096", - "templateHash": "18405917853511220559" + "version": "0.26.170.59819", + "templateHash": "13618261904162439978" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -205,8 +205,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16221226236637657314" + "version": "0.26.170.59819", + "templateHash": "6057169747302051267" } }, "parameters": { diff --git a/avm/res/storage/storage-account/local-user/main.json b/avm/res/storage/storage-account/local-user/main.json index 07d5ea2520..c5936241dd 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.26.54.24096", - "templateHash": "3388645117303533667" + "version": "0.26.170.59819", + "templateHash": "14593329616022153178" }, "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 1b4437ab21..58ba1d817e 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -701,7 +701,7 @@ type networkAclsType = { @description('Required. Specifies the default action of allow or deny when no other rules match.') defaultAction: ('Allow' | 'Deny') -}? +} type privateEndpointType = { @description('Optional. The name of the private endpoint.') diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index d4a1fa422f..91eb9c9f57 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.26.54.24096", - "templateHash": "13878793352750035767" + "version": "0.26.170.59819", + "templateHash": "8863225181818962940" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account.", @@ -1691,8 +1691,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "14052382142427178929" + "version": "0.26.170.59819", + "templateHash": "4153955640795225346" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy.", @@ -1801,8 +1801,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3388645117303533667" + "version": "0.26.170.59819", + "templateHash": "14593329616022153178" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication.", @@ -2019,8 +2019,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12175020972967228537" + "version": "0.26.170.59819", + "templateHash": "7278814029590745003" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -2417,8 +2417,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3297137219061666309" + "version": "0.26.170.59819", + "templateHash": "3805384021483033369" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -2686,8 +2686,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2429139364933838439" + "version": "0.26.170.59819", + "templateHash": "12849754295459852309" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", @@ -2865,8 +2865,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13860799899517512764" + "version": "0.26.170.59819", + "templateHash": "4306536926065797375" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service.", @@ -3146,8 +3146,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18405917853511220559" + "version": "0.26.170.59819", + "templateHash": "13618261904162439978" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -3346,8 +3346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16221226236637657314" + "version": "0.26.170.59819", + "templateHash": "6057169747302051267" } }, "parameters": { @@ -3615,8 +3615,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13677013226831946461" + "version": "0.26.170.59819", + "templateHash": "4494965320132257914" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", @@ -3860,8 +3860,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17219066722763101567" + "version": "0.26.170.59819", + "templateHash": "2870814699283520878" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", @@ -4117,8 +4117,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17373442840517371502" + "version": "0.26.170.59819", + "templateHash": "6806949296172999226" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", @@ -4359,8 +4359,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "10226619499666007193" + "version": "0.26.170.59819", + "templateHash": "5583637725561111612" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", diff --git a/avm/res/storage/storage-account/management-policy/main.json b/avm/res/storage/storage-account/management-policy/main.json index 323df90a49..d751651d08 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.26.54.24096", - "templateHash": "14052382142427178929" + "version": "0.26.170.59819", + "templateHash": "4153955640795225346" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy.", diff --git a/avm/res/storage/storage-account/queue-service/main.json b/avm/res/storage/storage-account/queue-service/main.json index ea2dd644a3..b1ddfd7258 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.26.54.24096", - "templateHash": "13677013226831946461" + "version": "0.26.170.59819", + "templateHash": "4494965320132257914" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", @@ -250,8 +250,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17219066722763101567" + "version": "0.26.170.59819", + "templateHash": "2870814699283520878" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", 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 981f57b5d0..7463266ec4 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.26.54.24096", - "templateHash": "17219066722763101567" + "version": "0.26.170.59819", + "templateHash": "2870814699283520878" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", diff --git a/avm/res/storage/storage-account/table-service/main.json b/avm/res/storage/storage-account/table-service/main.json index bb058e623a..cd4f72dbef 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.26.54.24096", - "templateHash": "17373442840517371502" + "version": "0.26.170.59819", + "templateHash": "6806949296172999226" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", @@ -247,8 +247,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "10226619499666007193" + "version": "0.26.170.59819", + "templateHash": "5583637725561111612" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", 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 1ba6eab43d..0abcc4d463 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.26.54.24096", - "templateHash": "10226619499666007193" + "version": "0.26.170.59819", + "templateHash": "5583637725561111612" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", diff --git a/avm/res/storage/storage-account/tests/e2e/defaults/main.test.bicep b/avm/res/storage/storage-account/tests/e2e/defaults/main.test.bicep index 31e888a7ff..c6de9c25bf 100644 --- a/avm/res/storage/storage-account/tests/e2e/defaults/main.test.bicep +++ b/avm/res/storage/storage-account/tests/e2e/defaults/main.test.bicep @@ -45,7 +45,7 @@ module testDeployment '../../../main.bicep' = [ allowBlobPublicAccess: false location: resourceLocation networkAcls: { - defaultAction: 'Allow' + defaultAction: 'Deny' bypass: 'AzureServices' } } diff --git a/avm/res/web/serverfarm/README.md b/avm/res/web/serverfarm/README.md index 7788f77636..74b625e14a 100644 --- a/avm/res/web/serverfarm/README.md +++ b/avm/res/web/serverfarm/README.md @@ -47,13 +47,8 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { params: { // Required parameters name: 'wsfmin001' - sku: { - capacity: 3 - family: 'P' - name: 'P1v3' - size: 'P1v3' - tier: 'Premium' - } + skuCapacity: 2 + skuName: 'S1' // Non-required parameters location: '' } @@ -76,14 +71,11 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { "name": { "value": "wsfmin001" }, - "sku": { - "value": { - "capacity": 3, - "family": "P", - "name": "P1v3", - "size": "P1v3", - "tier": "Premium" - } + "skuCapacity": { + "value": 2 + }, + "skuName": { + "value": "S1" }, // Non-required parameters "location": { @@ -111,13 +103,8 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { params: { // Required parameters name: 'wsfmax001' - sku: { - capacity: 3 - family: 'P' - name: 'P1v3' - size: 'P1v3' - tier: 'Premium' - } + skuCapacity: 1 + skuName: 'S1' // Non-required parameters diagnosticSettings: [ { @@ -162,7 +149,7 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - zoneRedundant: true + zoneRedundant: false } } ``` @@ -183,14 +170,11 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { "name": { "value": "wsfmax001" }, - "sku": { - "value": { - "capacity": 3, - "family": "P", - "name": "P1v3", - "size": "P1v3", - "tier": "Premium" - } + "skuCapacity": { + "value": 1 + }, + "skuName": { + "value": "S1" }, // Non-required parameters "diagnosticSettings": { @@ -251,7 +235,7 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { } }, "zoneRedundant": { - "value": true + "value": false } } } @@ -275,13 +259,8 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { params: { // Required parameters name: 'wsfwaf001' - sku: { - capacity: 3 - family: 'P' - name: 'P1v3' - size: 'P1v3' - tier: 'Premium' - } + skuCapacity: 3 + skuName: 'P1v3' // Non-required parameters diagnosticSettings: [ { @@ -329,14 +308,11 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { "name": { "value": "wsfwaf001" }, - "sku": { - "value": { - "capacity": 3, - "family": "P", - "name": "P1v3", - "size": "P1v3", - "tier": "Premium" - } + "skuCapacity": { + "value": 3 + }, + "skuName": { + "value": "P1v3" }, // Non-required parameters "diagnosticSettings": { @@ -392,7 +368,8 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`name`](#parameter-name) | string | Name of the app service plan. | -| [`sku`](#parameter-sku) | object | Defines the name, tier, size, family and capacity of the App Service Plan. | +| [`skuCapacity`](#parameter-skucapacity) | int | Number of workers associated with the App Service Plan. | +| [`skuName`](#parameter-skuname) | string | The name of the SKU will Determine the tier, size, family of the App Service Plan. | **Conditional parameters** @@ -426,12 +403,19 @@ Name of the app service plan. - Required: Yes - Type: string -### Parameter: `sku` +### Parameter: `skuCapacity` -Defines the name, tier, size, family and capacity of the App Service Plan. +Number of workers associated with the App Service Plan. - Required: Yes -- Type: object +- Type: int + +### Parameter: `skuName` + +The name of the SKU will Determine the tier, size, family of the App Service Plan. + +- Required: Yes +- Type: string ### Parameter: `reserved` @@ -439,7 +423,7 @@ Defaults to false when creating Windows/app App Service Plan. Required if creati - Required: No - Type: bool -- Default: `False` +- Default: `[equals(parameters('kind'), 'Linux')]` ### Parameter: `appServiceEnvironmentId` @@ -778,7 +762,7 @@ Zone Redundancy can only be used on Premium or ElasticPremium SKU Tiers within Z - Required: No - Type: bool -- Default: `[if(or(equals(parameters('sku').tier, 'Premium'), equals(parameters('sku').tier, 'ElasticPremium')), true(), false())]` +- Default: `[if(or(startsWith(parameters('skuName'), 'P'), startsWith(parameters('skuName'), 'EP')), true(), false())]` ## Outputs diff --git a/avm/res/web/serverfarm/main.bicep b/avm/res/web/serverfarm/main.bicep index 7cff682e16..5eb7e567b6 100644 --- a/avm/res/web/serverfarm/main.bicep +++ b/avm/res/web/serverfarm/main.bicep @@ -7,19 +7,19 @@ metadata owner = 'Azure/module-maintainers' @maxLength(60) param name string -@description('Required. Defines the name, tier, size, family and capacity of the App Service Plan.') +@description('Required. The name of the SKU will Determine the tier, size, family of the App Service Plan.') @metadata({ example: ''' - { - name: 'P1v3' - tier: 'Premium' - size: 'P1v3' - family: 'P' - capacity: 3 - } + 'F1' + 'B1' + 'P1v3' + 'I1v2' ''' }) -param sku object +param skuName string + +@description('Required. Number of workers associated with the App Service Plan.') +param skuCapacity int @description('Optional. Location for all resources.') param location string = resourceGroup().location @@ -35,7 +35,7 @@ param location string = resourceGroup().location param kind string = 'App' @description('Conditional. Defaults to false when creating Windows/app App Service Plan. Required if creating a Linux App Service Plan and must be set to true.') -param reserved bool = false +param reserved bool = (kind == 'Linux') @description('Optional. The Resource ID of the App Service Environment to use for the App Service Plan.') param appServiceEnvironmentId string = '' @@ -61,7 +61,7 @@ param targetWorkerCount int = 0 param targetWorkerSize int = 0 @description('Optional. Zone Redundancy can only be used on Premium or ElasticPremium SKU Tiers within ZRS Supported regions (https://learn.microsoft.com/en-us/azure/storage/common/redundancy-regions-zrs).') -param zoneRedundant bool = (sku.tier == 'Premium' || sku.tier == 'ElasticPremium') ? true : false +param zoneRedundant bool = startsWith(skuName, 'P') || startsWith(skuName, 'EP') ? true : false @description('Optional. The lock settings of the service.') param lock lockType @@ -124,7 +124,10 @@ resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = { kind: kind location: location tags: tags - sku: sku + sku: { + name: skuName + capacity: skuCapacity + } properties: { workerTierName: workerTierName hostingEnvironmentProfile: !empty(appServiceEnvironmentId) diff --git a/avm/res/web/serverfarm/main.json b/avm/res/web/serverfarm/main.json index bf40400bed..1e9678c755 100644 --- a/avm/res/web/serverfarm/main.json +++ b/avm/res/web/serverfarm/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12495804892051718765" + "version": "0.26.170.59819", + "templateHash": "5846778493081554072" }, "name": "App Service Plan", "description": "This module deploys an App Service Plan.", @@ -201,11 +201,17 @@ "description": "Required. Name of the app service plan." } }, - "sku": { - "type": "object", + "skuName": { + "type": "string", + "metadata": { + "example": " 'F1'\n 'B1'\n 'P1v3'\n 'I1v2'\n ", + "description": "Required. The name of the SKU will Determine the tier, size, family of the App Service Plan." + } + }, + "skuCapacity": { + "type": "int", "metadata": { - "example": " {\n name: 'P1v3'\n tier: 'Premium'\n size: 'P1v3'\n family: 'P'\n capacity: 3\n }\n ", - "description": "Required. Defines the name, tier, size, family and capacity of the App Service Plan." + "description": "Required. Number of workers associated with the App Service Plan." } }, "location": { @@ -231,7 +237,7 @@ }, "reserved": { "type": "bool", - "defaultValue": false, + "defaultValue": "[equals(parameters('kind'), 'Linux')]", "metadata": { "description": "Conditional. Defaults to false when creating Windows/app App Service Plan. Required if creating a Linux App Service Plan and must be set to true." } @@ -285,7 +291,7 @@ }, "zoneRedundant": { "type": "bool", - "defaultValue": "[if(or(equals(parameters('sku').tier, 'Premium'), equals(parameters('sku').tier, 'ElasticPremium')), true(), false())]", + "defaultValue": "[if(or(startsWith(parameters('skuName'), 'P'), startsWith(parameters('skuName'), 'EP')), true(), false())]", "metadata": { "description": "Optional. Zone Redundancy can only be used on Premium or ElasticPremium SKU Tiers within ZRS Supported regions (https://learn.microsoft.com/en-us/azure/storage/common/redundancy-regions-zrs)." } @@ -362,7 +368,10 @@ "kind": "[parameters('kind')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", - "sku": "[parameters('sku')]", + "sku": { + "name": "[parameters('skuName')]", + "capacity": "[parameters('skuCapacity')]" + }, "properties": { "workerTierName": "[parameters('workerTierName')]", "hostingEnvironmentProfile": "[if(not(empty(parameters('appServiceEnvironmentId'))), createObject('id', parameters('appServiceEnvironmentId')), null())]", diff --git a/avm/res/web/serverfarm/tests/e2e/defaults/main.test.bicep b/avm/res/web/serverfarm/tests/e2e/defaults/main.test.bicep index 9f2ffe8e94..0183742502 100644 --- a/avm/res/web/serverfarm/tests/e2e/defaults/main.test.bicep +++ b/avm/res/web/serverfarm/tests/e2e/defaults/main.test.bicep @@ -46,13 +46,8 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: tempLocation - sku: { - name: 'P1v3' - tier: 'Premium' - size: 'P1v3' - family: 'P' - capacity: 3 - } + skuName: 'S1' + skuCapacity: 2 } } ] diff --git a/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep b/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep index 5451356bd1..b987bfb1ae 100644 --- a/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep +++ b/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep @@ -67,15 +67,10 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: tempLocation - sku: { - name: 'P1v3' - tier: 'Premium' - size: 'P1v3' - family: 'P' - capacity: 3 - } + skuName: 'S1' + skuCapacity: 1 perSiteScaling: true - zoneRedundant: true + zoneRedundant: false kind: 'App' lock: { name: 'lock' diff --git a/avm/res/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep b/avm/res/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep index 5cd23e57c9..45080f9d3b 100644 --- a/avm/res/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep @@ -58,13 +58,8 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: tempLocation - sku: { - name: 'P1v3' - tier: 'Premium' - size: 'P1v3' - family: 'P' - capacity: 3 - } + skuName: 'P1v3' + skuCapacity: 3 zoneRedundant: true kind: 'App' lock: { diff --git a/avm/res/web/serverfarm/version.json b/avm/res/web/serverfarm/version.json index 8def869ede..daf1a794d9 100644 --- a/avm/res/web/serverfarm/version.json +++ b/avm/res/web/serverfarm/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/web/site/main.bicep b/avm/res/web/site/main.bicep index 19fa05ed2d..9c2aefa0e4 100644 --- a/avm/res/web/site/main.bicep +++ b/avm/res/web/site/main.bicep @@ -169,7 +169,7 @@ var identity = !empty(managedIdentities) ? { type: (managedIdentities.?systemAssigned ?? false) ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') - : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : null) + : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None') userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null } : null diff --git a/avm/res/web/site/main.json b/avm/res/web/site/main.json index 3b31a486aa..3c2d02b4b7 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.26.54.24096", - "templateHash": "14301920664802681394" + "version": "0.26.170.59819", + "templateHash": "5830272725104890600" }, "name": "Web/Function Apps", "description": "This module deploys a Web or Function App.", @@ -747,7 +747,7 @@ }, "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())]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { "App Compliance Automation Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f37683f-2463-46b6-9ce7-9b788b988ba2')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -929,8 +929,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18064037455551234601" + "version": "0.26.170.59819", + "templateHash": "12051629915105082529" }, "name": "Site App Settings", "description": "This module deploys a Site App Setting.", @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "9660352953607316036" + "version": "0.26.170.59819", + "templateHash": "14303407385986258247" }, "name": "Site Auth Settings V2 Config", "description": "This module deploys a Site Auth Settings V2 Configuration.", @@ -1300,8 +1300,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "7436278786647572492" + "version": "0.26.170.59819", + "templateHash": "5665258089530156840" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot.", @@ -2208,8 +2208,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12764984503745572183" + "version": "0.26.170.59819", + "templateHash": "16550727222833899283" }, "name": "Site Slot App Settings", "description": "This module deploys a Site Slot App Setting.", @@ -2378,8 +2378,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1333776366661137381" + "version": "0.26.170.59819", + "templateHash": "512112730780239094" }, "name": "Site Slot Auth Settings V2 Config", "description": "This module deploys a Site Auth Settings V2 Configuration.", @@ -2494,8 +2494,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2236066853450471067" + "version": "0.26.170.59819", + "templateHash": "4923254671011353973" }, "name": "Web Site Slot Basic Publishing Credentials Policies", "description": "This module deploys a Web Site Slot Basic Publishing Credentials Policy.", @@ -2620,8 +2620,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1058820827217282473" + "version": "0.26.170.59819", + "templateHash": "15033433727480709023" }, "name": "Web/Function Apps Slot Hybrid Connection Relay", "description": "This module deploys a Site Slot Hybrid Connection Namespace Relay.", @@ -3440,8 +3440,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12379291046700283915" + "version": "0.26.170.59819", + "templateHash": "1590652329081458395" }, "name": "Web Site Basic Publishing Credentials Policies", "description": "This module deploys a Web Site Basic Publishing Credentials Policy.", @@ -3556,8 +3556,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12607693486765150465" + "version": "0.26.170.59819", + "templateHash": "15287918657229788223" }, "name": "Web/Function Apps Hybrid Connection Relay", "description": "This module deploys a Site Hybrid Connection Namespace Relay.", @@ -4366,4 +4366,4 @@ "value": "[reference('app').defaultHostName]" } } -} \ No newline at end of file +} diff --git a/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 b/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 index 42bbfda099..3ef3637972 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 @@ -35,6 +35,7 @@ function Set-AvmGitHubIssueOwnerConfig { # Loading helper functions . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Get-AvmCsvData.ps1') + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Add-GithubIssueToProject.ps1') $issue = gh issue view $IssueUrl.Replace('api.', '').Replace('repos/', '') --json 'author,title,url,body,comments' --repo $Repo | ConvertFrom-Json -Depth 100 diff --git a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 index a2c1a5f98d..fed872e51d 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 @@ -95,6 +95,7 @@ function Set-AvmGithubIssueForWorkflow { $ProjectNumber = 538 # AVM - Issue Triage $comment = @" > [!IMPORTANT] +> This module is currently orphaned (has no owner), therefore expect a higher response time. > @Azure/avm-core-team-technical-bicep, the workflow for the ``$moduleName`` module has failed. Please investigate the failed workflow run. "@ diff --git a/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 b/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 index cd9d0222e4..ea303f47e3 100644 --- a/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 +++ b/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 @@ -44,7 +44,7 @@ function Add-GithubIssueToProject { }' -f organization=$Organization -F number=$ProjectNumber | ConvertFrom-Json -Depth 10 $ProjectId = $Project.data.organization.projectV2.id - $IssueId = (gh issue view $IssueUrl --repo $Repo --json 'id' | ConvertFrom-Json -Depth 100).id + $IssueId = (gh issue view $IssueUrl.Replace('api.', '').Replace('repos/', '') --repo $Repo --json 'id' | ConvertFrom-Json -Depth 100).id gh api graphql -f query=' mutation($project:ID!, $issue:ID!) { diff --git a/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 b/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 index 17f6e077b9..821f476bb7 100644 --- a/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 +++ b/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 @@ -30,7 +30,6 @@ Function Get-AvmCsvData { 'Bicep-Resource' { try { $unfilteredCSV = Invoke-WebRequest -Uri $BicepResourceUrl - } catch { throw 'Unable to retrieve CSV file - Check network connection.' } diff --git a/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/custom-rules.Rule.yaml b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/custom-rules.Rule.yaml new file mode 100644 index 0000000000..c3bb28aae8 --- /dev/null +++ b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/custom-rules.Rule.yaml @@ -0,0 +1,14 @@ +# 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.yaml b/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml index f5bc660b72..b241ac0b1a 100644 --- a/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml +++ b/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml @@ -74,8 +74,9 @@ configuration: rule: # Enable custom rules that don't exist in the baseline - includeLocal: false + includeLocal: true exclude: # Ignore the following rules for all resources - Azure.KeyVault.PurgeProtect - Azure.VM.UseHybridUseBenefit + - Azure.Storage.UseReplication