From e4baf3f37bab7dff7879e9c70ca5a4053616a447 Mon Sep 17 00:00:00 2001 From: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:42:05 +0200 Subject: [PATCH 01/13] fix: minor fixes in issue automation (#1717) --- .github/workflows/avm.platform.manage-workflow-issue.yml | 3 ++- .../avm.platform.set-avm-github-issue-owner-config.yml | 2 +- .github/workflows/avm.platform.sync-avm-modules-list.yml | 1 + .../pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 | 1 + .../pipelines/platform/helper/Add-GithubIssueToProject.ps1 | 2 +- avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 | 1 - 6 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/avm.platform.manage-workflow-issue.yml b/.github/workflows/avm.platform.manage-workflow-issue.yml index 2125028fe3..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: 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/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.' } From 573eedd0004388ebb9e6580f0a29b26c3a39b488 Mon Sep 17 00:00:00 2001 From: Nate Arnold Date: Fri, 19 Apr 2024 09:28:05 -0600 Subject: [PATCH 02/13] feat: `avm/ptn/authorization/policy-assignment` (#1688) ## Description Migration of policy-assignment pattern module from CARML to AVM Closes https://github.com/Azure/Azure-Verified-Modules/issues/774 ## Pipeline Reference [![avm.ptn.authorization.policy-assignment](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.ptn.authorization.policy-assignment.yml/badge.svg?branch=avm%2Fptn%2Fauthorization%2Fpolicy-assignment)](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.ptn.authorization.policy-assignment.yml) ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [X] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [X] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- .github/CODEOWNERS | 3 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + ...m.ptn.authorization.policy-assignment.yml} | 10 +- .../authorization/policy-assignment/README.md | 1063 +++++++++++++++++ .../policy-assignment/main.bicep | 172 +++ .../authorization/policy-assignment/main.json | 989 +++++++++++++++ .../modules/management-group.bicep | 112 ++ .../modules/resource-group.bicep | 119 ++ .../modules/subscription.bicep | 112 ++ .../tests/e2e/mg.defaults/main.test.bicep | 33 + .../tests/e2e/mg.max/main.test.bicep | 96 ++ .../tests/e2e/rg.defaults/main.test.bicep | 57 + .../tests/e2e/rg.max/dependencies.bicep | 33 + .../tests/e2e/rg.max/main.test.bicep | 127 ++ .../tests/e2e/sub.defaults/main.test.bicep | 39 + .../tests/e2e/sub.max/dependencies.bicep | 13 + .../e2e/sub.max/interim.dependencies.bicep | 28 + .../tests/e2e/sub.max/main.test.bicep | 126 ++ .../policy-assignment/version.json | 7 + 19 files changed, 3134 insertions(+), 6 deletions(-) rename .github/workflows/{avm.ptn.authorization.role-assignment.yml => avm.ptn.authorization.policy-assignment.yml} (89%) create mode 100644 avm/ptn/authorization/policy-assignment/README.md create mode 100644 avm/ptn/authorization/policy-assignment/main.bicep create mode 100644 avm/ptn/authorization/policy-assignment/main.json create mode 100644 avm/ptn/authorization/policy-assignment/modules/management-group.bicep create mode 100644 avm/ptn/authorization/policy-assignment/modules/resource-group.bicep create mode 100644 avm/ptn/authorization/policy-assignment/modules/subscription.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/mg.defaults/main.test.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/mg.max/main.test.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/rg.defaults/main.test.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/dependencies.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/main.test.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/sub.defaults/main.test.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/dependencies.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/interim.dependencies.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/main.test.bicep create mode 100644 avm/ptn/authorization/policy-assignment/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7290c25df5..2851e9058b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,7 +3,9 @@ /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 @@ -11,7 +13,6 @@ /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 diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 681087787b..6c1a8d60bf 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -38,6 +38,7 @@ 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" diff --git a/.github/workflows/avm.ptn.authorization.role-assignment.yml b/.github/workflows/avm.ptn.authorization.policy-assignment.yml similarity index 89% rename from .github/workflows/avm.ptn.authorization.role-assignment.yml rename to .github/workflows/avm.ptn.authorization.policy-assignment.yml index c9ad828152..89b9f28b0f 100644 --- a/.github/workflows/avm.ptn.authorization.role-assignment.yml +++ b/.github/workflows/avm.ptn.authorization.policy-assignment.yml @@ -1,4 +1,4 @@ -name: "avm.ptn.authorization.role-assignment" +name: "avm.ptn.authorization.policy-assignment" on: schedule: @@ -26,15 +26,15 @@ on: paths: - ".github/actions/templates/avm-**" - ".github/workflows/avm.template.module.yml" - - ".github/workflows/avm.ptn.authorization.role-assignment.yml" - - "avm/ptn/authorization/role-assignment/**" + - ".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/role-assignment" - workflowPath: ".github/workflows/avm.ptn.authorization.role-assignment.yml" + modulePath: "avm/ptn/authorization/policy-assignment" + workflowPath: ".github/workflows/avm.ptn.authorization.policy-assignment.yml" concurrency: group: ${{ github.workflow }} 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" + ] +} From 77b523b1f592ca3b27ef51411dbe883309afd813 Mon Sep 17 00:00:00 2001 From: Buddy <38195643+tsc-buddy@users.noreply.github.com> Date: Sat, 20 Apr 2024 03:39:53 +1200 Subject: [PATCH 03/13] fix: 1507 linux asp (#1711) ## Description Fixes [issue 1507](https://github.com/Azure/bicep-registry-modules/issues/1507) logged relating to Linux ASP selection and the reserved property. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.web.serverfarm](https://github.com/tsc-buddy/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml/badge.svg?branch=fix%2F1507-linux-asp)](https://github.com/tsc-buddy/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/web/serverfarm/README.md | 26 +++++++++---------- avm/res/web/serverfarm/main.bicep | 2 +- avm/res/web/serverfarm/main.json | 6 ++--- .../serverfarm/tests/e2e/max/main.test.bicep | 12 ++++----- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/avm/res/web/serverfarm/README.md b/avm/res/web/serverfarm/README.md index 7788f77636..1a3e585904 100644 --- a/avm/res/web/serverfarm/README.md +++ b/avm/res/web/serverfarm/README.md @@ -112,11 +112,11 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { // Required parameters name: 'wsfmax001' sku: { - capacity: 3 - family: 'P' - name: 'P1v3' - size: 'P1v3' - tier: 'Premium' + capacity: 1 + family: 'S' + name: 'S1' + size: 'S1' + tier: 'Standard' } // Non-required parameters diagnosticSettings: [ @@ -162,7 +162,7 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - zoneRedundant: true + zoneRedundant: false } } ``` @@ -185,11 +185,11 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { }, "sku": { "value": { - "capacity": 3, - "family": "P", - "name": "P1v3", - "size": "P1v3", - "tier": "Premium" + "capacity": 1, + "family": "S", + "name": "S1", + "size": "S1", + "tier": "Standard" } }, // Non-required parameters @@ -251,7 +251,7 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { } }, "zoneRedundant": { - "value": true + "value": false } } } @@ -439,7 +439,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` diff --git a/avm/res/web/serverfarm/main.bicep b/avm/res/web/serverfarm/main.bicep index 7cff682e16..5deb3fe099 100644 --- a/avm/res/web/serverfarm/main.bicep +++ b/avm/res/web/serverfarm/main.bicep @@ -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 = '' diff --git a/avm/res/web/serverfarm/main.json b/avm/res/web/serverfarm/main.json index bf40400bed..68999ed0f2 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": "16669238654401736455" }, "name": "App Service Plan", "description": "This module deploys an App Service Plan.", @@ -231,7 +231,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." } 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..f7e0fb3a88 100644 --- a/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep +++ b/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep @@ -68,14 +68,14 @@ module testDeployment '../../../main.bicep' = [ name: '${namePrefix}${serviceShort}001' location: tempLocation sku: { - name: 'P1v3' - tier: 'Premium' - size: 'P1v3' - family: 'P' - capacity: 3 + name: 'S1' + tier: 'Standard' + size: 'S1' + family: 'S' + capacity: 1 } perSiteScaling: true - zoneRedundant: true + zoneRedundant: false kind: 'App' lock: { name: 'lock' From cc66c2b41a9438dbaed78aa4501124f775029a36 Mon Sep 17 00:00:00 2001 From: Ilhaan Rasheed Date: Fri, 19 Apr 2024 08:41:25 -0700 Subject: [PATCH 04/13] feat: `avm/res/network/application-gateway` (#835) New module for Application Gateway migrated from CARML [![avm.res.network.application-gateway](https://github.com/ilhaan/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway.yml/badge.svg?branch=avm-application-gateway)](https://github.com/ilhaan/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway.yml) --------- Co-authored-by: Alexander Sehr Co-authored-by: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> --- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 2 +- .../avm.res.network.application-gateway.yml | 86 + avm/res/network/application-gateway/README.md | 3257 +++++++++++++++++ .../network/application-gateway/main.bicep | 616 ++++ avm/res/network/application-gateway/main.json | 1633 +++++++++ .../tests/e2e/defaults/dependencies.bicep | 60 + .../tests/e2e/defaults/main.test.bicep | 146 + .../tests/e2e/max/dependencies.bicep | 154 + .../tests/e2e/max/main.test.bicep | 524 +++ .../tests/e2e/waf-aligned/dependencies.bicep | 154 + .../tests/e2e/waf-aligned/main.test.bicep | 486 +++ .../network/application-gateway/version.json | 7 + 13 files changed, 7125 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/avm.res.network.application-gateway.yml create mode 100644 avm/res/network/application-gateway/README.md create mode 100644 avm/res/network/application-gateway/main.bicep create mode 100644 avm/res/network/application-gateway/main.json create mode 100644 avm/res/network/application-gateway/tests/e2e/defaults/dependencies.bicep create mode 100644 avm/res/network/application-gateway/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/network/application-gateway/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/network/application-gateway/tests/e2e/max/main.test.bicep create mode 100644 avm/res/network/application-gateway/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/network/application-gateway/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/network/application-gateway/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2851e9058b..e04de640ac 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -78,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 diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 6c1a8d60bf..40c763986f 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -111,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" 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/avm/res/network/application-gateway/README.md b/avm/res/network/application-gateway/README.md new file mode 100644 index 0000000000..f3faed0358 --- /dev/null +++ b/avm/res/network/application-gateway/README.md @@ -0,0 +1,3257 @@ +# Network Application Gateways `[Microsoft.Network/applicationGateways]` + +This module deploys a Network Application Gateway. + +## 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.Network/applicationGateways` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/applicationGateways) | +| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | + +## 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/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" + ] +} From 4d1e57a4d242ad35088e3ab37526d276dbc4bb7d Mon Sep 17 00:00:00 2001 From: Nate Arnold Date: Fri, 19 Apr 2024 09:54:43 -0600 Subject: [PATCH 05/13] feat: `avm/res/sql/managed-instance` (#1618) Migration of SQL Managed Instance module from CARML to AVM ## Pipeline Reference [![avm.res.sql.managed-instance](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.res.sql.managed-instance.yml/badge.svg?branch=test%2Fsqlmi)](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.res.sql.managed-instance.yml) ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [X] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [X] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> Co-authored-by: Alexander Sehr --- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 2 +- .../avm.res.sql.managed-instance.yml | 85 + avm/res/sql/managed-instance/README.md | 1485 ++++++++++++ .../managed-instance/administrator/README.md | 84 + .../managed-instance/administrator/main.bicep | 39 + .../managed-instance/administrator/main.json | 77 + .../sql/managed-instance/database/README.md | 349 +++ .../README.md | 111 + .../main.bicep | 52 + .../main.json | 98 + .../README.md | 84 + .../main.bicep | 40 + .../main.json | 74 + .../sql/managed-instance/database/main.bicep | 198 ++ .../sql/managed-instance/database/main.json | 596 +++++ .../encryption-protector/README.md | 92 + .../encryption-protector/main.bicep | 42 + .../encryption-protector/main.json | 81 + avm/res/sql/managed-instance/key/README.md | 92 + avm/res/sql/managed-instance/key/main.bicep | 47 + avm/res/sql/managed-instance/key/main.json | 84 + avm/res/sql/managed-instance/main.bicep | 460 ++++ avm/res/sql/managed-instance/main.json | 1986 +++++++++++++++++ .../security-alert-policy/README.md | 92 + .../security-alert-policy/main.bicep | 41 + .../security-alert-policy/main.json | 80 + .../tests/e2e/defaults/dependencies.bicep | 288 +++ .../tests/e2e/defaults/main.test.bicep | 64 + .../tests/e2e/max/dependencies.bicep | 326 +++ .../tests/e2e/max/main.test.bicep | 189 ++ .../tests/e2e/vulnAssm/dependencies.bicep | 386 ++++ .../tests/e2e/vulnAssm/main.test.bicep | 90 + .../tests/e2e/waf-aligned/dependencies.bicep | 326 +++ .../tests/e2e/waf-aligned/main.test.bicep | 172 ++ avm/res/sql/managed-instance/version.json | 7 + .../vulnerability-assessment/README.md | 121 + .../vulnerability-assessment/main.bicep | 64 + .../vulnerability-assessment/main.json | 167 ++ .../nested_storageRoleAssignment.bicep | 20 + 40 files changed, 8691 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/avm.res.sql.managed-instance.yml create mode 100644 avm/res/sql/managed-instance/README.md create mode 100644 avm/res/sql/managed-instance/administrator/README.md create mode 100644 avm/res/sql/managed-instance/administrator/main.bicep create mode 100644 avm/res/sql/managed-instance/administrator/main.json create mode 100644 avm/res/sql/managed-instance/database/README.md create mode 100644 avm/res/sql/managed-instance/database/backup-long-term-retention-policy/README.md create mode 100644 avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.bicep create mode 100644 avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.json create mode 100644 avm/res/sql/managed-instance/database/backup-short-term-retention-policy/README.md create mode 100644 avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.bicep create mode 100644 avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.json create mode 100644 avm/res/sql/managed-instance/database/main.bicep create mode 100644 avm/res/sql/managed-instance/database/main.json create mode 100644 avm/res/sql/managed-instance/encryption-protector/README.md create mode 100644 avm/res/sql/managed-instance/encryption-protector/main.bicep create mode 100644 avm/res/sql/managed-instance/encryption-protector/main.json create mode 100644 avm/res/sql/managed-instance/key/README.md create mode 100644 avm/res/sql/managed-instance/key/main.bicep create mode 100644 avm/res/sql/managed-instance/key/main.json create mode 100644 avm/res/sql/managed-instance/main.bicep create mode 100644 avm/res/sql/managed-instance/main.json create mode 100644 avm/res/sql/managed-instance/security-alert-policy/README.md create mode 100644 avm/res/sql/managed-instance/security-alert-policy/main.bicep create mode 100644 avm/res/sql/managed-instance/security-alert-policy/main.json create mode 100644 avm/res/sql/managed-instance/tests/e2e/defaults/dependencies.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/max/main.test.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/vulnAssm/dependencies.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/vulnAssm/main.test.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/sql/managed-instance/version.json create mode 100644 avm/res/sql/managed-instance/vulnerability-assessment/README.md create mode 100644 avm/res/sql/managed-instance/vulnerability-assessment/main.bicep create mode 100644 avm/res/sql/managed-instance/vulnerability-assessment/main.json create mode 100644 avm/res/sql/managed-instance/vulnerability-assessment/modules/nested_storageRoleAssignment.bicep diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e04de640ac..0734b0fb91 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -131,7 +131,7 @@ /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/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 40c763986f..8ad0c07acd 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -164,7 +164,7 @@ 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/managed-instance" - "avm/res/sql/server" - "avm/res/storage/storage-account" - "avm/res/synapse/private-link-hub" 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/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' + } +} From 950223f8de83d690fc272ed5cbac812b17e5ad43 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Fri, 19 Apr 2024 22:05:40 +0200 Subject: [PATCH 06/13] fix: Recovered missing role assignment workflow (#1719) ## Description Recovered missing role assignment workflow --- .../avm.ptn.authorization.role-assignment.yml | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 .github/workflows/avm.ptn.authorization.role-assignment.yml 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 From 7c25f74a8288ef15f8c1b158e48312e54e979af0 Mon Sep 17 00:00:00 2001 From: Richard Hooper Date: Fri, 19 Apr 2024 22:32:15 +0100 Subject: [PATCH 07/13] feat: added option to set backendpooltype. - `avm/res/container-service/managed-cluster` (#1709) ## Description This adds the option to change the backendpooltype. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.container-service.managed-cluster](https://github.com/PixelRobots/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml/badge.svg?branch=feat-rh-backendpooltype)](https://github.com/PixelRobots/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [X] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Signed-off-by: PixelRobots <22979170+PixelRobots@users.noreply.github.com> --- .../managed-cluster/README.md | 16 ++++++++++++ .../managed-cluster/main.bicep | 20 ++++++++++----- .../managed-cluster/main.json | 25 +++++++++++++++---- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/avm/res/container-service/managed-cluster/README.md b/avm/res/container-service/managed-cluster/README.md index 43e75d0fb7..f1f8091e4e 100644 --- a/avm/res/container-service/managed-cluster/README.md +++ b/avm/res/container-service/managed-cluster/README.md @@ -1501,6 +1501,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster: Date: Fri, 19 Apr 2024 23:48:50 +0100 Subject: [PATCH 08/13] docs: fixing issue #1064 - `avm/res/container-service/managed-cluster` (#1715) ## Description Closes #1064 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.container-service.managed-cluster](https://github.com/PixelRobots/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml/badge.svg?branch=rh-container-service-docs-update)](https://github.com/PixelRobots/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [X] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings Signed-off-by: PixelRobots <22979170+PixelRobots@users.noreply.github.com> --- .../managed-cluster/README.md | 72 +++++++------------ .../managed-cluster/main.json | 2 +- .../tests/e2e/azure/main.test.bicep | 9 +-- .../tests/e2e/kubenet/main.test.bicep | 9 +-- .../tests/e2e/priv/main.test.bicep | 9 +-- .../tests/e2e/waf-aligned/main.test.bicep | 9 +-- 6 files changed, 37 insertions(+), 73 deletions(-) diff --git a/avm/res/container-service/managed-cluster/README.md b/avm/res/container-service/managed-cluster/README.md index f1f8091e4e..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: Date: Sun, 21 Apr 2024 22:15:19 +0200 Subject: [PATCH 09/13] feat: Added custom Azure storage replication rule and updated ps-rule (#1725) --- .../psrule/.ps-rule/custom-rules.Rule.yaml | 14 ++++++++++++++ .../pipelines/staticValidation/psrule/ps-rule.yaml | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 avm/utilities/pipelines/staticValidation/psrule/.ps-rule/custom-rules.Rule.yaml 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 From 557203abd1fe4993c82b0d4355aee1dbe09911c6 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 22 Apr 2024 23:47:05 +0200 Subject: [PATCH 10/13] fix: Update instance size options for API Management service - `avm/res/api-management/service` (#1629) Added '0' as an allowed value for instance size selection. Fixes #1628 ## Description ## Pipeline Reference | Pipeline | | -------- | | | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [X] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [X] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/api-management/service/README.md | 1 + avm/res/api-management/service/main.bicep | 1 + avm/res/api-management/service/main.json | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) 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 ], From 1dd8628c121e4bb2ba8fa6372bb3a202f2c6ad46 Mon Sep 17 00:00:00 2001 From: Buddy <38195643+tsc-buddy@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:20:19 +1200 Subject: [PATCH 11/13] fix: AppServicePlan - SKU selection improvements (#1736) ## Description Provides improvements to simplify the selection of SKUs for App Service Plans by relying on the RP to provide the values for 'tier', 'family' and 'size' properties based on the 'name' provided. Logic adjusted on ZR param to accommodate the change also. Closes #1506 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.web.serverfarm](https://github.com/tsc-buddy/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml/badge.svg?branch=fix%2F1506-sku-updates)](https://github.com/tsc-buddy/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/web/serverfarm/README.md | 84 ++++++++----------- avm/res/web/serverfarm/main.bicep | 25 +++--- avm/res/web/serverfarm/main.json | 23 +++-- .../tests/e2e/defaults/main.test.bicep | 9 +- .../serverfarm/tests/e2e/max/main.test.bicep | 9 +- .../tests/e2e/waf-aligned/main.test.bicep | 9 +- avm/res/web/serverfarm/version.json | 2 +- 7 files changed, 71 insertions(+), 90 deletions(-) diff --git a/avm/res/web/serverfarm/README.md b/avm/res/web/serverfarm/README.md index 1a3e585904..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: 1 - family: 'S' - name: 'S1' - size: 'S1' - tier: 'Standard' - } + skuCapacity: 1 + skuName: 'S1' // Non-required parameters diagnosticSettings: [ { @@ -183,14 +170,11 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { "name": { "value": "wsfmax001" }, - "sku": { - "value": { - "capacity": 1, - "family": "S", - "name": "S1", - "size": "S1", - "tier": "Standard" - } + "skuCapacity": { + "value": 1 + }, + "skuName": { + "value": "S1" }, // Non-required parameters "diagnosticSettings": { @@ -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` @@ -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 5deb3fe099..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 @@ -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 68999ed0f2..1e9678c755 100644 --- a/avm/res/web/serverfarm/main.json +++ b/avm/res/web/serverfarm/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "16669238654401736455" + "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": " {\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." + "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": { + "description": "Required. Number of workers associated with the App Service Plan." } }, "location": { @@ -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 f7e0fb3a88..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,13 +67,8 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: tempLocation - sku: { - name: 'S1' - tier: 'Standard' - size: 'S1' - family: 'S' - capacity: 1 - } + skuName: 'S1' + skuCapacity: 1 perSiteScaling: true zoneRedundant: false kind: 'App' 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" ] From 9fc50746ca6981becc7d2e6df9a611c835da685f Mon Sep 17 00:00:00 2001 From: Yas Date: Tue, 23 Apr 2024 12:28:57 +1000 Subject: [PATCH 12/13] feat: `avm/res/sql/instance-pool` (#1714) ## Description This PR adds the new Azure SQL Instance pool and its tests Closes [Module Proposal : avm/res/sql/instance-pool](#752) ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.sql.instance-pool](https://github.com/yashints/bicep-registry-modules/actions/workflows/avm.res.sql.instance-pool.yml/badge.svg)](https://github.com/yashints/bicep-registry-modules/actions/workflows/avm.res.sql.instance-pool.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + .../workflows/avm.res.sql.instance-pool.yml | 86 ++++++ avm/res/sql/instance-pool/README.md | 286 ++++++++++++++++++ avm/res/sql/instance-pool/main.bicep | 102 +++++++ avm/res/sql/instance-pool/main.json | 176 +++++++++++ .../tests/e2e/defaults/dependencies.bicep | 286 ++++++++++++++++++ .../tests/e2e/defaults/main.test.bicep | 61 ++++ .../tests/e2e/waf-aligned/dependencies.bicep | 286 ++++++++++++++++++ .../tests/e2e/waf-aligned/main.test.bicep | 62 ++++ avm/res/sql/instance-pool/version.json | 7 + 11 files changed, 1354 insertions(+) create mode 100644 .github/workflows/avm.res.sql.instance-pool.yml create mode 100644 avm/res/sql/instance-pool/README.md create mode 100644 avm/res/sql/instance-pool/main.bicep create mode 100644 avm/res/sql/instance-pool/main.json create mode 100644 avm/res/sql/instance-pool/tests/e2e/defaults/dependencies.bicep create mode 100644 avm/res/sql/instance-pool/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/sql/instance-pool/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/sql/instance-pool/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/sql/instance-pool/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0734b0fb91..68a24d86e1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -131,6 +131,7 @@ /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/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 diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 8ad0c07acd..9a7da432aa 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -164,6 +164,7 @@ body: - "avm/res/service-fabric/cluster" - "avm/res/signal-r-service/signal-r" - "avm/res/signal-r-service/web-pub-sub" + - "avm/res/sql/instance-pool" - "avm/res/sql/managed-instance" - "avm/res/sql/server" - "avm/res/storage/storage-account" 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/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" + ] +} From 31898eddd28967a6560f1d98d97f5c144935b652 Mon Sep 17 00:00:00 2001 From: Christoph Vollmann Date: Tue, 23 Apr 2024 04:58:27 +0200 Subject: [PATCH 13/13] fix: 1704 Web Site without Managed Identity (#1738) ## Description This PR changes the default value for Managed Identity Type from `null` to `'None'` to allow creation of sites without Managed Identity as per documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.web/sites?pivots=deployment-language-bicep#managedserviceidentity. The pipeline run shows Bicep Linter warnings for the `identity` property in the pester tests: https://github.com/cloudchristoph/bicep-registry-modules/actions/runs/8783746073/job/24100560440#step:4:234 ``` /home/runner/work/bicep-registry-modules/bicep-registry-modules/avm/res/web/site/main.bicep(227,13) : Warning BCP036: The property "type" expected a value of type "'None' | 'SystemAssigned' | 'SystemAssigned, UserAssigned' | 'UserAssigned' | null" but the provided value in source declaration "identity" is of type "'None' | 'SystemAssigned' | 'SystemAssigned,UserAssigned' | 'UserAssigned'". If this is an inaccuracy in the documentation, please report it to the Bicep Team. [https://aka.ms/bicep-type-issues] ``` The warning also has existed before, but for the missing `'None'` value, so I think it's not an issue. Regarding the backwards compatibility: This change doesn't affect the actual deployment. Managed Identities are disabled no matter which of the two values is given. Fixes #1704 Closes #1704 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.web.site](https://github.com/cloudchristoph/bicep-registry-modules/actions/workflows/avm.res.web.site.yml/badge.svg?branch=1704_site_without_managed_identity)](https://github.com/cloudchristoph/bicep-registry-modules/actions/workflows/avm.res.web.site.yml)| ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings (see above) --- avm/res/web/site/main.bicep | 2 +- avm/res/web/site/main.json | 42 ++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 22 deletions(-) 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..607d3adbb5 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.",