From 69afc8b59625ec817f5e4b478d00b718749800d4 Mon Sep 17 00:00:00 2001 From: Ahmad Abdalla <28486158+ahmadabdalla@users.noreply.github.com> Date: Tue, 24 Oct 2023 14:35:32 +1100 Subject: [PATCH 1/5] [Modules] Uplifted the Vulnerability Assessment child module for SQL to align with SQL MI --- .../server/.test/vulnAssm/dependencies.bicep | 38 ++++++ .../sql/server/.test/vulnAssm/main.test.bicep | 91 +++++++++++++ modules/sql/server/README.md | 128 ++++++++++++++++++ modules/sql/server/main.bicep | 4 +- modules/sql/server/main.json | 87 +++++++++++- .../.bicep/nested_storageRoleAssignment.bicep | 17 +++ .../server/vulnerability-assessment/README.md | 31 ++++- .../vulnerability-assessment/main.bicep | 26 +++- .../server/vulnerability-assessment/main.json | 79 ++++++++++- 9 files changed, 479 insertions(+), 22 deletions(-) create mode 100644 modules/sql/server/.test/vulnAssm/dependencies.bicep create mode 100644 modules/sql/server/.test/vulnAssm/main.test.bicep create mode 100644 modules/sql/server/vulnerability-assessment/.bicep/nested_storageRoleAssignment.bicep diff --git a/modules/sql/server/.test/vulnAssm/dependencies.bicep b/modules/sql/server/.test/vulnAssm/dependencies.bicep new file mode 100644 index 0000000000..3d89db3b2a --- /dev/null +++ b/modules/sql/server/.test/vulnAssm/dependencies.bicep @@ -0,0 +1,38 @@ +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + allowBlobPublicAccess: false + networkAcls: { + defaultAction: 'Deny' + bypass: 'AzureServices' + } + } +} + +@description('The principal ID of the created managed identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created managed identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id diff --git a/modules/sql/server/.test/vulnAssm/main.test.bicep b/modules/sql/server/.test/vulnAssm/main.test.bicep new file mode 100644 index 0000000000..5dd0f342e9 --- /dev/null +++ b/modules/sql/server/.test/vulnAssm/main.test.bicep @@ -0,0 +1,91 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-sql.servers-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'sqlsvln' + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@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: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + storageAccountName: 'dep${namePrefix}cdnstore${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '${namePrefix}-${serviceShort}' + primaryUserAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId + administratorLogin: 'adminUserName' + administratorLoginPassword: password + location: location + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId + useStorageAccountAccessKey: false + createStorageRoleAssignment: true + } + securityAlertPolicies: [ + { + name: 'Default' + state: 'Enabled' + emailAccountAdmins: true + } + ] + systemAssignedIdentity: true + userAssignedIdentities: { + '${nestedDependencies.outputs.managedIdentityResourceId}': {} + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/modules/sql/server/README.md b/modules/sql/server/README.md index 20749d71d0..4df3bfef12 100644 --- a/modules/sql/server/README.md +++ b/modules/sql/server/README.md @@ -44,6 +44,7 @@ The following section provides usage examples for the module, which were used to - [Using large parameter set](#example-2-using-large-parameter-set) - [Pe](#example-3-pe) - [Secondary](#example-4-secondary) +- [Vulnassm](#example-5-vulnassm) ### Example 1: _Admin_ @@ -594,6 +595,133 @@ module server 'br:bicep/modules/sql.server:1.0.0' = {
+### Example 5: _Vulnassm_
+
+via Bicep module
+
+```bicep
+module server 'br:bicep/modules/sql.server:1.0.0' = {
+ name: '${uniqueString(deployment().name, location)}-test-sqlsvln'
+ params: {
+ // Required parameters
+ name: 'sqlsvln'
+ // Non-required parameters
+ administratorLogin: 'adminUserName'
+ administratorLoginPassword: '
+
+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": "sqlsvln"
+ },
+ // Non-required parameters
+ "administratorLogin": {
+ "value": "adminUserName"
+ },
+ "administratorLoginPassword": {
+ "value": "
+ ## Parameters diff --git a/modules/sql/server/main.bicep b/modules/sql/server/main.bicep index 9a3ba48092..2442fef83a 100644 --- a/modules/sql/server/main.bicep +++ b/modules/sql/server/main.bicep @@ -311,7 +311,9 @@ module server_vulnerabilityAssessment 'vulnerability-assessment/main.bicep' = if recurringScansEmails: contains(vulnerabilityAssessmentsObj, 'recurringScansEmails') ? vulnerabilityAssessmentsObj.recurringScansEmails : [] recurringScansEmailSubscriptionAdmins: contains(vulnerabilityAssessmentsObj, 'recurringScansEmailSubscriptionAdmins') ? vulnerabilityAssessmentsObj.recurringScansEmailSubscriptionAdmins : false recurringScansIsEnabled: contains(vulnerabilityAssessmentsObj, 'recurringScansIsEnabled') ? vulnerabilityAssessmentsObj.recurringScansIsEnabled : false - storageAccountResourceId: contains(vulnerabilityAssessmentsObj, 'storageAccountResourceId') ? vulnerabilityAssessmentsObj.storageAccountResourceId : '' + storageAccountResourceId: vulnerabilityAssessmentsObj.storageAccountResourceId + useStorageAccountAccessKey: contains(vulnerabilityAssessmentsObj, 'useStorageAccountAccessKey') ? vulnerabilityAssessmentsObj.useStorageAccountAccessKey : false + createStorageRoleAssignment: contains(vulnerabilityAssessmentsObj, 'createStorageRoleAssignment') ? vulnerabilityAssessmentsObj.createStorageRoleAssignment : true enableDefaultTelemetry: enableReferencedModulesTelemetry } dependsOn: [ diff --git a/modules/sql/server/main.json b/modules/sql/server/main.json index 8becec3ebd..87c1a714fb 100644 --- a/modules/sql/server/main.json +++ b/modules/sql/server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "6602628409746140291" + "templateHash": "10290542827488983748" }, "name": "Azure SQL Servers", "description": "This module deploys an Azure SQL Server.", @@ -2375,7 +2375,11 @@ "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": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'storageAccountResourceId'), createObject('value', parameters('vulnerabilityAssessmentsObj').storageAccountResourceId), createObject('value', ''))]", + "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()))]", "enableDefaultTelemetry": { "value": "[variables('enableReferencedModulesTelemetry')]" } @@ -2387,7 +2391,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "2049927305875122003" + "templateHash": "7362346898062262239" }, "name": "Azure SQL Server Vulnerability Assessments", "description": "This module deploys an Azure SQL Server Vulnerability Assessment.", @@ -2403,7 +2407,7 @@ "serverName": { "type": "string", "metadata": { - "description": "Required. The Name of SQL Server." + "description": "Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment" } }, "recurringScansIsEnabled": { @@ -2429,9 +2433,22 @@ }, "storageAccountResourceId": { "type": "string", - "defaultValue": "", "metadata": { - "description": "Optional. A blob storage to hold the scan results." + "description": "Required. A blob storage to hold the scan results." + } + }, + "useStorageAccountAccessKey": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL Server system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account." + } + }, + "createStorageRoleAssignment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account." } }, "enableDefaultTelemetry": { @@ -2442,6 +2459,9 @@ } } }, + "variables": { + "splitStorageAccountResourceId": "[split(parameters('storageAccountResourceId'), '/')]" + }, "resources": [ { "condition": "[parameters('enableDefaultTelemetry')]", @@ -2463,13 +2483,66 @@ "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", "properties": { "storageContainerPath": "[format('https://{0}.blob.{1}/vulnerability-assessment/', last(split(parameters('storageAccountResourceId'), '/')), environment().suffixes.storage)]", - "storageAccountAccessKey": "[listKeys(parameters('storageAccountResourceId'), '2019-06-01').keys[0].value]", + "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('serverName'))]", + "resourceGroup": "[variables('splitStorageAccountResourceId')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[last(variables('splitStorageAccountResourceId'))]" + }, + "managedInstanceIdentityPrincipalId": { + "value": "[reference(resourceId('Microsoft.Sql/servers', parameters('serverName')), '2022-05-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.22.6.54827", + "templateHash": "9210546972730714858" + } + }, + "parameters": { + "storageAccountName": { + "type": "string" + }, + "managedInstanceIdentityPrincipalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", + "name": "[guid(format('{0}-{1}-Storage-Blob-Data-Contributor', resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('managedInstanceIdentityPrincipalId')))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "principalId": "[parameters('managedInstanceIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + } + } + ] + } + } } ], "outputs": { diff --git a/modules/sql/server/vulnerability-assessment/.bicep/nested_storageRoleAssignment.bicep b/modules/sql/server/vulnerability-assessment/.bicep/nested_storageRoleAssignment.bicep new file mode 100644 index 0000000000..7855e9f142 --- /dev/null +++ b/modules/sql/server/vulnerability-assessment/.bicep/nested_storageRoleAssignment.bicep @@ -0,0 +1,17 @@ +param storageAccountName string +param managedInstanceIdentityPrincipalId string + +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = { + name: storageAccountName +} + +// Assign Storage Blob Data Contributor RBAC role +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('${storageAccount.id}-${managedInstanceIdentityPrincipalId}-Storage-Blob-Data-Contributor') + scope: storageAccount + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + principalId: managedInstanceIdentityPrincipalId + principalType: 'ServicePrincipal' + } +} diff --git a/modules/sql/server/vulnerability-assessment/README.md b/modules/sql/server/vulnerability-assessment/README.md index ba96061893..5a2818dfe8 100644 --- a/modules/sql/server/vulnerability-assessment/README.md +++ b/modules/sql/server/vulnerability-assessment/README.md @@ -13,6 +13,7 @@ This module deploys an Azure SQL Server Vulnerability Assessment. | 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/servers/vulnerabilityAssessments` | [2022-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/servers/vulnerabilityAssessments) | ## Parameters @@ -22,17 +23,31 @@ This module deploys an Azure SQL Server Vulnerability Assessment. | Parameter | Type | Description | | :-- | :-- | :-- | | [`name`](#parameter-name) | string | The name of the vulnerability assessment. | -| [`serverName`](#parameter-servername) | string | The Name of SQL Server. | +| [`storageAccountResourceId`](#parameter-storageaccountresourceid) | string | A blob storage to hold the scan results. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`serverName`](#parameter-servername) | string | The Name of SQL Server. 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. | | [`enableDefaultTelemetry`](#parameter-enabledefaulttelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). | | [`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. | -| [`storageAccountResourceId`](#parameter-storageaccountresourceid) | string | A blob storage to hold the scan results. | +| [`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 Server system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account. | + +### 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: `enableDefaultTelemetry` @@ -70,16 +85,22 @@ Recurring scans state. ### Parameter: `serverName` -The Name of SQL Server. +The Name of SQL Server. Required if the template is used in a standalone deployment - Required: Yes - Type: string ### Parameter: `storageAccountResourceId` A blob storage to hold the scan results. -- Required: No +- Required: Yes - Type: string -- Default: `''` + +### 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 Server system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account. +- Required: No +- Type: bool +- Default: `False` ## Outputs diff --git a/modules/sql/server/vulnerability-assessment/main.bicep b/modules/sql/server/vulnerability-assessment/main.bicep index 7821e1dea5..709c9a89ef 100644 --- a/modules/sql/server/vulnerability-assessment/main.bicep +++ b/modules/sql/server/vulnerability-assessment/main.bicep @@ -5,7 +5,7 @@ metadata owner = 'Azure/module-maintainers' @description('Required. The name of the vulnerability assessment.') param name string -@description('Required. The Name of SQL Server.') +@description('Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment') param serverName string @description('Optional. Recurring scans state.') @@ -17,12 +17,20 @@ param recurringScansEmailSubscriptionAdmins bool = false @description('Optional. Specifies an array of email addresses to which the scan notification is sent.') param recurringScansEmails array = [] -@description('Optional. A blob storage to hold the scan results.') -param storageAccountResourceId string = '' +@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 Server 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 @description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') param enableDefaultTelemetry bool = true +var splitStorageAccountResourceId = split(storageAccountResourceId, '/') + resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { name: 'pid-9319755b-f697-4146-b966-4656e0b46cac-${uniqueString(deployment().name)}' properties: { @@ -39,12 +47,22 @@ resource server 'Microsoft.Sql/servers@2022-05-01-preview' existing = { name: serverName } +// Assign SQL Server MSI access to storage account +module storageAccount_sbdc_rbac '.bicep/nested_storageRoleAssignment.bicep' = if (!useStorageAccountAccessKey && createStorageRoleAssignment) { + name: '${server.name}-sbdc-rbac' + scope: resourceGroup(splitStorageAccountResourceId[4]) + params: { + storageAccountName: last(splitStorageAccountResourceId) + managedInstanceIdentityPrincipalId: server.identity.principalId + } +} + resource vulnerabilityAssessment 'Microsoft.Sql/servers/vulnerabilityAssessments@2022-05-01-preview' = { name: name parent: server properties: { storageContainerPath: 'https://${last(split(storageAccountResourceId, '/'))}.blob.${environment().suffixes.storage}/vulnerability-assessment/' - storageAccountAccessKey: listKeys(storageAccountResourceId, '2019-06-01').keys[0].value + storageAccountAccessKey: useStorageAccountAccessKey ? listKeys(storageAccountResourceId, '2019-06-01').keys[0].value : any(null) recurringScans: { isEnabled: recurringScansIsEnabled emailSubscriptionAdmins: recurringScansEmailSubscriptionAdmins diff --git a/modules/sql/server/vulnerability-assessment/main.json b/modules/sql/server/vulnerability-assessment/main.json index 29a24e8faa..adb54b6892 100644 --- a/modules/sql/server/vulnerability-assessment/main.json +++ b/modules/sql/server/vulnerability-assessment/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "2049927305875122003" + "templateHash": "7362346898062262239" }, "name": "Azure SQL Server Vulnerability Assessments", "description": "This module deploys an Azure SQL Server Vulnerability Assessment.", @@ -21,7 +21,7 @@ "serverName": { "type": "string", "metadata": { - "description": "Required. The Name of SQL Server." + "description": "Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment" } }, "recurringScansIsEnabled": { @@ -47,9 +47,22 @@ }, "storageAccountResourceId": { "type": "string", - "defaultValue": "", "metadata": { - "description": "Optional. A blob storage to hold the scan results." + "description": "Required. A blob storage to hold the scan results." + } + }, + "useStorageAccountAccessKey": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL Server system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account." + } + }, + "createStorageRoleAssignment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account." } }, "enableDefaultTelemetry": { @@ -60,6 +73,9 @@ } } }, + "variables": { + "splitStorageAccountResourceId": "[split(parameters('storageAccountResourceId'), '/')]" + }, "resources": [ { "condition": "[parameters('enableDefaultTelemetry')]", @@ -81,13 +97,66 @@ "name": "[format('{0}/{1}', parameters('serverName'), parameters('name'))]", "properties": { "storageContainerPath": "[format('https://{0}.blob.{1}/vulnerability-assessment/', last(split(parameters('storageAccountResourceId'), '/')), environment().suffixes.storage)]", - "storageAccountAccessKey": "[listKeys(parameters('storageAccountResourceId'), '2019-06-01').keys[0].value]", + "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('serverName'))]", + "resourceGroup": "[variables('splitStorageAccountResourceId')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[last(variables('splitStorageAccountResourceId'))]" + }, + "managedInstanceIdentityPrincipalId": { + "value": "[reference(resourceId('Microsoft.Sql/servers', parameters('serverName')), '2022-05-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.22.6.54827", + "templateHash": "9210546972730714858" + } + }, + "parameters": { + "storageAccountName": { + "type": "string" + }, + "managedInstanceIdentityPrincipalId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", + "name": "[guid(format('{0}-{1}-Storage-Blob-Data-Contributor', resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('managedInstanceIdentityPrincipalId')))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "principalId": "[parameters('managedInstanceIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + } + } + ] + } + } } ], "outputs": { From 565c30e23b260ba914f5444dfc2bbf1eb43e90b8 Mon Sep 17 00:00:00 2001 From: Ahmad Abdalla <28486158+ahmadabdalla@users.noreply.github.com> Date: Tue, 24 Oct 2023 14:47:23 +1100 Subject: [PATCH 2/5] updated readme --- modules/sql/server/main.json | 6 +++--- modules/sql/server/vulnerability-assessment/README.md | 4 ++-- modules/sql/server/vulnerability-assessment/main.bicep | 2 +- modules/sql/server/vulnerability-assessment/main.json | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/sql/server/main.json b/modules/sql/server/main.json index 87c1a714fb..7c0dc42a4f 100644 --- a/modules/sql/server/main.json +++ b/modules/sql/server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "10290542827488983748" + "templateHash": "7339009855010154380" }, "name": "Azure SQL Servers", "description": "This module deploys an Azure SQL Server.", @@ -2391,7 +2391,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "7362346898062262239" + "templateHash": "15718774987027357704" }, "name": "Azure SQL Server Vulnerability Assessments", "description": "This module deploys an Azure SQL Server Vulnerability Assessment.", @@ -2407,7 +2407,7 @@ "serverName": { "type": "string", "metadata": { - "description": "Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment" + "description": "Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment." } }, "recurringScansIsEnabled": { diff --git a/modules/sql/server/vulnerability-assessment/README.md b/modules/sql/server/vulnerability-assessment/README.md index 5a2818dfe8..145b70da61 100644 --- a/modules/sql/server/vulnerability-assessment/README.md +++ b/modules/sql/server/vulnerability-assessment/README.md @@ -29,7 +29,7 @@ This module deploys an Azure SQL Server Vulnerability Assessment. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`serverName`](#parameter-servername) | string | The Name of SQL Server. Required if the template is used in a standalone deployment | +| [`serverName`](#parameter-servername) | string | The Name of SQL Server. Required if the template is used in a standalone deployment. | **Optional parameters** @@ -85,7 +85,7 @@ Recurring scans state. ### Parameter: `serverName` -The Name of SQL Server. Required if the template is used in a standalone deployment +The Name of SQL Server. Required if the template is used in a standalone deployment. - Required: Yes - Type: string diff --git a/modules/sql/server/vulnerability-assessment/main.bicep b/modules/sql/server/vulnerability-assessment/main.bicep index 709c9a89ef..aad5f8ba2f 100644 --- a/modules/sql/server/vulnerability-assessment/main.bicep +++ b/modules/sql/server/vulnerability-assessment/main.bicep @@ -5,7 +5,7 @@ metadata owner = 'Azure/module-maintainers' @description('Required. The name of the vulnerability assessment.') param name string -@description('Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment') +@description('Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment.') param serverName string @description('Optional. Recurring scans state.') diff --git a/modules/sql/server/vulnerability-assessment/main.json b/modules/sql/server/vulnerability-assessment/main.json index adb54b6892..d3e286563b 100644 --- a/modules/sql/server/vulnerability-assessment/main.json +++ b/modules/sql/server/vulnerability-assessment/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "7362346898062262239" + "templateHash": "15718774987027357704" }, "name": "Azure SQL Server Vulnerability Assessments", "description": "This module deploys an Azure SQL Server Vulnerability Assessment.", @@ -21,7 +21,7 @@ "serverName": { "type": "string", "metadata": { - "description": "Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment" + "description": "Conditional. The Name of SQL Server. Required if the template is used in a standalone deployment." } }, "recurringScansIsEnabled": { From 918bcc951d78e0893bb7eafc2bd0a241c4c95f73 Mon Sep 17 00:00:00 2001 From: Ahmad Abdalla <28486158+ahmadabdalla@users.noreply.github.com> Date: Tue, 24 Oct 2023 15:17:09 +1100 Subject: [PATCH 3/5] Updated dependencies to remove MI Principal ID, not needed --- modules/sql/server/.test/vulnAssm/dependencies.bicep | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/sql/server/.test/vulnAssm/dependencies.bicep b/modules/sql/server/.test/vulnAssm/dependencies.bicep index 3d89db3b2a..6eb808e8c6 100644 --- a/modules/sql/server/.test/vulnAssm/dependencies.bicep +++ b/modules/sql/server/.test/vulnAssm/dependencies.bicep @@ -7,12 +7,12 @@ param storageAccountName string @description('Optional. The location to deploy resources to.') param location string = resourceGroup().location -resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { name: managedIdentityName location: location } -resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = { +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { name: storageAccountName location: location sku: { @@ -28,9 +28,6 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = { } } -@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 From a9b88323083e88252a8d8fb56828ea04cea6c6ec Mon Sep 17 00:00:00 2001 From: Ahmad Abdalla <28486158+ahmadabdalla@users.noreply.github.com> Date: Tue, 24 Oct 2023 19:13:31 +1100 Subject: [PATCH 4/5] Aligned SQL MI and SQL Server --- modules/sql/managed-instance/main.json | 11 ++++------- .../vulnerability-assessment/main.bicep | 8 +++----- .../vulnerability-assessment/main.json | 9 +++------ .../nested_storageRoleAssignment.bicep | 0 modules/sql/server/main.json | 11 ++++------- .../sql/server/vulnerability-assessment/main.bicep | 8 +++----- modules/sql/server/vulnerability-assessment/main.json | 9 +++------ .../nested_storageRoleAssignment.bicep | 0 8 files changed, 20 insertions(+), 36 deletions(-) rename modules/sql/managed-instance/vulnerability-assessment/{.bicep => modules}/nested_storageRoleAssignment.bicep (100%) rename modules/sql/server/vulnerability-assessment/{.bicep => modules}/nested_storageRoleAssignment.bicep (100%) diff --git a/modules/sql/managed-instance/main.json b/modules/sql/managed-instance/main.json index 925f909b08..c6bb21f7f8 100644 --- a/modules/sql/managed-instance/main.json +++ b/modules/sql/managed-instance/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "7571236887873003427" + "templateHash": "7653568276267549552" }, "name": "SQL Managed Instances", "description": "This module deploys a SQL Managed Instance.", @@ -1433,7 +1433,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "16419324698366777740" + "templateHash": "5582620280313265167" }, "name": "SQL Managed Instance Vulnerability Assessments", "description": "This module deploys a SQL Managed Instance Vulnerability Assessment.", @@ -1501,9 +1501,6 @@ } } }, - "variables": { - "splitStorageAccountResourceId": "[split(parameters('storageAccountResourceId'), '/')]" - }, "resources": [ { "condition": "[parameters('enableDefaultTelemetry')]", @@ -1538,7 +1535,7 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-sbdc-rbac', parameters('managedInstanceName'))]", - "resourceGroup": "[variables('splitStorageAccountResourceId')[4]]", + "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1546,7 +1543,7 @@ "mode": "Incremental", "parameters": { "storageAccountName": { - "value": "[last(variables('splitStorageAccountResourceId'))]" + "value": "[last(split(parameters('storageAccountResourceId'), '/'))]" }, "managedInstanceIdentityPrincipalId": { "value": "[reference(resourceId('Microsoft.Sql/managedInstances', parameters('managedInstanceName')), '2022-05-01-preview', 'full').identity.principalId]" diff --git a/modules/sql/managed-instance/vulnerability-assessment/main.bicep b/modules/sql/managed-instance/vulnerability-assessment/main.bicep index 522882e99a..81cc946945 100644 --- a/modules/sql/managed-instance/vulnerability-assessment/main.bicep +++ b/modules/sql/managed-instance/vulnerability-assessment/main.bicep @@ -29,8 +29,6 @@ param createStorageRoleAssignment bool = true @description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') param enableDefaultTelemetry bool = true -var splitStorageAccountResourceId = split(storageAccountResourceId, '/') - resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' properties: { @@ -48,11 +46,11 @@ resource managedInstance 'Microsoft.Sql/managedInstances@2022-05-01-preview' exi } // Assign SQL MI MSI access to storage account -module storageAccount_sbdc_rbac '.bicep/nested_storageRoleAssignment.bicep' = if (!useStorageAccountAccessKey && createStorageRoleAssignment) { +module storageAccount_sbdc_rbac 'modules/nested_storageRoleAssignment.bicep' = if (!useStorageAccountAccessKey && createStorageRoleAssignment) { name: '${managedInstance.name}-sbdc-rbac' - scope: resourceGroup(splitStorageAccountResourceId[4]) + scope: resourceGroup(split(storageAccountResourceId, '/')[4]) params: { - storageAccountName: last(splitStorageAccountResourceId) + storageAccountName: last(split(storageAccountResourceId, '/')) managedInstanceIdentityPrincipalId: managedInstance.identity.principalId } } diff --git a/modules/sql/managed-instance/vulnerability-assessment/main.json b/modules/sql/managed-instance/vulnerability-assessment/main.json index bf1f2597ca..eb70ed8caa 100644 --- a/modules/sql/managed-instance/vulnerability-assessment/main.json +++ b/modules/sql/managed-instance/vulnerability-assessment/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "16419324698366777740" + "templateHash": "5582620280313265167" }, "name": "SQL Managed Instance Vulnerability Assessments", "description": "This module deploys a SQL Managed Instance Vulnerability Assessment.", @@ -73,9 +73,6 @@ } } }, - "variables": { - "splitStorageAccountResourceId": "[split(parameters('storageAccountResourceId'), '/')]" - }, "resources": [ { "condition": "[parameters('enableDefaultTelemetry')]", @@ -110,7 +107,7 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-sbdc-rbac', parameters('managedInstanceName'))]", - "resourceGroup": "[variables('splitStorageAccountResourceId')[4]]", + "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -118,7 +115,7 @@ "mode": "Incremental", "parameters": { "storageAccountName": { - "value": "[last(variables('splitStorageAccountResourceId'))]" + "value": "[last(split(parameters('storageAccountResourceId'), '/'))]" }, "managedInstanceIdentityPrincipalId": { "value": "[reference(resourceId('Microsoft.Sql/managedInstances', parameters('managedInstanceName')), '2022-05-01-preview', 'full').identity.principalId]" diff --git a/modules/sql/managed-instance/vulnerability-assessment/.bicep/nested_storageRoleAssignment.bicep b/modules/sql/managed-instance/vulnerability-assessment/modules/nested_storageRoleAssignment.bicep similarity index 100% rename from modules/sql/managed-instance/vulnerability-assessment/.bicep/nested_storageRoleAssignment.bicep rename to modules/sql/managed-instance/vulnerability-assessment/modules/nested_storageRoleAssignment.bicep diff --git a/modules/sql/server/main.json b/modules/sql/server/main.json index 7c0dc42a4f..71de339e3d 100644 --- a/modules/sql/server/main.json +++ b/modules/sql/server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "7339009855010154380" + "templateHash": "3706987978525659012" }, "name": "Azure SQL Servers", "description": "This module deploys an Azure SQL Server.", @@ -2391,7 +2391,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "15718774987027357704" + "templateHash": "1780388510504326565" }, "name": "Azure SQL Server Vulnerability Assessments", "description": "This module deploys an Azure SQL Server Vulnerability Assessment.", @@ -2459,9 +2459,6 @@ } } }, - "variables": { - "splitStorageAccountResourceId": "[split(parameters('storageAccountResourceId'), '/')]" - }, "resources": [ { "condition": "[parameters('enableDefaultTelemetry')]", @@ -2496,7 +2493,7 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-sbdc-rbac', parameters('serverName'))]", - "resourceGroup": "[variables('splitStorageAccountResourceId')[4]]", + "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -2504,7 +2501,7 @@ "mode": "Incremental", "parameters": { "storageAccountName": { - "value": "[last(variables('splitStorageAccountResourceId'))]" + "value": "[last(split(parameters('storageAccountResourceId'), '/'))]" }, "managedInstanceIdentityPrincipalId": { "value": "[reference(resourceId('Microsoft.Sql/servers', parameters('serverName')), '2022-05-01-preview', 'full').identity.principalId]" diff --git a/modules/sql/server/vulnerability-assessment/main.bicep b/modules/sql/server/vulnerability-assessment/main.bicep index aad5f8ba2f..de649ee8d3 100644 --- a/modules/sql/server/vulnerability-assessment/main.bicep +++ b/modules/sql/server/vulnerability-assessment/main.bicep @@ -29,8 +29,6 @@ param createStorageRoleAssignment bool = true @description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') param enableDefaultTelemetry bool = true -var splitStorageAccountResourceId = split(storageAccountResourceId, '/') - resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { name: 'pid-9319755b-f697-4146-b966-4656e0b46cac-${uniqueString(deployment().name)}' properties: { @@ -48,11 +46,11 @@ resource server 'Microsoft.Sql/servers@2022-05-01-preview' existing = { } // Assign SQL Server MSI access to storage account -module storageAccount_sbdc_rbac '.bicep/nested_storageRoleAssignment.bicep' = if (!useStorageAccountAccessKey && createStorageRoleAssignment) { +module storageAccount_sbdc_rbac 'modules/nested_storageRoleAssignment.bicep' = if (!useStorageAccountAccessKey && createStorageRoleAssignment) { name: '${server.name}-sbdc-rbac' - scope: resourceGroup(splitStorageAccountResourceId[4]) + scope: resourceGroup(split(storageAccountResourceId, '/')[4]) params: { - storageAccountName: last(splitStorageAccountResourceId) + storageAccountName: last(split(storageAccountResourceId, '/')) managedInstanceIdentityPrincipalId: server.identity.principalId } } diff --git a/modules/sql/server/vulnerability-assessment/main.json b/modules/sql/server/vulnerability-assessment/main.json index d3e286563b..3942036e23 100644 --- a/modules/sql/server/vulnerability-assessment/main.json +++ b/modules/sql/server/vulnerability-assessment/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "15718774987027357704" + "templateHash": "1780388510504326565" }, "name": "Azure SQL Server Vulnerability Assessments", "description": "This module deploys an Azure SQL Server Vulnerability Assessment.", @@ -73,9 +73,6 @@ } } }, - "variables": { - "splitStorageAccountResourceId": "[split(parameters('storageAccountResourceId'), '/')]" - }, "resources": [ { "condition": "[parameters('enableDefaultTelemetry')]", @@ -110,7 +107,7 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-sbdc-rbac', parameters('serverName'))]", - "resourceGroup": "[variables('splitStorageAccountResourceId')[4]]", + "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -118,7 +115,7 @@ "mode": "Incremental", "parameters": { "storageAccountName": { - "value": "[last(variables('splitStorageAccountResourceId'))]" + "value": "[last(split(parameters('storageAccountResourceId'), '/'))]" }, "managedInstanceIdentityPrincipalId": { "value": "[reference(resourceId('Microsoft.Sql/servers', parameters('serverName')), '2022-05-01-preview', 'full').identity.principalId]" diff --git a/modules/sql/server/vulnerability-assessment/.bicep/nested_storageRoleAssignment.bicep b/modules/sql/server/vulnerability-assessment/modules/nested_storageRoleAssignment.bicep similarity index 100% rename from modules/sql/server/vulnerability-assessment/.bicep/nested_storageRoleAssignment.bicep rename to modules/sql/server/vulnerability-assessment/modules/nested_storageRoleAssignment.bicep From ff5adc8996d774dff4ffebe9ca68304f3174a9ea Mon Sep 17 00:00:00 2001 From: Ahmad Abdalla <28486158+ahmadabdalla@users.noreply.github.com> Date: Thu, 26 Oct 2023 06:53:30 +1100 Subject: [PATCH 5/5] Updated ARM Template --- modules/sql/server/main.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/sql/server/main.json b/modules/sql/server/main.json index 4d17281f4a..d3f0fb80b5 100644 --- a/modules/sql/server/main.json +++ b/modules/sql/server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "15785900556035209583" + "templateHash": "14708866930444205418" }, "name": "Azure SQL Servers", "description": "This module deploys an Azure SQL Server.", @@ -3047,4 +3047,4 @@ "value": "[reference('server', '2022-05-01-preview', 'full').location]" } } -} +} \ No newline at end of file