From 4022d6ca5d39948175e64e440d4ff02fd05c0f8d Mon Sep 17 00:00:00 2001 From: xuwu1 Date: Mon, 19 Jan 2026 17:14:34 +1100 Subject: [PATCH 1/6] allow databricks workspace to support hsm key --- .../databricks_workspace_resource.go | 196 +++++++++++----- .../databricks_workspace_resource_test.go | 222 +++++++++++++++++- 2 files changed, 353 insertions(+), 65 deletions(-) diff --git a/internal/services/databricks/databricks_workspace_resource.go b/internal/services/databricks/databricks_workspace_resource.go index 36ff4543a774..f19341d0f2d5 100644 --- a/internal/services/databricks/databricks_workspace_resource.go +++ b/internal/services/databricks/databricks_workspace_resource.go @@ -14,10 +14,12 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/keyvault" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" "github.com/hashicorp/go-azure-sdk/resource-manager/databricks/2022-10-01-preview/accessconnector" "github.com/hashicorp/go-azure-sdk/resource-manager/databricks/2024-05-01/workspaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/keyvault/2023-07-01/managedhsms" mlworkspace "github.com/hashicorp/go-azure-sdk/resource-manager/machinelearningservices/2025-06-01/workspaces" "github.com/hashicorp/go-azure-sdk/resource-manager/network/2023-09-01/loadbalancers" "github.com/hashicorp/go-azure-sdk/resource-manager/network/2025-01-01/subnets" @@ -27,8 +29,6 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" "github.com/hashicorp/terraform-provider-azurerm/internal/services/databricks/validate" - keyVaultParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse" - keyVaultValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" resourcesParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/resource/parse" storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -281,24 +281,24 @@ func resourceDatabricksWorkspace() *pluginsdk.Resource { "managed_services_cmk_key_vault_key_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: keyVaultValidate.KeyVaultChildID, + ValidateFunc: keyvault.ValidateNestedItemID(keyvault.VersionTypeVersioned, keyvault.NestedItemTypeKey), }, "managed_services_cmk_key_vault_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: commonids.ValidateKeyVaultID, + ValidateFunc: validation.Any(commonids.ValidateKeyVaultID, managedhsms.ValidateManagedHSMID), }, "managed_disk_cmk_key_vault_key_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: keyVaultValidate.KeyVaultChildID, + ValidateFunc: keyvault.ValidateNestedItemID(keyvault.VersionTypeVersioned, keyvault.NestedItemTypeKey), }, "managed_disk_cmk_key_vault_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: commonids.ValidateKeyVaultID, + ValidateFunc: validation.Any(commonids.ValidateKeyVaultID, managedhsms.ValidateManagedHSMID), }, "managed_disk_cmk_rotation_to_latest_version_enabled": { @@ -466,6 +466,7 @@ func resourceDatabricksWorkspaceCreate(d *pluginsdk.ResourceData, meta interface lbClient := meta.(*clients.Client).LoadBalancers.LoadBalancersClient subnetsClient := meta.(*clients.Client).Network.Subnets keyVaultsClient := meta.(*clients.Client).KeyVault + managedHSMsClient := meta.(*clients.Client).ManagedHSMs subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() @@ -485,7 +486,6 @@ func resourceDatabricksWorkspaceCreate(d *pluginsdk.ResourceData, meta interface var backendPoolName, loadBalancerId string skuName := d.Get("sku").(string) managedResourceGroupName := d.Get("managed_resource_group_name").(string) - location := location.Normalize(d.Get("location").(string)) backendPool := d.Get("load_balancer_backend_address_pool_id").(string) if backendPool != "" { @@ -595,27 +595,44 @@ func resourceDatabricksWorkspaceCreate(d *pluginsdk.ResourceData, meta interface diskResourceSubscriptionId := commonids.NewSubscriptionID(id.SubscriptionId) if servicesKeyVaultId != "" { - // If they passed the 'managed_cmk_key_vault_id' parse the Key Vault ID - // to extract the correct key vault subscription for the exists call... - v, err := commonids.ParseKeyVaultID(servicesKeyVaultId) - if err != nil { - return fmt.Errorf("parsing %q as a Key Vault ID: %+v", servicesKeyVaultId, err) - } + // Parse the Key Vault or Managed HSM ID to extract the correct subscription for the exists call. + if v, err := commonids.ParseKeyVaultID(servicesKeyVaultId); err == nil { + servicesResourceSubscriptionId = commonids.NewSubscriptionID(v.SubscriptionId) + } else { + managedHsmId, managedHsmErr := managedhsms.ParseManagedHSMID(servicesKeyVaultId) + if managedHsmErr != nil { + return fmt.Errorf("parsing %q as a Key Vault or Managed HSM ID: %v; %v", servicesKeyVaultId, err, managedHsmErr) + } - servicesResourceSubscriptionId = commonids.NewSubscriptionID(v.SubscriptionId) + servicesResourceSubscriptionId = commonids.NewSubscriptionID(managedHsmId.SubscriptionId) + } } if servicesKeyId != "" { setEncrypt = true - key, err := keyVaultParse.ParseNestedItemID(servicesKeyId) + key, err := keyvault.ParseNestedItemID(servicesKeyId, keyvault.VersionTypeAny, keyvault.NestedItemTypeKey) if err != nil { return err } - // make sure the key vault exists - _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, servicesResourceSubscriptionId, key.KeyVaultBaseUrl) - if err != nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Key Vault in subscription %q at URL %q: %+v", servicesResourceSubscriptionId, key.KeyVaultBaseUrl, err) + if key.IsManagedHSM() { + domainSuffix, ok := meta.(*clients.Client).Account.Environment.ManagedHSM.DomainSuffix() + if !ok { + return fmt.Errorf("could not determine Managed HSM domain suffix for environment %q", meta.(*clients.Client).Account.Environment.Name) + } + + managedHsmId, err := managedHSMsClient.ManagedHSMIDFromBaseUrl(ctx, servicesResourceSubscriptionId, key.KeyVaultBaseURL, domainSuffix) + if err != nil { + return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Managed HSM in subscription %q at URL %q: %+v", servicesResourceSubscriptionId, key.KeyVaultBaseURL, err) + } + if managedHsmId == nil { + return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Managed HSM in subscription %q at URL %q: not found", servicesResourceSubscriptionId, key.KeyVaultBaseURL) + } + } else { + // make sure the key vault exists + if _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, servicesResourceSubscriptionId, key.KeyVaultBaseURL); err != nil { + return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Key Vault in subscription %q at URL %q: %+v", servicesResourceSubscriptionId, key.KeyVaultBaseURL, err) + } } encrypt.Entities.ManagedServices = &workspaces.EncryptionV2{ @@ -623,33 +640,51 @@ func resourceDatabricksWorkspaceCreate(d *pluginsdk.ResourceData, meta interface KeyVaultProperties: &workspaces.EncryptionV2KeyVaultProperties{ KeyName: key.Name, KeyVersion: key.Version, - KeyVaultUri: key.KeyVaultBaseUrl, + KeyVaultUri: key.KeyVaultBaseURL, }, } } if diskKeyVaultId != "" { - // If they passed the 'managed_disk_cmk_key_vault_id' parse the Key Vault ID - // to extract the correct key vault subscription for the exists call... - v, err := commonids.ParseKeyVaultID(diskKeyVaultId) - if err != nil { - return fmt.Errorf("parsing %q as a Key Vault ID: %+v", diskKeyVaultId, err) - } + // If they passed the 'managed_disk_cmk_key_vault_id' parse the Key Vault or Managed HSM ID + // to extract the correct subscription for the exists call... + if v, err := commonids.ParseKeyVaultID(diskKeyVaultId); err == nil { + diskResourceSubscriptionId = commonids.NewSubscriptionID(v.SubscriptionId) + } else { + managedHsmId, managedHsmErr := managedhsms.ParseManagedHSMID(diskKeyVaultId) + if managedHsmErr != nil { + return fmt.Errorf("parsing %q as a Key Vault or Managed HSM ID: %v; %v", diskKeyVaultId, err, managedHsmErr) + } - diskResourceSubscriptionId = commonids.NewSubscriptionID(v.SubscriptionId) + diskResourceSubscriptionId = commonids.NewSubscriptionID(managedHsmId.SubscriptionId) + } } if diskKeyId != "" { setEncrypt = true - key, err := keyVaultParse.ParseNestedItemID(diskKeyId) + key, err := keyvault.ParseNestedItemID(diskKeyId, keyvault.VersionTypeAny, keyvault.NestedItemTypeKey) if err != nil { return err } - // make sure the key vault exists - _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, diskResourceSubscriptionId, key.KeyVaultBaseUrl) - if err != nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Key Vault in subscription %q at URL %q: %+v", diskResourceSubscriptionId, key.KeyVaultBaseUrl, err) + if key.IsManagedHSM() { + domainSuffix, ok := meta.(*clients.Client).Account.Environment.ManagedHSM.DomainSuffix() + if !ok { + return fmt.Errorf("could not determine Managed HSM domain suffix for environment %q", meta.(*clients.Client).Account.Environment.Name) + } + + managedHsmId, err := managedHSMsClient.ManagedHSMIDFromBaseUrl(ctx, diskResourceSubscriptionId, key.KeyVaultBaseURL, domainSuffix) + if err != nil { + return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Managed HSM in subscription %q at URL %q: %+v", diskResourceSubscriptionId, key.KeyVaultBaseURL, err) + } + if managedHsmId == nil { + return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Managed HSM in subscription %q at URL %q: not found", diskResourceSubscriptionId, key.KeyVaultBaseURL) + } + } else { + // make sure the key vault exists + if _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, diskResourceSubscriptionId, key.KeyVaultBaseURL); err != nil { + return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Key Vault in subscription %q at URL %q: %+v", diskResourceSubscriptionId, key.KeyVaultBaseURL, err) + } } encrypt.Entities.ManagedDisk = &workspaces.ManagedDiskEncryption{ @@ -657,7 +692,7 @@ func resourceDatabricksWorkspaceCreate(d *pluginsdk.ResourceData, meta interface KeyVaultProperties: workspaces.ManagedDiskEncryptionKeyVaultProperties{ KeyName: key.Name, KeyVersion: key.Version, - KeyVaultUri: key.KeyVaultBaseUrl, + KeyVaultUri: key.KeyVaultBaseURL, }, } } @@ -672,7 +707,7 @@ func resourceDatabricksWorkspaceCreate(d *pluginsdk.ResourceData, meta interface Sku: &workspaces.Sku{ Name: skuName, }, - Location: location, + Location: location.Normalize(d.Get("location").(string)), Properties: workspaces.WorkspaceProperties{ PublicNetworkAccess: &publicNetworkAccess, ManagedResourceGroupId: managedResourceGroupID, @@ -875,7 +910,7 @@ func resourceDatabricksWorkspaceRead(d *pluginsdk.ResourceData, meta interface{} if encryption := model.Properties.Encryption; encryption != nil { if encryptionProps := encryption.Entities.ManagedServices; encryptionProps != nil { if encryptionProps.KeyVaultProperties.KeyVaultUri != "" { - key, err := keyVaultParse.NewNestedItemID(encryptionProps.KeyVaultProperties.KeyVaultUri, keyVaultParse.NestedItemTypeKey, encryptionProps.KeyVaultProperties.KeyName, encryptionProps.KeyVaultProperties.KeyVersion) + key, err := keyvault.NewNestedItemID(encryptionProps.KeyVaultProperties.KeyVaultUri, keyvault.NestedItemTypeKey, encryptionProps.KeyVaultProperties.KeyName, encryptionProps.KeyVaultProperties.KeyVersion) if err == nil { servicesKeyId = key.ID() } @@ -888,7 +923,7 @@ func resourceDatabricksWorkspaceRead(d *pluginsdk.ResourceData, meta interface{} if encryption := model.Properties.Encryption; encryption != nil { if encryptionProps := encryption.Entities.ManagedDisk; encryptionProps != nil { if encryptionProps.KeyVaultProperties.KeyVaultUri != "" { - key, err := keyVaultParse.NewNestedItemID(encryptionProps.KeyVaultProperties.KeyVaultUri, keyVaultParse.NestedItemTypeKey, encryptionProps.KeyVaultProperties.KeyName, encryptionProps.KeyVaultProperties.KeyVersion) + key, err := keyvault.NewNestedItemID(encryptionProps.KeyVaultProperties.KeyVaultUri, keyvault.NestedItemTypeKey, encryptionProps.KeyVaultProperties.KeyName, encryptionProps.KeyVaultProperties.KeyVersion) if err == nil { diskKeyId = key.ID() } @@ -948,6 +983,7 @@ func resourceDatabricksWorkspaceUpdate(d *pluginsdk.ResourceData, meta interface client := meta.(*clients.Client).DataBricks.WorkspacesClient acClient := meta.(*clients.Client).DataBricks.AccessConnectorClient keyVaultsClient := meta.(*clients.Client).KeyVault + managedHSMsClient := meta.(*clients.Client).ManagedHSMs ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) defer cancel() @@ -1178,27 +1214,45 @@ func resourceDatabricksWorkspaceUpdate(d *pluginsdk.ResourceData, meta interface diskResourceSubscriptionId := commonids.NewSubscriptionID(id.SubscriptionId) if servicesKeyVaultId != "" { - // If they passed the 'managed_cmk_key_vault_id' parse the Key Vault ID - // to extract the correct key vault subscription for the exists call... - v, err := commonids.ParseKeyVaultID(servicesKeyVaultId) - if err != nil { - return err - } + // If they passed the 'managed_cmk_key_vault_id' parse the Key Vault or Managed HSM ID + // to extract the correct subscription for the exists call... + if v, err := commonids.ParseKeyVaultID(servicesKeyVaultId); err == nil { + servicesResourceSubscriptionId = commonids.NewSubscriptionID(v.SubscriptionId) + } else { + managedHsmId, managedHsmErr := managedhsms.ParseManagedHSMID(servicesKeyVaultId) + if managedHsmErr != nil { + return fmt.Errorf("parsing %q as a Key Vault or Managed HSM ID: %v; %v", servicesKeyVaultId, err, managedHsmErr) + } - servicesResourceSubscriptionId = commonids.NewSubscriptionID(v.SubscriptionId) + servicesResourceSubscriptionId = commonids.NewSubscriptionID(managedHsmId.SubscriptionId) + } } if servicesKeyId != "" { setEncrypt = true - key, err := keyVaultParse.ParseNestedItemID(servicesKeyId) + key, err := keyvault.ParseNestedItemID(servicesKeyId, keyvault.VersionTypeAny, keyvault.NestedItemTypeKey) if err != nil { return err } - // make sure the key vault exists - _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, servicesResourceSubscriptionId, key.KeyVaultBaseUrl) - if err != nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Key Vault in subscription %q at URL %q: %+v", servicesResourceSubscriptionId, key.KeyVaultBaseUrl, err) + if key.IsManagedHSM() { + domainSuffix, ok := meta.(*clients.Client).Account.Environment.ManagedHSM.DomainSuffix() + if !ok { + return fmt.Errorf("could not determine Managed HSM domain suffix for environment %q", meta.(*clients.Client).Account.Environment.Name) + } + + managedHsmId, err := managedHSMsClient.ManagedHSMIDFromBaseUrl(ctx, servicesResourceSubscriptionId, key.KeyVaultBaseURL, domainSuffix) + if err != nil { + return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Managed HSM in subscription %q at URL %q: %+v", servicesResourceSubscriptionId, key.KeyVaultBaseURL, err) + } + if managedHsmId == nil { + return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Managed HSM in subscription %q at URL %q: not found", servicesResourceSubscriptionId, key.KeyVaultBaseURL) + } + } else { + // make sure the key vault exists + if _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, servicesResourceSubscriptionId, key.KeyVaultBaseURL); err != nil { + return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Key Vault in subscription %q at URL %q: %+v", servicesResourceSubscriptionId, key.KeyVaultBaseURL, err) + } } encrypt.Entities.ManagedServices = &workspaces.EncryptionV2{ @@ -1206,33 +1260,51 @@ func resourceDatabricksWorkspaceUpdate(d *pluginsdk.ResourceData, meta interface KeyVaultProperties: &workspaces.EncryptionV2KeyVaultProperties{ KeyName: key.Name, KeyVersion: key.Version, - KeyVaultUri: key.KeyVaultBaseUrl, + KeyVaultUri: key.KeyVaultBaseURL, }, } } if diskKeyVaultId != "" { - // If they passed the 'managed_disk_cmk_key_vault_id' parse the Key Vault ID - // to extract the correct key vault subscription for the exists call... - v, err := commonids.ParseKeyVaultID(diskKeyVaultId) - if err != nil { - return err - } + // If they passed the 'managed_disk_cmk_key_vault_id' parse the Key Vault or Managed HSM ID + // to extract the correct subscription for the exists call... + if v, err := commonids.ParseKeyVaultID(diskKeyVaultId); err == nil { + diskResourceSubscriptionId = commonids.NewSubscriptionID(v.SubscriptionId) + } else { + managedHsmId, managedHsmErr := managedhsms.ParseManagedHSMID(diskKeyVaultId) + if managedHsmErr != nil { + return fmt.Errorf("parsing %q as a Key Vault or Managed HSM ID: %v; %v", diskKeyVaultId, err, managedHsmErr) + } - diskResourceSubscriptionId = commonids.NewSubscriptionID(v.SubscriptionId) + diskResourceSubscriptionId = commonids.NewSubscriptionID(managedHsmId.SubscriptionId) + } } if diskKeyId != "" { setEncrypt = true - key, err := keyVaultParse.ParseNestedItemID(diskKeyId) + key, err := keyvault.ParseNestedItemID(diskKeyId, keyvault.VersionTypeAny, keyvault.NestedItemTypeKey) if err != nil { return err } - // make sure the key vault exists - _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, diskResourceSubscriptionId, key.KeyVaultBaseUrl) - if err != nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Key Vault in subscription %q at URL %q: %+v", diskResourceSubscriptionId, key.KeyVaultBaseUrl, err) + if key.IsManagedHSM() { + domainSuffix, ok := meta.(*clients.Client).Account.Environment.ManagedHSM.DomainSuffix() + if !ok { + return fmt.Errorf("could not determine Managed HSM domain suffix for environment %q", meta.(*clients.Client).Account.Environment.Name) + } + + managedHsmId, err := managedHSMsClient.ManagedHSMIDFromBaseUrl(ctx, diskResourceSubscriptionId, key.KeyVaultBaseURL, domainSuffix) + if err != nil { + return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Managed HSM in subscription %q at URL %q: %+v", diskResourceSubscriptionId, key.KeyVaultBaseURL, err) + } + if managedHsmId == nil { + return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Managed HSM in subscription %q at URL %q: not found", diskResourceSubscriptionId, key.KeyVaultBaseURL) + } + } else { + // make sure the key vault exists + if _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, diskResourceSubscriptionId, key.KeyVaultBaseURL); err != nil { + return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Key Vault in subscription %q at URL %q: %+v", diskResourceSubscriptionId, key.KeyVaultBaseURL, err) + } } encrypt.Entities.ManagedDisk = &workspaces.ManagedDiskEncryption{ @@ -1240,7 +1312,7 @@ func resourceDatabricksWorkspaceUpdate(d *pluginsdk.ResourceData, meta interface KeyVaultProperties: workspaces.ManagedDiskEncryptionKeyVaultProperties{ KeyName: key.Name, KeyVersion: key.Version, - KeyVaultUri: key.KeyVaultBaseUrl, + KeyVaultUri: key.KeyVaultBaseURL, }, } } diff --git a/internal/services/databricks/databricks_workspace_resource_test.go b/internal/services/databricks/databricks_workspace_resource_test.go index 84914bba0096..cd202557f416 100644 --- a/internal/services/databricks/databricks_workspace_resource_test.go +++ b/internal/services/databricks/databricks_workspace_resource_test.go @@ -321,6 +321,27 @@ func TestAccDatabricksWorkspace_managedDiskCMK(t *testing.T) { }) } +func TestAccDatabricksWorkspace_managedHsmCMK(t *testing.T) { + if os.Getenv("ARM_TEST_HSM_KEY") == "" { + t.Skip("Skipping as ARM_TEST_HSM_KEY is not specified") + return + } + + data := acceptance.BuildTestData(t, "azurerm_databricks_workspace", "test") + databricksPrincipalID := getDatabricksPrincipalId(data.Client().SubscriptionID) + r := DatabricksWorkspaceResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.managedHsmCMK(data, databricksPrincipalID), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccDatabricksWorkspace_managedServicesRootDbfsCMKAndPrivateLink(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_databricks_workspace", "test") databricksPrincipalID := getDatabricksPrincipalId(data.Client().SubscriptionID) @@ -562,8 +583,12 @@ func TestAccDatabricksWorkspace_withForceDeleteSetToFalse(t *testing.T) { func getDatabricksPrincipalId(subscriptionId string) string { databricksPrincipalID := "bb9ef821-a78b-4312-90cc-5ece3fad3430" - if strings.HasPrefix(strings.ToLower(subscriptionId), "85b3dbca") { + if sub := strings.ToLower(subscriptionId); strings.HasPrefix(sub, "85b3dbca") { databricksPrincipalID = "fe597bb2-377c-44f1-8515-82c8a1a62e3d" + } else if strings.HasPrefix(sub, "2350ac68") { + // update databricks principal id of tenant: azclitools20251114 + // intended not use azuread data source as test service principal may not have directory read permissions + databricksPrincipalID = "2f1b5070-cfa2-4b49-8a3d-ba64dfcd4a83" } return databricksPrincipalID @@ -583,13 +608,13 @@ func altSubscriptionCheck() *DatabricksWorkspaceAlternateSubscription { } } -func (DatabricksWorkspaceResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { +func (DatabricksWorkspaceResource) Exists(ctx context.Context, acctClients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := workspaces.ParseWorkspaceID(state.ID) if err != nil { return nil, err } - resp, err := clients.DataBricks.WorkspacesClient.Get(ctx, *id) + resp, err := acctClients.DataBricks.WorkspacesClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("retrieving Analysis Services Server %q (resource group: %q): %+v", id.WorkspaceName, id.ResourceGroupName, err) } @@ -2281,6 +2306,197 @@ resource "azurerm_key_vault_access_policy" "databricks" { `, data.RandomInteger, data.Locations.Primary, data.RandomString, databricksPrincipalID) } +func (r DatabricksWorkspaceResource) managedHsmTemplate(data acceptance.TestData, databricksPrincipalID string) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + key_vault { + purge_soft_deleted_hardware_security_modules_on_destroy = true + } + } +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-databricks-hsm-%[1]d" + location = "%[2]s" +} + +resource "azurerm_key_vault" "security_domain" { + name = "acctest%[3]s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + soft_delete_retention_days = 7 + + access_policy { + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "Create", + "Delete", + "Get", + "Purge", + "Recover", + "Update", + "GetRotationPolicy", + ] + + secret_permissions = [ + "Delete", + "Get", + "Set", + ] + + certificate_permissions = [ + "Create", + "Delete", + "DeleteIssuers", + "Get", + "Purge", + "Update" + ] + } +} + +resource "azurerm_key_vault_certificate" "security_domain" { + count = 3 + name = "acchsmcert${count.index}" + key_vault_id = azurerm_key_vault.security_domain.id + + certificate_policy { + issuer_parameters { + name = "Self" + } + + key_properties { + exportable = true + key_size = 2048 + key_type = "RSA" + reuse_key = true + } + + lifetime_action { + action { + action_type = "AutoRenew" + } + trigger { + days_before_expiry = 30 + } + } + + secret_properties { + content_type = "application/x-pkcs12" + } + + x509_certificate_properties { + key_usage = [ + "cRLSign", + "dataEncipherment", + "digitalSignature", + "keyAgreement", + "keyCertSign", + "keyEncipherment", + ] + subject = "CN=hello-world" + validity_in_months = 12 + } + } +} + +resource "azurerm_key_vault_managed_hardware_security_module" "test" { + name = "accHsm%[1]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku_name = "Standard_B1" + tenant_id = data.azurerm_client_config.current.tenant_id + admin_object_ids = [data.azurerm_client_config.current.object_id] + purge_protection_enabled = false + + security_domain_key_vault_certificate_ids = [for cert in azurerm_key_vault_certificate.security_domain : cert.id] + security_domain_quorum = 3 +} + +data "azurerm_key_vault_managed_hardware_security_module_role_definition" "crypto_user" { + managed_hsm_id = azurerm_key_vault_managed_hardware_security_module.test.id + name = "21dbd100-6940-42c2-9190-5d6cb909625b" +} + +data "azurerm_key_vault_managed_hardware_security_module_role_definition" "crypto_officer" { + managed_hsm_id = azurerm_key_vault_managed_hardware_security_module.test.id + name = "515eb02d-2335-4d2d-92f2-b1cbdf9c3778" +} + +resource "azurerm_key_vault_managed_hardware_security_module_role_assignment" "crypto_user_admin" { + managed_hsm_id = azurerm_key_vault_managed_hardware_security_module.test.id + name = "1e243909-064c-6ac3-84e9-1c8bf8d6ad22" + scope = "/keys" + role_definition_id = data.azurerm_key_vault_managed_hardware_security_module_role_definition.crypto_user.resource_manager_id + principal_id = data.azurerm_client_config.current.object_id +} + +resource "azurerm_key_vault_managed_hardware_security_module_role_assignment" "crypto_officer_admin" { + managed_hsm_id = azurerm_key_vault_managed_hardware_security_module.test.id + name = "1e243909-064c-6ac3-84e9-1c8bf8d6ad23" + scope = "/keys" + role_definition_id = data.azurerm_key_vault_managed_hardware_security_module_role_definition.crypto_officer.resource_manager_id + principal_id = data.azurerm_client_config.current.object_id +} + +resource "azurerm_key_vault_managed_hardware_security_module_role_assignment" "crypto_user_databricks" { + managed_hsm_id = azurerm_key_vault_managed_hardware_security_module.test.id + name = "2b3c4d5e-6f70-4780-8a91-b2c3d4e5f6a7" + scope = "/keys" + role_definition_id = data.azurerm_key_vault_managed_hardware_security_module_role_definition.crypto_user.resource_manager_id + principal_id = "%[4]s" +} + +resource "azurerm_key_vault_managed_hardware_security_module_key" "primary" { + name = "acctest-hsmk1-%[3]s" + managed_hsm_id = azurerm_key_vault_managed_hardware_security_module.test.id + key_type = "RSA-HSM" + key_size = 2048 + key_opts = ["wrapKey", "unwrapKey"] + + depends_on = [ + azurerm_key_vault_managed_hardware_security_module_role_assignment.crypto_user_admin, + azurerm_key_vault_managed_hardware_security_module_role_assignment.crypto_officer_admin, + ] +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString, databricksPrincipalID) +} + +func (r DatabricksWorkspaceResource) managedHsmCMK(data acceptance.TestData, databricksPrincipalID string) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_databricks_workspace" "test" { + name = "acctestDBW-hsm-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "premium" + managed_resource_group_name = "acctestRG-DBW-%[2]d-managed" + + customer_managed_key_enabled = true + + managed_services_cmk_key_vault_key_id = azurerm_key_vault_managed_hardware_security_module_key.primary.versioned_id + managed_disk_cmk_key_vault_key_id = azurerm_key_vault_managed_hardware_security_module_key.primary.versioned_id + + tags = { + Environment = "Production" + Pricing = "Premium" + } + + depends_on = [ + azurerm_key_vault_managed_hardware_security_module_role_assignment.crypto_user_databricks, + ] +} +`, r.managedHsmTemplate(data, databricksPrincipalID), data.RandomInteger) +} + func (DatabricksWorkspaceResource) managedServicesRootDbfsCMKAndPrivateLink(data acceptance.TestData, databricksPrincipalID string) string { return fmt.Sprintf(` provider "azurerm" { From 701722fa908a388d60489333291a04cd97359613 Mon Sep 17 00:00:00 2001 From: xuwu1 Date: Tue, 20 Jan 2026 11:37:58 +1100 Subject: [PATCH 2/6] update error wording --- .../databricks_workspace_resource.go | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/services/databricks/databricks_workspace_resource.go b/internal/services/databricks/databricks_workspace_resource.go index f19341d0f2d5..64810fcdb93b 100644 --- a/internal/services/databricks/databricks_workspace_resource.go +++ b/internal/services/databricks/databricks_workspace_resource.go @@ -623,15 +623,15 @@ func resourceDatabricksWorkspaceCreate(d *pluginsdk.ResourceData, meta interface managedHsmId, err := managedHSMsClient.ManagedHSMIDFromBaseUrl(ctx, servicesResourceSubscriptionId, key.KeyVaultBaseURL, domainSuffix) if err != nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Managed HSM in subscription %q at URL %q: %+v", servicesResourceSubscriptionId, key.KeyVaultBaseURL, err) + return fmt.Errorf("retrieving managed service Managed HSM at URL %q: %+v", key.KeyVaultBaseURL, err) } if managedHsmId == nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Managed HSM in subscription %q at URL %q: not found", servicesResourceSubscriptionId, key.KeyVaultBaseURL) + return fmt.Errorf("retrieving managed service Managed HSM at URL %q: not found", key.KeyVaultBaseURL) } } else { // make sure the key vault exists if _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, servicesResourceSubscriptionId, key.KeyVaultBaseURL); err != nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Key Vault in subscription %q at URL %q: %+v", servicesResourceSubscriptionId, key.KeyVaultBaseURL, err) + return fmt.Errorf("retrieving Key Vault at URL %q: %+v", key.KeyVaultBaseURL, err) } } @@ -675,15 +675,15 @@ func resourceDatabricksWorkspaceCreate(d *pluginsdk.ResourceData, meta interface managedHsmId, err := managedHSMsClient.ManagedHSMIDFromBaseUrl(ctx, diskResourceSubscriptionId, key.KeyVaultBaseURL, domainSuffix) if err != nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Managed HSM in subscription %q at URL %q: %+v", diskResourceSubscriptionId, key.KeyVaultBaseURL, err) + return fmt.Errorf("retrieving managed disk Managed HSM at URL %q: %+v", key.KeyVaultBaseURL, err) } if managedHsmId == nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Managed HSM in subscription %q at URL %q: not found", diskResourceSubscriptionId, key.KeyVaultBaseURL) + return fmt.Errorf("retrieving managed disk Managed HSM at URL %q: not found", key.KeyVaultBaseURL) } } else { // make sure the key vault exists if _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, diskResourceSubscriptionId, key.KeyVaultBaseURL); err != nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Key Vault in subscription %q at URL %q: %+v", diskResourceSubscriptionId, key.KeyVaultBaseURL, err) + return fmt.Errorf("retrieving for managed disk Key Vault at URL %q: %+v", key.KeyVaultBaseURL, err) } } @@ -1243,15 +1243,15 @@ func resourceDatabricksWorkspaceUpdate(d *pluginsdk.ResourceData, meta interface managedHsmId, err := managedHSMsClient.ManagedHSMIDFromBaseUrl(ctx, servicesResourceSubscriptionId, key.KeyVaultBaseURL, domainSuffix) if err != nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Managed HSM in subscription %q at URL %q: %+v", servicesResourceSubscriptionId, key.KeyVaultBaseURL, err) + return fmt.Errorf("retrieving managed services Managed HSM at URL %q: %+v", key.KeyVaultBaseURL, err) } if managedHsmId == nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Managed HSM in subscription %q at URL %q: not found", servicesResourceSubscriptionId, key.KeyVaultBaseURL) + return fmt.Errorf("retrieving managed services Managed HSM at URL %q: not found", key.KeyVaultBaseURL) } } else { // make sure the key vault exists if _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, servicesResourceSubscriptionId, key.KeyVaultBaseURL); err != nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Key Vault in subscription %q at URL %q: %+v", servicesResourceSubscriptionId, key.KeyVaultBaseURL, err) + return fmt.Errorf("retrieving managed services Key Vault at URL %q: %+v", key.KeyVaultBaseURL, err) } } @@ -1295,15 +1295,15 @@ func resourceDatabricksWorkspaceUpdate(d *pluginsdk.ResourceData, meta interface managedHsmId, err := managedHSMsClient.ManagedHSMIDFromBaseUrl(ctx, diskResourceSubscriptionId, key.KeyVaultBaseURL, domainSuffix) if err != nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Managed HSM in subscription %q at URL %q: %+v", diskResourceSubscriptionId, key.KeyVaultBaseURL, err) + return fmt.Errorf("retrieving managed disk Managed HSM at URL %q: %+v", key.KeyVaultBaseURL, err) } if managedHsmId == nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Managed HSM in subscription %q at URL %q: not found", diskResourceSubscriptionId, key.KeyVaultBaseURL) + return fmt.Errorf("retrieving managed disk Managed HSM at URL %q: not found", key.KeyVaultBaseURL) } } else { // make sure the key vault exists if _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, diskResourceSubscriptionId, key.KeyVaultBaseURL); err != nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Key Vault in subscription %q at URL %q: %+v", diskResourceSubscriptionId, key.KeyVaultBaseURL, err) + return fmt.Errorf("retrieving managed disk Key Vault at URL %q: %+v", key.KeyVaultBaseURL, err) } } From 7430d53085d7b81162ef211427558070a580ff67 Mon Sep 17 00:00:00 2001 From: xuwu1 Date: Tue, 20 Jan 2026 17:07:21 +1100 Subject: [PATCH 3/6] add read databricks service principal from envrionment variable first --- .../databricks/databricks_workspace_resource_test.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/internal/services/databricks/databricks_workspace_resource_test.go b/internal/services/databricks/databricks_workspace_resource_test.go index cd202557f416..3cdc926ae82d 100644 --- a/internal/services/databricks/databricks_workspace_resource_test.go +++ b/internal/services/databricks/databricks_workspace_resource_test.go @@ -321,7 +321,7 @@ func TestAccDatabricksWorkspace_managedDiskCMK(t *testing.T) { }) } -func TestAccDatabricksWorkspace_managedHsmCMK(t *testing.T) { +func TestAccDatabricksWorkspace_managedHSMCMK(t *testing.T) { if os.Getenv("ARM_TEST_HSM_KEY") == "" { t.Skip("Skipping as ARM_TEST_HSM_KEY is not specified") return @@ -333,7 +333,7 @@ func TestAccDatabricksWorkspace_managedHsmCMK(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.managedHsmCMK(data, databricksPrincipalID), + Config: r.managedHSMCMK(data, databricksPrincipalID), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -582,6 +582,10 @@ func TestAccDatabricksWorkspace_withForceDeleteSetToFalse(t *testing.T) { } func getDatabricksPrincipalId(subscriptionId string) string { + if envID := os.Getenv("ARM_DATABRICKS_SERVICE_PRINCIPAL_ID"); envID != "" { + return envID + } + databricksPrincipalID := "bb9ef821-a78b-4312-90cc-5ece3fad3430" if sub := strings.ToLower(subscriptionId); strings.HasPrefix(sub, "85b3dbca") { databricksPrincipalID = "fe597bb2-377c-44f1-8515-82c8a1a62e3d" @@ -2469,7 +2473,7 @@ resource "azurerm_key_vault_managed_hardware_security_module_key" "primary" { `, data.RandomInteger, data.Locations.Primary, data.RandomString, databricksPrincipalID) } -func (r DatabricksWorkspaceResource) managedHsmCMK(data acceptance.TestData, databricksPrincipalID string) string { +func (r DatabricksWorkspaceResource) managedHSMCMK(data acceptance.TestData, databricksPrincipalID string) string { return fmt.Sprintf(` %[1]s From 8d40ae635cd4154b6d6711ed33c6893735f1230c Mon Sep 17 00:00:00 2001 From: xuwu1 Date: Mon, 30 Mar 2026 15:38:39 +1100 Subject: [PATCH 4/6] fix acctest merge conflict --- .../services/databricks/databricks_workspace_resource_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/services/databricks/databricks_workspace_resource_test.go b/internal/services/databricks/databricks_workspace_resource_test.go index 835c77535334..1c2d6d4d0055 100644 --- a/internal/services/databricks/databricks_workspace_resource_test.go +++ b/internal/services/databricks/databricks_workspace_resource_test.go @@ -2633,7 +2633,6 @@ resource "azurerm_key_vault_access_policy" "diskencryption" { "GetRotationPolicy", "UnwrapKey", "WrapKey", ->>>>>>> origin/main ] } `, data.RandomInteger, data.Locations.Primary, data.RandomString, databricksPrincipalID) From 40fac0ac10c68287113eb090105b1e05c8e8ed92 Mon Sep 17 00:00:00 2001 From: xuwu1 Date: Mon, 30 Mar 2026 15:59:22 +1100 Subject: [PATCH 5/6] restore acctest env of hsm --- .../services/databricks/databricks_workspace_resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/databricks/databricks_workspace_resource_test.go b/internal/services/databricks/databricks_workspace_resource_test.go index 1c2d6d4d0055..182b10a8df1a 100644 --- a/internal/services/databricks/databricks_workspace_resource_test.go +++ b/internal/services/databricks/databricks_workspace_resource_test.go @@ -322,7 +322,7 @@ func TestAccDatabricksWorkspace_managedDiskCMK(t *testing.T) { } func TestAccDatabricksWorkspace_managedHSMCMK(t *testing.T) { - if os.Getenv("ARM_TEST_HSM_KEY2") == "" { + if os.Getenv("ARM_TEST_HSM_KEY") == "" { t.Skip("Skipping as ARM_TEST_HSM_KEY is not specified") return } From 1ed1a547d5df28cd658497d1cf4d7c7be9f1fd6c Mon Sep 17 00:00:00 2001 From: xuwu1 Date: Mon, 30 Mar 2026 16:08:53 +1100 Subject: [PATCH 6/6] acctest terrafmt --- .../services/databricks/databricks_workspace_resource_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/services/databricks/databricks_workspace_resource_test.go b/internal/services/databricks/databricks_workspace_resource_test.go index 182b10a8df1a..860a7c07d4ec 100644 --- a/internal/services/databricks/databricks_workspace_resource_test.go +++ b/internal/services/databricks/databricks_workspace_resource_test.go @@ -2339,6 +2339,7 @@ provider "azurerm" { purge_soft_deleted_hardware_security_modules_on_destroy = true } } +} data "azurerm_client_config" "current" {}