Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Updated Kusto Cluster CMK implementation #3766

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
350 changes: 287 additions & 63 deletions avm/res/kusto/cluster/README.md

Large diffs are not rendered by default.

296 changes: 72 additions & 224 deletions avm/res/kusto/cluster/main.bicep

Large diffs are not rendered by default.

1,121 changes: 615 additions & 506 deletions avm/res/kusto/cluster/main.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions avm/res/kusto/cluster/principal-assignment/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.29.47.4906",
"templateHash": "15388375145433684348"
"version": "0.31.92.45157",
"templateHash": "8181427043902078904"
},
"name": "Kusto Cluster Principal Assignments",
"description": "This module deploys a Kusto Cluster Principal Assignment.",
Expand Down
2 changes: 1 addition & 1 deletion avm/res/kusto/cluster/tests/e2e/defaults/main.test.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -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_#'
Expand Down
5 changes: 0 additions & 5 deletions avm/res/kusto/cluster/tests/e2e/max/bicepconfig.json

This file was deleted.

18 changes: 2 additions & 16 deletions avm/res/kusto/cluster/tests/e2e/max/dependencies.bicep
Original file line number Diff line number Diff line change
@@ -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/[email protected]' = {
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
8 changes: 4 additions & 4 deletions avm/res/kusto/cluster/tests/e2e/max/main.test.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -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_#'
Expand All @@ -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}'
}
}

Expand All @@ -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'
}
]
Expand Down
2 changes: 1 addition & 1 deletion avm/res/kusto/cluster/tests/e2e/pe/main.test.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -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_#'
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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
}
managedIdentities: {
systemAssigned: true
}
}
}
]
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
]
}
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -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_#'
Expand Down Expand Up @@ -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
Expand Down
Loading