diff --git a/modules/Microsoft.Sql/servers/.test/common/dependencies.bicep b/modules/Microsoft.Sql/servers/.test/common/dependencies.bicep index 0a65dc7910..da6583678c 100644 --- a/modules/Microsoft.Sql/servers/.test/common/dependencies.bicep +++ b/modules/Microsoft.Sql/servers/.test/common/dependencies.bicep @@ -7,6 +7,9 @@ param virtualNetworkName string @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 + resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { name: managedIdentityName location: location @@ -56,11 +59,46 @@ resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { } } +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 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-Vault-Crypto-Service-Encryption-User-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 principal ID of the created managed identity.') output managedIdentityPrincipalId string = managedIdentity.properties.principalId @description('The resource ID of the created managed identity.') -output managedIdentitResourceId string = managedIdentity.id +output managedIdentityResourceId string = managedIdentity.id @description('The resource ID of the created virtual network subnet for a Private Endpoint.') output privateEndpointSubnetResourceId string = virtualNetwork.properties.subnets[0].id @@ -70,3 +108,12 @@ output serviceEndpointSubnetResourceId string = virtualNetwork.properties.subnet @description('The resource ID of the created Private DNS Zone.') output privateDNSResourceId string = privateDNSZone.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/modules/Microsoft.Sql/servers/.test/common/deploy.test.bicep b/modules/Microsoft.Sql/servers/.test/common/deploy.test.bicep index 213e5f34cc..87c83e02e5 100644 --- a/modules/Microsoft.Sql/servers/.test/common/deploy.test.bicep +++ b/modules/Microsoft.Sql/servers/.test/common/deploy.test.bicep @@ -35,6 +35,7 @@ module resourceGroupResources 'dependencies.bicep' = { scope: resourceGroup name: '${uniqueString(deployment().name, location)}-nestedDependencies' params: { + keyVaultName: 'dep-<>-kv-${serviceShort}' managedIdentityName: 'dep-<>-msi-${serviceShort}' virtualNetworkName: 'dep-<>-vnet-${serviceShort}' location: location @@ -66,6 +67,7 @@ module testDeployment '../../deploy.bicep' = { enableDefaultTelemetry: enableDefaultTelemetry name: '<>-${serviceShort}' lock: 'CanNotDelete' + primaryUserAssignedIdentityId: resourceGroupResources.outputs.managedIdentityResourceId administratorLogin: 'adminUserName' administratorLoginPassword: password location: location @@ -129,9 +131,16 @@ module testDeployment '../../deploy.bicep' = { emailAccountAdmins: true } ] + keys: [ + { + name: '${resourceGroupResources.outputs.keyVaultName}_${resourceGroupResources.outputs.keyVaultKeyName}_${last(split(resourceGroupResources.outputs.keyVaultEncryptionKeyUrl, '/'))}' + serverKeyType: 'AzureKeyVault' + uri: resourceGroupResources.outputs.keyVaultEncryptionKeyUrl + } + ] systemAssignedIdentity: true userAssignedIdentities: { - '${resourceGroupResources.outputs.managedIdentitResourceId}': {} + '${resourceGroupResources.outputs.managedIdentityResourceId}': {} } privateEndpoints: [ { @@ -153,3 +162,4 @@ module testDeployment '../../deploy.bicep' = { ] } } + diff --git a/modules/Microsoft.Sql/servers/deploy.bicep b/modules/Microsoft.Sql/servers/deploy.bicep index dc8419762b..735bd32b4c 100644 --- a/modules/Microsoft.Sql/servers/deploy.bicep +++ b/modules/Microsoft.Sql/servers/deploy.bicep @@ -17,6 +17,9 @@ param systemAssignedIdentity bool = false @description('Optional. The ID(s) to assign to the resource.') param userAssignedIdentities object = {} +@description('Conditional. The resource ID of a user assigned identity to be used by default. Required if "userAssignedIdentities" is not empty.') +param primaryUserAssignedIdentityId string = '' + @allowed([ '' 'CanNotDelete' @@ -49,6 +52,9 @@ param virtualNetworkRules array = [] @description('Optional. The security alert policies to create in the server.') param securityAlertPolicies array = [] +@description('Optional. The keys to configure.') +param keys array = [] + @description('Conditional. The Azure Active Directory (AAD) administrator authentication. Required if no `administratorLogin` & `administratorLoginPassword` is provided.') param administrators object = {} @@ -95,7 +101,7 @@ resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (ena } } -resource server 'Microsoft.Sql/servers@2022-02-01-preview' = { +resource server 'Microsoft.Sql/servers@2022-05-01-preview' = { location: location name: name tags: tags @@ -113,6 +119,7 @@ resource server 'Microsoft.Sql/servers@2022-02-01-preview' = { } : null version: '12.0' minimalTlsVersion: minimalTlsVersion + primaryUserAssignedIdentityId: !empty(primaryUserAssignedIdentityId) ? primaryUserAssignedIdentityId : null publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) && empty(firewallRules) && empty(virtualNetworkRules) ? 'Disabled' : null) } } @@ -275,6 +282,17 @@ module server_vulnerabilityAssessment 'vulnerabilityAssessments/deploy.bicep' = ] } +module server_keys 'keys/deploy.bicep' = [for (key, index) in keys: { + name: '${uniqueString(deployment().name, location)}-Sql-Key-${index}' + params: { + name: key.name + serverName: server.name + serverKeyType: contains(key, 'serverKeyType') ? key.serverKeyType : 'ServiceManaged' + uri: contains(key, 'uri') ? key.uri : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + @description('The name of the deployed SQL server.') output name string = server.name diff --git a/modules/Microsoft.Sql/servers/keys/deploy.bicep b/modules/Microsoft.Sql/servers/keys/deploy.bicep new file mode 100644 index 0000000000..afa7bfce79 --- /dev/null +++ b/modules/Microsoft.Sql/servers/keys/deploy.bicep @@ -0,0 +1,58 @@ +@description('Required. The name of the key. Must follow the [__] pattern.') +param name string + +@description('Conditional. The name of the parent SQL server. Required if the template is used in a standalone deployment.') +param serverName 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 = '' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +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 defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource server 'Microsoft.Sql/servers@2022-05-01-preview' existing = { + name: serverName +} + +resource key 'Microsoft.Sql/servers/keys@2022-05-01-preview' = { + name: !empty(name) ? name : serverKeyName + parent: server + properties: { + serverKeyType: serverKeyType + uri: uri + } +} + +@description('The name of the deployed server key.') +output name string = key.name + +@description('The resource ID of the deployed server key.') +output resourceId string = key.id + +@description('The resource group of the deployed server key.') +output resourceGroupName string = resourceGroup().name diff --git a/modules/Microsoft.Sql/servers/keys/readme.md b/modules/Microsoft.Sql/servers/keys/readme.md new file mode 100644 index 0000000000..6e60ef00e8 --- /dev/null +++ b/modules/Microsoft.Sql/servers/keys/readme.md @@ -0,0 +1,51 @@ +# SQL Servers Keys `[Microsoft.Sql/servers/keys]` + +This module deploys a key for a SQL server. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/servers/keys` | [2022-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/servers/keys) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the key. Must follow the [__] pattern. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `serverName` | string | The name of the parent SQL server. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `serverKeyType` | string | `'ServiceManaged'` | `[AzureKeyVault, ServiceManaged]` | The encryption protector type like "ServiceManaged", "AzureKeyVault". | +| `uri` | string | `''` | | The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed server key. | +| `resourceGroupName` | string | The resource group of the deployed server key. | +| `resourceId` | string | The resource ID of the deployed server key. | + +## Cross-referenced modules + +_None_ diff --git a/modules/Microsoft.Sql/servers/keys/version.json b/modules/Microsoft.Sql/servers/keys/version.json new file mode 100644 index 0000000000..56f8d9ca40 --- /dev/null +++ b/modules/Microsoft.Sql/servers/keys/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/modules/Microsoft.Sql/servers/readme.md b/modules/Microsoft.Sql/servers/readme.md index 90eb13d3be..8d376a76ae 100644 --- a/modules/Microsoft.Sql/servers/readme.md +++ b/modules/Microsoft.Sql/servers/readme.md @@ -19,10 +19,11 @@ This module deploys a SQL server. | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | | `Microsoft.Network/privateEndpoints` | [2022-05-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-05-01/privateEndpoints) | | `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2022-05-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-05-01/privateEndpoints/privateDnsZoneGroups) | -| `Microsoft.Sql/servers` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers) | +| `Microsoft.Sql/servers` | [2022-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/servers) | | `Microsoft.Sql/servers/databases` | [2021-11-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2021-11-01/servers/databases) | | `Microsoft.Sql/servers/elasticPools` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/elasticPools) | | `Microsoft.Sql/servers/firewallRules` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/firewallRules) | +| `Microsoft.Sql/servers/keys` | [2022-05-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/servers/keys) | | `Microsoft.Sql/servers/securityAlertPolicies` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/securityAlertPolicies) | | `Microsoft.Sql/servers/virtualNetworkRules` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/virtualNetworkRules) | | `Microsoft.Sql/servers/vulnerabilityAssessments` | [2022-02-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-02-01-preview/servers/vulnerabilityAssessments) | @@ -42,6 +43,7 @@ This module deploys a SQL server. | `administratorLogin` | string | `''` | The administrator username for the server. Required if no `administrators` object for AAD authentication is provided. | | `administratorLoginPassword` | secureString | `''` | The administrator login password. Required if no `administrators` object for AAD authentication is provided. | | `administrators` | object | `{object}` | The Azure Active Directory (AAD) administrator authentication. Required if no `administratorLogin` & `administratorLoginPassword` is provided. | +| `primaryUserAssignedIdentityId` | string | `''` | The resource ID of a user assigned identity to be used by default. Required if "userAssignedIdentities" is not empty. | **Optional parameters** @@ -51,6 +53,7 @@ This module deploys a SQL server. | `elasticPools` | _[elasticPools](elasticPools/readme.md)_ array | `[]` | | The Elastic Pools to create in the server. | | `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | | `firewallRules` | _[firewallRules](firewallRules/readme.md)_ array | `[]` | | The firewall rules to create in the server. | +| `keys` | _[keys](keys/readme.md)_ array | `[]` | | The keys to configure. | | `location` | string | `[resourceGroup().location]` | | Location for all resources. | | `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | | `minimalTlsVersion` | string | `'1.2'` | `[1.0, 1.1, 1.2]` | Minimal TLS version allowed. | @@ -415,9 +418,7 @@ module servers './Microsoft.Sql/servers/deploy.bicep' = { module servers './Microsoft.Sql/servers/deploy.bicep' = { name: '${uniqueString(deployment().name)}-test-sqlscom' params: { - // Required parameters name: '<>-sqlscom' - // Non-required parameters administratorLogin: 'adminUserName' administratorLoginPassword: '' databases: [ @@ -454,8 +455,16 @@ module servers './Microsoft.Sql/servers/deploy.bicep' = { startIpAddress: '0.0.0.0' } ] + keys: [ + { + name: '' + serverKeyType: 'AzureKeyVault' + uri: '' + } + ] location: '' lock: 'CanNotDelete' + primaryUserAssignedIdentityId: '' privateEndpoints: [ { privateDnsZoneGroup: { @@ -485,7 +494,7 @@ module servers './Microsoft.Sql/servers/deploy.bicep' = { ] systemAssignedIdentity: true userAssignedIdentities: { - '': {} + '': {} } virtualNetworkRules: [ { @@ -520,11 +529,9 @@ module servers './Microsoft.Sql/servers/deploy.bicep' = { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - // Required parameters "name": { "value": "<>-sqlscom" }, - // Non-required parameters "administratorLogin": { "value": "adminUserName" }, @@ -573,12 +580,24 @@ module servers './Microsoft.Sql/servers/deploy.bicep' = { } ] }, + "keys": { + "value": [ + { + "name": "", + "serverKeyType": "AzureKeyVault", + "uri": "" + } + ] + }, "location": { "value": "" }, "lock": { "value": "CanNotDelete" }, + "primaryUserAssignedIdentityId": { + "value": "" + }, "privateEndpoints": { "value": [ { @@ -617,7 +636,7 @@ module servers './Microsoft.Sql/servers/deploy.bicep' = { }, "userAssignedIdentities": { "value": { - "": {} + "": {} } }, "virtualNetworkRules": {