diff --git a/modules/app-configuration/configuration-store/.test/encr/main.test.bicep b/modules/app-configuration/configuration-store/.test/encr/main.test.bicep index 51e9ff0202..a0e639988a 100644 --- a/modules/app-configuration/configuration-store/.test/encr/main.test.bicep +++ b/modules/app-configuration/configuration-store/.test/encr/main.test.bicep @@ -90,8 +90,10 @@ module testDeployment '../../main.bicep' = { Environment: 'Non-Prod' Role: 'DeploymentValidation' } - cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKKeyName: nestedDependencies.outputs.keyName - cMKUserAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + customerManagedKey: { + keyName: nestedDependencies.outputs.keyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } } } diff --git a/modules/app-configuration/configuration-store/README.md b/modules/app-configuration/configuration-store/README.md index cb805dd2ec..b7cd4a7c0d 100644 --- a/modules/app-configuration/configuration-store/README.md +++ b/modules/app-configuration/configuration-store/README.md @@ -224,10 +224,12 @@ module configurationStore 'br:bicep/modules/app-configuration.configuration-stor // Required parameters name: 'accencr001' // Non-required parameters - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUserAssignedIdentityResourceId: '' createMode: 'Default' + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } disableLocalAuth: false enableDefaultTelemetry: '' enablePurgeProtection: false @@ -284,18 +286,16 @@ module configurationStore 'br:bicep/modules/app-configuration.configuration-stor "value": "accencr001" }, // Non-required parameters - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" - }, - "cMKUserAssignedIdentityResourceId": { - "value": "" - }, "createMode": { "value": "Default" }, + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } + }, "disableLocalAuth": { "value": false }, @@ -512,20 +512,12 @@ module configurationStore 'br:bicep/modules/app-configuration.configuration-stor | :-- | :-- | :-- | | [`name`](#parameter-name) | string | Name of the Azure App Configuration. | -**Conditional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`cMKKeyVaultResourceId`](#parameter-cmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if "cMKKeyName" is not empty. | -| [`cMKUserAssignedIdentityResourceId`](#parameter-cmkuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if "cMKKeyName" is not empty. | - **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`cMKKeyName`](#parameter-cmkkeyname) | string | The name of the customer managed key to use for encryption. | -| [`cMKKeyVersion`](#parameter-cmkkeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | | [`createMode`](#parameter-createmode) | string | Indicates whether the configuration store need to be recovered. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`disableLocalAuth`](#parameter-disablelocalauth) | bool | Disables all authentication methods other than AAD authentication. | | [`enableDefaultTelemetry`](#parameter-enabledefaulttelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). | @@ -541,47 +533,61 @@ module configurationStore 'br:bicep/modules/app-configuration.configuration-stor | [`softDeleteRetentionInDays`](#parameter-softdeleteretentionindays) | int | The amount of time in days that the configuration store will be retained when it is soft deleted. | | [`tags`](#parameter-tags) | object | Tags of the resource. | -### Parameter: `cMKKeyName` +### Parameter: `createMode` -The name of the customer managed key to use for encryption. +Indicates whether the configuration store need to be recovered. - Required: No - Type: string -- Default: `''` +- Default: `'Default'` +- Allowed: + ```Bicep + [ + 'Default' + 'Recover' + ] + ``` -### Parameter: `cMKKeyVaultResourceId` +### Parameter: `customerManagedKey` -The resource ID of a key vault to reference a customer managed key for encryption from. Required if "cMKKeyName" is not empty. +The customer managed key definition. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | No | string | Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVersion` +### Parameter: `customerManagedKey.keyVaultResourceId` -The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. -- Required: No +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKUserAssignedIdentityResourceId` +### Parameter: `customerManagedKey.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if "cMKKeyName" is not empty. - Required: No - Type: string -- Default: `''` -### Parameter: `createMode` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. -Indicates whether the configuration store need to be recovered. - Required: No - Type: string -- Default: `'Default'` -- Allowed: - ```Bicep - [ - 'Default' - 'Recover' - ] - ``` ### Parameter: `diagnosticSettings` diff --git a/modules/app-configuration/configuration-store/main.bicep b/modules/app-configuration/configuration-store/main.bicep index 54abbcefaa..4b902c8093 100644 --- a/modules/app-configuration/configuration-store/main.bicep +++ b/modules/app-configuration/configuration-store/main.bicep @@ -44,17 +44,8 @@ param publicNetworkAccess string = '' @maxValue(7) param softDeleteRetentionInDays int = 1 -@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if "cMKKeyName" is not empty.') -param cMKKeyVaultResourceId string = '' - -@description('Optional. The name of the customer managed key to use for encryption.') -param cMKKeyName string = '' - -@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') -param cMKKeyVersion string = '' - -@description('Conditional. User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if "cMKKeyName" is not empty.') -param cMKUserAssignedIdentityResourceId string = '' +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType @description('Optional. All Key / Values to create. Requires local authentication to be enabled.') param keyValues array = [] @@ -110,18 +101,18 @@ resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (ena } } -resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { - name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) - resource cMKKey 'keys@2023-02-01' existing = if (!empty(cMKKeyName)) { - name: !empty(cMKKeyName) ? cMKKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' } } -resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(cMKUserAssignedIdentityResourceId)) { - name: last(split((!empty(cMKUserAssignedIdentityResourceId) ? cMKUserAssignedIdentityResourceId : 'dummyMsi'), '/'))! - scope: resourceGroup(split((!empty(cMKUserAssignedIdentityResourceId) ? cMKUserAssignedIdentityResourceId : '//'), '/')[2], split((!empty(cMKUserAssignedIdentityResourceId) ? cMKUserAssignedIdentityResourceId : '////'), '/')[4]) +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) } resource configurationStore 'Microsoft.AppConfiguration/configurationStores@2023-03-01' = { @@ -136,10 +127,10 @@ resource configurationStore 'Microsoft.AppConfiguration/configurationStores@2023 createMode: createMode disableLocalAuth: disableLocalAuth enablePurgeProtection: sku == 'Free' ? false : enablePurgeProtection - encryption: !empty(cMKKeyName) ? { + encryption: !empty(customerManagedKey) ? { keyVaultProperties: { - keyIdentifier: !empty(cMKKeyVersion) ? '${cMKKeyVault::cMKKey.properties.keyUri}/${cMKKeyVersion}' : cMKKeyVault::cMKKey.properties.keyUriWithVersion - identityClientId: cMKUserAssignedIdentity.properties.clientId + keyIdentifier: !empty(customerManagedKey.?keyVersion ?? '') ? '${cMKKeyVault::cMKKey.properties.keyUri}/${customerManagedKey!.keyVersion}' : cMKKeyVault::cMKKey.properties.keyUriWithVersion + identityClientId: !empty(customerManagedKey.?userAssignedIdentityResourceId ?? '') ? cMKUserAssignedIdentity.properties.clientId : null } } : null publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : null @@ -395,3 +386,17 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? +}? diff --git a/modules/app-configuration/configuration-store/main.json b/modules/app-configuration/configuration-store/main.json index ca7d97bad1..d56245e7bf 100644 --- a/modules/app-configuration/configuration-store/main.json +++ b/modules/app-configuration/configuration-store/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "14821162059319342865" + "templateHash": "4494236567093935129" }, "name": "App Configuration Stores", "description": "This module deploys an App Configuration Store.", @@ -407,6 +407,38 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true } }, "parameters": { @@ -486,32 +518,10 @@ "description": "Optional. The amount of time in days that the configuration store will be retained when it is soft deleted." } }, - "cMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \"cMKKeyName\" is not empty." - } - }, - "cMKKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption." - } - }, - "cMKKeyVersion": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." - } - }, - "cMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Conditional. User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if \"cMKKeyName\" is not empty." + "description": "Optional. The customer managed key definition." } }, "keyValues": { @@ -578,13 +588,13 @@ }, "resources": { "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(parameters('cMKKeyVaultResourceId'))), not(empty(parameters('cMKKeyName'))))]", + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKKeyName'))), parameters('cMKKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", "dependsOn": [ "cMKKeyVault" ] @@ -604,22 +614,22 @@ } }, "cMKKeyVault": { - "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" }, "cMKUserAssignedIdentity": { - "condition": "[not(empty(parameters('cMKUserAssignedIdentityResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", "existing": true, "type": "Microsoft.ManagedIdentity/userAssignedIdentities", "apiVersion": "2023-01-31", - "subscriptionId": "[split(if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), parameters('cMKUserAssignedIdentityResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), parameters('cMKUserAssignedIdentityResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), parameters('cMKUserAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, "configurationStore": { "type": "Microsoft.AppConfiguration/configurationStores", @@ -635,7 +645,7 @@ "createMode": "[parameters('createMode')]", "disableLocalAuth": "[parameters('disableLocalAuth')]", "enablePurgeProtection": "[if(equals(parameters('sku'), 'Free'), false(), parameters('enablePurgeProtection'))]", - "encryption": "[if(not(empty(parameters('cMKKeyName'))), createObject('keyVaultProperties', createObject('keyIdentifier', if(not(empty(parameters('cMKKeyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('cMKKeyVersion')), reference('cMKKeyVault::cMKKey').keyUriWithVersion), 'identityClientId', reference('cMKUserAssignedIdentity').clientId)), null())]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keyVaultProperties', createObject('keyIdentifier', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion), 'identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()))), null())]", "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), null())]", "softDeleteRetentionInDays": "[if(equals(parameters('sku'), 'Free'), 0, parameters('softDeleteRetentionInDays'))]" }, diff --git a/modules/automation/automation-account/.test/encr/main.test.bicep b/modules/automation/automation-account/.test/encr/main.test.bicep index 389ca3eae8..f417d2261d 100644 --- a/modules/automation/automation-account/.test/encr/main.test.bicep +++ b/modules/automation/automation-account/.test/encr/main.test.bicep @@ -54,9 +54,11 @@ module testDeployment '../../main.bicep' = { params: { enableDefaultTelemetry: enableDefaultTelemetry name: '${namePrefix}${serviceShort}001' - cMKKeyName: nestedDependencies.outputs.keyVaultEncryptionKeyName - cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKUserAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultEncryptionKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } managedIdentities: { userAssignedResourcesIds: [ nestedDependencies.outputs.managedIdentityResourceId diff --git a/modules/automation/automation-account/README.md b/modules/automation/automation-account/README.md index 26fb4ade02..a2f5f9fd1f 100644 --- a/modules/automation/automation-account/README.md +++ b/modules/automation/automation-account/README.md @@ -510,9 +510,11 @@ module automationAccount 'br:bicep/modules/automation.automation-account:1.0.0' // Required parameters name: 'aaencr001' // Non-required parameters - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUserAssignedIdentityResourceId: '' + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } enableDefaultTelemetry: '' managedIdentities: { userAssignedResourcesIds: [ @@ -540,14 +542,12 @@ module automationAccount 'br:bicep/modules/automation.automation-account:1.0.0' "value": "aaencr001" }, // Non-required parameters - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" - }, - "cMKUserAssignedIdentityResourceId": { - "value": "" + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } }, "enableDefaultTelemetry": { "value": "" @@ -623,19 +623,11 @@ module automationAccount 'br:bicep/modules/automation.automation-account:1.0.0' | :-- | :-- | :-- | | [`name`](#parameter-name) | string | Name of the Automation Account. | -**Conditional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`cMKKeyVaultResourceId`](#parameter-cmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. | -| [`cMKUserAssignedIdentityResourceId`](#parameter-cmkuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty. | - **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`cMKKeyName`](#parameter-cmkkeyname) | string | The name of the customer managed key to use for encryption. | -| [`cMKKeyVersion`](#parameter-cmkkeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`disableLocalAuth`](#parameter-disablelocalauth) | bool | Disable local authentication profile used within the resource. | | [`enableDefaultTelemetry`](#parameter-enabledefaulttelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). | @@ -656,33 +648,47 @@ module automationAccount 'br:bicep/modules/automation.automation-account:1.0.0' | [`tags`](#parameter-tags) | object | Tags of the Automation Account resource. | | [`variables`](#parameter-variables) | array | List of variables to be created in the automation account. | -### Parameter: `cMKKeyName` +### Parameter: `customerManagedKey` -The name of the customer managed key to use for encryption. +The customer managed key definition. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | No | string | Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVaultResourceId` +### Parameter: `customerManagedKey.keyVaultResourceId` -The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. -- Required: No +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVersion` +### Parameter: `customerManagedKey.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. - Required: No - Type: string -- Default: `''` -### Parameter: `cMKUserAssignedIdentityResourceId` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. -User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty. - Required: No - Type: string -- Default: `''` ### Parameter: `diagnosticSettings` diff --git a/modules/automation/automation-account/main.bicep b/modules/automation/automation-account/main.bicep index 1f5fc86dad..69820ae56d 100644 --- a/modules/automation/automation-account/main.bicep +++ b/modules/automation/automation-account/main.bicep @@ -15,17 +15,8 @@ param location string = resourceGroup().location ]) param skuName string = 'Basic' -@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \'cMKKeyName\' is not empty.') -param cMKKeyVaultResourceId string = '' - -@description('Optional. The name of the customer managed key to use for encryption.') -param cMKKeyName string = '' - -@description('Conditional. User assigned identity to use when fetching the customer managed key. Required if \'cMKKeyName\' is not empty.') -param cMKUserAssignedIdentityResourceId string = '' - -@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') -param cMKKeyVersion string = '' +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType @description('Optional. List of modules to be created in the automation account.') param modules array = [] @@ -116,15 +107,20 @@ resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (ena } } -resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { - name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) - resource cMKKey 'keys@2023-02-01' existing = if (!empty(cMKKeyName)) { - name: !empty(cMKKeyName) ? cMKKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' } } +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) +} + resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' = { name: name location: location @@ -134,15 +130,15 @@ resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' sku: { name: skuName } - encryption: !empty(cMKKeyName) ? { + encryption: !empty(customerManagedKey) ? { keySource: 'Microsoft.KeyVault' - identity: { - userAssignedIdentity: cMKUserAssignedIdentityResourceId - } + identity: !empty(customerManagedKey.?userAssignedIdentityResourceId) ? { + userAssignedIdentity: cMKUserAssignedIdentity.id + } : null keyVaultProperties: { - keyName: cMKKeyName + keyName: customerManagedKey!.keyName keyVaultUri: cMKKeyVault.properties.vaultUri - keyVersion: !empty(cMKKeyVersion) ? cMKKeyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) + keyVersion: !empty(customerManagedKey.?keyVersion ?? '') ? customerManagedKey!.keyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) } } : null publicNetworkAccess: !empty(publicNetworkAccess) ? (publicNetworkAccess == 'Disabled' ? false : true) : (!empty(privateEndpoints) ? false : null) @@ -539,3 +535,17 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? +}? diff --git a/modules/automation/automation-account/main.json b/modules/automation/automation-account/main.json index 0a2a91c660..09e14c3e3b 100644 --- a/modules/automation/automation-account/main.json +++ b/modules/automation/automation-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "7186571646898746589" + "templateHash": "11493438009443560879" }, "name": "Automation Accounts", "description": "This module deploys an Azure Automation Account.", @@ -406,6 +406,38 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true } }, "parameters": { @@ -433,32 +465,10 @@ "description": "Optional. SKU name of the account." } }, - "cMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty." - } - }, - "cMKKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption." - } - }, - "cMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Conditional. User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty." - } - }, - "cMKKeyVersion": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." + "description": "Optional. The customer managed key definition." } }, "modules": { @@ -599,13 +609,13 @@ }, "resources": { "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(parameters('cMKKeyVaultResourceId'))), not(empty(parameters('cMKKeyName'))))]", + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKKeyName'))), parameters('cMKKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", "dependsOn": [ "cMKKeyVault" ] @@ -625,13 +635,22 @@ } }, "cMKKeyVault": { - "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, "automationAccount": { "type": "Microsoft.Automation/automationAccounts", @@ -644,12 +663,13 @@ "sku": { "name": "[parameters('skuName')]" }, - "encryption": "[if(not(empty(parameters('cMKKeyName'))), createObject('keySource', 'Microsoft.KeyVault', 'identity', createObject('userAssignedIdentity', parameters('cMKUserAssignedIdentityResourceId')), 'keyVaultProperties', createObject('keyName', parameters('cMKKeyName'), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyVersion', if(not(empty(parameters('cMKKeyVersion'))), parameters('cMKKeyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'identity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null()), 'keyVaultProperties', createObject('keyName', parameters('customerManagedKey').keyName, 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), if(equals(parameters('publicNetworkAccess'), 'Disabled'), false(), true()), if(not(empty(parameters('privateEndpoints'))), false(), null()))]", "disableLocalAuth": "[parameters('disableLocalAuth')]" }, "dependsOn": [ - "cMKKeyVault" + "cMKKeyVault", + "cMKUserAssignedIdentity" ] }, "automationAccount_lock": { diff --git a/modules/container-instance/container-group/.test/encr/main.test.bicep b/modules/container-instance/container-group/.test/encr/main.test.bicep index ade6cdb091..df9bcfd467 100644 --- a/modules/container-instance/container-group/.test/encr/main.test.bicep +++ b/modules/container-instance/container-group/.test/encr/main.test.bicep @@ -120,9 +120,11 @@ module testDeployment '../../main.bicep' = { nestedDependencies.outputs.managedIdentityResourceId ] } - cMKKeyName: nestedDependencies.outputs.keyVaultEncryptionKeyName - cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKUserAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultEncryptionKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/modules/container-instance/container-group/README.md b/modules/container-instance/container-group/README.md index f5d59c9161..342a9f3fc0 100644 --- a/modules/container-instance/container-group/README.md +++ b/modules/container-instance/container-group/README.md @@ -290,9 +290,11 @@ module containerGroup 'br:bicep/modules/container-instance.container-group:1.0.0 ] name: 'cicgenc001' // Non-required parameters - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUserAssignedIdentityResourceId: '' + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } enableDefaultTelemetry: '' ipAddressPorts: [ { @@ -388,14 +390,12 @@ module containerGroup 'br:bicep/modules/container-instance.container-group:1.0.0 "value": "cicgenc001" }, // Non-required parameters - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" - }, - "cMKUserAssignedIdentityResourceId": { - "value": "" + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } }, "enableDefaultTelemetry": { "value": "" @@ -797,7 +797,6 @@ module containerGroup 'br:bicep/modules/container-instance.container-group:1.0.0 | Parameter | Type | Description | | :-- | :-- | :-- | -| [`cMKUserAssignedIdentityResourceId`](#parameter-cmkuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty. | | [`ipAddressPorts`](#parameter-ipaddressports) | array | Ports to open on the public IP address. Must include all ports assigned on container level. Required if `ipAddressType` is set to `public`. | **Optional parameters** @@ -805,9 +804,7 @@ module containerGroup 'br:bicep/modules/container-instance.container-group:1.0.0 | Parameter | Type | Description | | :-- | :-- | :-- | | [`autoGeneratedDomainNameLabelScope`](#parameter-autogenerateddomainnamelabelscope) | string | Specify level of protection of the domain name label. | -| [`cMKKeyName`](#parameter-cmkkeyname) | string | The name of the customer managed key to use for encryption. | -| [`cMKKeyVaultResourceId`](#parameter-cmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. | -| [`cMKKeyVersion`](#parameter-cmkkeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`dnsNameLabel`](#parameter-dnsnamelabel) | string | The Dns name label for the resource. | | [`dnsNameServers`](#parameter-dnsnameservers) | array | List of dns servers used by the containers for lookups. | | [`dnsSearchDomains`](#parameter-dnssearchdomains) | string | DNS search domain which will be appended to each DNS lookup. | @@ -842,39 +839,53 @@ Specify level of protection of the domain name label. ] ``` -### Parameter: `cMKKeyName` +### Parameter: `containers` -The name of the customer managed key to use for encryption. -- Required: No -- Type: string -- Default: `''` +The containers and their respective config within the container group. +- Required: Yes +- Type: array -### Parameter: `cMKKeyVaultResourceId` +### Parameter: `customerManagedKey` -The resource ID of a key vault to reference a customer managed key for encryption from. +The customer managed key definition. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | No | string | Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVersion` +### Parameter: `customerManagedKey.keyVaultResourceId` -The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. -- Required: No +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKUserAssignedIdentityResourceId` +### Parameter: `customerManagedKey.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty. - Required: No - Type: string -- Default: `''` -### Parameter: `containers` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` -The containers and their respective config within the container group. -- Required: Yes -- Type: array +Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. + +- Required: No +- Type: string ### Parameter: `dnsNameLabel` diff --git a/modules/container-instance/container-group/main.bicep b/modules/container-instance/container-group/main.bicep index e2dbd5acf4..07bf526131 100644 --- a/modules/container-instance/container-group/main.bicep +++ b/modules/container-instance/container-group/main.bicep @@ -82,17 +82,8 @@ param enableDefaultTelemetry bool = true ]) param sku string = 'Standard' -@description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from.') -param cMKKeyVaultResourceId string = '' - -@description('Optional. The name of the customer managed key to use for encryption.') -param cMKKeyName string = '' - -@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') -param cMKKeyVersion string = '' - -@description('Conditional. User assigned identity to use when fetching the customer managed key. Required if \'cMKKeyName\' is not empty.') -param cMKUserAssignedIdentityResourceId string = '' +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType var formattedUserAssignedIdentities = reduce(map((managedIdentities.?userAssignedResourcesIds ?? []), (id) => { '${id}': {} }), {}, (cur, next) => union(cur, next)) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} } @@ -113,15 +104,20 @@ resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (ena } } -resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { - name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) - resource cMKKey 'keys@2023-02-01' existing = if (!empty(cMKKeyName)) { - name: !empty(cMKKeyName) ? cMKKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' } } +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) +} + resource containergroup 'Microsoft.ContainerInstance/containerGroups@2022-09-01' = { name: name location: location @@ -129,10 +125,10 @@ resource containergroup 'Microsoft.ContainerInstance/containerGroups@2022-09-01' tags: tags properties: union({ containers: containers - encryptionProperties: !empty(cMKKeyName) ? { - identity: cMKUserAssignedIdentityResourceId - keyName: cMKKeyName - keyVersion: !empty(cMKKeyVersion) ? cMKKeyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) + encryptionProperties: !empty(customerManagedKey) ? { + identity: !empty(customerManagedKey.?userAssignedIdentityResourceId ?? '') ? cMKUserAssignedIdentity.id : null + keyName: customerManagedKey!.keyName + keyVersion: !empty(customerManagedKey.?keyVersion ?? '') ? customerManagedKey!.keyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) vaultBaseUrl: cMKKeyVault.properties.vaultUri } : null imageRegistryCredentials: imageRegistryCredentials @@ -206,3 +202,17 @@ type lockType = { @description('Optional. Specify the type of lock.') kind: ('CanNotDelete' | 'ReadOnly' | 'None')? }? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? +}? diff --git a/modules/container-instance/container-group/main.json b/modules/container-instance/container-group/main.json index 9b3e6173ad..3738d8b870 100644 --- a/modules/container-instance/container-group/main.json +++ b/modules/container-instance/container-group/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "15985356083477047348" + "templateHash": "9232184615208401604" }, "name": "Container Instances Container Groups", "description": "This module deploys a Container Instance Container Group.", @@ -60,6 +60,38 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true } }, "parameters": { @@ -219,32 +251,10 @@ "description": "Optional. The container group SKU." } }, - "cMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The resource ID of a key vault to reference a customer managed key for encryption from." - } - }, - "cMKKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption." - } - }, - "cMKKeyVersion": { - "type": "string", - "defaultValue": "", + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." - } - }, - "cMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty." + "description": "Optional. The customer managed key definition." } } }, @@ -254,13 +264,13 @@ }, "resources": { "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(parameters('cMKKeyVaultResourceId'))), not(empty(parameters('cMKKeyName'))))]", + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKKeyName'))), parameters('cMKKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", "dependsOn": [ "cMKKeyVault" ] @@ -280,13 +290,22 @@ } }, "cMKKeyVault": { - "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, "containergroup": { "type": "Microsoft.ContainerInstance/containerGroups", @@ -295,9 +314,10 @@ "location": "[parameters('location')]", "identity": "[variables('identity')]", "tags": "[parameters('tags')]", - "properties": "[union(createObject('containers', parameters('containers'), 'encryptionProperties', if(not(empty(parameters('cMKKeyName'))), createObject('identity', parameters('cMKUserAssignedIdentityResourceId'), 'keyName', parameters('cMKKeyName'), 'keyVersion', if(not(empty(parameters('cMKKeyVersion'))), parameters('cMKKeyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))), 'vaultBaseUrl', reference('cMKKeyVault').vaultUri), null()), 'imageRegistryCredentials', parameters('imageRegistryCredentials'), 'initContainers', parameters('initContainers'), 'restartPolicy', parameters('restartPolicy'), 'osType', parameters('osType'), 'ipAddress', createObject('type', parameters('ipAddressType'), 'autoGeneratedDomainNameLabelScope', if(not(empty(parameters('dnsNameServers'))), parameters('autoGeneratedDomainNameLabelScope'), null()), 'dnsNameLabel', parameters('dnsNameLabel'), 'ports', parameters('ipAddressPorts')), 'sku', parameters('sku'), 'subnetIds', if(not(empty(parameters('subnetId'))), createArray(createObject('id', parameters('subnetId'))), null()), 'volumes', parameters('volumes')), if(not(empty(parameters('dnsNameServers'))), createObject('dnsConfig', createObject('nameServers', parameters('dnsNameServers'), 'searchDomains', parameters('dnsSearchDomains'))), createObject()))]", + "properties": "[union(createObject('containers', parameters('containers'), 'encryptionProperties', if(not(empty(parameters('customerManagedKey'))), createObject('identity', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))), null()), 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))), 'vaultBaseUrl', reference('cMKKeyVault').vaultUri), null()), 'imageRegistryCredentials', parameters('imageRegistryCredentials'), 'initContainers', parameters('initContainers'), 'restartPolicy', parameters('restartPolicy'), 'osType', parameters('osType'), 'ipAddress', createObject('type', parameters('ipAddressType'), 'autoGeneratedDomainNameLabelScope', if(not(empty(parameters('dnsNameServers'))), parameters('autoGeneratedDomainNameLabelScope'), null()), 'dnsNameLabel', parameters('dnsNameLabel'), 'ports', parameters('ipAddressPorts')), 'sku', parameters('sku'), 'subnetIds', if(not(empty(parameters('subnetId'))), createArray(createObject('id', parameters('subnetId'))), null()), 'volumes', parameters('volumes')), if(not(empty(parameters('dnsNameServers'))), createObject('dnsConfig', createObject('nameServers', parameters('dnsNameServers'), 'searchDomains', parameters('dnsSearchDomains'))), createObject()))]", "dependsOn": [ - "cMKKeyVault" + "cMKKeyVault", + "cMKUserAssignedIdentity" ] }, "containergroup_lock": { diff --git a/modules/container-registry/registry/.test/encr/main.test.bicep b/modules/container-registry/registry/.test/encr/main.test.bicep index 6865689145..0e804e410a 100644 --- a/modules/container-registry/registry/.test/encr/main.test.bicep +++ b/modules/container-registry/registry/.test/encr/main.test.bicep @@ -56,9 +56,11 @@ module testDeployment '../../main.bicep' = { enableDefaultTelemetry: enableDefaultTelemetry name: '${namePrefix}${serviceShort}001' acrSku: 'Premium' - cMKKeyName: nestedDependencies.outputs.keyVaultEncryptionKeyName - cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKUserAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultEncryptionKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } publicNetworkAccess: 'Disabled' managedIdentities: { userAssignedResourcesIds: [ diff --git a/modules/container-registry/registry/README.md b/modules/container-registry/registry/README.md index 4f7663a06d..15270996f2 100644 --- a/modules/container-registry/registry/README.md +++ b/modules/container-registry/registry/README.md @@ -307,9 +307,11 @@ module registry 'br:bicep/modules/container-registry.registry:1.0.0' = { name: 'crrencr001' // Non-required parameters acrSku: 'Premium' - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUserAssignedIdentityResourceId: '' + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } enableDefaultTelemetry: '' managedIdentities: { userAssignedResourcesIds: [ @@ -346,14 +348,12 @@ module registry 'br:bicep/modules/container-registry.registry:1.0.0' = { "acrSku": { "value": "Premium" }, - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" - }, - "cMKUserAssignedIdentityResourceId": { - "value": "" + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } }, "enableDefaultTelemetry": { "value": "" @@ -528,12 +528,6 @@ module registry 'br:bicep/modules/container-registry.registry:1.0.0' = { | :-- | :-- | :-- | | [`name`](#parameter-name) | string | Name of your Azure container registry. | -**Conditional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`cMKUserAssignedIdentityResourceId`](#parameter-cmkuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. Note, CMK requires the 'acrSku' to be 'Premium'. Required if 'cMKKeyName' is not empty. | - **Optional parameters** | Parameter | Type | Description | @@ -543,9 +537,7 @@ module registry 'br:bicep/modules/container-registry.registry:1.0.0' = { | [`anonymousPullEnabled`](#parameter-anonymouspullenabled) | bool | Enables registry-wide pull from unauthenticated clients. It's in preview and available in the Standard and Premium service tiers. | | [`azureADAuthenticationAsArmPolicyStatus`](#parameter-azureadauthenticationasarmpolicystatus) | string | The value that indicates whether the policy for using ARM audience token for a container registr is enabled or not. Default is enabled. | | [`cacheRules`](#parameter-cacherules) | array | Array of Cache Rules. Note: This is a preview feature ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache#cache-for-acr-preview)). | -| [`cMKKeyName`](#parameter-cmkkeyname) | string | The name of the customer managed key to use for encryption. Note, CMK requires the 'acrSku' to be 'Premium'. | -| [`cMKKeyVaultResourceId`](#parameter-cmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Note, CMK requires the 'acrSku' to be 'Premium'. | -| [`cMKKeyVersion`](#parameter-cmkkeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`dataEndpointEnabled`](#parameter-dataendpointenabled) | bool | Enable a single data endpoint per region for serving data. Not relevant in case of disabled public access. Note, requires the 'acrSku' to be 'Premium'. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`enableDefaultTelemetry`](#parameter-enabledefaulttelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). | @@ -620,33 +612,47 @@ Array of Cache Rules. Note: This is a preview feature ([ref](https://learn.micro - Type: array - Default: `[]` -### Parameter: `cMKKeyName` +### Parameter: `customerManagedKey` -The name of the customer managed key to use for encryption. Note, CMK requires the 'acrSku' to be 'Premium'. +The customer managed key definition. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | No | string | Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVaultResourceId` +### Parameter: `customerManagedKey.keyVaultResourceId` -The resource ID of a key vault to reference a customer managed key for encryption from. Note, CMK requires the 'acrSku' to be 'Premium'. -- Required: No +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVersion` +### Parameter: `customerManagedKey.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. - Required: No - Type: string -- Default: `''` -### Parameter: `cMKUserAssignedIdentityResourceId` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. -User assigned identity to use when fetching the customer managed key. Note, CMK requires the 'acrSku' to be 'Premium'. Required if 'cMKKeyName' is not empty. - Required: No - Type: string -- Default: `''` ### Parameter: `dataEndpointEnabled` diff --git a/modules/container-registry/registry/main.bicep b/modules/container-registry/registry/main.bicep index e5fe2166d4..57b8409f5c 100644 --- a/modules/container-registry/registry/main.bicep +++ b/modules/container-registry/registry/main.bicep @@ -134,17 +134,8 @@ param diagnosticSettings diagnosticSettingType @description('Optional. Enables registry-wide pull from unauthenticated clients. It\'s in preview and available in the Standard and Premium service tiers.') param anonymousPullEnabled bool = false -@description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from. Note, CMK requires the \'acrSku\' to be \'Premium\'.') -param cMKKeyVaultResourceId string = '' - -@description('Optional. The name of the customer managed key to use for encryption. Note, CMK requires the \'acrSku\' to be \'Premium\'.') -param cMKKeyName string = '' - -@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') -param cMKKeyVersion string = '' - -@description('Conditional. User assigned identity to use when fetching the customer managed key. Note, CMK requires the \'acrSku\' to be \'Premium\'. Required if \'cMKKeyName\' is not empty.') -param cMKUserAssignedIdentityResourceId string = '' +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType @description('Optional. Array of Cache Rules. Note: This is a preview feature ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache#cache-for-acr-preview)).') param cacheRules array = [] @@ -184,18 +175,18 @@ resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (ena } } -resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { - name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) - resource cMKKey 'keys@2023-02-01' existing = if (!empty(cMKKeyName)) { - name: !empty(cMKKeyName) ? cMKKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' } } -resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(cMKUserAssignedIdentityResourceId)) { - name: last(split((!empty(cMKUserAssignedIdentityResourceId) ? cMKUserAssignedIdentityResourceId : 'dummyMsi'), '/'))! - scope: resourceGroup(split((!empty(cMKUserAssignedIdentityResourceId) ? cMKUserAssignedIdentityResourceId : '//'), '/')[2], split((!empty(cMKUserAssignedIdentityResourceId) ? cMKUserAssignedIdentityResourceId : '////'), '/')[4]) +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) } resource registry 'Microsoft.ContainerRegistry/registries@2023-06-01-preview' = { @@ -209,11 +200,11 @@ resource registry 'Microsoft.ContainerRegistry/registries@2023-06-01-preview' = properties: { anonymousPullEnabled: anonymousPullEnabled adminUserEnabled: acrAdminUserEnabled - encryption: !empty(cMKKeyName) ? { + encryption: !empty(customerManagedKey) ? { status: 'enabled' keyVaultProperties: { - identity: cMKUserAssignedIdentity.properties.clientId - keyIdentifier: !empty(cMKKeyVersion) ? '${cMKKeyVault::cMKKey.properties.keyUri}/${cMKKeyVersion}' : cMKKeyVault::cMKKey.properties.keyUriWithVersion + identity: !empty(customerManagedKey.?userAssignedIdentityResourceId ?? '') ? cMKUserAssignedIdentity.properties.clientId : null + keyIdentifier: !empty(customerManagedKey.?keyVersion ?? '') ? '${cMKKeyVault::cMKKey.properties.keyUri}/${customerManagedKey!.keyVersion}' : cMKKeyVault::cMKKey.properties.keyUriWithVersion } } : null policies: { @@ -536,3 +527,17 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? +}? diff --git a/modules/container-registry/registry/main.json b/modules/container-registry/registry/main.json index 6470bbd3ca..9d58201220 100644 --- a/modules/container-registry/registry/main.json +++ b/modules/container-registry/registry/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "14688875704864672455" + "templateHash": "1853795110758917166" }, "name": "Azure Container Registries (ACR)", "description": "This module deploys an Azure Container Registry (ACR).", @@ -407,6 +407,38 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true } }, "parameters": { @@ -648,32 +680,10 @@ "description": "Optional. Enables registry-wide pull from unauthenticated clients. It's in preview and available in the Standard and Premium service tiers." } }, - "cMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The resource ID of a key vault to reference a customer managed key for encryption from. Note, CMK requires the 'acrSku' to be 'Premium'." - } - }, - "cMKKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption. Note, CMK requires the 'acrSku' to be 'Premium'." - } - }, - "cMKKeyVersion": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." - } - }, - "cMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Conditional. User assigned identity to use when fetching the customer managed key. Note, CMK requires the 'acrSku' to be 'Premium'. Required if 'cMKKeyName' is not empty." + "description": "Optional. The customer managed key definition." } }, "cacheRules": { @@ -704,13 +714,13 @@ }, "resources": { "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(parameters('cMKKeyVaultResourceId'))), not(empty(parameters('cMKKeyName'))))]", + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKKeyName'))), parameters('cMKKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", "dependsOn": [ "cMKKeyVault" ] @@ -730,22 +740,22 @@ } }, "cMKKeyVault": { - "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" }, "cMKUserAssignedIdentity": { - "condition": "[not(empty(parameters('cMKUserAssignedIdentityResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", "existing": true, "type": "Microsoft.ManagedIdentity/userAssignedIdentities", "apiVersion": "2023-01-31", - "subscriptionId": "[split(if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), parameters('cMKUserAssignedIdentityResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), parameters('cMKUserAssignedIdentityResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), parameters('cMKUserAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, "registry": { "type": "Microsoft.ContainerRegistry/registries", @@ -760,7 +770,7 @@ "properties": { "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", "adminUserEnabled": "[parameters('acrAdminUserEnabled')]", - "encryption": "[if(not(empty(parameters('cMKKeyName'))), createObject('status', 'enabled', 'keyVaultProperties', createObject('identity', reference('cMKUserAssignedIdentity').clientId, 'keyIdentifier', if(not(empty(parameters('cMKKeyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('cMKKeyVersion')), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null())]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('status', 'enabled', 'keyVaultProperties', createObject('identity', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyIdentifier', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null())]", "policies": { "azureADAuthenticationAsArmPolicy": { "status": "[parameters('azureADAuthenticationAsArmPolicyStatus')]" diff --git a/modules/data-factory/factory/.test/common/main.test.bicep b/modules/data-factory/factory/.test/common/main.test.bicep index 84cd092e7e..2c9eacb8ec 100644 --- a/modules/data-factory/factory/.test/common/main.test.bicep +++ b/modules/data-factory/factory/.test/common/main.test.bicep @@ -69,9 +69,11 @@ module testDeployment '../../main.bicep' = { params: { enableDefaultTelemetry: enableDefaultTelemetry name: '${namePrefix}${serviceShort}001' - cMKKeyName: nestedDependencies.outputs.keyVaultEncryptionKeyName - cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKUserAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultEncryptionKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } diagnosticSettings: [ { name: 'customSetting' diff --git a/modules/data-factory/factory/README.md b/modules/data-factory/factory/README.md index c65c7a02e6..b01bb04610 100644 --- a/modules/data-factory/factory/README.md +++ b/modules/data-factory/factory/README.md @@ -52,9 +52,11 @@ module factory 'br:bicep/modules/data-factory.factory:1.0.0' = { // Required parameters name: 'dffcom001' // Non-required parameters - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUserAssignedIdentityResourceId: '' + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } diagnosticSettings: [ { eventHubAuthorizationRuleResourceId: '' @@ -159,14 +161,12 @@ module factory 'br:bicep/modules/data-factory.factory:1.0.0' = { "value": "dffcom001" }, // Non-required parameters - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" - }, - "cMKUserAssignedIdentityResourceId": { - "value": "" + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } }, "diagnosticSettings": { "value": [ @@ -339,19 +339,11 @@ module factory 'br:bicep/modules/data-factory.factory:1.0.0' = { | :-- | :-- | :-- | | [`name`](#parameter-name) | string | The name of the Azure Factory to create. | -**Conditional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`cMKKeyVaultResourceId`](#parameter-cmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. | -| [`cMKUserAssignedIdentityResourceId`](#parameter-cmkuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty. | - **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`cMKKeyName`](#parameter-cmkkeyname) | string | The name of the customer managed key to use for encryption. | -| [`cMKKeyVersion`](#parameter-cmkkeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`enableDefaultTelemetry`](#parameter-enabledefaulttelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). | | [`gitAccountName`](#parameter-gitaccountname) | string | The account name. | @@ -375,33 +367,47 @@ module factory 'br:bicep/modules/data-factory.factory:1.0.0' = { | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | | [`tags`](#parameter-tags) | object | Tags of the resource. | -### Parameter: `cMKKeyName` +### Parameter: `customerManagedKey` -The name of the customer managed key to use for encryption. +The customer managed key definition. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | No | string | Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVaultResourceId` +### Parameter: `customerManagedKey.keyVaultResourceId` -The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. -- Required: No +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVersion` +### Parameter: `customerManagedKey.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. - Required: No - Type: string -- Default: `''` -### Parameter: `cMKUserAssignedIdentityResourceId` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. -User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty. - Required: No - Type: string -- Default: `''` ### Parameter: `diagnosticSettings` diff --git a/modules/data-factory/factory/main.bicep b/modules/data-factory/factory/main.bicep index 381ed4e1db..810d6c0200 100644 --- a/modules/data-factory/factory/main.bicep +++ b/modules/data-factory/factory/main.bicep @@ -67,17 +67,8 @@ param managedIdentities managedIdentitiesType @description('Optional. Configuration Details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') param privateEndpoints privateEndpointType -@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \'cMKKeyName\' is not empty.') -param cMKKeyVaultResourceId string = '' - -@description('Optional. The name of the customer managed key to use for encryption.') -param cMKKeyName string = '' - -@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') -param cMKKeyVersion string = '' - -@description('Conditional. User assigned identity to use when fetching the customer managed key. Required if \'cMKKeyName\' is not empty.') -param cMKUserAssignedIdentityResourceId string = '' +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType @description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') param roleAssignments roleAssignmentType @@ -106,9 +97,18 @@ var builtInRoleNames = { 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') } -resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { - name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) + + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' + } +} + +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) } resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { @@ -142,12 +142,12 @@ resource dataFactory 'Microsoft.DataFactory/factories@2018-06-01' = { } : {}), {}) globalParameters: !empty(globalParameters) ? globalParameters : null publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) ? 'Disabled' : null) - encryption: !empty(cMKKeyName) ? { - identity: { - userAssignedIdentity: cMKUserAssignedIdentityResourceId - } - keyName: cMKKeyName - keyVersion: !empty(cMKKeyVersion) ? cMKKeyVersion : null + encryption: !empty(customerManagedKey) ? { + identity: !empty(customerManagedKey.?userAssignedIdentityResourceId) ? { + userAssignedIdentity: cMKUserAssignedIdentity.id + } : null + keyName: customerManagedKey!.keyName + keyVersion: !empty(customerManagedKey.?keyVersion ?? '') ? customerManagedKey!.keyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) vaultBaseUrl: cMKKeyVault.properties.vaultUri } : null } @@ -414,3 +414,17 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? +}? diff --git a/modules/data-factory/factory/main.json b/modules/data-factory/factory/main.json index aa193cadf8..448f9f9614 100644 --- a/modules/data-factory/factory/main.json +++ b/modules/data-factory/factory/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "1415884638599377742" + "templateHash": "12379082331445276558" }, "name": "Data Factories", "description": "This module deploys a Data Factory.", @@ -407,6 +407,38 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true } }, "parameters": { @@ -550,32 +582,10 @@ "description": "Optional. Configuration Details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." } }, - "cMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty." - } - }, - "cMKKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption." - } - }, - "cMKKeyVersion": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." - } - }, - "cMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Conditional. User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty." + "description": "Optional. The customer managed key definition." } }, "roleAssignments": { @@ -613,14 +623,35 @@ } }, "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, "cMKKeyVault": { - "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, "defaultTelemetry": { "condition": "[parameters('enableDefaultTelemetry')]", @@ -647,10 +678,11 @@ "repoConfiguration": "[if(bool(parameters('gitConfigureLater')), null(), union(createObject('type', parameters('gitRepoType'), 'hostName', parameters('gitHostName'), 'accountName', parameters('gitAccountName'), 'repositoryName', parameters('gitRepositoryName'), 'collaborationBranch', parameters('gitCollaborationBranch'), 'rootFolder', parameters('gitRootFolder'), 'disablePublish', parameters('gitDisablePublish')), if(equals(parameters('gitRepoType'), 'FactoryVSTSConfiguration'), createObject('projectName', parameters('gitProjectName')), createObject()), createObject()))]", "globalParameters": "[if(not(empty(parameters('globalParameters'))), parameters('globalParameters'), null())]", "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(not(empty(parameters('privateEndpoints'))), 'Disabled', null()))]", - "encryption": "[if(not(empty(parameters('cMKKeyName'))), createObject('identity', createObject('userAssignedIdentity', parameters('cMKUserAssignedIdentityResourceId')), 'keyName', parameters('cMKKeyName'), 'keyVersion', if(not(empty(parameters('cMKKeyVersion'))), parameters('cMKKeyVersion'), null()), 'vaultBaseUrl', reference('cMKKeyVault').vaultUri), null())]" + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('identity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null()), 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))), 'vaultBaseUrl', reference('cMKKeyVault').vaultUri), null())]" }, "dependsOn": [ - "cMKKeyVault" + "cMKKeyVault", + "cMKUserAssignedIdentity" ] }, "dataFactory_lock": { diff --git a/modules/databricks/workspace/.test/common/main.test.bicep b/modules/databricks/workspace/.test/common/main.test.bicep index 93003db078..e331c84dec 100644 --- a/modules/databricks/workspace/.test/common/main.test.bicep +++ b/modules/databricks/workspace/.test/common/main.test.bicep @@ -112,11 +112,15 @@ module testDeployment '../../main.bicep' = { Environment: 'Non-Prod' Role: 'DeploymentValidation' } - cMKManagedServicesKeyName: nestedDependencies.outputs.keyVaultKeyName - cMKManagedServicesKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKManagedDisksKeyName: nestedDependencies.outputs.keyVaultDiskKeyName - cMKManagedDisksKeyVaultResourceId: nestedDependencies.outputs.keyVaultDiskResourceId - cMKManagedDisksKeyRotationToLatestKeyVersionEnabled: true + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + } + customerManagedKeyManagedDisk: { + keyName: nestedDependencies.outputs.keyVaultDiskKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultDiskResourceId + rotationToLatestKeyVersionEnabled: true + } storageAccountName: 'sa${namePrefix}${serviceShort}001' storageAccountSkuName: 'Standard_ZRS' publicIpName: 'nat-gw-public-ip' diff --git a/modules/databricks/workspace/README.md b/modules/databricks/workspace/README.md index bcf15863ba..3fed69efc9 100644 --- a/modules/databricks/workspace/README.md +++ b/modules/databricks/workspace/README.md @@ -50,11 +50,15 @@ module workspace 'br:bicep/modules/databricks.workspace:1.0.0' = { name: 'dwcom001' // Non-required parameters amlWorkspaceResourceId: '' - cMKManagedDisksKeyName: '' - cMKManagedDisksKeyRotationToLatestKeyVersionEnabled: true - cMKManagedDisksKeyVaultResourceId: '' - cMKManagedServicesKeyName: '' - cMKManagedServicesKeyVaultResourceId: '' + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + } + customerManagedKeyManagedDisk: { + keyName: '' + keyVaultResourceId: '' + rotationToLatestKeyVersionEnabled: true + } customPrivateSubnetName: '' customPublicSubnetName: '' customVirtualNetworkResourceId: '' @@ -143,20 +147,18 @@ module workspace 'br:bicep/modules/databricks.workspace:1.0.0' = { "amlWorkspaceResourceId": { "value": "" }, - "cMKManagedDisksKeyName": { - "value": "" - }, - "cMKManagedDisksKeyRotationToLatestKeyVersionEnabled": { - "value": true - }, - "cMKManagedDisksKeyVaultResourceId": { - "value": "" - }, - "cMKManagedServicesKeyName": { - "value": "" + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "" + } }, - "cMKManagedServicesKeyVaultResourceId": { - "value": "" + "customerManagedKeyManagedDisk": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "rotationToLatestKeyVersionEnabled": true + } }, "customPrivateSubnetName": { "value": "" @@ -334,23 +336,13 @@ module workspace 'br:bicep/modules/databricks.workspace:1.0.0' = { | :-- | :-- | :-- | | [`name`](#parameter-name) | string | The name of the Azure Databricks workspace to create. | -**Conditional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`cMKManagedDisksKeyVaultResourceId`](#parameter-cmkmanageddiskskeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. | -| [`cMKManagedServicesKeyVaultResourceId`](#parameter-cmkmanagedserviceskeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. | - **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | | [`amlWorkspaceResourceId`](#parameter-amlworkspaceresourceid) | string | The resource ID of a Azure Machine Learning workspace to link with Databricks workspace. | -| [`cMKManagedDisksKeyName`](#parameter-cmkmanageddiskskeyname) | string | The name of the customer managed key to use for encryption. | -| [`cMKManagedDisksKeyRotationToLatestKeyVersionEnabled`](#parameter-cmkmanageddiskskeyrotationtolatestkeyversionenabled) | bool | Enable Auto Rotation of Key. | -| [`cMKManagedDisksKeyVersion`](#parameter-cmkmanageddiskskeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | -| [`cMKManagedServicesKeyName`](#parameter-cmkmanagedserviceskeyname) | string | The name of the customer managed key to use for encryption. | -| [`cMKManagedServicesKeyVersion`](#parameter-cmkmanagedserviceskeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition to use for the managed service. | +| [`customerManagedKeyManagedDisk`](#parameter-customermanagedkeymanageddisk) | object | The customer managed key definition to use for the managed disk. | | [`customPrivateSubnetName`](#parameter-customprivatesubnetname) | string | The name of the Private Subnet within the Virtual Network. | | [`customPublicSubnetName`](#parameter-custompublicsubnetname) | string | The name of a Public Subnet within the Virtual Network. | | [`customVirtualNetworkResourceId`](#parameter-customvirtualnetworkresourceid) | string | The resource ID of a Virtual Network where this Databricks Cluster should be created. | @@ -383,54 +375,97 @@ The resource ID of a Azure Machine Learning workspace to link with Databricks wo - Type: string - Default: `''` -### Parameter: `cMKManagedDisksKeyName` +### Parameter: `customerManagedKey` -The name of the customer managed key to use for encryption. +The customer managed key definition to use for the managed service. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | No | string | Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKManagedDisksKeyRotationToLatestKeyVersionEnabled` +### Parameter: `customerManagedKey.keyVaultResourceId` -Enable Auto Rotation of Key. -- Required: No -- Type: bool -- Default: `True` +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes +- Type: string + +### Parameter: `customerManagedKey.keyVersion` -### Parameter: `cMKManagedDisksKeyVaultResourceId` +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. - Required: No - Type: string -- Default: `''` -### Parameter: `cMKManagedDisksKeyVersion` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. -The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. - Required: No - Type: string -- Default: `''` -### Parameter: `cMKManagedServicesKeyName` +### Parameter: `customerManagedKeyManagedDisk` -The name of the customer managed key to use for encryption. +The customer managed key definition to use for the managed disk. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeymanageddiskkeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeymanageddiskkeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeymanageddiskkeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`rotationToLatestKeyVersionEnabled`](#parameter-customermanagedkeymanageddiskrotationtolatestkeyversionenabled) | No | bool | Optional. Indicate whether the latest key version should be automatically used for Managed Disk Encryption. Enabled by default. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeymanageddiskuserassignedidentityresourceid) | No | string | Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKeyManagedDisk.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKManagedServicesKeyVaultResourceId` +### Parameter: `customerManagedKeyManagedDisk.keyVaultResourceId` + +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes +- Type: string + +### Parameter: `customerManagedKeyManagedDisk.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. - Required: No - Type: string -- Default: `''` -### Parameter: `cMKManagedServicesKeyVersion` +### Parameter: `customerManagedKeyManagedDisk.rotationToLatestKeyVersionEnabled` + +Optional. Indicate whether the latest key version should be automatically used for Managed Disk Encryption. Enabled by default. + +- Required: No +- Type: bool + +### Parameter: `customerManagedKeyManagedDisk.userAssignedIdentityResourceId` + +Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. -The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. - Required: No - Type: string -- Default: `''` ### Parameter: `customPrivateSubnetName` diff --git a/modules/databricks/workspace/main.bicep b/modules/databricks/workspace/main.bicep index d0f262ea88..3689a37f95 100644 --- a/modules/databricks/workspace/main.bicep +++ b/modules/databricks/workspace/main.bicep @@ -49,26 +49,11 @@ param customPublicSubnetName string = '' @description('Optional. Disable Public IP.') param disablePublicIp bool = false -@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \'cMKKeyName\' is not empty.') -param cMKManagedServicesKeyVaultResourceId string = '' +@description('Optional. The customer managed key definition to use for the managed service.') +param customerManagedKey customerManagedKeyType -@description('Optional. The name of the customer managed key to use for encryption.') -param cMKManagedServicesKeyName string = '' - -@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') -param cMKManagedServicesKeyVersion string = '' - -@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \'cMKKeyName\' is not empty.') -param cMKManagedDisksKeyVaultResourceId string = '' - -@description('Optional. The name of the customer managed key to use for encryption.') -param cMKManagedDisksKeyName string = '' - -@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') -param cMKManagedDisksKeyVersion string = '' - -@description('Optional. Enable Auto Rotation of Key.') -param cMKManagedDisksKeyRotationToLatestKeyVersionEnabled bool = true +@description('Optional. The customer managed key definition to use for the managed disk.') +param customerManagedKeyManagedDisk customerManagedKeyManagedDiskType @description('Optional. Name of the outbound Load Balancer Backend Pool for Secure Cluster Connectivity (No Public IP).') param loadBalancerBackendPoolName string = '' @@ -136,21 +121,21 @@ resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (ena } } -resource cMKManagedDisksKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKManagedDisksKeyVaultResourceId)) { - name: last(split((!empty(cMKManagedDisksKeyVaultResourceId) ? cMKManagedDisksKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKManagedDisksKeyVaultResourceId) ? cMKManagedDisksKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKManagedDisksKeyVaultResourceId) ? cMKManagedDisksKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) - resource cMKKeyDisk 'keys@2023-02-01' existing = if (!empty(cMKManagedDisksKeyName)) { - name: !empty(cMKManagedDisksKeyName) ? cMKManagedDisksKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' } } -resource cMKManagedServicesKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKManagedServicesKeyVaultResourceId)) { - name: last(split((!empty(cMKManagedServicesKeyVaultResourceId) ? cMKManagedServicesKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKManagedServicesKeyVaultResourceId) ? cMKManagedServicesKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKManagedServicesKeyVaultResourceId) ? cMKManagedServicesKeyVaultResourceId : '////'), '/')[4]) +resource cMKManagedDiskKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKeyManagedDisk.?keyVaultResourceId)) { + name: last(split((customerManagedKeyManagedDisk.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKeyManagedDisk.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKeyManagedDisk.?keyVaultResourceId ?? '////'), '/')[4]) - resource cMKKey 'keys@2023-02-01' existing = if (!empty(cMKManagedServicesKeyName)) { - name: !empty(cMKManagedServicesKeyName) ? cMKManagedServicesKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKeyManagedDisk.?keyVaultResourceId) && !empty(customerManagedKeyManagedDisk.?keyName)) { + name: customerManagedKeyManagedDisk.?keyName ?? 'dummyKey' } } @@ -232,24 +217,24 @@ resource workspace 'Microsoft.Databricks/workspaces@2023-02-01' = { } : {}) publicNetworkAccess: publicNetworkAccess requiredNsgRules: requiredNsgRules - encryption: !empty(cMKManagedServicesKeyName) || !empty(cMKManagedServicesKeyName) ? { + encryption: !empty(customerManagedKey) || !empty(customerManagedKeyManagedDisk) ? { entities: { - managedServices: !empty(cMKManagedServicesKeyName) ? { + managedServices: !empty(customerManagedKey) ? { keySource: 'Microsoft.Keyvault' keyVaultProperties: { - keyVaultUri: cMKManagedServicesKeyVault.properties.vaultUri - keyName: cMKManagedServicesKeyName - keyVersion: !empty(cMKManagedServicesKeyVersion) ? cMKManagedServicesKeyVersion : last(split(cMKManagedServicesKeyVault::cMKKey.properties.keyUriWithVersion, '/')) + keyVaultUri: cMKKeyVault.properties.vaultUri + keyName: customerManagedKey!.keyName + keyVersion: !empty(customerManagedKey.?keyVersion ?? '') ? customerManagedKey!.keyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) } } : null - managedDisk: !empty(cMKManagedDisksKeyName) ? { + managedDisk: !empty(customerManagedKeyManagedDisk) ? { keySource: 'Microsoft.Keyvault' keyVaultProperties: { - keyVaultUri: cMKManagedDisksKeyVault.properties.vaultUri - keyName: cMKManagedDisksKeyName - keyVersion: !empty(cMKManagedDisksKeyVersion) ? cMKManagedDisksKeyVersion : last(split(cMKManagedDisksKeyVault::cMKKeyDisk.properties.keyUriWithVersion, '/')) + keyVaultUri: cMKManagedDiskKeyVault.properties.vaultUri + keyName: customerManagedKeyManagedDisk!.keyName + keyVersion: !empty(customerManagedKeyManagedDisk.?keyVersion ?? '') ? customerManagedKeyManagedDisk!.keyVersion : last(split(cMKManagedDiskKeyVault::cMKKey.properties.keyUriWithVersion, '/')) } - rotationToLatestKeyVersionEnabled: cMKManagedDisksKeyRotationToLatestKeyVersionEnabled + rotationToLatestKeyVersionEnabled: customerManagedKeyManagedDisk.?rotationToLatestKeyVersionEnabled ?? true } : null } } : null @@ -469,3 +454,34 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? +}? + +type customerManagedKeyManagedDiskType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? + + @description('Optional. Indicate whether the latest key version should be automatically used for Managed Disk Encryption. Enabled by default.') + rotationToLatestKeyVersionEnabled: bool? +}? diff --git a/modules/databricks/workspace/main.json b/modules/databricks/workspace/main.json index 69e194ad09..e6dcbd3bd4 100644 --- a/modules/databricks/workspace/main.json +++ b/modules/databricks/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "1354063990980525308" + "templateHash": "3160595622135122462" }, "name": "Azure Databricks Workspaces", "description": "This module deploys an Azure Databricks Workspace.", @@ -366,6 +366,77 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true + }, + "customerManagedKeyManagedDiskType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + }, + "rotationToLatestKeyVersionEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicate whether the latest key version should be automatically used for Managed Disk Encryption. Enabled by default." + } + } + }, + "nullable": true } }, "parameters": { @@ -468,53 +539,16 @@ "description": "Optional. Disable Public IP." } }, - "cMKManagedServicesKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty." - } - }, - "cMKManagedServicesKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption." - } - }, - "cMKManagedServicesKeyVersion": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." - } - }, - "cMKManagedDisksKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty." - } - }, - "cMKManagedDisksKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption." - } - }, - "cMKManagedDisksKeyVersion": { - "type": "string", - "defaultValue": "", + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." + "description": "Optional. The customer managed key definition to use for the managed service." } }, - "cMKManagedDisksKeyRotationToLatestKeyVersionEnabled": { - "type": "bool", - "defaultValue": true, + "customerManagedKeyManagedDisk": { + "$ref": "#/definitions/customerManagedKeyManagedDiskType", "metadata": { - "description": "Optional. Enable Auto Rotation of Key." + "description": "Optional. The customer managed key definition to use for the managed disk." } }, "loadBalancerBackendPoolName": { @@ -620,28 +654,28 @@ } }, "resources": { - "cMKManagedDisksKeyVault::cMKKeyDisk": { - "condition": "[and(not(empty(parameters('cMKManagedDisksKeyVaultResourceId'))), not(empty(parameters('cMKManagedDisksKeyName'))))]", + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKManagedDisksKeyVaultResourceId'))), parameters('cMKManagedDisksKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKManagedDisksKeyVaultResourceId'))), parameters('cMKManagedDisksKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKManagedDisksKeyVaultResourceId'))), parameters('cMKManagedDisksKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKManagedDisksKeyName'))), parameters('cMKManagedDisksKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", "dependsOn": [ - "cMKManagedDisksKeyVault" + "cMKKeyVault" ] }, - "cMKManagedServicesKeyVault::cMKKey": { - "condition": "[and(not(empty(parameters('cMKManagedServicesKeyVaultResourceId'))), not(empty(parameters('cMKManagedServicesKeyName'))))]", + "cMKManagedDiskKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKManagedServicesKeyVaultResourceId'))), parameters('cMKManagedServicesKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKManagedServicesKeyVaultResourceId'))), parameters('cMKManagedServicesKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKManagedServicesKeyVaultResourceId'))), parameters('cMKManagedServicesKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKManagedServicesKeyName'))), parameters('cMKManagedServicesKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyName'), 'dummyKey'))]", "dependsOn": [ - "cMKManagedServicesKeyVault" + "cMKManagedDiskKeyVault" ] }, "defaultTelemetry": { @@ -658,23 +692,23 @@ } } }, - "cMKManagedDisksKeyVault": { - "condition": "[not(empty(parameters('cMKManagedDisksKeyVaultResourceId')))]", + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKManagedDisksKeyVaultResourceId'))), parameters('cMKManagedDisksKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKManagedDisksKeyVaultResourceId'))), parameters('cMKManagedDisksKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKManagedDisksKeyVaultResourceId'))), parameters('cMKManagedDisksKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" }, - "cMKManagedServicesKeyVault": { - "condition": "[not(empty(parameters('cMKManagedServicesKeyVaultResourceId')))]", + "cMKManagedDiskKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKManagedServicesKeyVaultResourceId'))), parameters('cMKManagedServicesKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKManagedServicesKeyVaultResourceId'))), parameters('cMKManagedServicesKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKManagedServicesKeyVaultResourceId'))), parameters('cMKManagedServicesKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" }, "workspace": { "type": "Microsoft.Databricks/workspaces", @@ -690,11 +724,11 @@ "parameters": "[union(createObject('enableNoPublicIp', createObject('value', parameters('disablePublicIp')), 'prepareEncryption', createObject('value', parameters('prepareEncryption')), 'vnetAddressPrefix', createObject('value', parameters('vnetAddressPrefix')), 'requireInfrastructureEncryption', createObject('value', parameters('requireInfrastructureEncryption'))), if(not(empty(parameters('customVirtualNetworkResourceId'))), createObject('customVirtualNetworkId', createObject('value', parameters('customVirtualNetworkResourceId'))), createObject()), if(not(empty(parameters('amlWorkspaceResourceId'))), createObject('amlWorkspaceId', createObject('value', parameters('amlWorkspaceResourceId'))), createObject()), if(not(empty(parameters('customPrivateSubnetName'))), createObject('customPrivateSubnetName', createObject('value', parameters('customPrivateSubnetName'))), createObject()), if(not(empty(parameters('customPublicSubnetName'))), createObject('customPublicSubnetName', createObject('value', parameters('customPublicSubnetName'))), createObject()), if(not(empty(parameters('loadBalancerBackendPoolName'))), createObject('loadBalancerBackendPoolName', createObject('value', parameters('loadBalancerBackendPoolName'))), createObject()), if(not(empty(parameters('loadBalancerResourceId'))), createObject('loadBalancerId', createObject('value', parameters('loadBalancerResourceId'))), createObject()), if(not(empty(parameters('natGatewayName'))), createObject('natGatewayName', createObject('value', parameters('natGatewayName'))), createObject()), if(not(empty(parameters('publicIpName'))), createObject('publicIpName', createObject('value', parameters('publicIpName'))), createObject()), if(not(empty(parameters('storageAccountName'))), createObject('storageAccountName', createObject('value', parameters('storageAccountName'))), createObject()), if(not(empty(parameters('storageAccountSkuName'))), createObject('storageAccountSkuName', createObject('value', parameters('storageAccountSkuName'))), createObject()))]", "publicNetworkAccess": "[parameters('publicNetworkAccess')]", "requiredNsgRules": "[parameters('requiredNsgRules')]", - "encryption": "[if(or(not(empty(parameters('cMKManagedServicesKeyName'))), not(empty(parameters('cMKManagedServicesKeyName')))), createObject('entities', createObject('managedServices', if(not(empty(parameters('cMKManagedServicesKeyName'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKManagedServicesKeyVault').vaultUri, 'keyName', parameters('cMKManagedServicesKeyName'), 'keyVersion', if(not(empty(parameters('cMKManagedServicesKeyVersion'))), parameters('cMKManagedServicesKeyVersion'), last(split(reference('cMKManagedServicesKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'managedDisk', if(not(empty(parameters('cMKManagedDisksKeyName'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKManagedDisksKeyVault').vaultUri, 'keyName', parameters('cMKManagedDisksKeyName'), 'keyVersion', if(not(empty(parameters('cMKManagedDisksKeyVersion'))), parameters('cMKManagedDisksKeyVersion'), last(split(reference('cMKManagedDisksKeyVault::cMKKeyDisk').keyUriWithVersion, '/')))), 'rotationToLatestKeyVersionEnabled', parameters('cMKManagedDisksKeyRotationToLatestKeyVersionEnabled')), null()))), null())]" + "encryption": "[if(or(not(empty(parameters('customerManagedKey'))), not(empty(parameters('customerManagedKeyManagedDisk')))), createObject('entities', createObject('managedServices', if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'managedDisk', if(not(empty(parameters('customerManagedKeyManagedDisk'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKManagedDiskKeyVault').vaultUri, 'keyName', parameters('customerManagedKeyManagedDisk').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVersion'), ''))), parameters('customerManagedKeyManagedDisk').keyVersion, last(split(reference('cMKManagedDiskKeyVault::cMKKey').keyUriWithVersion, '/')))), 'rotationToLatestKeyVersionEnabled', coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'rotationToLatestKeyVersionEnabled'), true())), null()))), null())]" }, "dependsOn": [ - "cMKManagedDisksKeyVault", - "cMKManagedServicesKeyVault" + "cMKKeyVault", + "cMKManagedDiskKeyVault" ] }, "workspace_lock": { diff --git a/modules/db-for-my-sql/flexible-server/.test/public/main.test.bicep b/modules/db-for-my-sql/flexible-server/.test/public/main.test.bicep index 04f9296d26..2dac6609f6 100644 --- a/modules/db-for-my-sql/flexible-server/.test/public/main.test.bicep +++ b/modules/db-for-my-sql/flexible-server/.test/public/main.test.bicep @@ -143,13 +143,17 @@ module testDeployment '../../main.bicep' = { highAvailability: 'SameZone' storageAutoGrow: 'Enabled' version: '8.0.21' - cMKKeyVaultResourceId: nestedDependencies2.outputs.keyVaultResourceId - cMKKeyName: nestedDependencies2.outputs.keyName - cMKUserAssignedIdentityResourceId: nestedDependencies2.outputs.managedIdentityResourceId + customerManagedKey: { + keyName: nestedDependencies2.outputs.keyName + keyVaultResourceId: nestedDependencies2.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies2.outputs.managedIdentityResourceId + } geoRedundantBackup: 'Enabled' - geoBackupCMKKeyVaultResourceId: nestedDependencies2.outputs.geoBackupKeyVaultResourceId - geoBackupCMKKeyName: nestedDependencies2.outputs.geoBackupKeyName - geoBackupCMKUserAssignedIdentityResourceId: nestedDependencies2.outputs.geoBackupManagedIdentityResourceId + customerManagedKeyGeo: { + keyName: nestedDependencies2.outputs.geoBackupKeyName + keyVaultResourceId: nestedDependencies2.outputs.geoBackupKeyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies2.outputs.geoBackupManagedIdentityResourceId + } managedIdentities: { userAssignedResourcesIds: [ nestedDependencies2.outputs.managedIdentityResourceId diff --git a/modules/db-for-my-sql/flexible-server/README.md b/modules/db-for-my-sql/flexible-server/README.md index fbc748a98e..e9c8cf81f8 100644 --- a/modules/db-for-my-sql/flexible-server/README.md +++ b/modules/db-for-my-sql/flexible-server/README.md @@ -322,9 +322,16 @@ module flexibleServer 'br:bicep/modules/db-for-my-sql.flexible-server:1.0.0' = { administratorLoginPassword: '' availabilityZone: '1' backupRetentionDays: 20 - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUserAssignedIdentityResourceId: '' + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } + customerManagedKeyGeo: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } databases: [ { name: 'testdb1' @@ -367,9 +374,6 @@ module flexibleServer 'br:bicep/modules/db-for-my-sql.flexible-server:1.0.0' = { startIpAddress: '100.100.100.1' } ] - geoBackupCMKKeyName: '' - geoBackupCMKKeyVaultResourceId: '' - geoBackupCMKUserAssignedIdentityResourceId: '' geoRedundantBackup: 'Enabled' highAvailability: 'SameZone' location: '' @@ -439,14 +443,19 @@ module flexibleServer 'br:bicep/modules/db-for-my-sql.flexible-server:1.0.0' = { "backupRetentionDays": { "value": 20 }, - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } }, - "cMKUserAssignedIdentityResourceId": { - "value": "" + "customerManagedKeyGeo": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } }, "databases": { "value": [ @@ -498,15 +507,6 @@ module flexibleServer 'br:bicep/modules/db-for-my-sql.flexible-server:1.0.0' = { } ] }, - "geoBackupCMKKeyName": { - "value": "" - }, - "geoBackupCMKKeyVaultResourceId": { - "value": "" - }, - "geoBackupCMKUserAssignedIdentityResourceId": { - "value": "" - }, "geoRedundantBackup": { "value": "Enabled" }, @@ -583,11 +583,7 @@ module flexibleServer 'br:bicep/modules/db-for-my-sql.flexible-server:1.0.0' = { | Parameter | Type | Description | | :-- | :-- | :-- | -| [`cMKKeyVaultResourceId`](#parameter-cmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if "cMKKeyName" is not empty. | -| [`cMKUserAssignedIdentityResourceId`](#parameter-cmkuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if "cMKKeyName" is not empty. | -| [`geoBackupCMKKeyVaultResourceId`](#parameter-geobackupcmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if "cMKKeyName" is not empty and geoRedundantBackup is "Enabled". | -| [`geoBackupCMKUserAssignedIdentityResourceId`](#parameter-geobackupcmkuserassignedidentityresourceid) | string | Geo backup user identity resource ID as identity cant cross region, need identity in same region as geo backup. The identity should have key usage permissions on the Key Vault Key. Required if "cMKKeyName" is not empty and geoRedundantBackup is "Enabled". | -| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. Required if 'cMKKeyName' is not empty. | +| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. Required if 'customerManagedKey' is not empty. | | [`privateDnsZoneResourceId`](#parameter-privatednszoneresourceid) | string | Private dns zone arm resource ID. Used when the desired connectivity mode is "Private Access". Required if "delegatedSubnetResourceId" is used and the Private DNS Zone name must end with mysql.database.azure.com in order to be linked to the MySQL Flexible Server. | | [`restorePointInTime`](#parameter-restorepointintime) | string | Restore point creation time (ISO8601 format), specifying the time to restore from. Required if "createMode" is set to "PointInTimeRestore". | | [`sourceServerResourceId`](#parameter-sourceserverresourceid) | string | The source MySQL server ID. Required if "createMode" is set to "PointInTimeRestore". | @@ -602,16 +598,14 @@ module flexibleServer 'br:bicep/modules/db-for-my-sql.flexible-server:1.0.0' = { | [`administrators`](#parameter-administrators) | array | The Azure AD administrators when AAD authentication enabled. | | [`availabilityZone`](#parameter-availabilityzone) | string | Availability zone information of the server. Default will have no preference set. | | [`backupRetentionDays`](#parameter-backupretentiondays) | int | Backup retention days for the server. | -| [`cMKKeyName`](#parameter-cmkkeyname) | string | The name of the customer managed key to use for encryption. | -| [`cMKKeyVersion`](#parameter-cmkkeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | | [`createMode`](#parameter-createmode) | string | The mode to create a new MySQL server. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition to use for the managed service. | +| [`customerManagedKeyGeo`](#parameter-customermanagedkeygeo) | object | The customer managed key definition to use when geoRedundantBackup is "Enabled". | | [`databases`](#parameter-databases) | array | The databases to create in the server. | | [`delegatedSubnetResourceId`](#parameter-delegatedsubnetresourceid) | string | Delegated subnet arm resource ID. Used when the desired connectivity mode is "Private Access" - virtual network integration. Delegation must be enabled on the subnet for MySQL Flexible Servers and subnet CIDR size is /29. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`enableDefaultTelemetry`](#parameter-enabledefaulttelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). | | [`firewallRules`](#parameter-firewallrules) | array | The firewall rules to create in the MySQL flexible server. | -| [`geoBackupCMKKeyName`](#parameter-geobackupcmkkeyname) | string | The name of the customer managed key to use for encryption when geoRedundantBackup is "Enabled". | -| [`geoBackupCMKKeyVersion`](#parameter-geobackupcmkkeyversion) | string | The version of the customer managed key to reference for encryption when geoRedundantBackup is "Enabled". If not provided, the latest key version is used. | | [`geoRedundantBackup`](#parameter-georedundantbackup) | string | A value indicating whether Geo-Redundant backup is enabled on the server. If "Enabled" and "cMKKeyName" is not empty, then "geoBackupCMKKeyVaultResourceId" and "cMKUserAssignedIdentityResourceId" are also required. | | [`highAvailability`](#parameter-highavailability) | string | The mode for High Availability (HA). It is not supported for the Burstable pricing tier and Zone redundant HA can only be set during server provisioning. | | [`location`](#parameter-location) | string | Location for all resources. | @@ -669,49 +663,105 @@ Backup retention days for the server. - Type: int - Default: `7` -### Parameter: `cMKKeyName` +### Parameter: `createMode` -The name of the customer managed key to use for encryption. +The mode to create a new MySQL server. - Required: No - Type: string -- Default: `''` +- Default: `'Default'` +- Allowed: + ```Bicep + [ + 'Default' + 'GeoRestore' + 'PointInTimeRestore' + 'Replica' + ] + ``` -### Parameter: `cMKKeyVaultResourceId` +### Parameter: `customerManagedKey` -The resource ID of a key vault to reference a customer managed key for encryption from. Required if "cMKKeyName" is not empty. +The customer managed key definition to use for the managed service. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | Yes | string | Required. User assigned identity to use when fetching the customer managed key. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVersion` +### Parameter: `customerManagedKey.keyVaultResourceId` + +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes +- Type: string + +### Parameter: `customerManagedKey.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. - Required: No - Type: string -- Default: `''` -### Parameter: `cMKUserAssignedIdentityResourceId` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +Required. User assigned identity to use when fetching the customer managed key. + +- Required: Yes +- Type: string + +### Parameter: `customerManagedKeyGeo` -User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if "cMKKeyName" is not empty. +The customer managed key definition to use when geoRedundantBackup is "Enabled". - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeygeokeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeygeokeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeygeokeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeygeouserassignedidentityresourceid) | Yes | string | Required. User assigned identity to use when fetching the customer managed key. | + +### Parameter: `customerManagedKeyGeo.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `createMode` +### Parameter: `customerManagedKeyGeo.keyVaultResourceId` + +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes +- Type: string + +### Parameter: `customerManagedKeyGeo.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -The mode to create a new MySQL server. - Required: No - Type: string -- Default: `'Default'` -- Allowed: - ```Bicep - [ - 'Default' - 'GeoRestore' - 'PointInTimeRestore' - 'Replica' - ] - ``` + +### Parameter: `customerManagedKeyGeo.userAssignedIdentityResourceId` + +Required. User assigned identity to use when fetching the customer managed key. + +- Required: Yes +- Type: string ### Parameter: `databases` @@ -856,34 +906,6 @@ The firewall rules to create in the MySQL flexible server. - Type: array - Default: `[]` -### Parameter: `geoBackupCMKKeyName` - -The name of the customer managed key to use for encryption when geoRedundantBackup is "Enabled". -- Required: No -- Type: string -- Default: `''` - -### Parameter: `geoBackupCMKKeyVaultResourceId` - -The resource ID of a key vault to reference a customer managed key for encryption from. Required if "cMKKeyName" is not empty and geoRedundantBackup is "Enabled". -- Required: No -- Type: string -- Default: `''` - -### Parameter: `geoBackupCMKKeyVersion` - -The version of the customer managed key to reference for encryption when geoRedundantBackup is "Enabled". If not provided, the latest key version is used. -- Required: No -- Type: string -- Default: `''` - -### Parameter: `geoBackupCMKUserAssignedIdentityResourceId` - -Geo backup user identity resource ID as identity cant cross region, need identity in same region as geo backup. The identity should have key usage permissions on the Key Vault Key. Required if "cMKKeyName" is not empty and geoRedundantBackup is "Enabled". -- Required: No -- Type: string -- Default: `''` - ### Parameter: `geoRedundantBackup` A value indicating whether Geo-Redundant backup is enabled on the server. If "Enabled" and "cMKKeyName" is not empty, then "geoBackupCMKKeyVaultResourceId" and "cMKUserAssignedIdentityResourceId" are also required. @@ -956,7 +978,7 @@ Properties for the maintenence window. If provided, "customWindow" property must ### Parameter: `managedIdentities` -The managed identity definition for this resource. Required if 'cMKKeyName' is not empty. +The managed identity definition for this resource. Required if 'customerManagedKey' is not empty. - Required: No - Type: object diff --git a/modules/db-for-my-sql/flexible-server/main.bicep b/modules/db-for-my-sql/flexible-server/main.bicep index 110a710c26..26fabc722a 100644 --- a/modules/db-for-my-sql/flexible-server/main.bicep +++ b/modules/db-for-my-sql/flexible-server/main.bicep @@ -65,32 +65,14 @@ param geoRedundantBackup string = 'Disabled' @description('Optional. The mode to create a new MySQL server.') param createMode string = 'Default' -@description('Conditional. The managed identity definition for this resource. Required if \'cMKKeyName\' is not empty.') +@description('Conditional. The managed identity definition for this resource. Required if \'customerManagedKey\' is not empty.') param managedIdentities managedIdentitiesType -@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if "cMKKeyName" is not empty.') -param cMKKeyVaultResourceId string = '' +@description('Optional. The customer managed key definition to use for the managed service.') +param customerManagedKey customerManagedKeyType -@description('Optional. The name of the customer managed key to use for encryption.') -param cMKKeyName string = '' - -@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') -param cMKKeyVersion string = '' - -@description('Conditional. User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if "cMKKeyName" is not empty.') -param cMKUserAssignedIdentityResourceId string = '' - -@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if "cMKKeyName" is not empty and geoRedundantBackup is "Enabled".') -param geoBackupCMKKeyVaultResourceId string = '' - -@description('Optional. The name of the customer managed key to use for encryption when geoRedundantBackup is "Enabled".') -param geoBackupCMKKeyName string = '' - -@description('Optional. The version of the customer managed key to reference for encryption when geoRedundantBackup is "Enabled". If not provided, the latest key version is used.') -param geoBackupCMKKeyVersion string = '' - -@description('Conditional. Geo backup user identity resource ID as identity cant cross region, need identity in same region as geo backup. The identity should have key usage permissions on the Key Vault Key. Required if "cMKKeyName" is not empty and geoRedundantBackup is "Enabled".') -param geoBackupCMKUserAssignedIdentityResourceId string = '' +@description('Optional. The customer managed key definition to use when geoRedundantBackup is "Enabled".') +param customerManagedKeyGeo customerManagedKeyType @allowed([ 'Disabled' @@ -210,24 +192,34 @@ resource defaultTelemetry 'Microsoft.Resources/deployments@2022-09-01' = if (ena } } -resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { - name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) - resource cMKKey 'keys@2023-02-01' existing = if (!empty(cMKKeyName)) { - name: !empty(cMKKeyName) ? cMKKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' } } -resource geoBackupCMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(geoBackupCMKKeyVaultResourceId)) { - name: last(split((!empty(geoBackupCMKKeyVaultResourceId) ? geoBackupCMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(geoBackupCMKKeyVaultResourceId) ? geoBackupCMKKeyVaultResourceId : '//'), '/')[2], split((!empty(geoBackupCMKKeyVaultResourceId) ? geoBackupCMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) +} + +resource cMKGeoKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKeyGeo.?keyVaultResourceId)) { + name: last(split((customerManagedKeyGeo.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKeyGeo.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKeyGeo.?keyVaultResourceId ?? '////'), '/')[4]) - resource geoBackupCMKKey 'keys@2023-02-01' existing = if (!empty(geoBackupCMKKeyName)) { - name: !empty(geoBackupCMKKeyName) ? geoBackupCMKKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKeyGeo.?keyVaultResourceId) && !empty(customerManagedKeyGeo.?keyName)) { + name: customerManagedKeyGeo.?keyName ?? 'dummyKey' } } +resource cMKGeoUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKeyGeo.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKeyGeo.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKeyGeo.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKeyGeo.?userAssignedIdentityResourceId ?? '////'), '/')[4]) +} + resource flexibleServer 'Microsoft.DBforMySQL/flexibleServers@2022-09-30-preview' = { name: name location: location @@ -246,12 +238,12 @@ resource flexibleServer 'Microsoft.DBforMySQL/flexibleServers@2022-09-30-preview geoRedundantBackup: geoRedundantBackup } createMode: createMode - dataEncryption: !empty(cMKKeyName) ? { + dataEncryption: !empty(customerManagedKey) ? { type: 'AzureKeyVault' - geoBackupKeyURI: geoRedundantBackup == 'Enabled' ? (!empty(geoBackupCMKKeyVersion) ? '${geoBackupCMKKeyVault::geoBackupCMKKey.properties.keyUri}/${geoBackupCMKKeyVersion}' : geoBackupCMKKeyVault::geoBackupCMKKey.properties.keyUriWithVersion) : null - geoBackupUserAssignedIdentityId: geoRedundantBackup == 'Enabled' ? geoBackupCMKUserAssignedIdentityResourceId : null - primaryKeyURI: !empty(cMKKeyVersion) ? '${cMKKeyVault::cMKKey.properties.keyUri}/${cMKKeyVersion}' : cMKKeyVault::cMKKey.properties.keyUriWithVersion - primaryUserAssignedIdentityId: cMKUserAssignedIdentityResourceId + geoBackupKeyURI: geoRedundantBackup == 'Enabled' ? (!empty(customerManagedKeyGeo.?keyVersion ?? '') ? '${cMKGeoKeyVault::cMKKey.properties.keyUri}/${customerManagedKeyGeo!.keyVersion}' : cMKGeoKeyVault::cMKKey.properties.keyUriWithVersion) : null + geoBackupUserAssignedIdentityId: geoRedundantBackup == 'Enabled' ? cMKGeoUserAssignedIdentity.id : null + primaryKeyURI: !empty(customerManagedKey.?keyVersion ?? '') ? '${cMKKeyVault::cMKKey.properties.keyUri}/${customerManagedKey!.keyVersion}' : cMKKeyVault::cMKKey.properties.keyUriWithVersion + primaryUserAssignedIdentityId: cMKUserAssignedIdentity.id } : null highAvailability: { mode: highAvailability @@ -451,3 +443,17 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Required. User assigned identity to use when fetching the customer managed key.') + userAssignedIdentityResourceId: string +}? diff --git a/modules/db-for-my-sql/flexible-server/main.json b/modules/db-for-my-sql/flexible-server/main.json index 45a154ff53..db1a78328e 100644 --- a/modules/db-for-my-sql/flexible-server/main.json +++ b/modules/db-for-my-sql/flexible-server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "2940458480347427239" + "templateHash": "6288349663504591009" }, "name": "DBforMySQL Flexible Servers", "description": "This module deploys a DBforMySQL Flexible Server.", @@ -224,6 +224,37 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "metadata": { + "description": "Required. User assigned identity to use when fetching the customer managed key." + } + } + }, + "nullable": true } }, "parameters": { @@ -340,63 +371,19 @@ "managedIdentities": { "$ref": "#/definitions/managedIdentitiesType", "metadata": { - "description": "Conditional. The managed identity definition for this resource. Required if 'cMKKeyName' is not empty." - } - }, - "cMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \"cMKKeyName\" is not empty." - } - }, - "cMKKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption." - } - }, - "cMKKeyVersion": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." - } - }, - "cMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if \"cMKKeyName\" is not empty." + "description": "Conditional. The managed identity definition for this resource. Required if 'customerManagedKey' is not empty." } }, - "geoBackupCMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \"cMKKeyName\" is not empty and geoRedundantBackup is \"Enabled\"." + "description": "Optional. The customer managed key definition to use for the managed service." } }, - "geoBackupCMKKeyName": { - "type": "string", - "defaultValue": "", + "customerManagedKeyGeo": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption when geoRedundantBackup is \"Enabled\"." - } - }, - "geoBackupCMKKeyVersion": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption when geoRedundantBackup is \"Enabled\". If not provided, the latest key version is used." - } - }, - "geoBackupCMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. Geo backup user identity resource ID as identity cant cross region, need identity in same region as geo backup. The identity should have key usage permissions on the Key Vault Key. Required if \"cMKKeyName\" is not empty and geoRedundantBackup is \"Enabled\"." + "description": "Optional. The customer managed key definition to use when geoRedundantBackup is \"Enabled\"." } }, "highAvailability": { @@ -569,27 +556,27 @@ }, "resources": { "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(parameters('cMKKeyVaultResourceId'))), not(empty(parameters('cMKKeyName'))))]", + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKKeyName'))), parameters('cMKKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", "dependsOn": [ "cMKKeyVault" ] }, - "geoBackupCMKKeyVault::geoBackupCMKKey": { - "condition": "[and(not(empty(parameters('geoBackupCMKKeyVaultResourceId'))), not(empty(parameters('geoBackupCMKKeyName'))))]", + "cMKGeoKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKeyGeo'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKeyGeo'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKeyGeo'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('geoBackupCMKKeyVaultResourceId'))), parameters('geoBackupCMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('geoBackupCMKKeyVaultResourceId'))), parameters('geoBackupCMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('geoBackupCMKKeyVaultResourceId'))), parameters('geoBackupCMKKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('geoBackupCMKKeyName'))), parameters('geoBackupCMKKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKeyGeo'), 'keyName'), 'dummyKey'))]", "dependsOn": [ - "geoBackupCMKKeyVault" + "cMKGeoKeyVault" ] }, "defaultTelemetry": { @@ -607,22 +594,40 @@ } }, "cMKKeyVault": { - "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, - "geoBackupCMKKeyVault": { - "condition": "[not(empty(parameters('geoBackupCMKKeyVaultResourceId')))]", + "cMKGeoKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKeyGeo'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('geoBackupCMKKeyVaultResourceId'))), parameters('geoBackupCMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('geoBackupCMKKeyVaultResourceId'))), parameters('geoBackupCMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('geoBackupCMKKeyVaultResourceId'))), parameters('geoBackupCMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKGeoUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKeyGeo'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, "flexibleServer": { "type": "Microsoft.DBforMySQL/flexibleServers", @@ -644,7 +649,7 @@ "geoRedundantBackup": "[parameters('geoRedundantBackup')]" }, "createMode": "[parameters('createMode')]", - "dataEncryption": "[if(not(empty(parameters('cMKKeyName'))), createObject('type', 'AzureKeyVault', 'geoBackupKeyURI', if(equals(parameters('geoRedundantBackup'), 'Enabled'), if(not(empty(parameters('geoBackupCMKKeyVersion'))), format('{0}/{1}', reference('geoBackupCMKKeyVault::geoBackupCMKKey').keyUri, parameters('geoBackupCMKKeyVersion')), reference('geoBackupCMKKeyVault::geoBackupCMKKey').keyUriWithVersion), null()), 'geoBackupUserAssignedIdentityId', if(equals(parameters('geoRedundantBackup'), 'Enabled'), parameters('geoBackupCMKUserAssignedIdentityResourceId'), null()), 'primaryKeyURI', if(not(empty(parameters('cMKKeyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('cMKKeyVersion')), reference('cMKKeyVault::cMKKey').keyUriWithVersion), 'primaryUserAssignedIdentityId', parameters('cMKUserAssignedIdentityResourceId')), null())]", + "dataEncryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('type', 'AzureKeyVault', 'geoBackupKeyURI', if(equals(parameters('geoRedundantBackup'), 'Enabled'), if(not(empty(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKGeoKeyVault::cMKKey').keyUri, parameters('customerManagedKeyGeo').keyVersion), reference('cMKGeoKeyVault::cMKKey').keyUriWithVersion), null()), 'geoBackupUserAssignedIdentityId', if(equals(parameters('geoRedundantBackup'), 'Enabled'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))), null()), 'primaryKeyURI', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion), 'primaryUserAssignedIdentityId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null())]", "highAvailability": { "mode": "[parameters('highAvailability')]", "standbyAvailabilityZone": "[if(equals(parameters('highAvailability'), 'SameZone'), parameters('availabilityZone'), null())]" @@ -663,8 +668,10 @@ "version": "[parameters('version')]" }, "dependsOn": [ + "cMKGeoKeyVault", + "cMKGeoUserAssignedIdentity", "cMKKeyVault", - "geoBackupCMKKeyVault" + "cMKUserAssignedIdentity" ] }, "flexibleServer_lock": { diff --git a/modules/db-for-postgre-sql/flexible-server/.test/public/main.test.bicep b/modules/db-for-postgre-sql/flexible-server/.test/public/main.test.bicep index 86320c6f6d..3cbc9ecdbc 100644 --- a/modules/db-for-postgre-sql/flexible-server/.test/public/main.test.bicep +++ b/modules/db-for-postgre-sql/flexible-server/.test/public/main.test.bicep @@ -132,9 +132,11 @@ module testDeployment '../../main.bicep' = { location: location storageSizeGB: 1024 version: '14' - cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKKeyName: nestedDependencies.outputs.keyName - cMKUserAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + customerManagedKey: { + keyName: nestedDependencies.outputs.keyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } managedIdentities: { userAssignedResourcesIds: [ nestedDependencies.outputs.managedIdentityResourceId diff --git a/modules/db-for-postgre-sql/flexible-server/README.md b/modules/db-for-postgre-sql/flexible-server/README.md index 157b30d978..8c9700bf38 100644 --- a/modules/db-for-postgre-sql/flexible-server/README.md +++ b/modules/db-for-postgre-sql/flexible-server/README.md @@ -286,9 +286,6 @@ module flexibleServer 'br:bicep/modules/db-for-postgre-sql.flexible-server:1.0.0 ] availabilityZone: '1' backupRetentionDays: 20 - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUserAssignedIdentityResourceId: '' configurations: [ { name: 'log_min_messages' @@ -296,6 +293,11 @@ module flexibleServer 'br:bicep/modules/db-for-postgre-sql.flexible-server:1.0.0 value: 'INFO' } ] + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } databases: [ { charset: 'UTF8' @@ -395,15 +397,6 @@ module flexibleServer 'br:bicep/modules/db-for-postgre-sql.flexible-server:1.0.0 "backupRetentionDays": { "value": 20 }, - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" - }, - "cMKUserAssignedIdentityResourceId": { - "value": "" - }, "configurations": { "value": [ { @@ -413,6 +406,13 @@ module flexibleServer 'br:bicep/modules/db-for-postgre-sql.flexible-server:1.0.0 } ] }, + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } + }, "databases": { "value": [ { @@ -514,8 +514,6 @@ module flexibleServer 'br:bicep/modules/db-for-postgre-sql.flexible-server:1.0.0 | Parameter | Type | Description | | :-- | :-- | :-- | -| [`cMKKeyVaultResourceId`](#parameter-cmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. | -| [`cMKUserAssignedIdentityResourceId`](#parameter-cmkuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if 'cMKKeyName' is not empty. | | [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. Required if 'cMKKeyName' is not empty. | | [`pointInTimeUTC`](#parameter-pointintimeutc) | string | Required if "createMode" is set to "PointInTimeRestore". | | [`sourceServerResourceId`](#parameter-sourceserverresourceid) | string | Required if "createMode" is set to "PointInTimeRestore". | @@ -530,10 +528,9 @@ module flexibleServer 'br:bicep/modules/db-for-postgre-sql.flexible-server:1.0.0 | [`administrators`](#parameter-administrators) | array | The Azure AD administrators when AAD authentication enabled. | | [`availabilityZone`](#parameter-availabilityzone) | string | Availability zone information of the server. Default will have no preference set. | | [`backupRetentionDays`](#parameter-backupretentiondays) | int | Backup retention days for the server. | -| [`cMKKeyName`](#parameter-cmkkeyname) | string | The name of the customer managed key to use for encryption. | -| [`cMKKeyVersion`](#parameter-cmkkeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | | [`configurations`](#parameter-configurations) | array | The configurations to create in the server. | | [`createMode`](#parameter-createmode) | string | The mode to create a new PostgreSQL server. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`databases`](#parameter-databases) | array | The databases to create in the server. | | [`delegatedSubnetResourceId`](#parameter-delegatedsubnetresourceid) | string | Delegated subnet arm resource ID. Used when the desired connectivity mode is "Private Access" - virtual network integration. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | @@ -610,34 +607,6 @@ Backup retention days for the server. - Type: int - Default: `7` -### Parameter: `cMKKeyName` - -The name of the customer managed key to use for encryption. -- Required: No -- Type: string -- Default: `''` - -### Parameter: `cMKKeyVaultResourceId` - -The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. -- Required: No -- Type: string -- Default: `''` - -### Parameter: `cMKKeyVersion` - -The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. -- Required: No -- Type: string -- Default: `''` - -### Parameter: `cMKUserAssignedIdentityResourceId` - -User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if 'cMKKeyName' is not empty. -- Required: No -- Type: string -- Default: `''` - ### Parameter: `configurations` The configurations to create in the server. @@ -661,6 +630,48 @@ The mode to create a new PostgreSQL server. ] ``` +### Parameter: `customerManagedKey` + +The customer managed key definition. +- Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | Yes | string | Required. User assigned identity to use when fetching the customer managed key. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes +- Type: string + +### Parameter: `customerManagedKey.keyVaultResourceId` + +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes +- Type: string + +### Parameter: `customerManagedKey.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. + +- Required: No +- Type: string + +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +Required. User assigned identity to use when fetching the customer managed key. + +- Required: Yes +- Type: string + ### Parameter: `databases` The databases to create in the server. diff --git a/modules/db-for-postgre-sql/flexible-server/main.bicep b/modules/db-for-postgre-sql/flexible-server/main.bicep index 84bb983ea8..e8457897dd 100644 --- a/modules/db-for-postgre-sql/flexible-server/main.bicep +++ b/modules/db-for-postgre-sql/flexible-server/main.bicep @@ -113,17 +113,8 @@ param createMode string = 'Default' @description('Conditional. The managed identity definition for this resource. Required if \'cMKKeyName\' is not empty.') param managedIdentities managedIdentitiesType -@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \'cMKKeyName\' is not empty.') -param cMKKeyVaultResourceId string = '' - -@description('Optional. The name of the customer managed key to use for encryption.') -param cMKKeyName string = '' - -@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') -param cMKKeyVersion string = '' - -@description('Conditional. User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if \'cMKKeyName\' is not empty.') -param cMKUserAssignedIdentityResourceId string = '' +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType @description('Optional. Properties for the maintenence window. If provided, "customWindow" property must exist and set to "Enabled".') param maintenanceWindow object = {} @@ -193,15 +184,20 @@ resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (ena } } -resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { - name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) - resource cMKKey 'keys@2023-02-01' existing = if (!empty(cMKKeyName)) { - name: !empty(cMKKeyName) ? cMKKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' } } +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) +} + resource flexibleServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { name: name location: location @@ -225,9 +221,9 @@ resource flexibleServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = geoRedundantBackup: geoRedundantBackup } createMode: createMode - dataEncryption: !empty(cMKKeyName) ? { - primaryKeyURI: !empty(cMKKeyVersion) ? '${cMKKeyVault::cMKKey.properties.keyUri}/${cMKKeyVersion}' : cMKKeyVault::cMKKey.properties.keyUriWithVersion - primaryUserAssignedIdentityId: cMKUserAssignedIdentityResourceId + dataEncryption: !empty(customerManagedKey) ? { + primaryKeyURI: !empty(customerManagedKey.?keyVersion ?? '') ? '${cMKKeyVault::cMKKey.properties.keyUri}/${customerManagedKey!.keyVersion}' : cMKKeyVault::cMKKey.properties.keyUriWithVersion + primaryUserAssignedIdentityId: cMKUserAssignedIdentity.id type: 'AzureKeyVault' } : null highAvailability: { @@ -442,3 +438,17 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Required. User assigned identity to use when fetching the customer managed key.') + userAssignedIdentityResourceId: string +}? diff --git a/modules/db-for-postgre-sql/flexible-server/main.json b/modules/db-for-postgre-sql/flexible-server/main.json index 74d5498241..f6629db5f8 100644 --- a/modules/db-for-postgre-sql/flexible-server/main.json +++ b/modules/db-for-postgre-sql/flexible-server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "2281015287111582702" + "templateHash": "4208024557828977061" }, "name": "DBforPostgreSQL Flexible Servers", "description": "This module deploys a DBforPostgreSQL Flexible Server.", @@ -224,6 +224,37 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "metadata": { + "description": "Required. User assigned identity to use when fetching the customer managed key." + } + } + }, + "nullable": true } }, "parameters": { @@ -404,32 +435,10 @@ "description": "Conditional. The managed identity definition for this resource. Required if 'cMKKeyName' is not empty." } }, - "cMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty." - } - }, - "cMKKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption." - } - }, - "cMKKeyVersion": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." - } - }, - "cMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if 'cMKKeyName' is not empty." + "description": "Optional. The customer managed key definition." } }, "maintenanceWindow": { @@ -535,13 +544,13 @@ }, "resources": { "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(parameters('cMKKeyVaultResourceId'))), not(empty(parameters('cMKKeyName'))))]", + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKKeyName'))), parameters('cMKKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", "dependsOn": [ "cMKKeyVault" ] @@ -561,13 +570,22 @@ } }, "cMKKeyVault": { - "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, "flexibleServer": { "type": "Microsoft.DBforPostgreSQL/flexibleServers", @@ -594,7 +612,7 @@ "geoRedundantBackup": "[parameters('geoRedundantBackup')]" }, "createMode": "[parameters('createMode')]", - "dataEncryption": "[if(not(empty(parameters('cMKKeyName'))), createObject('primaryKeyURI', if(not(empty(parameters('cMKKeyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('cMKKeyVersion')), reference('cMKKeyVault::cMKKey').keyUriWithVersion), 'primaryUserAssignedIdentityId', parameters('cMKUserAssignedIdentityResourceId'), 'type', 'AzureKeyVault'), null())]", + "dataEncryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('primaryKeyURI', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion), 'primaryUserAssignedIdentityId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))), 'type', 'AzureKeyVault'), null())]", "highAvailability": { "mode": "[parameters('highAvailability')]", "standbyAvailabilityZone": "[if(equals(parameters('highAvailability'), 'SameZone'), parameters('availabilityZone'), null())]" @@ -609,7 +627,8 @@ "version": "[parameters('version')]" }, "dependsOn": [ - "cMKKeyVault" + "cMKKeyVault", + "cMKUserAssignedIdentity" ] }, "flexibleServer_lock": { diff --git a/modules/event-hub/namespace/.test/encr/main.test.bicep b/modules/event-hub/namespace/.test/encr/main.test.bicep index ce45fd552e..b81e59b56c 100644 --- a/modules/event-hub/namespace/.test/encr/main.test.bicep +++ b/modules/event-hub/namespace/.test/encr/main.test.bicep @@ -68,9 +68,11 @@ module testDeployment '../../main.bicep' = { nestedDependencies.outputs.managedIdentityResourceId ] } - cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKKeyName: nestedDependencies.outputs.keyName - cMKUserAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + customerManagedKey: { + keyName: nestedDependencies.outputs.keyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } requireInfrastructureEncryption: true } } diff --git a/modules/event-hub/namespace/README.md b/modules/event-hub/namespace/README.md index b231ea9619..1329fdd23e 100644 --- a/modules/event-hub/namespace/README.md +++ b/modules/event-hub/namespace/README.md @@ -449,9 +449,11 @@ module namespace 'br:bicep/modules/event-hub.namespace:1.0.0' = { // Required parameters name: 'ehnenc001' // Non-required parameters - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUserAssignedIdentityResourceId: '' + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } enableDefaultTelemetry: '' managedIdentities: { systemAssigned: false @@ -488,14 +490,12 @@ module namespace 'br:bicep/modules/event-hub.namespace:1.0.0' = { "value": "ehnenc001" }, // Non-required parameters - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" - }, - "cMKUserAssignedIdentityResourceId": { - "value": "" + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } }, "enableDefaultTelemetry": { "value": "" @@ -685,20 +685,12 @@ module namespace 'br:bicep/modules/event-hub.namespace:1.0.0' = { | :-- | :-- | :-- | | [`name`](#parameter-name) | string | The name of the event hub namespace. | -**Conditional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`cMKKeyVaultResourceId`](#parameter-cmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if "cMKKeyName" is not empty. | -| [`cMKUserAssignedIdentityResourceId`](#parameter-cmkuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if "cMKKeyName" is not empty. | - **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | | [`authorizationRules`](#parameter-authorizationrules) | array | Authorization Rules for the Event Hub namespace. | -| [`cMKKeyName`](#parameter-cmkkeyname) | string | The name of the customer managed key to use for encryption. Customer-managed key encryption at rest is only available for namespaces of premium SKU or namespaces created in a Dedicated Cluster. | -| [`cMKKeyVersion`](#parameter-cmkkeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`disableLocalAuth`](#parameter-disablelocalauth) | bool | This property disables SAS authentication for the Event Hubs namespace. | | [`disasterRecoveryConfig`](#parameter-disasterrecoveryconfig) | object | The disaster recovery config for this namespace. | @@ -740,33 +732,47 @@ Authorization Rules for the Event Hub namespace. ] ``` -### Parameter: `cMKKeyName` +### Parameter: `customerManagedKey` -The name of the customer managed key to use for encryption. Customer-managed key encryption at rest is only available for namespaces of premium SKU or namespaces created in a Dedicated Cluster. +The customer managed key definition. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | No | string | Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVaultResourceId` +### Parameter: `customerManagedKey.keyVaultResourceId` -The resource ID of a key vault to reference a customer managed key for encryption from. Required if "cMKKeyName" is not empty. -- Required: No +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVersion` +### Parameter: `customerManagedKey.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. - Required: No - Type: string -- Default: `''` -### Parameter: `cMKUserAssignedIdentityResourceId` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. -User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if "cMKKeyName" is not empty. - Required: No - Type: string -- Default: `''` ### Parameter: `diagnosticSettings` diff --git a/modules/event-hub/namespace/main.bicep b/modules/event-hub/namespace/main.bicep index bc4eb48806..03215a757f 100644 --- a/modules/event-hub/namespace/main.bicep +++ b/modules/event-hub/namespace/main.bicep @@ -83,17 +83,8 @@ param lock lockType @description('Optional. The managed identity definition for this resource.') param managedIdentities managedIdentitiesType -@description('Optional. The name of the customer managed key to use for encryption. Customer-managed key encryption at rest is only available for namespaces of premium SKU or namespaces created in a Dedicated Cluster.') -param cMKKeyName string = '' - -@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if "cMKKeyName" is not empty.') -param cMKKeyVaultResourceId string = '' - -@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') -param cMKKeyVersion string = '' - -@description('Conditional. User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if "cMKKeyName" is not empty.') -param cMKUserAssignedIdentityResourceId string = '' +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType @description('Optional. Enable infrastructure encryption (double encryption). Note, this setting requires the configuration of Customer-Managed-Keys (CMK) via the corresponding module parameters.') param requireInfrastructureEncryption bool = false @@ -135,15 +126,20 @@ var builtInRoleNames = { 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') } -resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { - name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) - resource cMKKey 'keys@2023-02-01' existing = if (!empty(cMKKeyName)) { - name: !empty(cMKKeyName) ? cMKKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' } } +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) +} + resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' properties: { @@ -168,16 +164,16 @@ resource eventHubNamespace 'Microsoft.EventHub/namespaces@2022-10-01-preview' = } properties: { disableLocalAuth: disableLocalAuth - encryption: !empty(cMKKeyName) ? { + encryption: !empty(customerManagedKey) ? { keySource: 'Microsoft.KeyVault' keyVaultProperties: [ { - identity: !empty(cMKUserAssignedIdentityResourceId) ? { - userAssignedIdentity: cMKUserAssignedIdentityResourceId + identity: !empty(customerManagedKey.?userAssignedIdentityResourceId) ? { + userAssignedIdentity: cMKUserAssignedIdentity.id } : null - keyName: cMKKeyName + keyName: customerManagedKey!.keyName keyVaultUri: cMKKeyVault.properties.vaultUri - keyVersion: !empty(cMKKeyVersion) ? cMKKeyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) + keyVersion: !empty(customerManagedKey.?keyVersion ?? '') ? customerManagedKey!.keyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) } ] requireInfrastructureEncryption: requireInfrastructureEncryption @@ -497,3 +493,17 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? +}? diff --git a/modules/event-hub/namespace/main.json b/modules/event-hub/namespace/main.json index eebb91f004..77fb4e08c5 100644 --- a/modules/event-hub/namespace/main.json +++ b/modules/event-hub/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "6601963948564613336" + "templateHash": "14574780137698539874" }, "name": "Event Hub Namespaces", "description": "This module deploys an Event Hub Namespace.", @@ -407,6 +407,38 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true } }, "parameters": { @@ -554,32 +586,10 @@ "description": "Optional. The managed identity definition for this resource." } }, - "cMKKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption. Customer-managed key encryption at rest is only available for namespaces of premium SKU or namespaces created in a Dedicated Cluster." - } - }, - "cMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \"cMKKeyName\" is not empty." - } - }, - "cMKKeyVersion": { - "type": "string", - "defaultValue": "", + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." - } - }, - "cMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. User assigned identity to use when fetching the customer managed key. The identity should have key usage permissions on the Key Vault Key. Required if \"cMKKeyName\" is not empty." + "description": "Optional. The customer managed key definition." } }, "requireInfrastructureEncryption": { @@ -642,25 +652,34 @@ }, "resources": { "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(parameters('cMKKeyVaultResourceId'))), not(empty(parameters('cMKKeyName'))))]", + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKKeyName'))), parameters('cMKKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", "dependsOn": [ "cMKKeyVault" ] }, "cMKKeyVault": { - "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, "defaultTelemetry": { "condition": "[parameters('enableDefaultTelemetry')]", @@ -690,7 +709,7 @@ }, "properties": { "disableLocalAuth": "[parameters('disableLocalAuth')]", - "encryption": "[if(not(empty(parameters('cMKKeyName'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createArray(createObject('identity', if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), createObject('userAssignedIdentity', parameters('cMKUserAssignedIdentityResourceId')), null()), 'keyName', parameters('cMKKeyName'), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyVersion', if(not(empty(parameters('cMKKeyVersion'))), parameters('cMKKeyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), 'requireInfrastructureEncryption', parameters('requireInfrastructureEncryption')), null())]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createArray(createObject('identity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null()), 'keyName', parameters('customerManagedKey').keyName, 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), 'requireInfrastructureEncryption', parameters('requireInfrastructureEncryption')), null())]", "isAutoInflateEnabled": "[parameters('isAutoInflateEnabled')]", "kafkaEnabled": "[parameters('kafkaEnabled')]", "maximumThroughputUnits": "[variables('maximumThroughputUnitsVar')]", @@ -699,7 +718,8 @@ "zoneRedundant": "[parameters('zoneRedundant')]" }, "dependsOn": [ - "cMKKeyVault" + "cMKKeyVault", + "cMKUserAssignedIdentity" ] }, "eventHubNamespace_roleAssignments": { diff --git a/modules/machine-learning-services/workspace/.test/encr/main.test.bicep b/modules/machine-learning-services/workspace/.test/encr/main.test.bicep index 495c4a1b1e..784f07e453 100644 --- a/modules/machine-learning-services/workspace/.test/encr/main.test.bicep +++ b/modules/machine-learning-services/workspace/.test/encr/main.test.bicep @@ -61,9 +61,11 @@ module testDeployment '../../main.bicep' = { associatedKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId associatedStorageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId sku: 'Basic' - cMKKeyName: nestedDependencies.outputs.keyVaultEncryptionKeyName - cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKUserAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultEncryptionKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } primaryUserAssignedIdentity: nestedDependencies.outputs.managedIdentityResourceId privateEndpoints: [ { diff --git a/modules/machine-learning-services/workspace/README.md b/modules/machine-learning-services/workspace/README.md index 3dc1a08e10..c52e09855d 100644 --- a/modules/machine-learning-services/workspace/README.md +++ b/modules/machine-learning-services/workspace/README.md @@ -304,9 +304,11 @@ module workspace 'br:bicep/modules/machine-learning-services.workspace:1.0.0' = name: 'mlswecr001' sku: 'Basic' // Non-required parameters - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUserAssignedIdentityResourceId: '' + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } enableDefaultTelemetry: '' managedIdentities: { systemAssigned: false @@ -367,14 +369,12 @@ module workspace 'br:bicep/modules/machine-learning-services.workspace:1.0.0' = "value": "Basic" }, // Non-required parameters - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" - }, - "cMKUserAssignedIdentityResourceId": { - "value": "" + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } }, "enableDefaultTelemetry": { "value": "" @@ -509,7 +509,6 @@ module workspace 'br:bicep/modules/machine-learning-services.workspace:1.0.0' = | Parameter | Type | Description | | :-- | :-- | :-- | -| [`cMKKeyVaultResourceId`](#parameter-cmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. | | [`primaryUserAssignedIdentity`](#parameter-primaryuserassignedidentity) | string | The user assigned identity resource ID that represents the workspace identity. Required if 'userAssignedIdentities' is not empty and may not be used if 'systemAssignedIdentity' is enabled. | **Optional parameters** @@ -518,10 +517,8 @@ module workspace 'br:bicep/modules/machine-learning-services.workspace:1.0.0' = | :-- | :-- | :-- | | [`allowPublicAccessWhenBehindVnet`](#parameter-allowpublicaccesswhenbehindvnet) | bool | The flag to indicate whether to allow public access when behind VNet. | | [`associatedContainerRegistryResourceId`](#parameter-associatedcontainerregistryresourceid) | string | The resource ID of the associated Container Registry. | -| [`cMKKeyName`](#parameter-cmkkeyname) | string | The name of the customer managed key to use for encryption. | -| [`cMKKeyVersion`](#parameter-cmkkeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | -| [`cMKUserAssignedIdentityResourceId`](#parameter-cmkuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first. | | [`computes`](#parameter-computes) | array | Computes to create respectively attach to the workspace. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`description`](#parameter-description) | string | The description of this workspace. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`discoveryUrl`](#parameter-discoveryurl) | string | URL for the discovery service to identify regional endpoints for machine learning experimentation services. | @@ -570,40 +567,54 @@ The resource ID of the associated Storage Account. - Required: Yes - Type: string -### Parameter: `cMKKeyName` +### Parameter: `computes` -The name of the customer managed key to use for encryption. +Computes to create respectively attach to the workspace. - Required: No -- Type: string -- Default: `''` +- Type: array +- Default: `[]` -### Parameter: `cMKKeyVaultResourceId` +### Parameter: `customerManagedKey` -The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. +The customer managed key definition. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | No | string | Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVersion` +### Parameter: `customerManagedKey.keyVaultResourceId` -The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. -- Required: No +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKUserAssignedIdentityResourceId` +### Parameter: `customerManagedKey.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first. - Required: No - Type: string -- Default: `''` -### Parameter: `computes` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. -Computes to create respectively attach to the workspace. - Required: No -- Type: array -- Default: `[]` +- Type: string ### Parameter: `description` diff --git a/modules/machine-learning-services/workspace/main.bicep b/modules/machine-learning-services/workspace/main.bicep index 61a0422c1d..8225693123 100644 --- a/modules/machine-learning-services/workspace/main.bicep +++ b/modules/machine-learning-services/workspace/main.bicep @@ -72,17 +72,8 @@ param description string = '' @sys.description('Optional. URL for the discovery service to identify regional endpoints for machine learning experimentation services.') param discoveryUrl string = '' -@sys.description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \'cMKKeyName\' is not empty.') -param cMKKeyVaultResourceId string = '' - -@sys.description('Optional. The name of the customer managed key to use for encryption.') -param cMKKeyName string = '' - -@sys.description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') -param cMKKeyVersion string = '' - -@sys.description('Optional. User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first.') -param cMKUserAssignedIdentityResourceId string = '' +@sys.description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType @sys.description('Optional. The compute name for image build.') param imageBuildCompute string = '' @@ -143,15 +134,20 @@ resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (ena } } -resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { - name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) - resource cMKKey 'keys@2023-02-01' existing = if (!empty(cMKKeyName)) { - name: !empty(cMKKeyName) ? cMKKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' } } +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) +} + resource workspace 'Microsoft.MachineLearningServices/workspaces@2022-10-01' = { name: name location: location @@ -171,14 +167,14 @@ resource workspace 'Microsoft.MachineLearningServices/workspaces@2022-10-01' = { allowPublicAccessWhenBehindVnet: allowPublicAccessWhenBehindVnet description: description discoveryUrl: discoveryUrl - encryption: !empty(cMKKeyName) ? { + encryption: !empty(customerManagedKey) ? { status: 'Enabled' - identity: !empty(cMKUserAssignedIdentityResourceId) ? { - userAssignedIdentity: cMKUserAssignedIdentityResourceId + identity: !empty(customerManagedKey.?userAssignedIdentityResourceId) ? { + userAssignedIdentity: cMKUserAssignedIdentity.id } : null keyVaultProperties: { - keyVaultArmId: cMKKeyVaultResourceId - keyIdentifier: !empty(cMKKeyVersion) ? '${cMKKeyVault::cMKKey.properties.keyUri}/${cMKKeyVersion}' : cMKKeyVault::cMKKey.properties.keyUriWithVersion + keyVaultArmId: cMKKeyVault.id + keyIdentifier: !empty(customerManagedKey.?keyVersion ?? '') ? '${cMKKeyVault::cMKKey.properties.keyUri}/${customerManagedKey!.keyVersion}' : cMKKeyVault::cMKKey.properties.keyUriWithVersion } } : null imageBuildCompute: imageBuildCompute @@ -440,3 +436,17 @@ type diagnosticSettingType = { @sys.description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type customerManagedKeyType = { + @sys.description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @sys.description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @sys.description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @sys.description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? +}? diff --git a/modules/machine-learning-services/workspace/main.json b/modules/machine-learning-services/workspace/main.json index 03013e4d23..d31ece6308 100644 --- a/modules/machine-learning-services/workspace/main.json +++ b/modules/machine-learning-services/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "8299613323505664553" + "templateHash": "308162699302204935" }, "name": "Machine Learning Services Workspaces", "description": "This module deploys a Machine Learning Services Workspace.", @@ -385,6 +385,38 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true } }, "parameters": { @@ -520,32 +552,10 @@ "description": "Optional. URL for the discovery service to identify regional endpoints for machine learning experimentation services." } }, - "cMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty." - } - }, - "cMKKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption." - } - }, - "cMKKeyVersion": { - "type": "string", - "defaultValue": "", + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." - } - }, - "cMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first." + "description": "Optional. The customer managed key definition." } }, "imageBuildCompute": { @@ -607,13 +617,13 @@ }, "resources": { "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(parameters('cMKKeyVaultResourceId'))), not(empty(parameters('cMKKeyName'))))]", + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKKeyName'))), parameters('cMKKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", "dependsOn": [ "cMKKeyVault" ] @@ -633,13 +643,22 @@ } }, "cMKKeyVault": { - "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, "workspace": { "type": "Microsoft.MachineLearningServices/workspaces", @@ -662,7 +681,7 @@ "allowPublicAccessWhenBehindVnet": "[parameters('allowPublicAccessWhenBehindVnet')]", "description": "[parameters('description')]", "discoveryUrl": "[parameters('discoveryUrl')]", - "encryption": "[if(not(empty(parameters('cMKKeyName'))), createObject('status', 'Enabled', 'identity', if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), createObject('userAssignedIdentity', parameters('cMKUserAssignedIdentityResourceId')), null()), 'keyVaultProperties', createObject('keyVaultArmId', parameters('cMKKeyVaultResourceId'), 'keyIdentifier', if(not(empty(parameters('cMKKeyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('cMKKeyVersion')), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null())]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('status', 'Enabled', 'identity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null()), 'keyVaultProperties', createObject('keyVaultArmId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]), 'Microsoft.KeyVault/vaults', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))), 'keyIdentifier', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null())]", "imageBuildCompute": "[parameters('imageBuildCompute')]", "primaryUserAssignedIdentity": "[parameters('primaryUserAssignedIdentity')]", "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(not(empty(parameters('privateEndpoints'))), 'Disabled', 'Enabled'))]", @@ -670,7 +689,8 @@ "sharedPrivateLinkResources": "[parameters('sharedPrivateLinkResources')]" }, "dependsOn": [ - "cMKKeyVault" + "cMKKeyVault", + "cMKUserAssignedIdentity" ] }, "workspace_lock": { diff --git a/modules/service-bus/namespace/.test/encr/main.test.bicep b/modules/service-bus/namespace/.test/encr/main.test.bicep index e1f3da9f89..961376bee9 100644 --- a/modules/service-bus/namespace/.test/encr/main.test.bicep +++ b/modules/service-bus/namespace/.test/encr/main.test.bicep @@ -106,9 +106,11 @@ module testDeployment '../../main.bicep' = { nestedDependencies.outputs.managedIdentityResourceId ] } - cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKKeyName: nestedDependencies.outputs.keyName - cMKUserAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + customerManagedKey: { + keyName: nestedDependencies.outputs.keyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/modules/service-bus/namespace/README.md b/modules/service-bus/namespace/README.md index 76336df69c..bd23507761 100644 --- a/modules/service-bus/namespace/README.md +++ b/modules/service-bus/namespace/README.md @@ -462,9 +462,11 @@ module namespace 'br:bicep/modules/service-bus.namespace:1.0.0' = { ] } ] - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUserAssignedIdentityResourceId: '' + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } enableDefaultTelemetry: '' managedIdentities: { systemAssigned: false @@ -545,14 +547,12 @@ module namespace 'br:bicep/modules/service-bus.namespace:1.0.0' = { } ] }, - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" - }, - "cMKUserAssignedIdentityResourceId": { - "value": "" + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } }, "enableDefaultTelemetry": { "value": "" @@ -763,21 +763,13 @@ module namespace 'br:bicep/modules/service-bus.namespace:1.0.0' = { | :-- | :-- | :-- | | [`name`](#parameter-name) | string | Name of the Service Bus Namespace. | -**Conditional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`cMKKeyVaultResourceId`](#parameter-cmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. | - **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | | [`alternateName`](#parameter-alternatename) | string | Alternate name for namespace. | | [`authorizationRules`](#parameter-authorizationrules) | array | Authorization Rules for the Service Bus namespace. | -| [`cMKKeyName`](#parameter-cmkkeyname) | string | The name of the customer managed key to use for encryption. If not provided, encryption is automatically enabled with a Microsoft-managed key. | -| [`cMKKeyVersion`](#parameter-cmkkeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | -| [`cMKUserAssignedIdentityResourceId`](#parameter-cmkuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`disableLocalAuth`](#parameter-disablelocalauth) | bool | This property disables SAS authentication for the Service Bus namespace. | | [`disasterRecoveryConfigs`](#parameter-disasterrecoveryconfigs) | object | The disaster recovery configuration. | @@ -826,33 +818,47 @@ Authorization Rules for the Service Bus namespace. ] ``` -### Parameter: `cMKKeyName` +### Parameter: `customerManagedKey` -The name of the customer managed key to use for encryption. If not provided, encryption is automatically enabled with a Microsoft-managed key. +The customer managed key definition. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | No | string | Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVaultResourceId` +### Parameter: `customerManagedKey.keyVaultResourceId` -The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. -- Required: No +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVersion` +### Parameter: `customerManagedKey.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. - Required: No - Type: string -- Default: `''` -### Parameter: `cMKUserAssignedIdentityResourceId` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. -User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first. - Required: No - Type: string -- Default: `''` ### Parameter: `diagnosticSettings` diff --git a/modules/service-bus/namespace/main.bicep b/modules/service-bus/namespace/main.bicep index ad86360a7d..612cabf621 100644 --- a/modules/service-bus/namespace/main.bicep +++ b/modules/service-bus/namespace/main.bicep @@ -105,17 +105,8 @@ param queues array = [] @description('Optional. The topics to create in the service bus namespace.') param topics array = [] -@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \'cMKKeyName\' is not empty.') -param cMKKeyVaultResourceId string = '' - -@description('Optional. The name of the customer managed key to use for encryption. If not provided, encryption is automatically enabled with a Microsoft-managed key.') -param cMKKeyName string = '' - -@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') -param cMKKeyVersion string = '' - -@description('Optional. User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first.') -param cMKUserAssignedIdentityResourceId string = '' +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType @description('Optional. Enable infrastructure encryption (double encryption). Note, this setting requires the configuration of Customer-Managed-Keys (CMK) via the corresponding module parameters.') param requireInfrastructureEncryption bool = true @@ -152,15 +143,20 @@ resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (ena } } -resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { - name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) - resource cMKKey 'keys@2023-02-01' existing = if (!empty(cMKKeyName)) { - name: !empty(cMKKeyName) ? cMKKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' } } +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) +} + resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' = { name: name location: location @@ -177,16 +173,16 @@ resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2022-10-01-preview zoneRedundant: zoneRedundant disableLocalAuth: disableLocalAuth premiumMessagingPartitions: skuName == 'Premium' ? premiumMessagingPartitions : 0 - encryption: !empty(cMKKeyName) ? { + encryption: !empty(customerManagedKey) ? { keySource: 'Microsoft.KeyVault' keyVaultProperties: [ { - identity: !empty(cMKUserAssignedIdentityResourceId) ? { - userAssignedIdentity: cMKUserAssignedIdentityResourceId + identity: !empty(customerManagedKey.?userAssignedIdentityResourceId) ? { + userAssignedIdentity: cMKUserAssignedIdentity.id } : null - keyName: cMKKeyName + keyName: customerManagedKey!.keyName keyVaultUri: cMKKeyVault.properties.vaultUri - keyVersion: !empty(cMKKeyVersion) ? cMKKeyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) + keyVersion: !empty(customerManagedKey.?keyVersion ?? '') ? customerManagedKey!.keyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) } ] requireInfrastructureEncryption: requireInfrastructureEncryption @@ -543,3 +539,17 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? +}? diff --git a/modules/service-bus/namespace/main.json b/modules/service-bus/namespace/main.json index dcf89241ef..eaf0ce5f14 100644 --- a/modules/service-bus/namespace/main.json +++ b/modules/service-bus/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "17643203096817666176" + "templateHash": "17171509116984372740" }, "name": "Service Bus Namespaces", "description": "This module deploys a Service Bus Namespace.", @@ -407,6 +407,38 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true } }, "parameters": { @@ -599,32 +631,10 @@ "description": "Optional. The topics to create in the service bus namespace." } }, - "cMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty." - } - }, - "cMKKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption. If not provided, encryption is automatically enabled with a Microsoft-managed key." - } - }, - "cMKKeyVersion": { - "type": "string", - "defaultValue": "", + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." - } - }, - "cMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. User assigned identity to use when fetching the customer managed key. If not provided, a system-assigned identity can be used - but must be given access to the referenced key vault first." + "description": "Optional. The customer managed key definition." } }, "requireInfrastructureEncryption": { @@ -652,13 +662,13 @@ }, "resources": { "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(parameters('cMKKeyVaultResourceId'))), not(empty(parameters('cMKKeyName'))))]", + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKKeyName'))), parameters('cMKKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", "dependsOn": [ "cMKKeyVault" ] @@ -678,13 +688,22 @@ } }, "cMKKeyVault": { - "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, "serviceBusNamespace": { "type": "Microsoft.ServiceBus/namespaces", @@ -704,10 +723,11 @@ "zoneRedundant": "[parameters('zoneRedundant')]", "disableLocalAuth": "[parameters('disableLocalAuth')]", "premiumMessagingPartitions": "[if(equals(parameters('skuName'), 'Premium'), parameters('premiumMessagingPartitions'), 0)]", - "encryption": "[if(not(empty(parameters('cMKKeyName'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createArray(createObject('identity', if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), createObject('userAssignedIdentity', parameters('cMKUserAssignedIdentityResourceId')), null()), 'keyName', parameters('cMKKeyName'), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyVersion', if(not(empty(parameters('cMKKeyVersion'))), parameters('cMKKeyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), 'requireInfrastructureEncryption', parameters('requireInfrastructureEncryption')), null())]" + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createArray(createObject('identity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null()), 'keyName', parameters('customerManagedKey').keyName, 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), 'requireInfrastructureEncryption', parameters('requireInfrastructureEncryption')), null())]" }, "dependsOn": [ - "cMKKeyVault" + "cMKKeyVault", + "cMKUserAssignedIdentity" ] }, "serviceBusNamespace_lock": { diff --git a/modules/storage/storage-account/.test/encr/main.test.bicep b/modules/storage/storage-account/.test/encr/main.test.bicep index 8a298cdee5..9dc1ac8fc8 100644 --- a/modules/storage/storage-account/.test/encr/main.test.bicep +++ b/modules/storage/storage-account/.test/encr/main.test.bicep @@ -99,9 +99,11 @@ module testDeployment '../../main.bicep' = { nestedDependencies.outputs.managedIdentityResourceId ] } - cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKKeyName: nestedDependencies.outputs.keyName - cMKUserAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + customerManagedKey: { + keyName: nestedDependencies.outputs.keyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/modules/storage/storage-account/README.md b/modules/storage/storage-account/README.md index b1cece3b7a..01647f91e5 100644 --- a/modules/storage/storage-account/README.md +++ b/modules/storage/storage-account/README.md @@ -696,9 +696,11 @@ module storageAccount 'br:bicep/modules/storage.storage-account:1.0.0' = { restorePolicyDays: 8 restorePolicyEnabled: true } - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUserAssignedIdentityResourceId: '' + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } enableDefaultTelemetry: '' managedIdentities: { systemAssigned: false @@ -774,14 +776,12 @@ module storageAccount 'br:bicep/modules/storage.storage-account:1.0.0' = { "restorePolicyEnabled": true } }, - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" - }, - "cMKUserAssignedIdentityResourceId": { - "value": "" + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } }, "enableDefaultTelemetry": { "value": "" @@ -1122,8 +1122,6 @@ module storageAccount 'br:bicep/modules/storage.storage-account:1.0.0' = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`accessTier`](#parameter-accesstier) | string | Required if the Storage Account kind is set to BlobStorage. The access tier is used for billing. The "Premium" access tier is the default value for premium block blobs storage account type and it cannot be changed for the premium block blobs storage account type. | -| [`cMKKeyVaultResourceId`](#parameter-cmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. | -| [`cMKUserAssignedIdentityResourceId`](#parameter-cmkuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty. | | [`enableHierarchicalNamespace`](#parameter-enablehierarchicalnamespace) | bool | If true, enables Hierarchical Namespace for the storage account. Required if enableSftp or enableNfsV3 is set to true. | **Optional parameters** @@ -1136,10 +1134,9 @@ module storageAccount 'br:bicep/modules/storage.storage-account:1.0.0' = { | [`allowSharedKeyAccess`](#parameter-allowsharedkeyaccess) | bool | Indicates whether the storage account permits requests to be authorized with the account access key via Shared Key. If false, then all requests, including shared access signatures, must be authorized with Azure Active Directory (Azure AD). The default value is null, which is equivalent to true. | | [`azureFilesIdentityBasedAuthentication`](#parameter-azurefilesidentitybasedauthentication) | object | Provides the identity based authentication settings for Azure Files. | | [`blobServices`](#parameter-blobservices) | object | Blob service and containers to deploy. | -| [`cMKKeyName`](#parameter-cmkkeyname) | string | The name of the customer managed key to use for encryption. Cannot be deployed together with the parameter 'systemAssignedIdentity' enabled. | -| [`cMKKeyVersion`](#parameter-cmkkeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, latest is used. | | [`customDomainName`](#parameter-customdomainname) | string | Sets the custom domain name assigned to the storage account. Name is the CNAME source. | | [`customDomainUseSubDomainName`](#parameter-customdomainusesubdomainname) | bool | Indicates whether indirect CName validation is enabled. This should only be set on updates. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`defaultToOAuthAuthentication`](#parameter-defaulttooauthauthentication) | bool | A boolean flag which indicates whether the default authentication is OAuth or not. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`dnsEndpointType`](#parameter-dnsendpointtype) | string | Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a large number of accounts in a single subscription, which creates accounts in an Azure DNS Zone and the endpoint URL will have an alphanumeric DNS Zone identifier. | @@ -1233,47 +1230,61 @@ Blob service and containers to deploy. - Type: object - Default: `{}` -### Parameter: `cMKKeyName` +### Parameter: `customDomainName` -The name of the customer managed key to use for encryption. Cannot be deployed together with the parameter 'systemAssignedIdentity' enabled. +Sets the custom domain name assigned to the storage account. Name is the CNAME source. - Required: No - Type: string - Default: `''` -### Parameter: `cMKKeyVaultResourceId` +### Parameter: `customDomainUseSubDomainName` -The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. +Indicates whether indirect CName validation is enabled. This should only be set on updates. - Required: No -- Type: string -- Default: `''` +- Type: bool +- Default: `False` -### Parameter: `cMKKeyVersion` +### Parameter: `customerManagedKey` -The version of the customer managed key to reference for encryption. If not provided, latest is used. +The customer managed key definition. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | No | string | Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKUserAssignedIdentityResourceId` +### Parameter: `customerManagedKey.keyVaultResourceId` -User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty. -- Required: No +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `customDomainName` +### Parameter: `customerManagedKey.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -Sets the custom domain name assigned to the storage account. Name is the CNAME source. - Required: No - Type: string -- Default: `''` -### Parameter: `customDomainUseSubDomainName` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. -Indicates whether indirect CName validation is enabled. This should only be set on updates. - Required: No -- Type: bool -- Default: `False` +- Type: string ### Parameter: `defaultToOAuthAuthentication` diff --git a/modules/storage/storage-account/main.bicep b/modules/storage/storage-account/main.bicep index 0bdcd02d3a..606556391a 100644 --- a/modules/storage/storage-account/main.bicep +++ b/modules/storage/storage-account/main.bicep @@ -160,17 +160,8 @@ param publicNetworkAccess string = '' @description('Optional. Allows HTTPS traffic only to storage service if sets to true.') param supportsHttpsTrafficOnly bool = true -@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \'cMKKeyName\' is not empty.') -param cMKKeyVaultResourceId string = '' - -@description('Optional. The name of the customer managed key to use for encryption. Cannot be deployed together with the parameter \'systemAssignedIdentity\' enabled.') -param cMKKeyName string = '' - -@description('Conditional. User assigned identity to use when fetching the customer managed key. Required if \'cMKKeyName\' is not empty.') -param cMKUserAssignedIdentityResourceId string = '' - -@description('Optional. The version of the customer managed key to reference for encryption. If not provided, latest is used.') -param cMKKeyVersion string = '' +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType @description('Optional. The SAS expiration period. DD.HH:MM:SS.') param sasExpirationPeriod string = '' @@ -224,9 +215,18 @@ resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (ena } } -resource keyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { - name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) + + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' + } +} + +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) } resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { @@ -250,7 +250,7 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { dnsEndpointType: !empty(dnsEndpointType) ? dnsEndpointType : null isLocalUserEnabled: isLocalUserEnabled encryption: { - keySource: !empty(cMKKeyName) ? 'Microsoft.Keyvault' : 'Microsoft.Storage' + keySource: !empty(customerManagedKey) ? 'Microsoft.Keyvault' : 'Microsoft.Storage' services: { blob: supportsBlobService ? { enabled: true @@ -266,13 +266,13 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { } } requireInfrastructureEncryption: kind != 'Storage' ? requireInfrastructureEncryption : null - keyvaultproperties: !empty(cMKKeyName) ? { - keyname: cMKKeyName - keyvaulturi: keyVault.properties.vaultUri - keyversion: !empty(cMKKeyVersion) ? cMKKeyVersion : null + keyvaultproperties: !empty(customerManagedKey) ? { + keyname: customerManagedKey!.keyName + keyvaulturi: cMKKeyVault.properties.vaultUri + keyversion: !empty(customerManagedKey.?keyVersion ?? '') ? customerManagedKey!.keyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) } : null - identity: !empty(cMKKeyName) ? { - userAssignedIdentity: cMKUserAssignedIdentityResourceId + identity: !empty(customerManagedKey.?userAssignedIdentityResourceId) ? { + userAssignedIdentity: cMKUserAssignedIdentity.id } : null } accessTier: kind != 'Storage' ? accessTier : null @@ -615,3 +615,17 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? +}? diff --git a/modules/storage/storage-account/main.json b/modules/storage/storage-account/main.json index 91ade8e95f..ec2df4dff2 100644 --- a/modules/storage/storage-account/main.json +++ b/modules/storage/storage-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "12032978716554990629" + "templateHash": "3909379204431877149" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account.", @@ -380,6 +380,38 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true } }, "parameters": { @@ -683,32 +715,10 @@ "description": "Optional. Allows HTTPS traffic only to storage service if sets to true." } }, - "cMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty." - } - }, - "cMKKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption. Cannot be deployed together with the parameter 'systemAssignedIdentity' enabled." - } - }, - "cMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Conditional. User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty." - } - }, - "cMKKeyVersion": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, latest is used." + "description": "Optional. The customer managed key definition." } }, "sasExpirationPeriod": { @@ -751,6 +761,18 @@ } }, "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, "defaultTelemetry": { "condition": "[parameters('enableDefaultTelemetry')]", "type": "Microsoft.Resources/deployments", @@ -765,14 +787,23 @@ } } }, - "keyVault": { - "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, "storageAccount": { "type": "Microsoft.Storage/storageAccounts", @@ -797,7 +828,7 @@ "dnsEndpointType": "[if(not(empty(parameters('dnsEndpointType'))), parameters('dnsEndpointType'), null())]", "isLocalUserEnabled": "[parameters('isLocalUserEnabled')]", "encryption": { - "keySource": "[if(not(empty(parameters('cMKKeyName'))), 'Microsoft.Keyvault', 'Microsoft.Storage')]", + "keySource": "[if(not(empty(parameters('customerManagedKey'))), 'Microsoft.Keyvault', 'Microsoft.Storage')]", "services": { "blob": "[if(variables('supportsBlobService'), createObject('enabled', true()), null())]", "file": "[if(variables('supportsFileService'), createObject('enabled', true()), null())]", @@ -809,8 +840,8 @@ } }, "requireInfrastructureEncryption": "[if(not(equals(parameters('kind'), 'Storage')), parameters('requireInfrastructureEncryption'), null())]", - "keyvaultproperties": "[if(not(empty(parameters('cMKKeyName'))), createObject('keyname', parameters('cMKKeyName'), 'keyvaulturi', reference('keyVault').vaultUri, 'keyversion', if(not(empty(parameters('cMKKeyVersion'))), parameters('cMKKeyVersion'), null())), null())]", - "identity": "[if(not(empty(parameters('cMKKeyName'))), createObject('userAssignedIdentity', parameters('cMKUserAssignedIdentityResourceId')), null())]" + "keyvaultproperties": "[if(not(empty(parameters('customerManagedKey'))), createObject('keyname', parameters('customerManagedKey').keyName, 'keyvaulturi', reference('cMKKeyVault').vaultUri, 'keyversion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/')))), null())]", + "identity": "[if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null())]" }, "accessTier": "[if(not(equals(parameters('kind'), 'Storage')), parameters('accessTier'), null())]", "sasPolicy": "[if(not(empty(parameters('sasExpirationPeriod'))), createObject('expirationAction', 'Log', 'sasExpirationPeriod', parameters('sasExpirationPeriod')), null())]", @@ -826,7 +857,8 @@ "azureFilesIdentityBasedAuthentication": "[if(not(empty(parameters('azureFilesIdentityBasedAuthentication'))), parameters('azureFilesIdentityBasedAuthentication'), null())]" }, "dependsOn": [ - "keyVault" + "cMKKeyVault", + "cMKUserAssignedIdentity" ] }, "storageAccount_diagnosticSettings": { diff --git a/modules/synapse/workspace/.test/encrwsai/main.test.bicep b/modules/synapse/workspace/.test/encrwsai/main.test.bicep index 31ef9e1a20..4c019dad24 100644 --- a/modules/synapse/workspace/.test/encrwsai/main.test.bicep +++ b/modules/synapse/workspace/.test/encrwsai/main.test.bicep @@ -56,10 +56,10 @@ module testDeployment '../../main.bicep' = { defaultDataLakeStorageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId defaultDataLakeStorageFilesystem: nestedDependencies.outputs.storageContainerName sqlAdministratorLogin: 'synwsadmin' - encryption: true - cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKKeyName: nestedDependencies.outputs.keyVaultEncryptionKeyName - cMKUseSystemAssignedIdentity: true + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultEncryptionKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + } encryptionActivateWorkspace: true enableDefaultTelemetry: enableDefaultTelemetry } diff --git a/modules/synapse/workspace/.test/encrwuai/main.test.bicep b/modules/synapse/workspace/.test/encrwuai/main.test.bicep index 85911c61ec..f9da575edc 100644 --- a/modules/synapse/workspace/.test/encrwuai/main.test.bicep +++ b/modules/synapse/workspace/.test/encrwuai/main.test.bicep @@ -57,10 +57,11 @@ module testDeployment '../../main.bicep' = { defaultDataLakeStorageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId defaultDataLakeStorageFilesystem: nestedDependencies.outputs.storageContainerName sqlAdministratorLogin: 'synwsadmin' - encryption: true - cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - cMKKeyName: nestedDependencies.outputs.keyVaultEncryptionKeyName - cMKUserAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultEncryptionKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/modules/synapse/workspace/README.md b/modules/synapse/workspace/README.md index d51d7c2797..903f30470b 100644 --- a/modules/synapse/workspace/README.md +++ b/modules/synapse/workspace/README.md @@ -226,11 +226,11 @@ module workspace 'br:bicep/modules/synapse.workspace:1.0.0' = { name: 'swensa001' sqlAdministratorLogin: 'synwsadmin' // Non-required parameters - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUseSystemAssignedIdentity: true + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + } enableDefaultTelemetry: '' - encryption: true encryptionActivateWorkspace: true } } @@ -262,21 +262,15 @@ module workspace 'br:bicep/modules/synapse.workspace:1.0.0' = { "value": "synwsadmin" }, // Non-required parameters - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" - }, - "cMKUseSystemAssignedIdentity": { - "value": true + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "" + } }, "enableDefaultTelemetry": { "value": "" }, - "encryption": { - "value": true - }, "encryptionActivateWorkspace": { "value": true } @@ -303,11 +297,12 @@ module workspace 'br:bicep/modules/synapse.workspace:1.0.0' = { name: 'swenua001' sqlAdministratorLogin: 'synwsadmin' // Non-required parameters - cMKKeyName: '' - cMKKeyVaultResourceId: '' - cMKUserAssignedIdentityResourceId: '' + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } enableDefaultTelemetry: '' - encryption: true tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -343,21 +338,16 @@ module workspace 'br:bicep/modules/synapse.workspace:1.0.0' = { "value": "synwsadmin" }, // Non-required parameters - "cMKKeyName": { - "value": "" - }, - "cMKKeyVaultResourceId": { - "value": "" - }, - "cMKUserAssignedIdentityResourceId": { - "value": "" + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } }, "enableDefaultTelemetry": { "value": "" }, - "encryption": { - "value": true - }, "tags": { "value": { "Environment": "Non-Prod", @@ -529,25 +519,16 @@ module workspace 'br:bicep/modules/synapse.workspace:1.0.0' = { | [`name`](#parameter-name) | string | The name of the Synapse Workspace. | | [`sqlAdministratorLogin`](#parameter-sqladministratorlogin) | string | Login for administrator access to the workspace's SQL pools. | -**Conditional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`cMKKeyVaultResourceId`](#parameter-cmkkeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. | - **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | | [`allowedAadTenantIdsForLinking`](#parameter-allowedaadtenantidsforlinking) | array | Allowed AAD Tenant IDs For Linking. | | [`azureADOnlyAuthentication`](#parameter-azureadonlyauthentication) | bool | Enable or Disable AzureADOnlyAuthentication on All Workspace sub-resource. | -| [`cMKKeyName`](#parameter-cmkkeyname) | string | The name of the customer managed key to use for encryption. | -| [`cMKUserAssignedIdentityResourceId`](#parameter-cmkuserassignedidentityresourceid) | string | The ID of User Assigned Managed identity that will be used to access your customer-managed key stored in key vault. | -| [`cMKUseSystemAssignedIdentity`](#parameter-cmkusesystemassignedidentity) | bool | Use System Assigned Managed identity that will be used to access your customer-managed key stored in key vault. | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`defaultDataLakeStorageCreateManagedPrivateEndpoint`](#parameter-defaultdatalakestoragecreatemanagedprivateendpoint) | bool | Create managed private endpoint to the default storage account or not. If Yes is selected, a managed private endpoint connection request is sent to the workspace's primary Data Lake Storage Gen2 account for Spark pools to access data. This must be approved by an owner of the storage account. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`enableDefaultTelemetry`](#parameter-enabledefaulttelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). | -| [`encryption`](#parameter-encryption) | bool | Double encryption using a customer-managed key. | | [`encryptionActivateWorkspace`](#parameter-encryptionactivateworkspace) | bool | Activate workspace by adding the system managed identity in the KeyVault containing the customer managed key and activating the workspace. | | [`initialWorkspaceAdminObjectID`](#parameter-initialworkspaceadminobjectid) | string | AAD object ID of initial workspace admin. | | [`integrationRuntimes`](#parameter-integrationruntimes) | array | The Integration Runtimes to create. | @@ -580,33 +561,47 @@ Enable or Disable AzureADOnlyAuthentication on All Workspace sub-resource. - Type: bool - Default: `False` -### Parameter: `cMKKeyName` +### Parameter: `customerManagedKey` -The name of the customer managed key to use for encryption. +The customer managed key definition. - Required: No +- Type: object + + +| Name | Required | Type | Description | +| :-- | :-- | :--| :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | Yes | string | Required. The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | Yes | string | Required. The resource ID of a key vault to reference a customer managed key for encryption from. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | No | string | Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | No | string | Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +Required. The name of the customer managed key to use for encryption. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKKeyVaultResourceId` +### Parameter: `customerManagedKey.keyVaultResourceId` -The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. -- Required: No +Required. The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: Yes - Type: string -- Default: `''` -### Parameter: `cMKUserAssignedIdentityResourceId` +### Parameter: `customerManagedKey.keyVersion` + +Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'. -The ID of User Assigned Managed identity that will be used to access your customer-managed key stored in key vault. - Required: No - Type: string -- Default: `''` -### Parameter: `cMKUseSystemAssignedIdentity` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. -Use System Assigned Managed identity that will be used to access your customer-managed key stored in key vault. - Required: No -- Type: bool -- Default: `False` +- Type: string ### Parameter: `defaultDataLakeStorageAccountResourceId` @@ -729,13 +724,6 @@ Enable telemetry via a Globally Unique Identifier (GUID). - Type: bool - Default: `True` -### Parameter: `encryption` - -Double encryption using a customer-managed key. -- Required: No -- Type: bool -- Default: `False` - ### Parameter: `encryptionActivateWorkspace` Activate workspace by adding the system managed identity in the KeyVault containing the customer managed key and activating the workspace. diff --git a/modules/synapse/workspace/key/README.md b/modules/synapse/workspace/key/README.md index 59e663a007..2221af30c0 100644 --- a/modules/synapse/workspace/key/README.md +++ b/modules/synapse/workspace/key/README.md @@ -22,6 +22,7 @@ This module deploys a Synapse Workspaces Key. | Parameter | Type | Description | | :-- | :-- | :-- | | [`isActiveCMK`](#parameter-isactivecmk) | bool | Used to activate the workspace after a customer managed key is provided. | +| [`keyVaultResourceId`](#parameter-keyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. | | [`name`](#parameter-name) | string | Encryption key name. | **Conditional parameters** @@ -35,7 +36,6 @@ This module deploys a Synapse Workspaces Key. | Parameter | Type | Description | | :-- | :-- | :-- | | [`enableDefaultTelemetry`](#parameter-enabledefaulttelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). | -| [`keyVaultResourceId`](#parameter-keyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. | | [`location`](#parameter-location) | string | The geo-location where the resource lives. | ### Parameter: `enableDefaultTelemetry` @@ -54,9 +54,8 @@ Used to activate the workspace after a customer managed key is provided. ### Parameter: `keyVaultResourceId` The resource ID of a key vault to reference a customer managed key for encryption from. -- Required: No +- Required: Yes - Type: string -- Default: `''` ### Parameter: `location` diff --git a/modules/synapse/workspace/key/main.bicep b/modules/synapse/workspace/key/main.bicep index 7fd1bd0543..7ae64222fc 100644 --- a/modules/synapse/workspace/key/main.bicep +++ b/modules/synapse/workspace/key/main.bicep @@ -14,15 +14,19 @@ param location string = resourceGroup().location @description('Required. Used to activate the workspace after a customer managed key is provided.') param isActiveCMK bool -@description('Optional. The resource ID of a key vault to reference a customer managed key for encryption from.') -param keyVaultResourceId string = '' +@description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') +param keyVaultResourceId string @description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') param enableDefaultTelemetry bool = true -resource cMKKeyVaultKey 'Microsoft.KeyVault/vaults/keys@2022-07-01' existing = if (!empty(keyVaultResourceId)) { - name: '${last(split(keyVaultResourceId, '/'))}/${name}' +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = { + name: last(split(keyVaultResourceId, '/')) scope: resourceGroup(split(keyVaultResourceId, '/')[2], split(keyVaultResourceId, '/')[4]) + + resource cMKKey 'keys@2023-02-01' existing = { + name: name + } } resource workspace 'Microsoft.Synapse/workspaces@2021-06-01' existing = { @@ -34,7 +38,7 @@ resource key 'Microsoft.Synapse/workspaces/keys@2021-06-01' = { parent: workspace properties: { isActiveCMK: isActiveCMK - keyVaultUrl: cMKKeyVaultKey.properties.keyUri + keyVaultUrl: cMKKeyVault::cMKKey.properties.keyUri } } diff --git a/modules/synapse/workspace/key/main.json b/modules/synapse/workspace/key/main.json index 7000d1e035..938863a640 100644 --- a/modules/synapse/workspace/key/main.json +++ b/modules/synapse/workspace/key/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "14713531383006172248" + "templateHash": "5952844918734432483" }, "name": "Synapse Workspaces Keys", "description": "This module deploys a Synapse Workspaces Key.", @@ -39,9 +39,8 @@ }, "keyVaultResourceId": { "type": "string", - "defaultValue": "", "metadata": { - "description": "Optional. The resource ID of a key vault to reference a customer managed key for encryption from." + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." } }, "enableDefaultTelemetry": { @@ -59,7 +58,7 @@ "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", "properties": { "isActiveCMK": "[parameters('isActiveCMK')]", - "keyVaultUrl": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('keyVaultResourceId'), '/')[2], split(parameters('keyVaultResourceId'), '/')[4]), 'Microsoft.KeyVault/vaults/keys', split(format('{0}/{1}', last(split(parameters('keyVaultResourceId'), '/')), parameters('name')), '/')[0], split(format('{0}/{1}', last(split(parameters('keyVaultResourceId'), '/')), parameters('name')), '/')[1]), '2022-07-01').keyUri]" + "keyVaultUrl": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('keyVaultResourceId'), '/')[2], split(parameters('keyVaultResourceId'), '/')[4]), 'Microsoft.KeyVault/vaults/keys', last(split(parameters('keyVaultResourceId'), '/')), parameters('name')), '2023-02-01').keyUri]" } }, { diff --git a/modules/synapse/workspace/main.bicep b/modules/synapse/workspace/main.bicep index 5b2eac3596..3eaad04764 100644 --- a/modules/synapse/workspace/main.bicep +++ b/modules/synapse/workspace/main.bicep @@ -28,20 +28,8 @@ param defaultDataLakeStorageFilesystem string @description('Optional. Create managed private endpoint to the default storage account or not. If Yes is selected, a managed private endpoint connection request is sent to the workspace\'s primary Data Lake Storage Gen2 account for Spark pools to access data. This must be approved by an owner of the storage account.') param defaultDataLakeStorageCreateManagedPrivateEndpoint bool = false -@description('Optional. Double encryption using a customer-managed key.') -param encryption bool = false - -@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \'cMKKeyName\' is not empty.') -param cMKKeyVaultResourceId string = '' - -@description('Optional. The name of the customer managed key to use for encryption.') -param cMKKeyName string = '' - -@description('Optional. Use System Assigned Managed identity that will be used to access your customer-managed key stored in key vault.') -param cMKUseSystemAssignedIdentity bool = false - -@description('Optional. The ID of User Assigned Managed identity that will be used to access your customer-managed key stored in key vault.') -param cMKUserAssignedIdentityResourceId string = '' +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType @description('Optional. Activate workspace by adding the system managed identity in the KeyVault containing the customer managed key and activating the workspace.') param encryptionActivateWorkspace bool = false @@ -104,8 +92,8 @@ param privateEndpoints privateEndpointType param diagnosticSettings diagnosticSettingType // Variables -var userAssignedIdentitiesUnion = union(userAssignedIdentities, !empty(cMKUserAssignedIdentityResourceId) ? { - '${cMKUserAssignedIdentityResourceId}': {} +var userAssignedIdentitiesUnion = union(userAssignedIdentities, !empty(customerManagedKey.?userAssignedIdentityResourceId ?? []) ? { + '${customerManagedKey!.userAssignedIdentityResourceId}': {} } : {}) var identityType = !empty(userAssignedIdentitiesUnion) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned' @@ -126,15 +114,20 @@ var builtInRoleNames = { 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') } -resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { - name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! - scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) - resource cMKKey 'keys@2023-02-01' existing = if (!empty(cMKKeyName)) { - name: !empty(cMKKeyName) ? cMKKeyName : 'dummyKey' + resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' } } +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup(split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]) +} + resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' properties: { @@ -163,15 +156,21 @@ resource workspace 'Microsoft.Synapse/workspaces@2021-06-01' = { filesystem: defaultDataLakeStorageFilesystem createManagedPrivateEndpoint: managedVirtualNetwork ? defaultDataLakeStorageCreateManagedPrivateEndpoint : null } - encryption: encryption ? { + encryption: !empty(customerManagedKey) ? { cmk: { - kekIdentity: { - userAssignedIdentity: !empty(cMKUserAssignedIdentityResourceId) ? cMKUserAssignedIdentityResourceId : null - useSystemAssignedIdentity: cMKUseSystemAssignedIdentity + kekIdentity: !empty(customerManagedKey.?userAssignedIdentityResourceId) ? { + userAssignedIdentity: cMKUserAssignedIdentity.id + } : { + useSystemAssignedIdentity: empty(customerManagedKey.?userAssignedIdentityResourceId) } + + identity: !empty(customerManagedKey.?userAssignedIdentityResourceId) ? { + userAssignedIdentity: cMKUserAssignedIdentity.id + } : null + key: { keyVaultUrl: cMKKeyVault::cMKKey.properties.keyUri - name: cMKKeyName + name: customerManagedKey!.keyName } } } : null @@ -210,19 +209,19 @@ module workspace_cmk_rbac 'modules/nested_cmkRbac.bicep' = if (encryptionActivat name: '${workspace.name}-cmk-rbac' params: { workspaceIndentityPrincipalId: workspace.identity.principalId - keyvaultName: !empty(cMKKeyVaultResourceId) ? cMKKeyVault.name : '' - usesRbacAuthorization: !empty(cMKKeyVaultResourceId) ? cMKKeyVault.properties.enableRbacAuthorization : true + keyvaultName: !empty(customerManagedKey.?keyVaultResourceId) ? cMKKeyVault.name : '' + usesRbacAuthorization: !empty(customerManagedKey.?keyVaultResourceId) ? cMKKeyVault.properties.enableRbacAuthorization : true } - scope: encryptionActivateWorkspace ? resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) : resourceGroup() + scope: encryptionActivateWorkspace ? resourceGroup(split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]) : resourceGroup() } // - Workspace encryption - Activate Workspace module workspace_key 'key/main.bicep' = if (encryptionActivateWorkspace) { name: '${workspace.name}-cmk-activation' params: { - name: cMKKeyName + name: customerManagedKey!.keyName isActiveCMK: true - keyVaultResourceId: cMKKeyVaultResourceId + keyVaultResourceId: cMKKeyVault.id workspaceName: workspace.name } dependsOn: [ @@ -452,3 +451,17 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type customerManagedKeyType = { + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? + + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? +}? diff --git a/modules/synapse/workspace/main.json b/modules/synapse/workspace/main.json index 6a13d3b652..e96aed1c93 100644 --- a/modules/synapse/workspace/main.json +++ b/modules/synapse/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "15444302507528482650" + "templateHash": "2450269560530411916" }, "name": "Synapse Workspaces", "description": "This module deploys a Synapse Workspace.", @@ -365,6 +365,38 @@ } }, "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true } }, "parameters": { @@ -422,39 +454,10 @@ "description": "Optional. Create managed private endpoint to the default storage account or not. If Yes is selected, a managed private endpoint connection request is sent to the workspace's primary Data Lake Storage Gen2 account for Spark pools to access data. This must be approved by an owner of the storage account." } }, - "encryption": { - "type": "bool", - "defaultValue": false, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", "metadata": { - "description": "Optional. Double encryption using a customer-managed key." - } - }, - "cMKKeyVaultResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty." - } - }, - "cMKKeyName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The name of the customer managed key to use for encryption." - } - }, - "cMKUseSystemAssignedIdentity": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Use System Assigned Managed identity that will be used to access your customer-managed key stored in key vault." - } - }, - "cMKUserAssignedIdentityResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. The ID of User Assigned Managed identity that will be used to access your customer-managed key stored in key vault." + "description": "Optional. The customer managed key definition." } }, "encryptionActivateWorkspace": { @@ -585,7 +588,7 @@ } }, "variables": { - "userAssignedIdentitiesUnion": "[union(parameters('userAssignedIdentities'), if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), createObject(format('{0}', parameters('cMKUserAssignedIdentityResourceId')), createObject()), createObject()))]", + "userAssignedIdentitiesUnion": "[union(parameters('userAssignedIdentities'), if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), createArray()))), createObject(format('{0}', parameters('customerManagedKey').userAssignedIdentityResourceId), createObject()), createObject()))]", "identityType": "[if(not(empty(variables('userAssignedIdentitiesUnion'))), 'SystemAssigned,UserAssigned', 'SystemAssigned')]", "identity": { "type": "[variables('identityType')]", @@ -603,25 +606,34 @@ }, "resources": { "cMKKeyVault::cMKKey": { - "condition": "[and(not(empty(parameters('cMKKeyVaultResourceId'))), not(empty(parameters('cMKKeyName'))))]", + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", "existing": true, "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2023-02-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKKeyName'))), parameters('cMKKeyName'), 'dummyKey'))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", "dependsOn": [ "cMKKeyVault" ] }, "cMKKeyVault": { - "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", "existing": true, "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2021-10-01", - "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", - "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" }, "defaultTelemetry": { "condition": "[parameters('enableDefaultTelemetry')]", @@ -653,7 +665,7 @@ "filesystem": "[parameters('defaultDataLakeStorageFilesystem')]", "createManagedPrivateEndpoint": "[if(parameters('managedVirtualNetwork'), parameters('defaultDataLakeStorageCreateManagedPrivateEndpoint'), null())]" }, - "encryption": "[if(parameters('encryption'), createObject('cmk', createObject('kekIdentity', createObject('userAssignedIdentity', if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), parameters('cMKUserAssignedIdentityResourceId'), null()), 'useSystemAssignedIdentity', parameters('cMKUseSystemAssignedIdentity')), 'key', createObject('keyVaultUrl', reference('cMKKeyVault::cMKKey').keyUri, 'name', parameters('cMKKeyName')))), null())]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('cmk', createObject('kekIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), createObject('useSystemAssignedIdentity', empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))), 'identity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null()), 'key', createObject('keyVaultUrl', reference('cMKKeyVault::cMKKey').keyUri, 'name', parameters('customerManagedKey').keyName))), null())]", "managedResourceGroupName": "[if(not(empty(parameters('managedResourceGroupName'))), parameters('managedResourceGroupName'), null())]", "managedVirtualNetwork": "[if(parameters('managedVirtualNetwork'), 'default', null())]", "managedVirtualNetworkSettings": "[if(parameters('managedVirtualNetwork'), createObject('allowedAadTenantIdsForLinking', parameters('allowedAadTenantIdsForLinking'), 'linkedAccessCheckOnTargetResource', parameters('linkedAccessCheckOnTargetResource'), 'preventDataExfiltration', parameters('preventDataExfiltration')), null())]", @@ -664,7 +676,8 @@ "workspaceRepositoryConfiguration": "[parameters('workspaceRepositoryConfiguration')]" }, "dependsOn": [ - "cMKKeyVault" + "cMKKeyVault", + "cMKUserAssignedIdentity" ] }, "workspace_lock": { @@ -869,8 +882,8 @@ "workspaceIndentityPrincipalId": { "value": "[reference('workspace', '2021-06-01', 'full').identity.principalId]" }, - "keyvaultName": "[if(not(empty(parameters('cMKKeyVaultResourceId'))), createObject('value', last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))), createObject('value', ''))]", - "usesRbacAuthorization": "[if(not(empty(parameters('cMKKeyVaultResourceId'))), createObject('value', reference('cMKKeyVault').enableRbacAuthorization), createObject('value', true()))]" + "keyvaultName": "[if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), createObject('value', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))), createObject('value', ''))]", + "usesRbacAuthorization": "[if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), createObject('value', reference('cMKKeyVault').enableRbacAuthorization), createObject('value', true()))]" }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -948,13 +961,13 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[parameters('cMKKeyName')]" + "value": "[parameters('customerManagedKey').keyName]" }, "isActiveCMK": { "value": true }, "keyVaultResourceId": { - "value": "[parameters('cMKKeyVaultResourceId')]" + "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]), 'Microsoft.KeyVault/vaults', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')))]" }, "workspaceName": { "value": "[parameters('name')]" @@ -967,7 +980,7 @@ "_generator": { "name": "bicep", "version": "0.22.6.54827", - "templateHash": "14713531383006172248" + "templateHash": "5952844918734432483" }, "name": "Synapse Workspaces Keys", "description": "This module deploys a Synapse Workspaces Key.", @@ -1001,9 +1014,8 @@ }, "keyVaultResourceId": { "type": "string", - "defaultValue": "", "metadata": { - "description": "Optional. The resource ID of a key vault to reference a customer managed key for encryption from." + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." } }, "enableDefaultTelemetry": { @@ -1021,7 +1033,7 @@ "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", "properties": { "isActiveCMK": "[parameters('isActiveCMK')]", - "keyVaultUrl": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('keyVaultResourceId'), '/')[2], split(parameters('keyVaultResourceId'), '/')[4]), 'Microsoft.KeyVault/vaults/keys', split(format('{0}/{1}', last(split(parameters('keyVaultResourceId'), '/')), parameters('name')), '/')[0], split(format('{0}/{1}', last(split(parameters('keyVaultResourceId'), '/')), parameters('name')), '/')[1]), '2022-07-01').keyUri]" + "keyVaultUrl": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('keyVaultResourceId'), '/')[2], split(parameters('keyVaultResourceId'), '/')[4]), 'Microsoft.KeyVault/vaults/keys', last(split(parameters('keyVaultResourceId'), '/')), parameters('name')), '2023-02-01').keyUri]" } }, { @@ -1065,6 +1077,7 @@ } }, "dependsOn": [ + "cMKKeyVault", "workspace", "workspace_cmk_rbac" ]