From 6bab973790c46b74dfe928f2a35d35ce83c06940 Mon Sep 17 00:00:00 2001 From: wuxu92 Date: Thu, 28 Nov 2024 14:16:49 +0800 Subject: [PATCH] data bricks workspaces managed service/disk support managed hsm key, deprecate xx_key_vault_id for cross subscription support as not required add deprecate in 5.0 beta --- .../key_vault_or_managed_hsm_key.go | 50 ++ .../databricks_workspace_resource.go | 768 +++++++++--------- .../databricks_workspace_resource_test.go | 541 ++++++++++-- website/docs/5.0-upgrade-guide.html.markdown | 4 + .../docs/r/databricks_workspace.html.markdown | 16 +- 5 files changed, 876 insertions(+), 503 deletions(-) diff --git a/internal/customermanagedkeys/key_vault_or_managed_hsm_key.go b/internal/customermanagedkeys/key_vault_or_managed_hsm_key.go index 2131964f73f3..e6a651070cb4 100644 --- a/internal/customermanagedkeys/key_vault_or_managed_hsm_key.go +++ b/internal/customermanagedkeys/key_vault_or_managed_hsm_key.go @@ -5,7 +5,9 @@ package customermanagedkeys import ( "fmt" + "strings" + "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-sdk/sdk/environments" "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse" hsmParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/managedhsm/parse" @@ -85,6 +87,48 @@ func (k *KeyVaultOrManagedHSMKey) BaseUri() string { return "" } +func (k *KeyVaultOrManagedHSMKey) Name() string { + if k.KeyVaultKeyId != nil { + return k.KeyVaultKeyId.Name + } + + if k.ManagedHSMKeyId != nil { + return k.ManagedHSMKeyId.KeyName + } + + if k.ManagedHSMKeyVersionlessId != nil { + return k.ManagedHSMKeyVersionlessId.KeyName + } + + return "" +} + +func (k *KeyVaultOrManagedHSMKey) Version() string { + if k.KeyVaultKeyId != nil { + return k.KeyVaultKeyId.Version + } + + if k.ManagedHSMKeyId != nil { + return k.ManagedHSMKeyId.KeyVersion + } + + return "" +} + +// VersionPtr returns a pointer to the version string if it exists, otherwise nil. +// This is useful for resources supporting versioned and versionless keys. +func (k *KeyVaultOrManagedHSMKey) VersionPtr() *string { + if k.KeyVaultKeyId != nil && k.KeyVaultKeyId.Version != "" { + return pointer.To(k.KeyVaultKeyId.Version) + } + + if k.ManagedHSMKeyId != nil { + return pointer.To(k.ManagedHSMKeyId.KeyVersion) + } + + return nil +} + func parseKeyvaultID(keyRaw string, requireVersion VersionType, _ environments.Api) (*parse.NestedItemId, error) { keyID, err := parse.ParseOptionallyVersionedNestedKeyID(keyRaw) if err != nil { @@ -197,3 +241,9 @@ func FlattenKeyVaultOrManagedHSMID(id string, hsmEnv environments.Api) (*KeyVaul return nil, fmt.Errorf("cannot parse given id to key vault key nor managed hsm key: %s", id) } + +func FlattenKeyVaultOrManagedHSMIDByComponents(baseUri, name, version string, hsmEnv environments.Api) (*KeyVaultOrManagedHSMKey, error) { + id := fmt.Sprintf("%s/keys/%s/%s", strings.TrimRight(baseUri, "/"), name, version) + id = strings.TrimSuffix(id, "/") + return FlattenKeyVaultOrManagedHSMID(id, hsmEnv) +} diff --git a/internal/services/databricks/databricks_workspace_resource.go b/internal/services/databricks/databricks_workspace_resource.go index 9bf6a6836b9e..7c10f4c8641b 100644 --- a/internal/services/databricks/databricks_workspace_resource.go +++ b/internal/services/databricks/databricks_workspace_resource.go @@ -23,10 +23,12 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/customermanagedkeys" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "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" + hsmValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/managedhsm/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" @@ -35,344 +37,372 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/utils" ) -func resourceDatabricksWorkspace() *pluginsdk.Resource { - resource := &pluginsdk.Resource{ - Create: resourceDatabricksWorkspaceCreateUpdate, - Read: resourceDatabricksWorkspaceRead, - Update: resourceDatabricksWorkspaceCreateUpdate, - Delete: resourceDatabricksWorkspaceDelete, - - Timeouts: &pluginsdk.ResourceTimeout{ - Create: pluginsdk.DefaultTimeout(30 * time.Minute), - Read: pluginsdk.DefaultTimeout(5 * time.Minute), - Update: pluginsdk.DefaultTimeout(30 * time.Minute), - Delete: pluginsdk.DefaultTimeout(30 * time.Minute), +func databricksWorkspaceSchema() map[string]*pluginsdk.Schema { + schema := map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.WorkspaceName, }, - Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := workspaces.ParseWorkspaceID(id) - return err - }), + "location": commonschema.Location(), - Schema: map[string]*pluginsdk.Schema{ - "name": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validate.WorkspaceName, - }, + "resource_group_name": commonschema.ResourceGroupName(), - "location": commonschema.Location(), - - "resource_group_name": commonschema.ResourceGroupName(), - - "sku": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - "standard", - "premium", - "trial", - }, false), - }, + "sku": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "standard", + "premium", + "trial", + }, false), + }, - "managed_resource_group_name": { - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, - // NOTE: O+C We set a value for this if omitted so this should remain Computed - Computed: true, - ValidateFunc: validation.StringIsNotEmpty, - }, + "managed_resource_group_name": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + // NOTE: O+C We set a value for this if omitted so this should remain Computed + Computed: true, + ValidateFunc: validation.StringIsNotEmpty, + }, - "customer_managed_key_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, - }, + "customer_managed_key_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, - "managed_disk_identity": { - Type: pluginsdk.TypeList, - Computed: true, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "principal_id": { - Type: pluginsdk.TypeString, - Sensitive: true, - Computed: true, - }, + "managed_disk_identity": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "principal_id": { + Type: pluginsdk.TypeString, + Sensitive: true, + Computed: true, + }, - "tenant_id": { - Type: pluginsdk.TypeString, - Sensitive: true, - Computed: true, - }, + "tenant_id": { + Type: pluginsdk.TypeString, + Sensitive: true, + Computed: true, + }, - "type": { - Type: pluginsdk.TypeString, - Computed: true, - }, + "type": { + Type: pluginsdk.TypeString, + Computed: true, }, }, }, + }, - "infrastructure_encryption_enabled": { - Type: pluginsdk.TypeBool, - ForceNew: true, - Optional: true, - Default: false, - }, + "infrastructure_encryption_enabled": { + Type: pluginsdk.TypeBool, + ForceNew: true, + Optional: true, + Default: false, + }, - "public_network_access_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: true, - }, + "public_network_access_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, - "default_storage_firewall_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - RequiredWith: []string{"access_connector_id"}, - }, + "default_storage_firewall_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + RequiredWith: []string{"access_connector_id"}, + }, - "access_connector_id": { - Type: pluginsdk.TypeString, - Optional: true, - RequiredWith: []string{"default_storage_firewall_enabled"}, - }, + "access_connector_id": { + Type: pluginsdk.TypeString, + Optional: true, + RequiredWith: []string{"default_storage_firewall_enabled"}, + }, - "network_security_group_rules_required": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{ - string(workspaces.RequiredNsgRulesAllRules), - string(workspaces.RequiredNsgRulesNoAzureDatabricksRules), - string(workspaces.RequiredNsgRulesNoAzureServiceRules), - }, false), - }, + "network_security_group_rules_required": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(workspaces.RequiredNsgRulesAllRules), + string(workspaces.RequiredNsgRulesNoAzureDatabricksRules), + string(workspaces.RequiredNsgRulesNoAzureServiceRules), + }, false), + }, - "load_balancer_backend_address_pool_id": { - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: loadbalancers.ValidateLoadBalancerBackendAddressPoolID, - }, + "load_balancer_backend_address_pool_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: loadbalancers.ValidateLoadBalancerBackendAddressPoolID, + }, - "custom_parameters": { - Type: pluginsdk.TypeList, - Optional: true, - // NOTE: O+C The API populates these and since many are ForceNew there doesn't appear to be a need to remove this once set to use the defaults - Computed: true, - MaxItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "machine_learning_workspace_id": { - Type: pluginsdk.TypeString, - ForceNew: true, - Optional: true, - ValidateFunc: mlworkspace.ValidateWorkspaceID, - AtLeastOneOf: workspaceCustomParametersString(), - }, + "custom_parameters": { + Type: pluginsdk.TypeList, + Optional: true, + // NOTE: O+C The API populates these and since many are ForceNew there doesn't appear to be a need to remove this once set to use the defaults + Computed: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "machine_learning_workspace_id": { + Type: pluginsdk.TypeString, + ForceNew: true, + Optional: true, + ValidateFunc: mlworkspace.ValidateWorkspaceID, + AtLeastOneOf: workspaceCustomParametersString(), + }, - "nat_gateway_name": { - Type: pluginsdk.TypeString, - ForceNew: true, - Optional: true, - Computed: true, - AtLeastOneOf: workspaceCustomParametersString(), - }, + "nat_gateway_name": { + Type: pluginsdk.TypeString, + ForceNew: true, + Optional: true, + Computed: true, + AtLeastOneOf: workspaceCustomParametersString(), + }, - "no_public_ip": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: true, - AtLeastOneOf: workspaceCustomParametersString(), - }, + "no_public_ip": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + AtLeastOneOf: workspaceCustomParametersString(), + }, - "public_ip_name": { - Type: pluginsdk.TypeString, - ForceNew: true, - Optional: true, - Computed: true, - AtLeastOneOf: workspaceCustomParametersString(), - }, + "public_ip_name": { + Type: pluginsdk.TypeString, + ForceNew: true, + Optional: true, + Computed: true, + AtLeastOneOf: workspaceCustomParametersString(), + }, - "public_subnet_name": { - Type: pluginsdk.TypeString, - ForceNew: true, - Optional: true, - AtLeastOneOf: workspaceCustomParametersString(), - }, + "public_subnet_name": { + Type: pluginsdk.TypeString, + ForceNew: true, + Optional: true, + AtLeastOneOf: workspaceCustomParametersString(), + }, - "public_subnet_network_security_group_association_id": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: azure.ValidateResourceID, - AtLeastOneOf: workspaceCustomParametersString(), - }, + "public_subnet_network_security_group_association_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: azure.ValidateResourceID, + AtLeastOneOf: workspaceCustomParametersString(), + }, - "private_subnet_name": { - Type: pluginsdk.TypeString, - ForceNew: true, - Optional: true, - AtLeastOneOf: workspaceCustomParametersString(), - }, + "private_subnet_name": { + Type: pluginsdk.TypeString, + ForceNew: true, + Optional: true, + AtLeastOneOf: workspaceCustomParametersString(), + }, - "private_subnet_network_security_group_association_id": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: azure.ValidateResourceID, - AtLeastOneOf: workspaceCustomParametersString(), - }, + "private_subnet_network_security_group_association_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: azure.ValidateResourceID, + AtLeastOneOf: workspaceCustomParametersString(), + }, - "virtual_network_id": { - Type: pluginsdk.TypeString, - ForceNew: true, - Optional: true, - ValidateFunc: commonids.ValidateVirtualNetworkID, - AtLeastOneOf: workspaceCustomParametersString(), - }, + "virtual_network_id": { + Type: pluginsdk.TypeString, + ForceNew: true, + Optional: true, + ValidateFunc: commonids.ValidateVirtualNetworkID, + AtLeastOneOf: workspaceCustomParametersString(), + }, - "storage_account_name": { - Type: pluginsdk.TypeString, - ForceNew: true, - Optional: true, - Computed: true, - ValidateFunc: storageValidate.StorageAccountName, - AtLeastOneOf: workspaceCustomParametersString(), - }, + "storage_account_name": { + Type: pluginsdk.TypeString, + ForceNew: true, + Optional: true, + Computed: true, + ValidateFunc: storageValidate.StorageAccountName, + AtLeastOneOf: workspaceCustomParametersString(), + }, - "storage_account_sku_name": { - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - AtLeastOneOf: workspaceCustomParametersString(), - }, + "storage_account_sku_name": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + AtLeastOneOf: workspaceCustomParametersString(), + }, - "vnet_address_prefix": { - Type: pluginsdk.TypeString, - ForceNew: true, - Optional: true, - Computed: true, - AtLeastOneOf: workspaceCustomParametersString(), - }, + "vnet_address_prefix": { + Type: pluginsdk.TypeString, + ForceNew: true, + Optional: true, + Computed: true, + AtLeastOneOf: workspaceCustomParametersString(), }, }, }, + }, - "managed_resource_group_id": { - Type: pluginsdk.TypeString, - Computed: true, - }, + "managed_resource_group_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, - "workspace_url": { - Type: pluginsdk.TypeString, - Computed: true, - }, + "workspace_url": { + Type: pluginsdk.TypeString, + Computed: true, + }, - "workspace_id": { - Type: pluginsdk.TypeString, - Computed: true, - }, + "workspace_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, - "managed_services_cmk_key_vault_key_id": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: keyVaultValidate.KeyVaultChildID, - }, + "managed_services_cmk_key_vault_key_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: keyVaultValidate.KeyVaultChildID, + ConflictsWith: []string{"managed_services_cmk_managed_hsm_key_id"}, + }, - "managed_services_cmk_key_vault_id": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: commonids.ValidateKeyVaultID, - }, + "managed_services_cmk_managed_hsm_key_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: hsmValidate.ManagedHSMDataPlaneVersionedKeyID, + ConflictsWith: []string{"managed_services_cmk_key_vault_key_id"}, + }, - "managed_disk_cmk_key_vault_key_id": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: keyVaultValidate.KeyVaultChildID, - }, - "managed_disk_cmk_key_vault_id": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: commonids.ValidateKeyVaultID, - }, + "managed_disk_cmk_key_vault_key_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: keyVaultValidate.KeyVaultChildID, + ConflictsWith: []string{"managed_disk_cmk_managed_hsm_key_id"}, + }, - "managed_disk_cmk_rotation_to_latest_version_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - RequiredWith: []string{"managed_disk_cmk_key_vault_key_id"}, - }, + "managed_disk_cmk_managed_hsm_key_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: hsmValidate.ManagedHSMDataPlaneVersionedKeyID, + ConflictsWith: []string{"managed_disk_cmk_key_vault_key_id"}, + }, - "disk_encryption_set_id": { - Type: pluginsdk.TypeString, - Computed: true, - }, + "managed_disk_cmk_rotation_to_latest_version_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + }, - "storage_account_identity": { - Type: pluginsdk.TypeList, - Computed: true, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "principal_id": { - Type: pluginsdk.TypeString, - Sensitive: true, - Computed: true, - }, + "disk_encryption_set_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, - "tenant_id": { - Type: pluginsdk.TypeString, - Sensitive: true, - Computed: true, - }, + "storage_account_identity": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "principal_id": { + Type: pluginsdk.TypeString, + Sensitive: true, + Computed: true, + }, - "type": { - Type: pluginsdk.TypeString, - Computed: true, - }, + "tenant_id": { + Type: pluginsdk.TypeString, + Sensitive: true, + Computed: true, + }, + + "type": { + Type: pluginsdk.TypeString, + Computed: true, }, }, }, + }, - "enhanced_security_compliance": { - Type: pluginsdk.TypeList, - Optional: true, - MaxItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "automatic_cluster_update_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, - }, - "compliance_security_profile_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, - }, - "compliance_security_profile_standards": { - Type: pluginsdk.TypeSet, - Optional: true, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - ValidateFunc: validation.StringInSlice([]string{ - string(workspaces.ComplianceStandardHIPAA), - string(workspaces.ComplianceStandardPCIDSS), - }, false), - }, - }, - "enhanced_security_monitoring_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, + "enhanced_security_compliance": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "automatic_cluster_update_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "compliance_security_profile_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "compliance_security_profile_standards": { + Type: pluginsdk.TypeSet, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + string(workspaces.ComplianceStandardHIPAA), + string(workspaces.ComplianceStandardPCIDSS), + }, false), }, }, + "enhanced_security_monitoring_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, }, }, + }, + + "tags": commonschema.Tags(), + } + if !features.FivePointOhBeta() { + schema["managed_services_cmk_key_vault_id"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: commonids.ValidateKeyVaultID, + Deprecated: "'managed_services_cmk_key_vault_id' has been deprecated in favor of 'managed_services_cmk_key_vault_key_id' and will be removed in v4.0 of the provider", + } - "tags": commonschema.Tags(), + schema["managed_services_cmk_managed_hsm_key_id"].ConflictsWith = []string{"managed_services_cmk_key_vault_key_id", "managed_services_cmk_key_vault_id"} + + schema["managed_disk_cmk_key_vault_id"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: commonids.ValidateKeyVaultID, + Deprecated: "'managed_disk_cmk_key_vault_id' has been deprecated in favor of 'managed_disk_cmk_key_vault_key_id' and will be removed in v4.0 of the provider", + } + + schema["managed_disk_cmk_managed_hsm_key_id"].ConflictsWith = []string{"managed_disk_cmk_key_vault_key_id", "managed_disk_cmk_key_vault_id"} + } + return schema +} + +func resourceDatabricksWorkspace() *pluginsdk.Resource { + resource := &pluginsdk.Resource{ + Create: resourceDatabricksWorkspaceCreateUpdate, + Read: resourceDatabricksWorkspaceRead, + Update: resourceDatabricksWorkspaceCreateUpdate, + Delete: resourceDatabricksWorkspaceDelete, + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), }, + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := workspaces.ParseWorkspaceID(id) + return err + }), + + Schema: databricksWorkspaceSchema(), + CustomizeDiff: pluginsdk.CustomDiffWithAll( pluginsdk.CustomizeDiffShim(func(ctx context.Context, d *pluginsdk.ResourceDiff, v interface{}) error { _, customerEncryptionEnabled := d.GetChange("customer_managed_key_enabled") @@ -382,8 +412,10 @@ func resourceDatabricksWorkspace() *pluginsdk.Resource { _, requireNsgRules := d.GetChange("network_security_group_rules_required") _, backendPool := d.GetChange("load_balancer_backend_address_pool_id") _, managedServicesCMK := d.GetChange("managed_services_cmk_key_vault_key_id") + _, managedServicesCMKHSM := d.GetChange("managed_services_cmk_managed_hsm_key_id") _, managedDiskCMK := d.GetChange("managed_disk_cmk_key_vault_key_id") _, enhancedSecurityCompliance := d.GetChange("enhanced_security_compliance") + _, managedDiskCMKHSM := d.GetChange("managed_disk_cmk_managed_hsm_key_id") oldSku, newSku := d.GetChange("sku") @@ -409,8 +441,9 @@ func resourceDatabricksWorkspace() *pluginsdk.Resource { } } - if (customerEncryptionEnabled.(bool) || defaultStorageFirewallEnabled.(bool) || len(enhancedSecurityCompliance.([]interface{})) > 0 || infrastructureEncryptionEnabled.(bool) || managedServicesCMK.(string) != "" || managedDiskCMK.(string) != "") && !strings.EqualFold("premium", newSku.(string)) { - return fmt.Errorf("`customer_managed_key_enabled`, `default_storage_firewall_enabled`, `enhanced_security_compliance`, `infrastructure_encryption_enabled`, `managed_disk_cmk_key_vault_key_id` and `managed_services_cmk_key_vault_key_id` are only available with a `premium` workspace `sku`, got %q", newSku) + if (customerEncryptionEnabled.(bool) || defaultStorageFirewallEnabled.(bool) || len(enhancedSecurityCompliance.([]interface{})) > 0 || infrastructureEncryptionEnabled.(bool) || managedServicesCMK.(string) != "" || managedServicesCMKHSM.(string) != "" || managedDiskCMK.(string) != "" || managedDiskCMKHSM.(string) != "") && + !strings.EqualFold("premium", newSku.(string)) { + return fmt.Errorf("`customer_managed_key_enabled`, `default_storage_firewall_enabled`, `enhanced_security_compliance`, `infrastructure_encryption_enabled`, `managed_disk_cmk_key_vault_key_id`, 'managed_disk_cmk_managed_hsm_key_id' and `managed_services_cmk_key_vault_key_id`, 'managed_services_cmk_managed_hsm_key_id' are only available with a `premium` workspace `sku`, got %q", newSku) } return nil @@ -458,11 +491,12 @@ func resourceDatabricksWorkspace() *pluginsdk.Resource { } func resourceDatabricksWorkspaceCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).DataBricks.WorkspacesClient - acClient := meta.(*clients.Client).DataBricks.AccessConnectorClient - lbClient := meta.(*clients.Client).LoadBalancers.LoadBalancersClient - keyVaultsClient := meta.(*clients.Client).KeyVault - subscriptionId := meta.(*clients.Client).Account.SubscriptionId + clientHub := meta.(*clients.Client) + client := clientHub.DataBricks.WorkspacesClient + acClient := clientHub.DataBricks.AccessConnectorClient + lbClient := clientHub.LoadBalancers.LoadBalancersClient + accountClient := clientHub.Account + subscriptionId := accountClient.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() @@ -563,95 +597,31 @@ func resourceDatabricksWorkspaceCreateUpdate(d *pluginsdk.ResourceData, meta int encrypt := &workspaces.WorkspacePropertiesEncryption{} encrypt.Entities = workspaces.EncryptionEntitiesDefinition{} - var servicesKeyId string - var servicesKeyVaultId string - var diskKeyId string - var diskKeyVaultId string - - if v, ok := d.GetOk("managed_services_cmk_key_vault_key_id"); ok { - servicesKeyId = v.(string) - } - - if v, ok := d.GetOk("managed_services_cmk_key_vault_id"); ok { - servicesKeyVaultId = v.(string) - } - - if v, ok := d.GetOk("managed_disk_cmk_key_vault_key_id"); ok { - diskKeyId = v.(string) - } - - if v, ok := d.GetOk("managed_disk_cmk_key_vault_id"); ok { - diskKeyVaultId = v.(string) - } - - // set default subscription as current subscription for key vault look-up... - servicesResourceSubscriptionId := commonids.NewSubscriptionID(id.SubscriptionId) - 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) - } - - servicesResourceSubscriptionId = commonids.NewSubscriptionID(v.SubscriptionId) - } - - if servicesKeyId != "" { + if cmkID, err := customermanagedkeys.ExpandKeyVaultOrManagedHSMKeyWithCustomFieldKey(d, customermanagedkeys.VersionTypeAny, "managed_services_cmk_key_vault_key_id", "managed_services_cmk_managed_hsm_key_id", accountClient.Environment.KeyVault, accountClient.Environment.ManagedHSM); err != nil { + return fmt.Errorf("expanding customer-managed key for managed services encryption: %+v", err) + } else if cmkID != nil { setEncrypt = true - key, err := keyVaultParse.ParseNestedItemID(servicesKeyId) - 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) - } encrypt.Entities.ManagedServices = &workspaces.EncryptionV2{ KeySource: workspaces.EncryptionKeySourceMicrosoftPointKeyvault, KeyVaultProperties: &workspaces.EncryptionV2KeyVaultProperties{ - KeyName: key.Name, - KeyVersion: key.Version, - KeyVaultUri: key.KeyVaultBaseUrl, + KeyName: cmkID.Name(), + KeyVersion: cmkID.Version(), + KeyVaultUri: cmkID.BaseUri(), }, } } - 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) - } - - diskResourceSubscriptionId = commonids.NewSubscriptionID(v.SubscriptionId) - } - - if diskKeyId != "" { + if diskCMKID, err := customermanagedkeys.ExpandKeyVaultOrManagedHSMKeyWithCustomFieldKey(d, customermanagedkeys.VersionTypeAny, "managed_disk_cmk_key_vault_key_id", "managed_disk_cmk_managed_hsm_key_id", accountClient.Environment.KeyVault, accountClient.Environment.ManagedHSM); err != nil { + return fmt.Errorf("expanding customer-managed key for managed disk encryption: %+v", err) + } else if diskCMKID != nil { setEncrypt = true - key, err := keyVaultParse.ParseNestedItemID(diskKeyId) - 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) - } - encrypt.Entities.ManagedDisk = &workspaces.ManagedDiskEncryption{ KeySource: workspaces.EncryptionKeySourceMicrosoftPointKeyvault, KeyVaultProperties: workspaces.ManagedDiskEncryptionKeyVaultProperties{ - KeyName: key.Name, - KeyVersion: key.Version, - KeyVaultUri: key.KeyVaultBaseUrl, + KeyName: diskCMKID.Name(), + KeyVersion: diskCMKID.Version(), + KeyVaultUri: diskCMKID.BaseUri(), }, } } @@ -755,13 +725,6 @@ func resourceDatabricksWorkspaceCreateUpdate(d *pluginsdk.ResourceData, meta int return fmt.Errorf("setting `custom_parameters`: %+v", err) } - // Always set these even if they are empty to keep the state file - // consistent with the configuration file... - d.Set("managed_services_cmk_key_vault_key_id", servicesKeyId) - d.Set("managed_disk_cmk_key_vault_key_id", diskKeyId) - d.Set("managed_services_cmk_key_vault_id", servicesKeyVaultId) - d.Set("managed_disk_cmk_key_vault_id", diskKeyVaultId) - return resourceDatabricksWorkspaceRead(d, meta) } @@ -775,17 +738,6 @@ func resourceDatabricksWorkspaceRead(d *pluginsdk.ResourceData, meta interface{} return err } - var encryptDiskRotationEnabled bool - var servicesKeyVaultId string - if v, ok := d.GetOk("managed_services_cmk_key_vault_id"); ok { - servicesKeyVaultId = v.(string) - } - - var diskKeyVaultId string - if v, ok := d.GetOk("managed_disk_cmk_key_vault_id"); ok { - diskKeyVaultId = v.(string) - } - resp, err := client.Get(ctx, *id) if err != nil { if response.WasNotFound(resp.HttpResponse) { @@ -877,34 +829,46 @@ func resourceDatabricksWorkspaceRead(d *pluginsdk.ResourceData, meta interface{} d.Set("workspace_id", workspaceId) // customer managed key for managed services - var servicesKeyId string 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) - if err == nil { - servicesKeyId = key.ID() + if kvProp := encryptionProps.KeyVaultProperties; kvProp != nil { + if kvProp.KeyVaultUri != "" { + cmkID, err := customermanagedkeys.FlattenKeyVaultOrManagedHSMIDByComponents(kvProp.KeyVaultUri, kvProp.KeyName, kvProp.KeyVersion, meta.(*clients.Client).Account.Environment.ManagedHSM) + if err != nil { + return fmt.Errorf("flattening customer-managed key for managed services encryption: %+v", err) + } + if cmkID.KeyVaultKeyId != nil { + d.Set("managed_services_cmk_key_vault_key_id", cmkID.KeyVaultKeyId.ID()) + } else { + d.Set("managed_services_cmk_managed_hsm_key_id", cmkID.ID()) + } } } } } // customer managed key for managed disk - var diskKeyId string + var encryptDiskRotationEnabled bool 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) - if err == nil { - diskKeyId = key.ID() + if kvProp := encryptionProps.KeyVaultProperties; kvProp.KeyVaultUri != "" { + cmkID, err := customermanagedkeys.FlattenKeyVaultOrManagedHSMIDByComponents(kvProp.KeyVaultUri, kvProp.KeyName, kvProp.KeyVersion, meta.(*clients.Client).Account.Environment.ManagedHSM) + if err != nil { + return fmt.Errorf("flattening customer-managed key for managed disk encryption: %+v", err) + } + if cmkID.KeyVaultKeyId != nil { + d.Set("managed_disk_cmk_key_vault_key_id", cmkID.KeyVaultKeyId.ID()) + } else { + d.Set("managed_disk_cmk_managed_hsm_key_id", cmkID.ID()) } } - encryptDiskRotationEnabled = *encryptionProps.RotationToLatestKeyVersionEnabled + encryptDiskRotationEnabled = pointer.From(encryptionProps.RotationToLatestKeyVersionEnabled) } } d.Set("enhanced_security_compliance", flattenWorkspaceEnhancedSecurity(model.Properties.EnhancedSecurityCompliance)) + d.Set("managed_disk_cmk_rotation_to_latest_version_enabled", encryptDiskRotationEnabled) var encryptDiskEncryptionSetId string if model.Properties.DiskEncryptionSetId != nil { @@ -912,13 +876,11 @@ func resourceDatabricksWorkspaceRead(d *pluginsdk.ResourceData, meta interface{} } d.Set("disk_encryption_set_id", encryptDiskEncryptionSetId) - // Always set these even if they are empty to keep the state file - // consistent with the configuration file... - d.Set("managed_services_cmk_key_vault_key_id", servicesKeyId) - d.Set("managed_services_cmk_key_vault_id", servicesKeyVaultId) - d.Set("managed_disk_cmk_key_vault_key_id", diskKeyId) - d.Set("managed_disk_cmk_key_vault_id", diskKeyVaultId) - d.Set("managed_disk_cmk_rotation_to_latest_version_enabled", encryptDiskRotationEnabled) + // Always set these even if they are empty to keep the state file in 4.x + if !features.FivePointOhBeta() { + d.Set("managed_services_cmk_key_vault_id", d.Get("managed_services_cmk_key_vault_id")) + d.Set("managed_disk_cmk_key_vault_id", d.Get("managed_disk_cmk_key_vault_id")) + } return tags.FlattenAndSet(d, model.Tags) } diff --git a/internal/services/databricks/databricks_workspace_resource_test.go b/internal/services/databricks/databricks_workspace_resource_test.go index b6e1bdf36eae..3b5f4f6daff1 100644 --- a/internal/services/databricks/databricks_workspace_resource_test.go +++ b/internal/services/databricks/databricks_workspace_resource_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -331,7 +332,12 @@ func TestAccDatabricksWorkspace_altSubscriptionCmkComplete(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("managed_services_cmk_key_vault_id", "managed_disk_cmk_key_vault_id"), + data.ImportStep(func() []string { + if !features.FivePointOhBeta() { + return []string{"managed_services_cmk_key_vault_id", "managed_disk_cmk_key_vault_id"} + } + return []string{} + }()...), }) } @@ -353,7 +359,12 @@ func TestAccDatabricksWorkspace_altSubscriptionCmkKeysInDifferentKeyVaultsAcross check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("managed_services_cmk_key_vault_id", "managed_disk_cmk_key_vault_id"), + data.ImportStep(func() []string { + if !features.FivePointOhBeta() { + return []string{"managed_services_cmk_key_vault_id", "managed_disk_cmk_key_vault_id"} + } + return []string{} + }()...), }) } @@ -375,7 +386,96 @@ func TestAccDatabricksWorkspace_altSubscriptionCmkServicesOnly(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("managed_services_cmk_key_vault_id"), + data.ImportStep(), + }) +} + +func TestAccDatabricksWorkspace_CMkWithManagedHSM(t *testing.T) { + acceptance.RunTestsInSequence(t, map[string]map[string]func(t *testing.T){ + "managed_service": { + "current_subscription": testAccDatabricksWorkspace_CmkManagedHSMServicesOnly, + "alt_subscription": testAccDatabricksWorkspace_CmkManagedHSMServicesOnlyAltSubscription, + }, + "managed_disk": { + "current_subscription": testAccDatabricksWorkspace_CmkManagedHSMDiskOnly, + "alt_subscription": testAccDatabricksWorkspace_CmkManagedHSMDiskOnlyAltSubscription, + }, + }) +} + +func testAccDatabricksWorkspace_CmkManagedHSMServicesOnly(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_databricks_workspace", "test") + databricksPrincipalID := getDatabricksPrincipalId(data.Client().SubscriptionID) + r := DatabricksWorkspaceResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.cmkManagedHSMServicesOnly(data, databricksPrincipalID, nil), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func testAccDatabricksWorkspace_CmkManagedHSMServicesOnlyAltSubscription(t *testing.T) { + altSubscription := altSubscriptionCheck() + + if altSubscription == nil { + t.Skip("Skipping: Test requires `ARM_SUBSCRIPTION_ID_ALT` and `ARM_TENANT_ID` environment variables to be specified") + } + + data := acceptance.BuildTestData(t, "azurerm_databricks_workspace", "test") + databricksPrincipalID := getDatabricksPrincipalId(data.Client().SubscriptionID) + r := DatabricksWorkspaceResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.cmkManagedHSMServicesOnly(data, databricksPrincipalID, altSubscription), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func testAccDatabricksWorkspace_CmkManagedHSMDiskOnly(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_databricks_workspace", "test") + databricksPrincipalID := getDatabricksPrincipalId(data.Client().SubscriptionID) + r := DatabricksWorkspaceResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.cmkManagedHSMDiskOnly(data, databricksPrincipalID, nil), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func testAccDatabricksWorkspace_CmkManagedHSMDiskOnlyAltSubscription(t *testing.T) { + altSubscription := altSubscriptionCheck() + + if altSubscription == nil { + t.Skip("Skipping: Test requires `ARM_SUBSCRIPTION_ID_ALT` and `ARM_TENANT_ID` environment variables to be specified") + } + + data := acceptance.BuildTestData(t, "azurerm_databricks_workspace", "test") + databricksPrincipalID := getDatabricksPrincipalId(data.Client().SubscriptionID) + r := DatabricksWorkspaceResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.cmkManagedHSMDiskOnly(data, databricksPrincipalID, altSubscription), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), }) } @@ -397,7 +497,7 @@ func TestAccDatabricksWorkspace_altSubscriptionCmkDiskOnly(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("managed_disk_cmk_key_vault_id"), + data.ImportStep(), }) } @@ -2272,7 +2372,7 @@ resource "azurerm_private_dns_cname_record" "test" { } func (DatabricksWorkspaceResource) altSubscriptionCmkComplete(data acceptance.TestData, databricksPrincipalID string, alt *DatabricksWorkspaceAlternateSubscription) string { - return fmt.Sprintf(` + template := fmt.Sprintf(` provider "azurerm" { features { resource_group { @@ -2302,27 +2402,6 @@ resource "azurerm_resource_group" "keyVault" { location = "West Europe" } -resource "azurerm_databricks_workspace" "test" { - depends_on = [azurerm_key_vault_access_policy.managed] - - name = "acctest-databricks-pri-sub-%[1]d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - sku = "premium" - managed_resource_group_name = "databricks-pri-sub-managed-rg-%[1]d" - - managed_services_cmk_key_vault_id = azurerm_key_vault.keyVault.id - managed_services_cmk_key_vault_key_id = azurerm_key_vault_key.services.id - - managed_disk_cmk_key_vault_id = azurerm_key_vault.keyVault.id - managed_disk_cmk_key_vault_key_id = azurerm_key_vault_key.disk.id - - tags = { - Environment = "Sandbox" - Pricing = "Premium" - } -} - # Create this in a different subscription... resource "azurerm_key_vault" "keyVault" { provider = azurerm-alt @@ -2417,10 +2496,59 @@ resource "azurerm_key_vault_access_policy" "managed" { ] } `, data.RandomInteger, data.Locations.Secondary, data.RandomString, databricksPrincipalID, alt.tenant_id, alt.subscription_id) + + if !features.FivePointOhBeta() { + return fmt.Sprintf(` +%s + +resource "azurerm_databricks_workspace" "test" { + depends_on = [azurerm_key_vault_access_policy.managed] + + name = "acctest-databricks-pri-sub-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "premium" + managed_resource_group_name = "databricks-pri-sub-managed-rg-%[2]d" + + managed_services_cmk_key_vault_id = azurerm_key_vault.keyVault.id + managed_services_cmk_key_vault_key_id = azurerm_key_vault_key.services.id + + managed_disk_cmk_key_vault_id = azurerm_key_vault.keyVault.id + managed_disk_cmk_key_vault_key_id = azurerm_key_vault_key.disk.id + + tags = { + Environment = "Sandbox" + Pricing = "Premium" + } } +`, template, data.RandomInteger) + } -func (DatabricksWorkspaceResource) altSubscriptionCmkKeysInDifferentKeyVaultsAcrossDifferentSubscriptions(data acceptance.TestData, databricksPrincipalID string, alt *DatabricksWorkspaceAlternateSubscription) string { return fmt.Sprintf(` +%s + +resource "azurerm_databricks_workspace" "test" { + depends_on = [azurerm_key_vault_access_policy.managed] + + name = "acctest-databricks-pri-sub-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "premium" + managed_resource_group_name = "databricks-pri-sub-managed-rg-%[2]d" + + managed_services_cmk_key_vault_key_id = azurerm_key_vault_key.services.id + managed_disk_cmk_key_vault_key_id = azurerm_key_vault_key.disk.id + + tags = { + Environment = "Sandbox" + Pricing = "Premium" + } +} +`, template, data.RandomInteger) +} + +func (DatabricksWorkspaceResource) altSubscriptionCmkKeysInDifferentKeyVaultsAcrossDifferentSubscriptions(data acceptance.TestData, databricksPrincipalID string, alt *DatabricksWorkspaceAlternateSubscription) string { + template := fmt.Sprintf(` provider "azurerm" { features { resource_group { @@ -2455,27 +2583,6 @@ resource "azurerm_resource_group" "keyVaultAlt" { location = "West Europe" } -resource "azurerm_databricks_workspace" "test" { - depends_on = [azurerm_key_vault_access_policy.managed] - - name = "acctest-databricks-pri-sub-%[1]d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - sku = "premium" - managed_resource_group_name = "databricks-pri-sub-managed-rg-%[1]d" - - managed_services_cmk_key_vault_id = azurerm_key_vault.keyVaultAlt.id - managed_services_cmk_key_vault_key_id = azurerm_key_vault_key.servicesAlt.id - - managed_disk_cmk_key_vault_id = azurerm_key_vault.keyVault.id - managed_disk_cmk_key_vault_key_id = azurerm_key_vault_key.disk.id - - tags = { - Environment = "Sandbox" - Pricing = "Premium" - } -} - resource "azurerm_key_vault" "keyVault" { name = "kv-prisub-%[3]s" resource_group_name = azurerm_resource_group.keyVault.name @@ -2618,64 +2725,109 @@ resource "azurerm_key_vault_access_policy" "managedAlt" { ] } `, data.RandomInteger, data.Locations.Secondary, data.RandomString, databricksPrincipalID, alt.tenant_id, alt.subscription_id) -} - -func (DatabricksWorkspaceResource) altSubscriptionCmkServicesOnly(data acceptance.TestData, databricksPrincipalID string, alt *DatabricksWorkspaceAlternateSubscription) string { - return fmt.Sprintf(` -provider "azurerm" { - features { - resource_group { - prevent_deletion_if_contains_resources = false - } - } -} -provider "azurerm-alt" { - features {} + if !features.FivePointOhBeta() { + return fmt.Sprintf(` +%s - tenant_id = "%[5]s" - subscription_id = "%[6]s" -} +resource "azurerm_databricks_workspace" "test" { + depends_on = [azurerm_key_vault_access_policy.managed] -data "azurerm_client_config" "current" {} + name = "acctest-databricks-pri-sub-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "premium" + managed_resource_group_name = "databricks-pri-sub-managed-rg-%[2]d" -resource "azurerm_resource_group" "test" { - name = "acctestRG-databricks-pri-sub-services-%[1]d" - location = "West Europe" -} + managed_services_cmk_key_vault_id = azurerm_key_vault.keyVaultAlt.id + managed_services_cmk_key_vault_key_id = azurerm_key_vault_key.servicesAlt.id -resource "azurerm_resource_group" "keyVault" { - provider = azurerm-alt + managed_disk_cmk_key_vault_id = azurerm_key_vault.keyVault.id + managed_disk_cmk_key_vault_key_id = azurerm_key_vault_key.disk.id - name = "acctestRG-databricks-alt-sub-services-%[1]d" - location = "West Europe" + tags = { + Environment = "Sandbox" + Pricing = "Premium" + } } +`, template, data.RandomInteger) + } + + return fmt.Sprintf(` +%s resource "azurerm_databricks_workspace" "test" { depends_on = [azurerm_key_vault_access_policy.managed] - name = "acctest-databricks-pri-sub-%[1]d" + name = "acctest-databricks-pri-sub-%[2]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location sku = "premium" - managed_resource_group_name = "databricks-pri-sub-managed-rg-%[1]d" + managed_resource_group_name = "databricks-pri-sub-managed-rg-%[2]d" - managed_services_cmk_key_vault_id = azurerm_key_vault.keyVault.id - managed_services_cmk_key_vault_key_id = azurerm_key_vault_key.services.id + managed_services_cmk_key_vault_key_id = azurerm_key_vault_key.servicesAlt.id + managed_disk_cmk_key_vault_key_id = azurerm_key_vault_key.disk.id tags = { Environment = "Sandbox" Pricing = "Premium" } } + `, template, data.RandomInteger) +} + +func (d DatabricksWorkspaceResource) cmkBaseTemplate(data acceptance.TestData, alt *DatabricksWorkspaceAlternateSubscription) string { + altConfig := "" + if alt != nil { + altConfig = fmt.Sprintf(` +provider "azurerm-alt" { + features {} + + tenant_id = "%[2]s" + subscription_id = "%[3]s" +} + +resource "azurerm_resource_group" "alt" { + provider = azurerm-alt + + name = "acctestRG-databricks-alt-sub-services-%[1]d" + location = "West Europe" +} +`, data.RandomInteger, alt.tenant_id, alt.subscription_id) + } + + return fmt.Sprintf(` +provider "azurerm" { + features { + resource_group { + prevent_deletion_if_contains_resources = false + } + } +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-databricks-pri-sub-services-%[1]d" + location = "West Europe" +} + +%s +`, data.RandomInteger, altConfig) +} + +func (d DatabricksWorkspaceResource) altSubscriptionCmkKeyVaultTemplate(data acceptance.TestData, databricksPrincipalID string, alt *DatabricksWorkspaceAlternateSubscription) string { + return fmt.Sprintf(` +%[1]s + # Create this in a different subscription... resource "azurerm_key_vault" "keyVault" { provider = azurerm-alt - name = "kv-altsub-%[3]s" - location = azurerm_resource_group.keyVault.location - resource_group_name = azurerm_resource_group.keyVault.name + name = "kv-altsub-%[2]s" + location = azurerm_resource_group.alt.location + resource_group_name = azurerm_resource_group.alt.name tenant_id = data.azurerm_client_config.current.tenant_id sku_name = "premium" @@ -2712,12 +2864,6 @@ resource "azurerm_key_vault_access_policy" "terraform" { "Get", "List", "Create", - "Decrypt", - "Encrypt", - "Sign", - "UnwrapKey", - "Verify", - "WrapKey", "Delete", "Restore", "Recover", @@ -2733,7 +2879,7 @@ resource "azurerm_key_vault_access_policy" "managed" { key_vault_id = azurerm_key_vault.keyVault.id tenant_id = azurerm_key_vault.keyVault.tenant_id - object_id = "%[4]s" + object_id = "%[3]s" key_permissions = [ "Get", @@ -2743,7 +2889,225 @@ resource "azurerm_key_vault_access_policy" "managed" { "SetRotationPolicy", ] } -`, data.RandomInteger, data.Locations.Secondary, data.RandomString, databricksPrincipalID, alt.tenant_id, alt.subscription_id) +`, d.cmkBaseTemplate(data, alt), data.RandomString, databricksPrincipalID) +} + +func (d DatabricksWorkspaceResource) cmkManagedHSMTemplate(data acceptance.TestData, databricksPrincipalID string, alt *DatabricksWorkspaceAlternateSubscription) string { + alt_provider := "" + rg_name := "test" + if alt != nil { + alt_provider = "provider = azurerm-alt" + rg_name = "alt" + } + + return fmt.Sprintf(` +%[1]s + + +resource "azurerm_key_vault" "test" { + %[2]s + name = "acckv%[3]d" + location = azurerm_resource_group.%[6]s.location + resource_group_name = azurerm_resource_group.%[6]s.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" + ] + } + tags = { + environment = "Production" + } +} +resource "azurerm_key_vault_certificate" "cert" { + %[2]s + count = 3 + name = "acchsmcert${count.index}" + key_vault_id = azurerm_key_vault.test.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 { + extended_key_usage = [] + 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" { + %[2]s + name = "kvHsm%[3]d" + resource_group_name = azurerm_resource_group.%[6]s.name + location = azurerm_resource_group.%[6]s.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.cert : cert.id] + security_domain_quorum = 3 +} + +resource "azurerm_key_vault_managed_hardware_security_module_role_assignment" "officer" { + %[2]s + managed_hsm_id = azurerm_key_vault_managed_hardware_security_module.test.id + name = "1e243909-064c-6ac3-84e9-1c8bf8d6ad23" + scope = "/keys" + role_definition_id = "/Microsoft.KeyVault/providers/Microsoft.Authorization/roleDefinitions/515eb02d-2335-4d2d-92f2-b1cbdf9c3778" + principal_id = data.azurerm_client_config.current.object_id +} + +resource "azurerm_key_vault_managed_hardware_security_module_role_assignment" "cryptor" { + %[2]s + managed_hsm_id = azurerm_key_vault_managed_hardware_security_module.test.id + name = "1e243909-064c-6ac3-84e9-1c8bf8d6ad22" + scope = "/keys" + role_definition_id = "/Microsoft.KeyVault/providers/Microsoft.Authorization/roleDefinitions/21dbd100-6940-42c2-9190-5d6cb909625b" + principal_id = data.azurerm_client_config.current.object_id +} + +resource "azurerm_key_vault_managed_hardware_security_module_role_assignment" "db" { + %[2]s + managed_hsm_id = azurerm_key_vault_managed_hardware_security_module.test.id + name = "ff248f45-efda-4122-bec0-71460ecc8add" + scope = "/keys" + role_definition_id = "/Microsoft.KeyVault/providers/Microsoft.Authorization/roleDefinitions/21dbd100-6940-42c2-9190-5d6cb909625b" + principal_id = "%[5]s" +} + +resource "azurerm_key_vault_managed_hardware_security_module_key" "services" { + %[2]s + name = "acctestHSMK-%[4]s" + managed_hsm_id = azurerm_key_vault_managed_hardware_security_module.test.id + key_type = "RSA-HSM" + key_size = 4096 + key_opts = ["sign", "unwrapKey", "wrapKey"] + + depends_on = [ + azurerm_key_vault_managed_hardware_security_module_role_assignment.officer, + azurerm_key_vault_managed_hardware_security_module_role_assignment.cryptor, + azurerm_key_vault_managed_hardware_security_module_role_assignment.db + ] +} +`, d.cmkBaseTemplate(data, alt), alt_provider, data.RandomInteger, data.RandomString, databricksPrincipalID, rg_name) +} + +func (d DatabricksWorkspaceResource) altSubscriptionCmkServicesOnly(data acceptance.TestData, databricksPrincipalID string, alt *DatabricksWorkspaceAlternateSubscription) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_databricks_workspace" "test" { + depends_on = [azurerm_key_vault_access_policy.managed] + + name = "acctest-databricks-pri-sub-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "premium" + managed_resource_group_name = "databricks-pri-sub-managed-rg-%[2]d" + + managed_services_cmk_key_vault_key_id = azurerm_key_vault_key.services.id + + tags = { + Environment = "Sandbox" + Pricing = "Premium" + } +} +`, d.altSubscriptionCmkKeyVaultTemplate(data, databricksPrincipalID, alt), data.RandomInteger) +} + +// set param alt to nil if you want to use the default subscription +func (d DatabricksWorkspaceResource) cmkManagedHSMServicesOnly(data acceptance.TestData, databricksPrincipalID string, alt *DatabricksWorkspaceAlternateSubscription) string { + return fmt.Sprintf(` + +%[1]s + +resource "azurerm_databricks_workspace" "test" { + name = "acctest-databricks-pri-sub-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "premium" + managed_resource_group_name = "databricks-pri-sub-managed-rg-%[2]d" + + managed_services_cmk_managed_hsm_key_id = azurerm_key_vault_managed_hardware_security_module_key.services.versioned_id + + tags = { + Environment = "Sandbox" + Pricing = "Premium" + } +} + +`, d.cmkManagedHSMTemplate(data, databricksPrincipalID, alt), data.RandomInteger) +} + +func (d DatabricksWorkspaceResource) cmkManagedHSMDiskOnly(data acceptance.TestData, databricksPrincipalID string, alt *DatabricksWorkspaceAlternateSubscription) string { + return fmt.Sprintf(` + +%[1]s + +resource "azurerm_databricks_workspace" "test" { + name = "acctest-databricks-pri-sub-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "premium" + managed_resource_group_name = "databricks-pri-sub-managed-rg-%[2]d" + + managed_disk_cmk_managed_hsm_key_id = azurerm_key_vault_managed_hardware_security_module_key.services.versioned_id + + tags = { + Environment = "Sandbox" + Pricing = "Premium" + } +} +`, d.cmkManagedHSMTemplate(data, databricksPrincipalID, alt), data.RandomInteger) } func (DatabricksWorkspaceResource) altSubscriptionCmkDiskOnly(data acceptance.TestData, databricksPrincipalID string, alt *DatabricksWorkspaceAlternateSubscription) string { @@ -2786,7 +3150,6 @@ resource "azurerm_databricks_workspace" "test" { sku = "premium" managed_resource_group_name = "databricks-pri-sub-managed-rg-%[1]d" - managed_disk_cmk_key_vault_id = azurerm_key_vault.keyVault.id managed_disk_cmk_key_vault_key_id = azurerm_key_vault_key.disk.id tags = { diff --git a/website/docs/5.0-upgrade-guide.html.markdown b/website/docs/5.0-upgrade-guide.html.markdown index ebafb0c7e535..7957edd79e24 100644 --- a/website/docs/5.0-upgrade-guide.html.markdown +++ b/website/docs/5.0-upgrade-guide.html.markdown @@ -176,6 +176,10 @@ Please follow the format in the example below for listing breaking changes in da * The deprecated `logging_storage_account` block has been removed. * The deprecated `managed_resource_group` property has been removed. +### `azurerm_databricks_workspace` +* The deprecated `managed_services_cmk_key_vault_id` property has been removed in favour of the `managed_services_cmk_key_vault_key_id` property. +* The deprecated `managed_disk_cmk_key_vault_id` property has been removed in favour of the `managed_disk_cmk_key_vault_key_id` property. + ### `azurerm_storage_container` * The deprecated `storage_account_name` property has been removed in favour of the `storage_account_id` property. diff --git a/website/docs/r/databricks_workspace.html.markdown b/website/docs/r/databricks_workspace.html.markdown index 6710632c90f6..42fa5cbbcf2f 100644 --- a/website/docs/r/databricks_workspace.html.markdown +++ b/website/docs/r/databricks_workspace.html.markdown @@ -48,21 +48,15 @@ The following arguments are supported: ~> **Note:** Downgrading to a `trial sku` from a `standard` or `premium sku` will force a new resource to be created. -* `managed_services_cmk_key_vault_id` - (Optional) Resource ID of the Key Vault which contains the `managed_services_cmk_key_vault_key_id` key. +* `managed_services_cmk_key_vault_key_id` - (Optional) Customer managed encryption properties using a versioned Key Vaule Key ID for the Databricks Workspace managed resources(e.g. Notebooks and Artifacts). --> **Note:** The `managed_services_cmk_key_vault_id` field is only required if the Key Vault exists in a different subscription than the Databricks Workspace. If the `managed_services_cmk_key_vault_id` field is not specified it is assumed that the `managed_services_cmk_key_vault_key_id` is hosted in the same subscriptioin as the Databricks Workspace. +* `managed_services_cmk_managed_hsm_key_id` - (Optional) Customer managed encryption properties using a versioned Managed Hardware Security Key ID for the Databricks Workspace managed resources(e.g. Notebooks and Artifacts). -> **Note:** If you are using multiple service principals to execute Terraform across subscriptions you will need to add an additional `azurerm_key_vault_access_policy` resource granting the service principal access to the key vault in that subscription. -* `managed_disk_cmk_key_vault_id` - (Optional) Resource ID of the Key Vault which contains the `managed_disk_cmk_key_vault_key_id` key. +* `managed_disk_cmk_key_vault_key_id` - (Optional) Customer managed encryption properties using a versioned Key Vaule Key ID for the Databricks Workspace managed disks. --> **Note:** The `managed_disk_cmk_key_vault_id` field is only required if the Key Vault exists in a different subscription than the Databricks Workspace. If the `managed_disk_cmk_key_vault_id` field is not specified it is assumed that the `managed_disk_cmk_key_vault_key_id` is hosted in the same subscriptioin as the Databricks Workspace. - --> **Note:** If you are using multiple service principals to execute Terraform across subscriptions you will need to add an additional `azurerm_key_vault_access_policy` resource granting the service principal access to the key vault in that subscription. - -* `managed_services_cmk_key_vault_key_id` - (Optional) Customer managed encryption properties for the Databricks Workspace managed resources(e.g. Notebooks and Artifacts). - -* `managed_disk_cmk_key_vault_key_id` - (Optional) Customer managed encryption properties for the Databricks Workspace managed disks. +* `managed_disk_cmk_managed_hsm_key_id` - (Optional) Customer managed encryption properties using a versioned Managed Hardware Security Key ID for the Databricks Workspace managed disks. * `managed_disk_cmk_rotation_to_latest_version_enabled` - (Optional) Whether customer managed keys for disk encryption will automatically be rotated to the latest version. @@ -78,7 +72,7 @@ The following arguments are supported: * `default_storage_firewall_enabled` - (Optional) Disallow public access to default storage account. Defaults to `false`. -* `access_connector_id` - (Optional) Access Connector ID to use when default storage account firewall is enabled. +* `access_connector_id` - (Optional) Access Connector ID to use when default storage account firewall is enabled. -> **Note:** The `access_connector_id` field is only required if `default_storage_firewall_enabled` is set to `true`.