diff --git a/avm/res/kusto/cluster/README.md b/avm/res/kusto/cluster/README.md index 407f17b4fa..8f53d06e5a 100644 --- a/avm/res/kusto/cluster/README.md +++ b/avm/res/kusto/cluster/README.md @@ -34,7 +34,9 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) - [Private endpoint-enabled deployment](#example-3-private-endpoint-enabled-deployment) -- [WAF-aligned](#example-4-waf-aligned) +- [Using Customer-Managed-Keys with System-Assigned identity](#example-4-using-customer-managed-keys-with-system-assigned-identity) +- [Using Customer-Managed-Keys with User-Assigned identity](#example-5-using-customer-managed-keys-with-user-assigned-identity) +- [WAF-aligned](#example-6-waf-aligned) ### Example 1: _Using only defaults_ @@ -50,7 +52,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { name: 'clusterDeployment' params: { // Required parameters - name: 'akcmin0001' + name: 'kcmin0001' sku: 'Standard_E2ads_v5' // Non-required parameters location: '' @@ -72,7 +74,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { "parameters": { // Required parameters "name": { - "value": "akcmin0001" + "value": "kcmin0001" }, "sku": { "value": "Standard_E2ads_v5" @@ -96,7 +98,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { using 'br/public:avm/res/kusto/cluster:' // Required parameters -param name = 'akcmin0001' +param name = 'kcmin0001' param sku = 'Standard_E2ads_v5' // Non-required parameters param location = '' @@ -119,7 +121,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { name: 'clusterDeployment' params: { // Required parameters - name: 'akcmax0001' + name: 'kcmax0001' sku: 'Standard_E2ads_v5' // Non-required parameters acceptedAudiences: [ @@ -159,7 +161,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { principalAssignments: [ { principalId: '' - principalType: 'Group' + principalType: 'App' role: 'AllDatabasesViewer' } ] @@ -201,7 +203,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { "parameters": { // Required parameters "name": { - "value": "akcmax0001" + "value": "kcmax0001" }, "sku": { "value": "Standard_E2ads_v5" @@ -283,7 +285,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { "value": [ { "principalId": "", - "principalType": "Group", + "principalType": "App", "role": "AllDatabasesViewer" } ] @@ -327,7 +329,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { using 'br/public:avm/res/kusto/cluster:' // Required parameters -param name = 'akcmax0001' +param name = 'kcmax0001' param sku = 'Standard_E2ads_v5' // Non-required parameters param acceptedAudiences = [ @@ -367,7 +369,7 @@ param managedIdentities = { param principalAssignments = [ { principalId: '' - principalType: 'Group' + principalType: 'App' role: 'AllDatabasesViewer' } ] @@ -410,7 +412,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { name: 'clusterDeployment' params: { // Required parameters - name: 'akcpe0001' + name: 'kcpe0001' sku: 'Standard_E2ads_v5' // Non-required parameters enablePublicNetworkAccess: false @@ -463,7 +465,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { "parameters": { // Required parameters "name": { - "value": "akcpe0001" + "value": "kcpe0001" }, "sku": { "value": "Standard_E2ads_v5" @@ -526,7 +528,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { using 'br/public:avm/res/kusto/cluster:' // Required parameters -param name = 'akcpe0001' +param name = 'kcpe0001' param sku = 'Standard_E2ads_v5' // Non-required parameters param enablePublicNetworkAccess = false @@ -566,7 +568,183 @@ param tags = {

-### Example 4: _WAF-aligned_ +### Example 4: _Using Customer-Managed-Keys with System-Assigned identity_ + +This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret. + + +

+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/kusto/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + name: '' + sku: 'Standard_E2ads_v5' + // Non-required parameters + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + } + } +} +``` + +
+

+ +

+ +via JSON parameters file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + "sku": { + "value": "Standard_E2ads_v5" + }, + // Non-required parameters + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "" + } + } + } +} +``` + +
+

+ +

+ +via Bicep parameters file + +```bicep-params +using 'br/public:avm/res/kusto/cluster:' + +// Required parameters +param name = '' +param sku = 'Standard_E2ads_v5' +// Non-required parameters +param customerManagedKey = { + keyName: '' + keyVaultResourceId: '' +} +``` + +
+

+ +### Example 5: _Using Customer-Managed-Keys with User-Assigned identity_ + +This instance deploys the module using Customer-Managed-Keys using a User-Assigned Identity to access the Customer-Managed-Key secret. + + +

+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/kusto/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + name: 'kcuencr0001' + sku: 'Standard_E2ads_v5' + // Non-required parameters + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } + managedIdentities: { + userAssignedResourceIds: [ + '' + ] + } + } +} +``` + +
+

+ +

+ +via JSON parameters file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "kcuencr0001" + }, + "sku": { + "value": "Standard_E2ads_v5" + }, + // Non-required parameters + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "" + ] + } + } + } +} +``` + +
+

+ +

+ +via Bicep parameters file + +```bicep-params +using 'br/public:avm/res/kusto/cluster:' + +// Required parameters +param name = 'kcuencr0001' +param sku = 'Standard_E2ads_v5' +// Non-required parameters +param customerManagedKey = { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' +} +param managedIdentities = { + userAssignedResourceIds: [ + '' + ] +} +``` + +
+

+ +### Example 6: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -580,7 +758,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { name: 'clusterDeployment' params: { // Required parameters - name: 'akcwaf0001' + name: 'kcwaf0001' sku: 'Standard_E2ads_v5' // Non-required parameters autoScaleMax: 10 @@ -593,10 +771,6 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { enablePublicNetworkAccess: false enableZoneRedundant: true location: '' - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } managedIdentities: { userAssignedResourceIds: [ '' @@ -625,7 +799,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { "parameters": { // Required parameters "name": { - "value": "akcwaf0001" + "value": "kcwaf0001" }, "sku": { "value": "Standard_E2ads_v5" @@ -661,12 +835,6 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { "location": { "value": "" }, - "lock": { - "value": { - "kind": "CanNotDelete", - "name": "myCustomLockName" - } - }, "managedIdentities": { "value": { "userAssignedResourceIds": [ @@ -698,7 +866,7 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { using 'br/public:avm/res/kusto/cluster:' // Required parameters -param name = 'akcwaf0001' +param name = 'kcwaf0001' param sku = 'Standard_E2ads_v5' // Non-required parameters param autoScaleMax = 10 @@ -711,10 +879,6 @@ param enableDoubleEncryption = true param enablePublicNetworkAccess = false param enableZoneRedundant = true param location = '' -param lock = { - kind: 'CanNotDelete' - name: 'myCustomLockName' -} param managedIdentities = { userAssignedResourceIds: [ '' @@ -864,13 +1028,13 @@ The customer managed key definition. | :-- | :-- | :-- | | [`keyName`](#parameter-customermanagedkeykeyname) | string | The name of the customer managed key to use for encryption. | | [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. | -| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. | **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`keyVersion`](#parameter-customermanagedkeykeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | ### Parameter: `customerManagedKey.keyName` @@ -886,16 +1050,16 @@ The resource ID of a key vault to reference a customer managed key for encryptio - Required: Yes - Type: string -### Parameter: `customerManagedKey.userAssignedIdentityResourceId` +### Parameter: `customerManagedKey.keyVersion` -User assigned identity to use when fetching the customer managed key. +The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time. -- Required: Yes +- Required: No - Type: string -### Parameter: `customerManagedKey.keyVersion` +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` -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 no system assigned identity is available for use. - Required: No - Type: string @@ -917,7 +1081,7 @@ The diagnostic settings of the service. | [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | | [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | | [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | -| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of the diagnostic setting. | | [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | | [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | @@ -1027,7 +1191,7 @@ Enable or disable the category explicitly. Default is `true`. ### Parameter: `diagnosticSettings.name` -The name of diagnostic setting. +The name of the diagnostic setting. - Required: No - Type: string @@ -1251,7 +1415,7 @@ The managed identity definition for this resource. | Parameter | Type | Description | | :-- | :-- | :-- | | [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. | -| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource id(s) to assign to the resource. | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption. | ### Parameter: `managedIdentities.systemAssigned` @@ -1262,7 +1426,7 @@ Enables system assigned managed identity on the resource. ### Parameter: `managedIdentities.userAssignedResourceIds` -The resource id(s) to assign to the resource. +The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption. - Required: No - Type: array @@ -1273,7 +1437,63 @@ The Principal Assignments for the Kusto Cluster. - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-principalassignmentsprincipalid) | string | The principal id assigned to the Kusto Cluster principal. It can be a user email, application id, or security group name. | +| [`principalType`](#parameter-principalassignmentsprincipaltype) | string | The principal type of the principal id. | +| [`role`](#parameter-principalassignmentsrole) | string | The Kusto Cluster role to be assigned to the principal id. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`tenantId`](#parameter-principalassignmentstenantid) | string | The tenant id of the principal. | + +### Parameter: `principalAssignments.principalId` + +The principal id assigned to the Kusto Cluster principal. It can be a user email, application id, or security group name. + +- Required: Yes +- Type: string + +### Parameter: `principalAssignments.principalType` + +The principal type of the principal id. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'App' + 'Group' + 'User' + ] + ``` + +### Parameter: `principalAssignments.role` + +The Kusto Cluster role to be assigned to the principal id. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'AllDatabasesAdmin' + 'AllDatabasesViewer' + ] + ``` + +### Parameter: `principalAssignments.tenantId` + +The tenant id of the principal. + +- Required: No +- Type: string ### Parameter: `privateEndpoints` @@ -1286,7 +1506,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | Parameter | Type | Description | | :-- | :-- | :-- | -| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file". | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file" for a Storage Account's Private Endpoints. | | [`subnetResourceId`](#parameter-privateendpointssubnetresourceid) | string | Resource ID of the subnet where the endpoint needs to be created. | **Optional parameters** @@ -1311,7 +1531,7 @@ Configuration details for private endpoints. For security reasons, it is recomme ### Parameter: `privateEndpoints.service` -The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file". +The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file" for a Storage Account's Private Endpoints. - Required: Yes - Type: string @@ -1509,7 +1729,7 @@ The private DNS zone group to configure for the private endpoint. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones. | **Optional parameters** @@ -1519,7 +1739,7 @@ The private DNS zone group to configure for the private endpoint. ### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` -The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. +The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones. - Required: Yes - Type: array @@ -1534,7 +1754,7 @@ The private DNS zone groups to associate the private endpoint. A DNS zone group | Parameter | Type | Description | | :-- | :-- | :-- | -| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS Zone Group config. | ### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` @@ -1545,7 +1765,7 @@ The resource id of the private DNS zone. ### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group config. +The name of the private DNS Zone Group config. - Required: No - Type: string @@ -1864,33 +2084,25 @@ The virtual network configuration of the Kusto Cluster. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`dataManagementPublicIpId`](#parameter-virtualnetworkconfigurationdatamanagementpublicipid) | string | The public IP address resource id of the data management service.. | -| [`enableVirtualNetworkInjection`](#parameter-virtualnetworkconfigurationenablevirtualnetworkinjection) | bool | Enable/disable virtual network injection. When enabled, the Kusto Cluster will be deployed into the specified subnet. When disabled, the Kusto Cluster will be removed from the specified subnet. | -| [`enginePublicIpId`](#parameter-virtualnetworkconfigurationenginepublicipid) | string | The public IP address resource id of the engine service. | -| [`subnetId`](#parameter-virtualnetworkconfigurationsubnetid) | string | The resource ID of the subnet to which to deploy the Kusto Cluster. | +| [`dataManagementPublicIpResourceId`](#parameter-virtualnetworkconfigurationdatamanagementpublicipresourceid) | string | The public IP address resource id of the data management service.. | +| [`enginePublicIpResourceId`](#parameter-virtualnetworkconfigurationenginepublicipresourceid) | string | The public IP address resource id of the engine service. | +| [`subnetResourceId`](#parameter-virtualnetworkconfigurationsubnetresourceid) | string | The resource ID of the subnet to which to deploy the Kusto Cluster. | -### Parameter: `virtualNetworkConfiguration.dataManagementPublicIpId` +### Parameter: `virtualNetworkConfiguration.dataManagementPublicIpResourceId` The public IP address resource id of the data management service.. - Required: Yes - Type: string -### Parameter: `virtualNetworkConfiguration.enableVirtualNetworkInjection` - -Enable/disable virtual network injection. When enabled, the Kusto Cluster will be deployed into the specified subnet. When disabled, the Kusto Cluster will be removed from the specified subnet. - -- Required: Yes -- Type: bool - -### Parameter: `virtualNetworkConfiguration.enginePublicIpId` +### Parameter: `virtualNetworkConfiguration.enginePublicIpResourceId` The public IP address resource id of the engine service. - Required: Yes - Type: string -### Parameter: `virtualNetworkConfiguration.subnetId` +### Parameter: `virtualNetworkConfiguration.subnetResourceId` The resource ID of the subnet to which to deploy the Kusto Cluster. @@ -1915,6 +2127,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | | `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | +| `br/public:avm/utl/types/avm-common-types:0.3.0` | Remote reference | ## Data Collection diff --git a/avm/res/kusto/cluster/main.bicep b/avm/res/kusto/cluster/main.bicep index 01c343a0d5..ed8ac33459 100644 --- a/avm/res/kusto/cluster/main.bicep +++ b/avm/res/kusto/cluster/main.bicep @@ -17,10 +17,15 @@ param capacity int = 2 param sku string @description('Optional. The tier of the Kusto Cluster.') -param tier kustoTierType = 'Standard' +@allowed([ + 'Basic' + 'Standard' +]) +param tier string = 'Standard' +import { managedIdentityAllType } from 'br/public:avm/utl/types/avm-common-types:0.3.0' @description('Optional. The managed identity definition for this resource.') -param managedIdentities managedIdentitiesType +param managedIdentities managedIdentityAllType? @description('Optional. The Kusto Cluster\'s accepted audiences.') param acceptedAudiences acceptedAudienceType[] = [] @@ -53,8 +58,9 @@ param enableStreamingIngest bool = false @description('Optional. The engine type of the Kusto Cluster.') param engineType string = 'V3' +import { customerManagedKeyType } from 'br/public:avm/utl/types/avm-common-types:0.3.0' @description('Optional. The customer managed key definition.') -param customerManagedKey customerManagedKeyType +param customerManagedKey customerManagedKeyType? @description('Optional. List of the language extensions of the Kusto Cluster.') param languageExtensions languageExtensionType[] = [] @@ -94,13 +100,15 @@ param trustedExternalTenants trustedExternalTenantType[] = [] param virtualClusterGraduationProperties string? @description('Optional. The virtual network configuration of the Kusto Cluster.') -param virtualNetworkConfiguration virtualNetworkConfigurationType +param virtualNetworkConfiguration virtualNetworkConfigurationType? +import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.3.0' @description('Optional. The lock settings of the service.') -param lock lockType +param lock lockType? +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.3.0' @description('Optional. Array of role assignments to create.') -param roleAssignments roleAssignmentType +param roleAssignments roleAssignmentType[]? @description('Optional. Tags of the resource.') param tags object? @@ -108,17 +116,19 @@ param tags object? @description('Optional. Enable/disable zone redundancy.') param enableZoneRedundant bool = false +import { privateEndpointMultiServiceType } from 'br/public:avm/utl/types/avm-common-types:0.3.0' @description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') -param privateEndpoints privateEndpointType +param privateEndpoints privateEndpointMultiServiceType[]? @description('Optional. Enable/disable usage telemetry for module.') param enableTelemetry bool = true +import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:0.3.0' @description('Optional. The diagnostic settings of the service.') -param diagnosticSettings diagnosticSettingType +param diagnosticSettings diagnosticSettingFullType[]? @description('Optional. The Principal Assignments for the Kusto Cluster.') -param principalAssignments array = [] +param principalAssignments principalAssignmentType[]? // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} } var formattedUserAssignedIdentities = reduce( @@ -208,7 +218,18 @@ resource kustoCluster 'Microsoft.Kusto/clusters@2023-08-15' = { enablePurge: enablePurge enableStreamingIngest: enableStreamingIngest engineType: engineType - keyVaultProperties: customerManagedKey + keyVaultProperties: !empty(customerManagedKey) + ? { + keyName: customerManagedKey!.keyName + keyVaultUri: cMKKeyVault.properties.vaultUri + keyVersion: !empty(customerManagedKey.?keyVersion ?? '') + ? customerManagedKey!.keyVersion + : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) + userIdentity: !empty(customerManagedKey.?userAssignedIdentityResourceId) + ? customerManagedKey!.userAssignedIdentityResourceId + : null + } + : null languageExtensions: { value: languageExtensions } @@ -223,7 +244,16 @@ resource kustoCluster 'Microsoft.Kusto/clusters@2023-08-15' = { restrictOutboundNetworkAccess: enableRestrictOutboundNetworkAccess ? 'Enabled' : 'Disabled' trustedExternalTenants: trustedExternalTenants virtualClusterGraduationProperties: virtualClusterGraduationProperties - virtualNetworkConfiguration: virtualNetworkConfiguration + virtualNetworkConfiguration: !empty(virtualNetworkConfiguration) + ? { + #disable-next-line use-resource-id-functions + dataManagementPublicIpId: virtualNetworkConfiguration!.dataManagementPublicIpResourceId + #disable-next-line use-resource-id-functions + enginePublicIpId: virtualNetworkConfiguration!.enginePublicIpResourceId + #disable-next-line use-resource-id-functions + subnetId: virtualNetworkConfiguration!.subnetResourceId + } + : null } zones: enableZoneRedundant ? [ @@ -291,14 +321,14 @@ resource kustoCluster_roleAssignments 'Microsoft.Authorization/roleAssignments@2 ] module kustoCluster_principalAssignments 'principal-assignment/main.bicep' = [ - for (principalAssignment, index) in (principalAssignments): { + for (principalAssignment, index) in (principalAssignments ?? []): { name: '${uniqueString(deployment().name, location)}-KustoCluster-PrincipalAssignment-${index}' params: { kustoClusterName: kustoCluster.name principalId: principalAssignment.principalId principalType: principalAssignment.principalType role: principalAssignment.role - tenantId: principalAssignment.?tenantId ?? tenant().tenantId + tenantId: principalAssignment.?tenantId } } ] @@ -367,7 +397,7 @@ output resourceGroupName string = resourceGroup().name output resourceId string = kustoCluster.?id @description('The principal ID of the system assigned identity.') -output systemAssignedMIPrincipalId string = kustoCluster.?identity.?principalId ?? '' +output systemAssignedMIPrincipalId string? = kustoCluster.?identity.?principalId @description('The name of the kusto cluster.') output name string = kustoCluster.name @@ -390,235 +420,53 @@ output privateEndpoints array = [ // Definitions // // =============== // +@export() type acceptedAudienceType = { @description('Required. GUID or valid URL representing an accepted audience.') value: string -}? - -type diagnosticSettingType = { - @description('Optional. The name of diagnostic setting.') - name: string? - - @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection.') - logCategoriesAndGroups: { - @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') - category: string? - - @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs.') - categoryGroup: string? - - @description('Optional. Enable or disable the category explicitly. Default is `true`.') - enabled: bool? - }[]? - - @description('Optional. The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection.') - metricCategories: { - @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.') - category: string - - @description('Optional. Enable or disable the category explicitly. Default is `true`.') - enabled: bool? - }[]? - - @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') - logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics')? - - @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') - workspaceResourceId: string? - - @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') - storageAccountResourceId: string? - - @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') - eventHubAuthorizationRuleResourceId: string? - - @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') - eventHubName: string? - - @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') - marketplacePartnerResourceId: string? -}[]? - -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 -}? - -type kustoTierType = 'Basic' | 'Standard' - -type languageExtensionCustomImageNameType = - | 'Python3_10_8' - | 'Python3_10_8_DL' - | 'Python3_6_5' - | 'PythonCustomImage' - | 'R' - -type languageExtensionNameType = 'PYTHON' | 'R' +} +@export() type languageExtensionType = { @description('Required. The name of the language extension custom image.') languageExtensionCustomImageName: string @description('Required. The name of the language extension image.') - languageExtensionImageName: languageExtensionCustomImageNameType + languageExtensionImageName: 'Python3_10_8' | 'Python3_10_8_DL' | 'Python3_6_5' | 'PythonCustomImage' | 'R' @description('Required. The name of the language extension.') - languageExtensionName: languageExtensionNameType -}? - -type lockType = { - @description('Optional. Specify the name of lock.') - name: string? - - @description('Optional. Specify the type of lock.') - kind: ('CanNotDelete' | 'ReadOnly' | 'None')? -}? - -type managedIdentitiesType = { - @description('Optional. Enables system assigned managed identity on the resource.') - systemAssigned: bool? - - @description('Optional. The resource id(s) to assign to the resource.') - userAssignedResourceIds: string[]? -}? - -type privateEndpointType = { - @description('Optional. The name of the private endpoint.') - name: string? - - @description('Optional. The location to deploy the private endpoint to.') - location: string? - - @description('Optional. The name of the private link connection to create.') - privateLinkServiceConnectionName: string? - - @description('Required. The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file".') - service: string - - @description('Required. Resource ID of the subnet where the endpoint needs to be created.') - subnetResourceId: string - - @description('Optional. The private DNS zone group to configure for the private endpoint.') - privateDnsZoneGroup: { - @description('Optional. The name of the Private DNS Zone Group.') - name: string? - - @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneGroupConfigs: { - @description('Optional. The name of the private DNS zone group config.') - name: string? - - @description('Required. The resource id of the private DNS zone.') - privateDnsZoneResourceId: string - }[] - }? - - @description('Optional. If Manual Private Link Connection is required.') - isManualConnection: bool? - - @description('Optional. A message passed to the owner of the remote resource with the manual connection request.') - @maxLength(140) - manualConnectionRequestMessage: string? - - @description('Optional. Custom DNS configurations.') - customDnsConfigs: { - @description('Optional. FQDN that resolves to private endpoint IP address.') - fqdn: string? - - @description('Required. A list of private IP addresses of the private endpoint.') - ipAddresses: string[] - }[]? - - @description('Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints.') - ipConfigurations: { - @description('Required. The name of the resource that is unique within a resource group.') - name: string - - @description('Required. Properties of private endpoint IP configurations.') - properties: { - @description('Required. The ID of a group obtained from the remote resource that this private endpoint should connect to.') - groupId: string - - @description('Required. The member name of a group obtained from the remote resource that this private endpoint should connect to.') - memberName: string - - @description('Required. A private IP address obtained from the private endpoint\'s subnet.') - privateIPAddress: string - } - }[]? - - @description('Optional. Application security groups in which the private endpoint IP configuration is included.') - applicationSecurityGroupResourceIds: string[]? - - @description('Optional. The custom name of the network interface attached to the private endpoint.') - customNetworkInterfaceName: string? - - @description('Optional. Specify the type of lock.') - lock: lockType - - @description('Optional. Array of role assignments to create.') - roleAssignments: roleAssignmentType - - @description('Optional. Tags to be applied on all resources/resource groups in this deployment.') - tags: object? - - @description('Optional. Enable/Disable usage telemetry for module.') - enableTelemetry: bool? - - @description('Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource.') - resourceGroupName: string? -}[]? - -type roleAssignmentType = { - @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') - name: string? - - @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') - roleDefinitionIdOrName: string - - @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') - principalId: string - - @description('Optional. The principal type of the assigned principal ID.') - principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? - - @description('Optional. The description of the role assignment.') - description: string? - - @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') - condition: string? - - @description('Optional. Version of the condition.') - conditionVersion: '2.0'? - - @description('Optional. The Resource Id of the delegated managed identity resource.') - delegatedManagedIdentityResourceId: string? -}[]? + languageExtensionName: 'PYTHON' | 'R' +} +@export() type trustedExternalTenantType = { @description('Required. GUID representing an external tenant.') value: string -}? +} +@export() type virtualNetworkConfigurationType = { @description('Required. The public IP address resource id of the data management service..') - dataManagementPublicIpId: string - - @description('Required. Enable/disable virtual network injection. When enabled, the Kusto Cluster will be deployed into the specified subnet. When disabled, the Kusto Cluster will be removed from the specified subnet.') - enableVirtualNetworkInjection: bool + dataManagementPublicIpResourceId: string @description('Required. The public IP address resource id of the engine service.') - enginePublicIpId: string + enginePublicIpResourceId: string @description('Required. The resource ID of the subnet to which to deploy the Kusto Cluster.') - subnetId: string -}? + subnetResourceId: string +} + +@export() +type principalAssignmentType = { + @description('Required. The principal id assigned to the Kusto Cluster principal. It can be a user email, application id, or security group name.') + principalId: string + + @description('Required. The principal type of the principal id.') + principalType: 'App' | 'Group' | 'User' + + @description('Required. The Kusto Cluster role to be assigned to the principal id.') + role: 'AllDatabasesAdmin' | 'AllDatabasesViewer' + + @description('Optional. The tenant id of the principal.') + tenantId: string? +} diff --git a/avm/res/kusto/cluster/main.json b/avm/res/kusto/cluster/main.json index edb61b98fe..a2d371232b 100644 --- a/avm/res/kusto/cluster/main.json +++ b/avm/res/kusto/cluster/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8362793153324787983" + "version": "0.31.34.60546", + "templateHash": "13977618446644040802" }, "name": "Kusto Cluster", "description": "This module deploys a Kusto Cluster.", @@ -23,127 +23,237 @@ } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } }, - "diagnosticSettingType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of diagnostic setting." - } + "languageExtensionType": { + "type": "object", + "properties": { + "languageExtensionCustomImageName": { + "type": "string", + "metadata": { + "description": "Required. The name of the language extension custom image." + } + }, + "languageExtensionImageName": { + "type": "string", + "allowedValues": [ + "Python3_10_8", + "Python3_10_8_DL", + "Python3_6_5", + "PythonCustomImage", + "R" + ], + "metadata": { + "description": "Required. The name of the language extension image." + } + }, + "languageExtensionName": { + "type": "string", + "allowedValues": [ + "PYTHON", + "R" + ], + "metadata": { + "description": "Required. The name of the language extension." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "trustedExternalTenantType": { + "type": "object", + "properties": { + "value": { + "type": "string", + "metadata": { + "description": "Required. GUID representing an external tenant." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "virtualNetworkConfigurationType": { + "type": "object", + "properties": { + "dataManagementPublicIpResourceId": { + "type": "string", + "metadata": { + "description": "Required. The public IP address resource id of the data management service.." + } + }, + "enginePublicIpResourceId": { + "type": "string", + "metadata": { + "description": "Required. The public IP address resource id of the engine service." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet to which to deploy the Kusto Cluster." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "principalAssignmentType": { + "type": "object", + "properties": { + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal id assigned to the Kusto Cluster principal. It can be a user email, application id, or security group name." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "App", + "Group", + "User" + ], + "metadata": { + "description": "Required. The principal type of the principal id." + } + }, + "role": { + "type": "string", + "allowedValues": [ + "AllDatabasesAdmin", + "AllDatabasesViewer" + ], + "metadata": { + "description": "Required. The Kusto Cluster role to be assigned to the principal id." + } + }, + "tenantId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tenant id of the principal." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" }, - "logCategoriesAndGroups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." - } - }, - "categoryGroup": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } - } + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." } }, - "nullable": true, - "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." - } - }, - "metricCategories": { - "type": "array", - "items": { - "type": "object", - "properties": { - "category": { - "type": "string", - "metadata": { - "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." - } - }, - "enabled": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable or disable the category explicitly. Default is `true`." - } - } + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." } }, - "nullable": true, - "metadata": { - "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." - } - }, - "logAnalyticsDestinationType": { - "type": "string", - "allowedValues": [ - "AzureDiagnostics", - "Dedicated" - ], - "nullable": true, - "metadata": { - "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." - } - }, - "workspaceResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." - } - }, - "storageAccountResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." - } - }, - "eventHubAuthorizationRuleResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } } }, - "eventHubName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } } }, - "marketplacePartnerResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." - } + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." } } }, - "nullable": true + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } }, "customerManagedKeyType": { "type": "object", @@ -164,447 +274,419 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." } }, "userAssignedIdentityResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. User assigned identity to use when fetching the customer managed key." + "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 + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } }, - "kustoTierType": { - "type": "string", - "allowedValues": [ - "Basic", - "Standard" - ] + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } }, - "languageExtensionCustomImageNameType": { - "type": "string", - "allowedValues": [ - "Python3_10_8", - "Python3_10_8_DL", - "Python3_6_5", - "PythonCustomImage", - "R" - ] + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } }, - "languageExtensionNameType": { - "type": "string", - "allowedValues": [ - "PYTHON", - "R" - ] + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } }, - "languageExtensionType": { + "privateEndpointMultiServiceType": { "type": "object", "properties": { - "languageExtensionCustomImageName": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\" for a Storage Account's Private Endpoints." + } + }, + "subnetResourceId": { "type": "string", "metadata": { - "description": "Required. The name of the language extension custom image." + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." } }, - "languageExtensionImageName": { - "$ref": "#/definitions/languageExtensionCustomImageNameType", + "tags": { + "type": "object", + "nullable": true, "metadata": { - "description": "Required. The name of the language extension image." + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." } }, - "languageExtensionName": { - "$ref": "#/definitions/languageExtensionNameType", - "metadata": { - "description": "Required. The name of the language extension." - } - } - }, - "nullable": true - }, - "lockType": { - "type": "object", - "properties": { - "name": { - "type": "string", + "enableTelemetry": { + "type": "bool", "nullable": true, "metadata": { - "description": "Optional. Specify the name of lock." + "description": "Optional. Enable/Disable usage telemetry for module." } }, - "kind": { + "resourceGroupName": { "type": "string", - "allowedValues": [ - "CanNotDelete", - "None", - "ReadOnly" - ], "nullable": true, "metadata": { - "description": "Optional. Specify the type of lock." + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can NOT be assumed (i.e., for services that have more than one subresource, like Storage Account with Blob (blob, table, queue, file, ...).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } }, - "managedIdentitiesType": { + "roleAssignmentType": { "type": "object", "properties": { - "systemAssigned": { - "type": "bool", + "name": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." } }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, + "roleDefinitionIdOrName": { + "type": "string", "metadata": { - "description": "Optional. The resource id(s) to assign to the resource." - } - } - }, - "nullable": true - }, - "privateEndpointType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private endpoint." - } - }, - "location": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The location to deploy the private endpoint to." - } - }, - "privateLinkServiceConnectionName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private link connection to create." - } - }, - "service": { - "type": "string", - "metadata": { - "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\"." - } - }, - "subnetResourceId": { - "type": "string", - "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." - } - }, - "privateDnsZoneGroup": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the Private DNS Zone Group." - } - }, - "privateDnsZoneGroupConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group config." - } - }, - "privateDnsZoneResourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource id of the private DNS zone." - } - } - } - }, - "metadata": { - "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone group to configure for the private endpoint." - } - }, - "isManualConnection": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. If Manual Private Link Connection is required." - } - }, - "manualConnectionRequestMessage": { - "type": "string", - "nullable": true, - "maxLength": 140, - "metadata": { - "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." - } - }, - "customDnsConfigs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fqdn": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. FQDN that resolves to private endpoint IP address." - } - }, - "ipAddresses": { - "type": "array", - "items": { - "type": "string" - }, - "metadata": { - "description": "Required. A list of private IP addresses of the private endpoint." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. Custom DNS configurations." - } - }, - "ipConfigurations": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the resource that is unique within a resource group." - } - }, - "properties": { - "type": "object", - "properties": { - "groupId": { - "type": "string", - "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "memberName": { - "type": "string", - "metadata": { - "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." - } - }, - "privateIPAddress": { - "type": "string", - "metadata": { - "description": "Required. A private IP address obtained from the private endpoint's subnet." - } - } - }, - "metadata": { - "description": "Required. Properties of private endpoint IP configurations." - } - } - } - }, - "nullable": true, - "metadata": { - "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." - } - }, - "applicationSecurityGroupResourceIds": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "metadata": { - "description": "Optional. Application security groups in which the private endpoint IP configuration is included." - } - }, - "customNetworkInterfaceName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The custom name of the network interface attached to the private endpoint." - } - }, - "lock": { - "$ref": "#/definitions/lockType", - "metadata": { - "description": "Optional. Specify the type of lock." - } - }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", - "metadata": { - "description": "Optional. Array of role assignments to create." - } - }, - "tags": { - "type": "object", - "nullable": true, - "metadata": { - "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." - } - }, - "enableTelemetry": { - "type": "bool", - "nullable": true, - "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." - } - }, - "resourceGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." - } + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." } - } - }, - "nullable": true - }, - "roleAssignmentType": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." - } - }, - "roleDefinitionIdOrName": { - "type": "string", - "metadata": { - "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." - } - }, - "principalId": { - "type": "string", - "metadata": { - "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." - } - }, - "principalType": { - "type": "string", - "allowedValues": [ - "Device", - "ForeignGroup", - "Group", - "ServicePrincipal", - "User" - ], - "nullable": true, - "metadata": { - "description": "Optional. The principal type of the assigned principal ID." - } - }, - "description": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The description of the role assignment." - } - }, - "condition": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." - } - }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." - } - }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." } - } - }, - "nullable": true - }, - "trustedExternalTenantType": { - "type": "object", - "properties": { - "value": { + }, + "principalType": { "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, "metadata": { - "description": "Required. GUID representing an external tenant." + "description": "Optional. The principal type of the assigned principal ID." } - } - }, - "nullable": true - }, - "virtualNetworkConfigurationType": { - "type": "object", - "properties": { - "dataManagementPublicIpId": { + }, + "description": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The public IP address resource id of the data management service.." + "description": "Optional. The description of the role assignment." } }, - "enableVirtualNetworkInjection": { - "type": "bool", + "condition": { + "type": "string", + "nullable": true, "metadata": { - "description": "Required. Enable/disable virtual network injection. When enabled, the Kusto Cluster will be deployed into the specified subnet. When disabled, the Kusto Cluster will be removed from the specified subnet." + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." } }, - "enginePublicIpId": { + "conditionVersion": { "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, "metadata": { - "description": "Required. The public IP address resource id of the engine service." + "description": "Optional. Version of the condition." } }, - "subnetId": { + "delegatedManagedIdentityResourceId": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The resource ID of the subnet to which to deploy the Kusto Cluster." + "description": "Optional. The Resource Id of the delegated managed identity resource." } } }, - "nullable": true + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } } }, "parameters": { @@ -637,14 +719,19 @@ } }, "tier": { - "$ref": "#/definitions/kustoTierType", + "type": "string", "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Standard" + ], "metadata": { "description": "Optional. The tier of the Kusto Cluster." } }, "managedIdentities": { - "$ref": "#/definitions/managedIdentitiesType", + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, "metadata": { "description": "Optional. The managed identity definition for this resource." } @@ -727,6 +814,7 @@ }, "customerManagedKey": { "$ref": "#/definitions/customerManagedKeyType", + "nullable": true, "metadata": { "description": "Optional. The customer managed key definition." } @@ -811,18 +899,24 @@ }, "virtualNetworkConfiguration": { "$ref": "#/definitions/virtualNetworkConfigurationType", + "nullable": true, "metadata": { "description": "Optional. The virtual network configuration of the Kusto Cluster." } }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } }, "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. Array of role assignments to create." } @@ -842,7 +936,11 @@ } }, "privateEndpoints": { - "$ref": "#/definitions/privateEndpointType", + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointMultiServiceType" + }, + "nullable": true, "metadata": { "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." } @@ -855,14 +953,21 @@ } }, "diagnosticSettings": { - "$ref": "#/definitions/diagnosticSettingType", + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, "metadata": { "description": "Optional. The diagnostic settings of the service." } }, "principalAssignments": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/principalAssignmentType" + }, + "nullable": true, "metadata": { "description": "Optional. The Principal Assignments for the Kusto Cluster." } @@ -892,10 +997,7 @@ "apiVersion": "2023-07-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" - ] + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]" }, "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", @@ -948,7 +1050,7 @@ "enablePurge": "[parameters('enablePurge')]", "enableStreamingIngest": "[parameters('enableStreamingIngest')]", "engineType": "[parameters('engineType')]", - "keyVaultProperties": "[parameters('customerManagedKey')]", + "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, '/'))), 'userIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), parameters('customerManagedKey').userAssignedIdentityResourceId, null())), null())]", "languageExtensions": { "value": "[parameters('languageExtensions')]" }, @@ -963,9 +1065,12 @@ "restrictOutboundNetworkAccess": "[if(parameters('enableRestrictOutboundNetworkAccess'), 'Enabled', 'Disabled')]", "trustedExternalTenants": "[parameters('trustedExternalTenants')]", "virtualClusterGraduationProperties": "[parameters('virtualClusterGraduationProperties')]", - "virtualNetworkConfiguration": "[parameters('virtualNetworkConfiguration')]" + "virtualNetworkConfiguration": "[if(not(empty(parameters('virtualNetworkConfiguration'))), createObject('dataManagementPublicIpId', parameters('virtualNetworkConfiguration').dataManagementPublicIpResourceId, 'enginePublicIpId', parameters('virtualNetworkConfiguration').enginePublicIpResourceId, 'subnetId', parameters('virtualNetworkConfiguration').subnetResourceId), null())]" }, - "zones": "[if(parameters('enableZoneRedundant'), createArray('1', '2', '3'), null())]" + "zones": "[if(parameters('enableZoneRedundant'), createArray('1', '2', '3'), null())]", + "dependsOn": [ + "cMKKeyVault" + ] }, "kustoCluster_diagnosticSettings": { "copy": { @@ -1047,7 +1152,7 @@ "kustoCluster_principalAssignments": { "copy": { "name": "kustoCluster_principalAssignments", - "count": "[length(parameters('principalAssignments'))]" + "count": "[length(coalesce(parameters('principalAssignments'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -1062,16 +1167,16 @@ "value": "[parameters('name')]" }, "principalId": { - "value": "[parameters('principalAssignments')[copyIndex()].principalId]" + "value": "[coalesce(parameters('principalAssignments'), createArray())[copyIndex()].principalId]" }, "principalType": { - "value": "[parameters('principalAssignments')[copyIndex()].principalType]" + "value": "[coalesce(parameters('principalAssignments'), createArray())[copyIndex()].principalType]" }, "role": { - "value": "[parameters('principalAssignments')[copyIndex()].role]" + "value": "[coalesce(parameters('principalAssignments'), createArray())[copyIndex()].role]" }, "tenantId": { - "value": "[coalesce(tryGet(parameters('principalAssignments')[copyIndex()], 'tenantId'), tenant().tenantId)]" + "value": "[tryGet(coalesce(parameters('principalAssignments'), createArray())[copyIndex()], 'tenantId')]" } }, "template": { @@ -1080,8 +1185,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "157221949480329840" + "version": "0.31.34.60546", + "templateHash": "2020706178910686237" }, "name": "Kusto Cluster Principal Assignments", "description": "This module deploys a Kusto Cluster Principal Assignment.", @@ -1959,10 +2064,11 @@ }, "systemAssignedMIPrincipalId": { "type": "string", + "nullable": true, "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[coalesce(tryGet(tryGet(reference('kustoCluster', '2023-08-15', 'full'), 'identity'), 'principalId'), '')]" + "value": "[tryGet(tryGet(reference('kustoCluster', '2023-08-15', 'full'), 'identity'), 'principalId')]" }, "name": { "type": "string", diff --git a/avm/res/kusto/cluster/principal-assignment/main.json b/avm/res/kusto/cluster/principal-assignment/main.json index e58f3f6a1c..506590db13 100644 --- a/avm/res/kusto/cluster/principal-assignment/main.json +++ b/avm/res/kusto/cluster/principal-assignment/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15388375145433684348" + "version": "0.31.34.60546", + "templateHash": "2020706178910686237" }, "name": "Kusto Cluster Principal Assignments", "description": "This module deploys a Kusto Cluster Principal Assignment.", diff --git a/avm/res/kusto/cluster/tests/e2e/defaults/main.test.bicep b/avm/res/kusto/cluster/tests/e2e/defaults/main.test.bicep index da419c6055..92f34c9927 100644 --- a/avm/res/kusto/cluster/tests/e2e/defaults/main.test.bicep +++ b/avm/res/kusto/cluster/tests/e2e/defaults/main.test.bicep @@ -15,7 +15,7 @@ param resourceGroupName string = 'dep-${namePrefix}-kusto.clusters-${serviceShor param resourceLocation string = deployment().location @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') -param serviceShort string = 'akcmin' +param serviceShort string = 'kcmin' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' diff --git a/avm/res/kusto/cluster/tests/e2e/max/bicepconfig.json b/avm/res/kusto/cluster/tests/e2e/max/bicepconfig.json deleted file mode 100644 index 3ce6221617..0000000000 --- a/avm/res/kusto/cluster/tests/e2e/max/bicepconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "experimentalFeaturesEnabled": { - "extensibility": true - } -} diff --git a/avm/res/kusto/cluster/tests/e2e/max/dependencies.bicep b/avm/res/kusto/cluster/tests/e2e/max/dependencies.bicep index 90a309f565..790303be68 100644 --- a/avm/res/kusto/cluster/tests/e2e/max/dependencies.bicep +++ b/avm/res/kusto/cluster/tests/e2e/max/dependencies.bicep @@ -1,33 +1,19 @@ -provider microsoftGraph - @description('Required. The location to deploy resources to.') param location string = resourceGroup().location -@description('Required. The name of the Azure AD group to create.') -param entraIdGroupName string - @description('Required. The name of the managed identity to create.') param managedIdentityName string -var entraIdGroupmailNickname = replace(entraIdGroupName, ' ', '') - resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { name: managedIdentityName location: location } -resource entraIdGroup 'Microsoft.Graph/groups@v1.0' = { - displayName: entraIdGroupName - mailEnabled: false - mailNickname: entraIdGroupmailNickname - securityEnabled: true - uniqueName: entraIdGroupName -} - @description('The resource ID of the created Managed Identity.') output managedIdentityResourceId string = managedIdentity.id @description('The principal ID of the created Managed Identity.') output managedIdentityPrincipalId string = managedIdentity.properties.principalId -output entraIdGroupDisplayName string = entraIdGroup.displayName +@description('The client ID of the created Managed Identity.') +output managedIdentityClientId string = managedIdentity.properties.clientId diff --git a/avm/res/kusto/cluster/tests/e2e/max/main.test.bicep b/avm/res/kusto/cluster/tests/e2e/max/main.test.bicep index 6a27624eaf..93be9a10ff 100644 --- a/avm/res/kusto/cluster/tests/e2e/max/main.test.bicep +++ b/avm/res/kusto/cluster/tests/e2e/max/main.test.bicep @@ -15,7 +15,7 @@ param resourceGroupName string = 'dep-${namePrefix}-kusto.clusters-${serviceShor param resourceLocation string = deployment().location @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') -param serviceShort string = 'akcmax' +param serviceShort string = 'kcmax' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' @@ -37,7 +37,7 @@ module nestedDependencies 'dependencies.bicep' = { params: { location: resourceLocation managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' - entraIdGroupName: 'dep-${namePrefix}-group-${serviceShort}' + // entraIdGroupName: 'dep-${namePrefix}-group-${serviceShort}' } } @@ -64,8 +64,8 @@ module testDeployment '../../../main.bicep' = [ enableAutoScale: true principalAssignments: [ { - principalId: nestedDependencies.outputs.entraIdGroupDisplayName - principalType: 'Group' + principalId: nestedDependencies.outputs.managedIdentityClientId + principalType: 'App' role: 'AllDatabasesViewer' } ] diff --git a/avm/res/kusto/cluster/tests/e2e/pe/main.test.bicep b/avm/res/kusto/cluster/tests/e2e/pe/main.test.bicep index bdb035d2c8..81369d9821 100644 --- a/avm/res/kusto/cluster/tests/e2e/pe/main.test.bicep +++ b/avm/res/kusto/cluster/tests/e2e/pe/main.test.bicep @@ -15,7 +15,7 @@ param resourceGroupName string = 'dep-${namePrefix}-kusto.clusters-${serviceShor param resourceLocation string = deployment().location @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') -param serviceShort string = 'akcpe' +param serviceShort string = 'kcpe' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' diff --git a/avm/res/kusto/cluster/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep b/avm/res/kusto/cluster/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep new file mode 100644 index 0000000000..d71bea248d --- /dev/null +++ b/avm/res/kusto/cluster/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep @@ -0,0 +1,68 @@ +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Kusto Cluster to create.') +param kustoClusterName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource kustoCluster 'Microsoft.Kusto/clusters@2023-08-15' = { + name: kustoClusterName + location: location + sku: { + name: 'Standard_E2ads_v5' + tier: 'Standard' + } + identity: { + type: 'SystemAssigned' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true // Required for encryption to work + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2023-02-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' + } + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-${kustoCluster.id}-Key-Reader-RoleAssignment') + scope: keyVault::key + properties: { + principalId: kustoCluster.identity.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '12338af0-0e69-4776-bea7-57ae8d297424' + ) // Key Vault Crypto User + principalType: 'ServicePrincipal' + } +} + +@description('The name of the created Kusto Cluster.') +output kustoClusterName string = kustoCluster.name + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The name of the created encryption key.') +output keyName string = keyVault::key.name diff --git a/avm/res/kusto/cluster/tests/e2e/system-assigned-cmk-encryption/main.test.bicep b/avm/res/kusto/cluster/tests/e2e/system-assigned-cmk-encryption/main.test.bicep new file mode 100644 index 0000000000..d8164b8446 --- /dev/null +++ b/avm/res/kusto/cluster/tests/e2e/system-assigned-cmk-encryption/main.test.bicep @@ -0,0 +1,66 @@ +targetScope = 'subscription' + +metadata name = 'Using Customer-Managed-Keys with System-Assigned identity' +metadata description = 'This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-kusto.clusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'kcsencr' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + kustoClusterName: '${namePrefix}${serviceShort}001' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: nestedDependencies.outputs.kustoClusterName + sku: 'Standard_E2ads_v5' + customerManagedKey: { + keyName: nestedDependencies.outputs.keyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + } + } + } +] diff --git a/avm/res/kusto/cluster/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep b/avm/res/kusto/cluster/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep new file mode 100644 index 0000000000..583d22ac20 --- /dev/null +++ b/avm/res/kusto/cluster/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep @@ -0,0 +1,64 @@ +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true // Required for encryption to work + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2023-02-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' + } + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Reader-RoleAssignment') + scope: keyVault::key + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '12338af0-0e69-4776-bea7-57ae8d297424' + ) // Key Vault Crypto User + principalType: 'ServicePrincipal' + } +} + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The client ID of the created Managed Identity.') +output managedIdentityClientId string = managedIdentity.properties.clientId + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The name of the created encryption key.') +output keyName string = keyVault::key.name diff --git a/avm/res/kusto/cluster/tests/e2e/user-assigned-cmk-encryption/main.test.bicep b/avm/res/kusto/cluster/tests/e2e/user-assigned-cmk-encryption/main.test.bicep new file mode 100644 index 0000000000..6308726462 --- /dev/null +++ b/avm/res/kusto/cluster/tests/e2e/user-assigned-cmk-encryption/main.test.bicep @@ -0,0 +1,72 @@ +targetScope = 'subscription' + +metadata name = 'Using Customer-Managed-Keys with User-Assigned identity' +metadata description = 'This instance deploys the module using Customer-Managed-Keys using a User-Assigned Identity to access the Customer-Managed-Key secret.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-kusto.clusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'kcuencr' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}0001' + sku: 'Standard_E2ads_v5' + customerManagedKey: { + keyName: nestedDependencies.outputs.keyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } + managedIdentities: { + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + } + } +] diff --git a/avm/res/kusto/cluster/tests/e2e/waf-aligned/main.test.bicep b/avm/res/kusto/cluster/tests/e2e/waf-aligned/main.test.bicep index 6b6610847d..865a070030 100644 --- a/avm/res/kusto/cluster/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/kusto/cluster/tests/e2e/waf-aligned/main.test.bicep @@ -15,7 +15,7 @@ param resourceGroupName string = 'dep-${namePrefix}-kusto.clusters-${serviceShor param resourceLocation string = deployment().location @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') -param serviceShort string = 'akcwaf' +param serviceShort string = 'kcwaf' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' @@ -54,10 +54,6 @@ module testDeployment '../../../main.bicep' = [ location: resourceLocation sku: 'Standard_E2ads_v5' tier: 'Standard' - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } capacity: 3 enableAutoScale: true autoScaleMin: 3 diff --git a/avm/res/kusto/cluster/version.json b/avm/res/kusto/cluster/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/kusto/cluster/version.json +++ b/avm/res/kusto/cluster/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ]