diff --git a/examples/databricks/customer-managed-key/dbfs-cross-subscription/README.md b/examples/databricks/customer-managed-key/dbfs-cross-subscription/README.md new file mode 100644 index 000000000000..c385f158e42e --- /dev/null +++ b/examples/databricks/customer-managed-key/dbfs-cross-subscription/README.md @@ -0,0 +1,7 @@ +## Example: Databricks Workspace with Root Databricks File System Customer Managed Keys in a Different Subscription + +This example provisions a Databricks Workspace within Azure with Root Databricks File System Customer Managed Keys enabled where the Key Vault and Key are hosted in a different subscription within the same tenant. + +### Variables + +* `prefix` - (Required) The prefix used for all resources in this example. diff --git a/examples/databricks/customer-managed-key/dbfs-cross-subscription/main.tf b/examples/databricks/customer-managed-key/dbfs-cross-subscription/main.tf new file mode 100644 index 000000000000..ec484c55504a --- /dev/null +++ b/examples/databricks/customer-managed-key/dbfs-cross-subscription/main.tf @@ -0,0 +1,124 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +provider "azurerm" { + features {} +} + +provider "azurerm" { + features {} + alias = "keyVaultSubscription" + subscription_id = "00000000-0000-0000-0000-000000000000" # Subscription where the Key Vault should be hosted +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "example" { + name = "${var.prefix}-databricks-cmk" + location = "West Europe" +} + +resource "azurerm_resource_group" "keyVault" { + provider = azurerm.keyVaultSubscription + + name = "${var.prefix}-databricks-cmk" + location = "West Europe" +} + +resource "azurerm_databricks_workspace" "example" { + name = "${var.prefix}-DBW" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + sku = "premium" + managed_resource_group_name = "${var.prefix}-DBW-managed-dbfs" + + customer_managed_key_enabled = true + + tags = { + Environment = "Sandbox" + } +} + +resource "azurerm_databricks_workspace_root_dbfs_customer_managed_key" "example" { + depends_on = [azurerm_key_vault_access_policy.databricks] + + workspace_id = azurerm_databricks_workspace.example.id + key_vault_id = azurerm_key_vault.example.id + key_vault_key_id = azurerm_key_vault_key.dbfs.id +} + +resource "azurerm_key_vault" "example" { + provider = azurerm.keyVaultSubscription + + name = "${var.prefix}-keyvault" + location = azurerm_resource_group.keyVault.location + resource_group_name = azurerm_resource_group.keyVault.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "premium" + + purge_protection_enabled = true + soft_delete_retention_days = 7 +} + +resource "azurerm_key_vault_key" "dbfs" { + depends_on = [azurerm_key_vault_access_policy.terraform] + + provider = azurerm.keyVaultSubscription + + name = "${var.prefix}-certificate" + key_vault_id = azurerm_key_vault.example.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_key_vault_access_policy" "terraform" { + provider = azurerm.keyVaultSubscription + + key_vault_id = azurerm_key_vault.example.id + tenant_id = azurerm_key_vault.example.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "Get", + "List", + "Create", + "Decrypt", + "Encrypt", + "Sign", + "UnwrapKey", + "Verify", + "WrapKey", + "Delete", + "Restore", + "Recover", + "Update", + "Purge", + "GetRotationPolicy", + "SetRotationPolicy", + ] +} + +resource "azurerm_key_vault_access_policy" "databricks" { + depends_on = [azurerm_databricks_workspace.example] + + provider = azurerm.keyVaultSubscription + + key_vault_id = azurerm_key_vault.example.id + tenant_id = azurerm_databricks_workspace.example.storage_account_identity.0.tenant_id + object_id = azurerm_databricks_workspace.example.storage_account_identity.0.principal_id + + key_permissions = [ + "Get", + "UnwrapKey", + "WrapKey", + ] +} diff --git a/examples/databricks/customer-managed-key/dbfs-cross-subscription/variables.tf b/examples/databricks/customer-managed-key/dbfs-cross-subscription/variables.tf new file mode 100644 index 000000000000..d48592b4e9a1 --- /dev/null +++ b/examples/databricks/customer-managed-key/dbfs-cross-subscription/variables.tf @@ -0,0 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +variable "prefix" { + description = "The Prefix used for all resources in this example" +} diff --git a/examples/databricks/customer-managed-key/dbfs/README.md b/examples/databricks/customer-managed-key/dbfs/README.md index fd3e3bb9cf42..101a11a3d1a1 100644 --- a/examples/databricks/customer-managed-key/dbfs/README.md +++ b/examples/databricks/customer-managed-key/dbfs/README.md @@ -1,4 +1,4 @@ -## Example: Databricks Workspace Root Databricks File System Customer Managed Keys +## Example: Databricks Workspace with Root Databricks File System Customer Managed Keys This example provisions a Databricks Workspace within Azure with Root Databricks File System Customer Managed Keys enabled. diff --git a/examples/databricks/customer-managed-key/dbfs/main.tf b/examples/databricks/customer-managed-key/dbfs/main.tf index 92907d8c0cdd..92335865fa47 100644 --- a/examples/databricks/customer-managed-key/dbfs/main.tf +++ b/examples/databricks/customer-managed-key/dbfs/main.tf @@ -82,6 +82,8 @@ resource "azurerm_key_vault_access_policy" "terraform" { "Recover", "Update", "Purge", + "GetRotationPolicy", + "SetRotationPolicy", ] } @@ -93,8 +95,8 @@ resource "azurerm_key_vault_access_policy" "databricks" { object_id = azurerm_databricks_workspace.example.storage_account_identity.0.principal_id key_permissions = [ - "get", - "unwrapKey", - "wrapKey", + "Get", + "UnwrapKey", + "WrapKey", ] } diff --git a/examples/databricks/customer-managed-key/managed-services/README.md b/examples/databricks/customer-managed-key/managed-services/README.md index 82c34ab73ba8..0f820b334b86 100644 --- a/examples/databricks/customer-managed-key/managed-services/README.md +++ b/examples/databricks/customer-managed-key/managed-services/README.md @@ -1,8 +1,8 @@ -## Example: Databricks Workspace Customer Managed Keys for Managed Services +## Example: Databricks Workspace with Customer Managed Keys for Managed Services This example provisions a Databricks Workspace within Azure with Customer Managed Keys for Managed Services enabled. -To find the correct Object ID to use for the `azurerm_key_vault_access_policy.managed` `object_id` field in your configuration file you will need to go to [portal](https://portal.azure.com/) -> `Azure Active Directory` and in the `search your tenant` bar enter the value `2ff814a6-3304-4ab8-85cb-cd0e6f879c1d`. You will see under `Enterprise application` results `AzureDatabricks`, click on the `AzureDatabricks` search result. This will open the `Enterprise Application` overview blade where you will see three values, the name of the application, the application ID, and the object ID. The value you want is the object ID, copy this value and paste it into the `object_id` field for your `azurerm_key_vault_access_policy.managed` configuration block. +To find the correct Object ID to use for the `azurerm_key_vault_access_policy.managed` `object_id` field in your configuration file you will need to go to [portal](https://portal.azure.com/) -> `Microsoft Entra ID` and in the `search your tenant` bar enter the value `2ff814a6-3304-4ab8-85cb-cd0e6f879c1d`. You will see under `Enterprise application` results `AzureDatabricks`, click on the `AzureDatabricks` search result. This will open the `Enterprise Application` overview blade where you will see three values, the name of the application, the application ID, and the object ID. The value you want is the object ID, copy this value and paste it into the `object_id` field for your `azurerm_key_vault_access_policy.managed` configuration block. ### Variables diff --git a/examples/databricks/customer-managed-key/managed-services/main.tf b/examples/databricks/customer-managed-key/managed-services/main.tf index d191a67b546d..83814c9849c0 100644 --- a/examples/databricks/customer-managed-key/managed-services/main.tf +++ b/examples/databricks/customer-managed-key/managed-services/main.tf @@ -24,7 +24,7 @@ resource "azurerm_databricks_workspace" "example" { managed_services_cmk_key_vault_key_id = azurerm_key_vault_key.example.id tags = { - Environment = "Production" + Environment = "Sandbox" } } @@ -62,31 +62,33 @@ resource "azurerm_key_vault_access_policy" "terraform" { object_id = data.azurerm_client_config.current.object_id key_permissions = [ - "get", - "list", - "create", - "decrypt", - "encrypt", - "sign", - "unwrapKey", - "verify", - "wrapKey", - "delete", - "restore", - "recover", - "update", - "purge", + "Get", + "List", + "Create", + "Decrypt", + "Encrypt", + "Sign", + "UnwrapKey", + "Verify", + "WrapKey", + "Delete", + "Restore", + "Recover", + "Update", + "Purge", + "GetRotationPolicy", + "SetRotationPolicy", ] } resource "azurerm_key_vault_access_policy" "managed" { key_vault_id = azurerm_key_vault.example.id tenant_id = azurerm_key_vault.example.tenant_id - object_id = "See the README.md file for instructions on how to lookup the correct value to enter here" + object_id = "00000000-0000-0000-0000-000000000000" # See the README.md file for instructions on how to lookup the correct value to enter here. key_permissions = [ - "get", - "unwrapKey", - "wrapKey", + "Get", + "UnwrapKey", + "WrapKey", ] } diff --git a/examples/databricks/managed-services-cross-subscription/README.md b/examples/databricks/managed-services-cross-subscription/README.md new file mode 100644 index 000000000000..ba98db907ff9 --- /dev/null +++ b/examples/databricks/managed-services-cross-subscription/README.md @@ -0,0 +1,9 @@ +## Example: Databricks Workspace with Customer Managed Keys for Managed Services with Key Vault and Key in a Different Subscription + +This example provisions a Databricks Workspace within Azure with Customer Managed Keys for Managed Services enabled where the Key Vault and Key are hosted in a different subscription within the same tenant. + +To find the correct Object ID to use for the `azurerm_key_vault_access_policy.managed` `object_id` field in your configuration file you will need to go to [portal](https://portal.azure.com/) -> `Microsoft Entra ID` and in the `search your tenant` bar enter the value `2ff814a6-3304-4ab8-85cb-cd0e6f879c1d`. You will see under `Enterprise application` results `AzureDatabricks`, click on the `AzureDatabricks` search result. This will open the `Enterprise Application` overview blade where you will see three values, the name of the application, the application ID, and the object ID. The value you want is the object ID, copy this value and paste it into the `object_id` field for your `azurerm_key_vault_access_policy.managed` configuration block. + +### Variables + +* `prefix` - (Required) The prefix used for all resources in this example. diff --git a/examples/databricks/managed-services-cross-subscription/main.tf b/examples/databricks/managed-services-cross-subscription/main.tf new file mode 100644 index 000000000000..d8aa23330351 --- /dev/null +++ b/examples/databricks/managed-services-cross-subscription/main.tf @@ -0,0 +1,139 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +provider "azurerm" { + features {} +} + +provider "azurerm" { + features {} + alias = "keyVaultSubscription" + subscription_id = "00000000-0000-0000-0000-000000000000" # Subscription where the Key Vault should be hosted +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "example" { + name = "${var.prefix}-databricks-managed-services" + location = "West Europe" +} + +resource "azurerm_resource_group" "keyVault" { + provider = azurerm.keyVaultSubscription + + name = "${var.prefix}-databricks-managed-services" + location = "West Europe" +} + +resource "azurerm_databricks_workspace" "example" { + depends_on = [azurerm_key_vault_access_policy.managed] + + name = "${var.prefix}-DBW" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + sku = "premium" + managed_resource_group_name = "${var.prefix}-DBW-managed-services" + + managed_services_cmk_key_vault_id = azurerm_key_vault.example.id + managed_services_cmk_key_vault_key_id = azurerm_key_vault_key.services.id + + managed_disk_cmk_key_vault_id = azurerm_key_vault.example.id + managed_disk_cmk_key_vault_key_id = azurerm_key_vault_key.disk.id + + tags = { + Environment = "Sandbox" + } +} + +resource "azurerm_key_vault" "example" { + provider = azurerm.keyVaultSubscription + + name = "${var.prefix}-keyvault" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "premium" + + soft_delete_retention_days = 7 +} + +resource "azurerm_key_vault_key" "services" { + depends_on = [azurerm_key_vault_access_policy.terraform] + + provider = azurerm.keyVaultSubscription + + name = "${var.prefix}-certificate" + key_vault_id = azurerm_key_vault.example.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_key_vault_key" "disk" { + depends_on = [azurerm_key_vault_access_policy.terraform] + + provider = azurerm.keyVaultSubscription + + name = "${var.prefix}-certificate" + key_vault_id = azurerm_key_vault.example.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_key_vault_access_policy" "terraform" { + provider = azurerm.keyVaultSubscription + + key_vault_id = azurerm_key_vault.example.id + tenant_id = azurerm_key_vault.example.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "Get", + "List", + "Create", + "Decrypt", + "Encrypt", + "Sign", + "UnwrapKey", + "Verify", + "WrapKey", + "Delete", + "Restore", + "Recover", + "Update", + "Purge", + "GetRotationPolicy", + "SetRotationPolicy", + ] +} + +resource "azurerm_key_vault_access_policy" "managed" { + provider = azurerm.keyVaultSubscription + + key_vault_id = azurerm_key_vault.example.id + tenant_id = azurerm_key_vault.example.tenant_id + object_id = "00000000-0000-0000-0000-000000000000" # See the README.md file for instructions on how to lookup the correct value to enter here. + + key_permissions = [ + "Get", + "UnwrapKey", + "WrapKey", + ] +} diff --git a/examples/databricks/managed-services-cross-subscription/variables.tf b/examples/databricks/managed-services-cross-subscription/variables.tf new file mode 100644 index 000000000000..d48592b4e9a1 --- /dev/null +++ b/examples/databricks/managed-services-cross-subscription/variables.tf @@ -0,0 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +variable "prefix" { + description = "The Prefix used for all resources in this example" +} diff --git a/examples/private-endpoint/databricks/managed-services/main.tf b/examples/private-endpoint/databricks/managed-services/main.tf index cacd09cf870f..0f4e22e9ac85 100644 --- a/examples/private-endpoint/databricks/managed-services/main.tf +++ b/examples/private-endpoint/databricks/managed-services/main.tf @@ -189,20 +189,22 @@ resource "azurerm_key_vault_access_policy" "terraform" { object_id = data.azurerm_client_config.current.object_id key_permissions = [ - "get", - "list", - "create", - "decrypt", - "encrypt", - "sign", - "unwrapKey", - "verify", - "wrapKey", - "delete", - "restore", - "recover", - "update", - "purge", + "Get", + "List", + "Create", + "Decrypt", + "Encrypt", + "Sign", + "UnwrapKey", + "Verify", + "WrapKey", + "Delete", + "Restore", + "Recover", + "Update", + "Purge", + "GetRotationPolicy", + "SetRotationPolicy", ] } @@ -214,21 +216,21 @@ resource "azurerm_key_vault_access_policy" "databricks" { object_id = azurerm_databricks_workspace.example.storage_account_identity.0.principal_id key_permissions = [ - "get", - "unwrapKey", - "wrapKey", + "Get", + "UnwrapKey", + "WrapKey", ] } resource "azurerm_key_vault_access_policy" "managed" { key_vault_id = azurerm_key_vault.example.id tenant_id = azurerm_key_vault.example.tenant_id - object_id = "See the README.md file for instructions on how to lookup the correct value to enter here" + object_id = "00000000-0000-0000-0000-000000000000" # See the README.md file for instructions on how to lookup the correct value to enter here. key_permissions = [ - "get", - "unwrapKey", - "wrapKey", + "Get", + "UnwrapKey", + "WrapKey", ] } diff --git a/internal/features/four_point_oh.go b/internal/features/four_point_oh.go index 423a527f657b..4387a9c18f48 100644 --- a/internal/features/four_point_oh.go +++ b/internal/features/four_point_oh.go @@ -3,6 +3,8 @@ package features +// import "os" + // nolint gocritic // DeprecatedInFourPointOh returns the deprecation message if the provider // is running in 4.0 mode - otherwise is returns an empty string (such that @@ -24,6 +26,10 @@ func DeprecatedInFourPointOh(deprecationMessage string) string { // This exists to allow breaking changes to be piped through the provider // during the development of 3.x until 4.0 is ready. func FourPointOh() bool { + // WodansSon: Added for testing 4.0 functionality, + // will comment out in final check-in... + // return !(os.Getenv("TF_FOUR_POINT_OH_BETA") == "") + return false } diff --git a/internal/services/databricks/databricks_root_dbfs_customer_managed_key_resource.go b/internal/services/databricks/databricks_root_dbfs_customer_managed_key_resource.go index a07439f6c789..1ebe5df87905 100644 --- a/internal/services/databricks/databricks_root_dbfs_customer_managed_key_resource.go +++ b/internal/services/databricks/databricks_root_dbfs_customer_managed_key_resource.go @@ -61,12 +61,17 @@ func resourceDatabricksWorkspaceRootDbfsCustomerManagedKey() *pluginsdk.Resource ValidateFunc: workspaces.ValidateWorkspaceID, }, - // Make this key vault key id and abstract everything from the string... "key_vault_key_id": { Type: pluginsdk.TypeString, Required: true, ValidateFunc: keyVaultValidate.KeyVaultChildID, }, + + "key_vault_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: commonids.ValidateKeyVaultID, + }, }, } } @@ -82,8 +87,19 @@ func databricksWorkspaceRootDbfsCustomerManagedKeyCreate(d *pluginsdk.ResourceDa return err } - keyIdRaw := d.Get("key_vault_key_id").(string) - key, err := keyVaultParse.ParseNestedItemID(keyIdRaw) + var keyIdRaw string + var keyVaultId string + var key *keyVaultParse.NestedItemId + + if v, ok := d.GetOk("key_vault_key_id"); ok { + keyIdRaw = v.(string) + } + + if v, ok := d.GetOk("key_vault_id"); ok { + keyVaultId = v.(string) + } + + key, err = keyVaultParse.ParseNestedItemID(keyIdRaw) if err != nil { return err } @@ -122,9 +138,23 @@ func databricksWorkspaceRootDbfsCustomerManagedKeyCreate(d *pluginsdk.ResourceDa return fmt.Errorf("%s: `customer_managed_key_enabled` must be set to `true`", *id) } + // If the 'root_dbfs_cmk_key_vault_id' was not defined assume the + // key vault exists in the same subscription as the workspace... + dbfsSubscriptionId := commonids.NewSubscriptionID(id.SubscriptionId) + + // If they passed the 'root_dbfs_cmk_key_vault_id' parse the Key Vault ID + // to extract the correct key vault subscription for the exists call... + if keyVaultId != "" { + keyVaultId, err := commonids.ParseKeyVaultID(keyVaultId) + if err != nil { + return fmt.Errorf("parsing %q as a Key Vault ID: %+v", keyVaultId, err) + } + + dbfsSubscriptionId = commonids.NewSubscriptionID(keyVaultId.SubscriptionId) + } + // make sure the key vault exists - subscriptionId := commonids.NewSubscriptionID(id.SubscriptionId) - keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, subscriptionId, key.KeyVaultBaseUrl) + keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, dbfsSubscriptionId, key.KeyVaultBaseUrl) if err != nil || keyVaultIdRaw == nil { return fmt.Errorf("retrieving the Resource ID for the Key Vault at URL %q: %+v", key.KeyVaultBaseUrl, err) } @@ -157,6 +187,11 @@ func databricksWorkspaceRootDbfsCustomerManagedKeyCreate(d *pluginsdk.ResourceDa } d.SetId(id.ID()) + + // Always set this even if it's empty to keep the state file + // consistent with the configuration file... + d.Set("key_vault_id", keyVaultId) + return databricksWorkspaceRootDbfsCustomerManagedKeyRead(d, meta) } @@ -170,6 +205,11 @@ func databricksWorkspaceRootDbfsCustomerManagedKeyRead(d *pluginsdk.ResourceData return err } + var keyVaultId string + if v, ok := d.GetOk("key_vault_id"); ok { + keyVaultId = v.(string) + } + resp, err := client.Get(ctx, *id) if err != nil { if response.WasNotFound(resp.HttpResponse) { @@ -181,45 +221,24 @@ func databricksWorkspaceRootDbfsCustomerManagedKeyRead(d *pluginsdk.ResourceData return fmt.Errorf("retrieving %s: %+v", *id, err) } - keySource := "" - keyName := "" - keyVersion := "" - keyVaultURI := "" - if model := resp.Model; model != nil { if model.Properties.Parameters != nil { if props := model.Properties.Parameters.Encryption; props != nil { - - if props.Value.KeySource != nil { - keySource = string(*props.Value.KeySource) - } - if props.Value.KeyName != nil { - keyName = *props.Value.KeyName - } - if props.Value.Keyversion != nil { - keyVersion = *props.Value.Keyversion + if strings.EqualFold(string(*props.Value.KeySource), string(workspaces.KeySourceMicrosoftPointKeyvault)) && (props.Value.KeyName == nil || props.Value.Keyversion == nil || props.Value.Keyvaulturi == nil) { + d.SetId("") + return nil } - if props.Value.Keyvaulturi != nil { - keyVaultURI = *props.Value.Keyvaulturi + + key, err := keyVaultParse.NewNestedItemID(*props.Value.Keyvaulturi, keyVaultParse.NestedItemTypeKey, *props.Value.KeyName, *props.Value.Keyversion) + if err == nil { + d.Set("key_vault_key_id", key.ID()) } } } } - if strings.EqualFold(keySource, string(workspaces.KeySourceMicrosoftPointKeyvault)) && (keyName == "" || keyVersion == "" || keyVaultURI == "") { - d.SetId("") - return nil - } - - d.SetId(id.ID()) d.Set("workspace_id", id.ID()) - - if keyVaultURI != "" { - key, err := keyVaultParse.NewNestedItemID(keyVaultURI, keyVaultParse.NestedItemTypeKey, keyName, keyVersion) - if err == nil { - d.Set("key_vault_key_id", key.ID()) - } - } + d.Set("key_vault_id", keyVaultId) return nil } @@ -235,8 +254,20 @@ func databricksWorkspaceRootDbfsCustomerManagedKeyUpdate(d *pluginsdk.ResourceDa return err } - keyIdRaw := d.Get("key_vault_key_id").(string) - key, err := keyVaultParse.ParseNestedItemID(keyIdRaw) + var key *keyVaultParse.NestedItemId + var params *workspaces.WorkspaceCustomParameters + var keyVaultId string + var keyVaultKeyId string + + if v, ok := d.GetOk("key_vault_key_id"); ok { + keyVaultKeyId = v.(string) + } + + if v, ok := d.GetOk("key_vault_id"); ok { + keyVaultId = v.(string) + } + + key, err = keyVaultParse.ParseNestedItemID(keyVaultKeyId) if err != nil { return err } @@ -252,8 +283,6 @@ func databricksWorkspaceRootDbfsCustomerManagedKeyUpdate(d *pluginsdk.ResourceDa return fmt.Errorf("retrieving %s: %+v", *id, err) } - var params *workspaces.WorkspaceCustomParameters - if model := workspace.Model; model != nil { if params = model.Properties.Parameters; params != nil { if params.PrepareEncryption != nil { @@ -270,11 +299,23 @@ func databricksWorkspaceRootDbfsCustomerManagedKeyUpdate(d *pluginsdk.ResourceDa return fmt.Errorf("%s: `customer_managed_key_enabled` must be set to `true`", *id) } + // If the 'root_dbfs_cmk_key_vault_id' was not defined assume + // the key vault exists in the same subscription as the workspace... + dbfsSubscriptionId := commonids.NewSubscriptionID(id.SubscriptionId) + + if keyVaultId != "" { + v, err := commonids.ParseKeyVaultID(keyVaultId) + if err != nil { + return fmt.Errorf("parsing %q as a Key Vault ID: %+v", keyVaultId, err) + } + + dbfsSubscriptionId = commonids.NewSubscriptionID(v.SubscriptionId) + } + // make sure the key vault exists - subscriptionId := commonids.NewSubscriptionID(id.SubscriptionId) - keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, subscriptionId, key.KeyVaultBaseUrl) - if err != nil || keyVaultIdRaw == nil { - return fmt.Errorf("retrieving the Resource ID for the Key Vault at URL %q: %+v", key.KeyVaultBaseUrl, err) + _, err = keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, dbfsSubscriptionId, key.KeyVaultBaseUrl) + if err != nil { + return fmt.Errorf("retrieving the Resource ID for the Key Vault in subscription %q at URL %q: %+v", dbfsSubscriptionId, key.KeyVaultBaseUrl, err) } // We need to pull all of the custom params from the parent @@ -299,6 +340,10 @@ func databricksWorkspaceRootDbfsCustomerManagedKeyUpdate(d *pluginsdk.ResourceDa return fmt.Errorf("updating Root DBFS Customer Managed Key for %s: %+v", *id, err) } + // Always set this even if it's empty to keep the state file + // consistent with the configuration file... + d.Set("key_vault_id", keyVaultId) + return databricksWorkspaceRootDbfsCustomerManagedKeyRead(d, meta) } diff --git a/internal/services/databricks/databricks_root_dbfs_customer_managed_key_resource_test.go b/internal/services/databricks/databricks_root_dbfs_customer_managed_key_resource_test.go index cfb0f91cf64a..5b1decc65765 100644 --- a/internal/services/databricks/databricks_root_dbfs_customer_managed_key_resource_test.go +++ b/internal/services/databricks/databricks_root_dbfs_customer_managed_key_resource_test.go @@ -105,6 +105,29 @@ func TestAccDatabricksWorkspaceRootDbfsCustomerManagedKey_noIp(t *testing.T) { }) } +func TestAccDatabricksWorkspaceRootDbfsCustomerManagedKey_altRootDbfsSubscriptionComplete(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_root_dbfs_customer_managed_key", "test") + parent := acceptance.BuildTestData(t, "azurerm_databricks_workspace", "test") + r := DatabricksWorkspaceRootDbfsCustomerManagedKeyResource{} + cmkAltTemplate := r.cmkAltTemplate() + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.altRootDbfsSubscriptionComplete(data, cmkAltTemplate, altSubscription), + Check: acceptance.ComposeTestCheckFunc( + check.That(parent.ResourceName).ExistsInAzure(r), + ), + }, + parent.ImportStep(), + }) +} + func (DatabricksWorkspaceRootDbfsCustomerManagedKeyResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := workspaces.ParseWorkspaceID(state.ID) if err != nil { @@ -212,6 +235,18 @@ resource "azurerm_databricks_workspace_root_dbfs_customer_managed_key" "test" { ` } +func (DatabricksWorkspaceRootDbfsCustomerManagedKeyResource) cmkAltTemplate() string { + return ` +resource "azurerm_databricks_workspace_root_dbfs_customer_managed_key" "test" { + depends_on = [azurerm_key_vault_access_policy.databricks] + + workspace_id = azurerm_databricks_workspace.test.id + key_vault_key_id = azurerm_key_vault_key.test.id + key_vault_id = azurerm_key_vault.test.id +} +` +} + func (DatabricksWorkspaceRootDbfsCustomerManagedKeyResource) keyVaultTemplate(data acceptance.TestData) string { return fmt.Sprintf(` resource "azurerm_key_vault" "test" { @@ -283,3 +318,125 @@ resource "azurerm_key_vault_access_policy" "databricks" { } `, data.RandomInteger, data.Locations.Primary, data.RandomString) } + +func (DatabricksWorkspaceRootDbfsCustomerManagedKeyResource) keyVaultAltSubscriptionTemplate(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azurerm_key_vault" "test" { + provider = azurerm-alt + + name = "kv-altsub-%[3]s" + location = azurerm_resource_group.keyVault.location + resource_group_name = azurerm_resource_group.keyVault.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "premium" + + soft_delete_retention_days = 7 +} + +resource "azurerm_key_vault_key" "test" { + depends_on = [azurerm_key_vault_access_policy.terraform] + provider = azurerm-alt + + name = "acctest-key-%[1]d" + key_vault_id = azurerm_key_vault.test.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_key_vault_access_policy" "terraform" { + provider = azurerm-alt + + key_vault_id = azurerm_key_vault.test.id + tenant_id = azurerm_key_vault.test.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "Get", + "List", + "Create", + "Decrypt", + "Encrypt", + "GetRotationPolicy", + "Sign", + "UnwrapKey", + "Verify", + "WrapKey", + "Delete", + "Restore", + "Recover", + "Update", + "Purge", + ] +} + +resource "azurerm_key_vault_access_policy" "databricks" { + depends_on = [azurerm_databricks_workspace.test] + provider = azurerm-alt + + key_vault_id = azurerm_key_vault.test.id + tenant_id = azurerm_databricks_workspace.test.storage_account_identity.0.tenant_id + object_id = azurerm_databricks_workspace.test.storage_account_identity.0.principal_id + + key_permissions = [ + "Get", + "GetRotationPolicy", + "UnwrapKey", + "WrapKey", + "Delete", + ] +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString) +} + +func (r DatabricksWorkspaceRootDbfsCustomerManagedKeyResource) altRootDbfsSubscriptionComplete(data acceptance.TestData, cmkAlt string, alt *DatabricksWorkspaceAlternateSubscription) string { + keyVault := r.keyVaultAltSubscriptionTemplate(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +provider "azurerm-alt" { + features {} + + tenant_id = "%[5]s" + subscription_id = "%[6]s" +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-databricks-dbfs-%[1]d" + location = "%[2]s" +} + +resource "azurerm_resource_group" "keyVault" { + provider = azurerm-alt + + name = "acctestRG-databricks-dbfs-alt-sub-%[1]d" + location = "%[2]s" +} + +%[3]s + +resource "azurerm_databricks_workspace" "test" { + name = "acctestDBW-%[1]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "premium" + + customer_managed_key_enabled = true + infrastructure_encryption_enabled = true +} + +%[4]s +`, data.RandomInteger, "eastus2", keyVault, cmkAlt, alt.tenant_id, alt.subscription_id) +} diff --git a/internal/services/databricks/databricks_workspace_resource.go b/internal/services/databricks/databricks_workspace_resource.go index 3f6055fac7f9..84e0324d2804 100644 --- a/internal/services/databricks/databricks_workspace_resource.go +++ b/internal/services/databricks/databricks_workspace_resource.go @@ -88,18 +88,6 @@ func resourceDatabricksWorkspace() *pluginsdk.Resource { Default: false, }, - "managed_services_cmk_key_vault_key_id": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: keyVaultValidate.KeyVaultChildID, - }, - - "managed_disk_cmk_key_vault_key_id": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: keyVaultValidate.KeyVaultChildID, - }, - "managed_disk_identity": { Type: pluginsdk.TypeList, Computed: true, @@ -125,12 +113,6 @@ func resourceDatabricksWorkspace() *pluginsdk.Resource { }, }, - "managed_disk_cmk_rotation_to_latest_version_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - RequiredWith: []string{"managed_disk_cmk_key_vault_key_id"}, - }, - "infrastructure_encryption_enabled": { Type: pluginsdk.TypeBool, ForceNew: true, @@ -280,6 +262,35 @@ func resourceDatabricksWorkspace() *pluginsdk.Resource { Computed: true, }, + "managed_services_cmk_key_vault_key_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: keyVaultValidate.KeyVaultChildID, + }, + + "managed_services_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, + }, + "managed_disk_cmk_key_vault_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: commonids.ValidateKeyVaultID, + }, + + "managed_disk_cmk_rotation_to_latest_version_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + RequiredWith: []string{"managed_disk_cmk_key_vault_key_id"}, + }, + "disk_encryption_set_id": { Type: pluginsdk.TypeString, Computed: true, @@ -454,23 +465,57 @@ func resourceDatabricksWorkspaceCreateUpdate(d *pluginsdk.ResourceData, meta int setEncrypt := false encrypt := &workspaces.WorkspacePropertiesEncryption{} encrypt.Entities = workspaces.EncryptionEntitiesDefinition{} - servicesKeyIdRaw := d.Get("managed_services_cmk_key_vault_key_id").(string) - if servicesKeyIdRaw != "" { + + 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 != "" { setEncrypt = true - key, err := keyVaultParse.ParseNestedItemID(servicesKeyIdRaw) + key, err := keyVaultParse.ParseNestedItemID(servicesKeyId) if err != nil { return err } // make sure the key vault exists - subscriptionResourceId := commonids.NewSubscriptionID(id.SubscriptionId) - keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, subscriptionResourceId, key.KeyVaultBaseUrl) - if err != nil || keyVaultIdRaw == nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed services Key Vault at URL %q: %+v", key.KeyVaultBaseUrl, err) + _, 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{ - // There is only one valid source for this field at this point in time so I have hardcoded the value KeySource: workspaces.EncryptionKeySourceMicrosoftPointKeyvault, KeyVaultProperties: &workspaces.EncryptionV2KeyVaultProperties{ KeyName: key.Name, @@ -480,23 +525,31 @@ func resourceDatabricksWorkspaceCreateUpdate(d *pluginsdk.ResourceData, meta int } } - diskKeyIdRaw := d.Get("managed_disk_cmk_key_vault_key_id").(string) - if diskKeyIdRaw != "" { + 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 != "" { setEncrypt = true - key, err := keyVaultParse.ParseNestedItemID(diskKeyIdRaw) + key, err := keyVaultParse.ParseNestedItemID(diskKeyId) if err != nil { return err } // make sure the key vault exists - subscriptionResourceId := commonids.NewSubscriptionID(id.SubscriptionId) - keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, subscriptionResourceId, key.KeyVaultBaseUrl) - if err != nil || keyVaultIdRaw == nil { - return fmt.Errorf("retrieving the Resource ID for the customer-managed keys for managed disk Key Vault at URL %q: %+v", key.KeyVaultBaseUrl, err) + _, 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{ - // There is only one valid source for this field at this point in time so I have hardcoded the value KeySource: workspaces.EncryptionKeySourceMicrosoftPointKeyvault, KeyVaultProperties: workspaces.ManagedDiskEncryptionKeyVaultProperties{ KeyName: key.Name, @@ -504,11 +557,10 @@ func resourceDatabricksWorkspaceCreateUpdate(d *pluginsdk.ResourceData, meta int KeyVaultUri: key.KeyVaultBaseUrl, }, } + } - rotationEnabled := d.Get("managed_disk_cmk_rotation_to_latest_version_enabled").(bool) - if rotationEnabled { - encrypt.Entities.ManagedDisk.RotationToLatestKeyVersionEnabled = utils.Bool(rotationEnabled) - } + if rotationEnabled := d.Get("managed_disk_cmk_rotation_to_latest_version_enabled").(bool); rotationEnabled { + encrypt.Entities.ManagedDisk.RotationToLatestKeyVersionEnabled = utils.Bool(rotationEnabled) } // Including the Tags in the workspace parameters will update the tags on @@ -566,13 +618,12 @@ func resourceDatabricksWorkspaceCreateUpdate(d *pluginsdk.ResourceData, meta int return fmt.Errorf("setting `custom_parameters`: %+v", err) } - if encrypt != nil && servicesKeyIdRaw != "" { - d.Set("managed_services_cmk_key_vault_key_id", servicesKeyIdRaw) - } - - if encrypt != nil && diskKeyIdRaw != "" { - d.Set("managed_disk_cmk_key_vault_key_id", diskKeyIdRaw) - } + // 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) } @@ -587,6 +638,17 @@ 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) { @@ -658,57 +720,59 @@ func resourceDatabricksWorkspaceRead(d *pluginsdk.ResourceData, meta interface{} return fmt.Errorf("setting `managed_disk_identity`: %+v", err) } + var workspaceUrl string if model.Properties.WorkspaceUrl != nil { - d.Set("workspace_url", model.Properties.WorkspaceUrl) + workspaceUrl = *model.Properties.WorkspaceUrl } + d.Set("workspace_url", workspaceUrl) + var workspaceId string if model.Properties.WorkspaceId != nil { - d.Set("workspace_id", model.Properties.WorkspaceId) + workspaceId = *model.Properties.WorkspaceId } + d.Set("workspace_id", workspaceId) // customer managed key for managed services - encryptKeyName := "" - encryptKeyVersion := "" - encryptKeyVaultURI := "" - + var servicesKeyId string if encryption := model.Properties.Encryption; encryption != nil { if encryptionProps := encryption.Entities.ManagedServices; encryptionProps != nil { - encryptKeyName = encryptionProps.KeyVaultProperties.KeyName - encryptKeyVersion = encryptionProps.KeyVaultProperties.KeyVersion - encryptKeyVaultURI = encryptionProps.KeyVaultProperties.KeyVaultUri + 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 encryptKeyVaultURI != "" { - key, err := keyVaultParse.NewNestedItemID(encryptKeyVaultURI, keyVaultParse.NestedItemTypeKey, encryptKeyName, encryptKeyVersion) - if err == nil { - d.Set("managed_services_cmk_key_vault_key_id", key.ID()) - } - } // customer managed key for managed disk - encryptDiskKeyName := "" - encryptDiskKeyVersion := "" - encryptDiskKeyVaultURI := "" - encryptDiskRotationEnabled := false - + var diskKeyId string if encryption := model.Properties.Encryption; encryption != nil { if encryptionProps := encryption.Entities.ManagedDisk; encryptionProps != nil { - encryptDiskKeyName = encryptionProps.KeyVaultProperties.KeyName - encryptDiskKeyVersion = encryptionProps.KeyVaultProperties.KeyVersion - encryptDiskKeyVaultURI = encryptionProps.KeyVaultProperties.KeyVaultUri + 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() + } + } + encryptDiskRotationEnabled = *encryptionProps.RotationToLatestKeyVersionEnabled } - } - if encryptDiskKeyVaultURI != "" { - key, err := keyVaultParse.NewNestedItemID(encryptDiskKeyVaultURI, keyVaultParse.NestedItemTypeKey, encryptDiskKeyName, encryptDiskKeyVersion) - if err == nil { - d.Set("managed_disk_cmk_key_vault_key_id", key.ID()) - } - d.Set("managed_disk_cmk_rotation_to_latest_version_enabled", encryptDiskRotationEnabled) - d.Set("disk_encryption_set_id", model.Properties.DiskEncryptionSetId) + var encryptDiskEncryptionSetId string + if model.Properties.DiskEncryptionSetId != nil { + encryptDiskEncryptionSetId = *model.Properties.DiskEncryptionSetId } + 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) return tags.FlattenAndSet(d, model.Tags) } @@ -740,26 +804,20 @@ func flattenWorkspaceManagedIdentity(input *workspaces.ManagedIdentityConfigurat e := make(map[string]interface{}) - if v := input; v != nil { - if t := v.PrincipalId; t != nil { - if t != nil { - e["principal_id"] = *t - } - } + if t := input.PrincipalId; t != nil { + e["principal_id"] = *t + } - if t := v.TenantId; t != nil { - e["tenant_id"] = *t - } + if t := input.TenantId; t != nil { + e["tenant_id"] = *t + } - if t := v.Type; t != nil { - if t != nil { - e["type"] = *t - } - } + if t := input.Type; t != nil { + e["type"] = *t + } - if len(e) != 0 { - return []interface{}{e} - } + if len(e) != 0 { + return []interface{}{e} } return []interface{}{e} diff --git a/internal/services/databricks/databricks_workspace_resource_test.go b/internal/services/databricks/databricks_workspace_resource_test.go index 02c4316ac155..1d09f5b3e4b2 100644 --- a/internal/services/databricks/databricks_workspace_resource_test.go +++ b/internal/services/databricks/databricks_workspace_resource_test.go @@ -6,6 +6,7 @@ package databricks_test import ( "context" "fmt" + "os" "strings" "testing" @@ -19,6 +20,11 @@ import ( type DatabricksWorkspaceResource struct{} +type DatabricksWorkspaceAlternateSubscription struct { + tenant_id string + subscription_id string +} + func TestAccDatabricksWorkspace_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_databricks_workspace", "test") r := DatabricksWorkspaceResource{} @@ -291,6 +297,94 @@ func TestAccDatabricksWorkspace_managedServicesRootDbfsCMKAndPrivateLink(t *test }) } +func TestAccDatabricksWorkspace_altSubscriptionCmkComplete(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.altSubscriptionCmkComplete(data, databricksPrincipalID, altSubscription), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("managed_services_cmk_key_vault_id", "managed_disk_cmk_key_vault_id"), + }) +} + +func TestAccDatabricksWorkspace_altSubscriptionCmkKeysInDifferentKeyVaultsAcrossDifferentSubscriptions(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.altSubscriptionCmkKeysInDifferentKeyVaultsAcrossDifferentSubscriptions(data, databricksPrincipalID, altSubscription), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("managed_services_cmk_key_vault_id", "managed_disk_cmk_key_vault_id"), + }) +} + +func TestAccDatabricksWorkspace_altSubscriptionCmkServicesOnly(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.altSubscriptionCmkServicesOnly(data, databricksPrincipalID, altSubscription), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("managed_services_cmk_key_vault_id"), + }) +} + +func TestAccDatabricksWorkspace_altSubscriptionCmkDiskOnly(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.altSubscriptionCmkDiskOnly(data, databricksPrincipalID, altSubscription), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("managed_disk_cmk_key_vault_id"), + }) +} + func getDatabricksPrincipalId(subscriptionId string) string { databricksPrincipalID := "bb9ef821-a78b-4312-90cc-5ece3fad3430" if strings.HasPrefix(strings.ToLower(subscriptionId), "85b3dbca") { @@ -300,6 +394,20 @@ func getDatabricksPrincipalId(subscriptionId string) string { return databricksPrincipalID } +func altSubscriptionCheck() *DatabricksWorkspaceAlternateSubscription { + altSubscriptonID := os.Getenv("ARM_SUBSCRIPTION_ID_ALT") + altTenantID := os.Getenv("ARM_TENANT_ID") + + if altSubscriptonID == "" || altTenantID == "" { + return nil + } + + return &DatabricksWorkspaceAlternateSubscription{ + tenant_id: altTenantID, + subscription_id: altSubscriptonID, + } +} + func (DatabricksWorkspaceResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := workspaces.ParseWorkspaceID(state.ID) if err != nil { @@ -1837,3 +1945,604 @@ resource "azurerm_private_dns_cname_record" "test" { } `, data.RandomInteger, data.Locations.Secondary, data.RandomString, databricksPrincipalID) } + +func (DatabricksWorkspaceResource) altSubscriptionCmkComplete(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 {} + + tenant_id = "%[5]s" + subscription_id = "%[6]s" +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-databricks-pri-sub-complete-%[1]d" + location = "West Europe" +} + +resource "azurerm_resource_group" "keyVault" { + provider = azurerm-alt + + name = "acctestRG-databricks-alt-sub-complete-%[1]d" + 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 + + name = "kv-altsub-%[3]s" + resource_group_name = azurerm_resource_group.keyVault.name + location = azurerm_resource_group.keyVault.location + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "premium" + + soft_delete_retention_days = 7 +} + +resource "azurerm_key_vault_key" "services" { + provider = azurerm-alt + depends_on = [azurerm_key_vault_access_policy.terraform] + + name = "acctest-services-certificate" + key_vault_id = azurerm_key_vault.keyVault.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_key_vault_key" "disk" { + provider = azurerm-alt + depends_on = [azurerm_key_vault_access_policy.terraform] + + name = "acctest-disk-certificate" + key_vault_id = azurerm_key_vault.keyVault.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_key_vault_access_policy" "terraform" { + provider = azurerm-alt + + key_vault_id = azurerm_key_vault.keyVault.id + tenant_id = azurerm_key_vault.keyVault.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "Get", + "List", + "Create", + "Decrypt", + "Encrypt", + "Sign", + "UnwrapKey", + "Verify", + "WrapKey", + "Delete", + "Restore", + "Recover", + "Update", + "Purge", + "GetRotationPolicy", + "SetRotationPolicy", + ] +} + +resource "azurerm_key_vault_access_policy" "managed" { + provider = azurerm-alt + + key_vault_id = azurerm_key_vault.keyVault.id + tenant_id = azurerm_key_vault.keyVault.tenant_id + object_id = "%[4]s" + + key_permissions = [ + "Get", + "UnwrapKey", + "WrapKey", + "GetRotationPolicy", + "SetRotationPolicy", + ] +} +`, data.RandomInteger, data.Locations.Secondary, data.RandomString, databricksPrincipalID, alt.tenant_id, alt.subscription_id) +} + +func (DatabricksWorkspaceResource) altSubscriptionCmkKeysInDifferentKeyVaultsAcrossDifferentSubscriptions(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 {} + + tenant_id = "%[5]s" + subscription_id = "%[6]s" +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-databricks-pri-sub-diff-%[1]d" + location = "West Europe" +} + +resource "azurerm_resource_group" "keyVault" { + name = "acctestRG-databricks-kv-pri-sub-diff-%[1]d" + location = "West Europe" +} + +resource "azurerm_resource_group" "keyVaultAlt" { + provider = azurerm-alt + + name = "acctestRG-databricks-kv-alt-sub-diff-%[1]d" + 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 + location = azurerm_resource_group.keyVault.location + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "premium" + + soft_delete_retention_days = 7 +} + +# Create this in a different subscription... +resource "azurerm_key_vault" "keyVaultAlt" { + provider = azurerm-alt + + name = "kv-altsub-%[3]s" + resource_group_name = azurerm_resource_group.keyVaultAlt.name + location = azurerm_resource_group.keyVaultAlt.location + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "premium" + + soft_delete_retention_days = 7 +} + +resource "azurerm_key_vault_key" "servicesAlt" { + provider = azurerm-alt + depends_on = [azurerm_key_vault_access_policy.terraformAlt] + + name = "acctest-services-certificate" + key_vault_id = azurerm_key_vault.keyVaultAlt.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_key_vault_key" "disk" { + depends_on = [azurerm_key_vault_access_policy.terraform] + + name = "acctest-disk-certificate" + key_vault_id = azurerm_key_vault.keyVault.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_key_vault_access_policy" "terraform" { + key_vault_id = azurerm_key_vault.keyVault.id + tenant_id = azurerm_key_vault.keyVault.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "Get", + "List", + "Create", + "Decrypt", + "Encrypt", + "Sign", + "UnwrapKey", + "Verify", + "WrapKey", + "Delete", + "Restore", + "Recover", + "Update", + "Purge", + "GetRotationPolicy", + "SetRotationPolicy", + ] +} + +resource "azurerm_key_vault_access_policy" "terraformAlt" { + provider = azurerm-alt + + key_vault_id = azurerm_key_vault.keyVaultAlt.id + tenant_id = azurerm_key_vault.keyVaultAlt.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "Get", + "List", + "Create", + "Decrypt", + "Encrypt", + "Sign", + "UnwrapKey", + "Verify", + "WrapKey", + "Delete", + "Restore", + "Recover", + "Update", + "Purge", + "GetRotationPolicy", + "SetRotationPolicy", + ] +} + +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" + + key_permissions = [ + "Get", + "UnwrapKey", + "WrapKey", + "GetRotationPolicy", + "SetRotationPolicy", + ] +} + +resource "azurerm_key_vault_access_policy" "managedAlt" { + provider = azurerm-alt + + key_vault_id = azurerm_key_vault.keyVaultAlt.id + tenant_id = azurerm_key_vault.keyVaultAlt.tenant_id + object_id = "%[4]s" + + key_permissions = [ + "Get", + "UnwrapKey", + "WrapKey", + "GetRotationPolicy", + "SetRotationPolicy", + ] +} +`, 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 {} + + tenant_id = "%[5]s" + subscription_id = "%[6]s" +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-databricks-pri-sub-services-%[1]d" + location = "West Europe" +} + +resource "azurerm_resource_group" "keyVault" { + provider = azurerm-alt + + name = "acctestRG-databricks-alt-sub-services-%[1]d" + 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 + + tags = { + Environment = "Sandbox" + Pricing = "Premium" + } +} + +# 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 + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "premium" + + soft_delete_retention_days = 7 +} + +resource "azurerm_key_vault_key" "services" { + provider = azurerm-alt + depends_on = [azurerm_key_vault_access_policy.terraform] + + name = "acctest-services-certificate" + key_vault_id = azurerm_key_vault.keyVault.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_key_vault_access_policy" "terraform" { + provider = azurerm-alt + + key_vault_id = azurerm_key_vault.keyVault.id + tenant_id = azurerm_key_vault.keyVault.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "Get", + "List", + "Create", + "Decrypt", + "Encrypt", + "Sign", + "UnwrapKey", + "Verify", + "WrapKey", + "Delete", + "Restore", + "Recover", + "Update", + "Purge", + "GetRotationPolicy", + "SetRotationPolicy", + ] +} + +resource "azurerm_key_vault_access_policy" "managed" { + provider = azurerm-alt + + key_vault_id = azurerm_key_vault.keyVault.id + tenant_id = azurerm_key_vault.keyVault.tenant_id + object_id = "%[4]s" + + key_permissions = [ + "Get", + "UnwrapKey", + "WrapKey", + "GetRotationPolicy", + "SetRotationPolicy", + ] +} +`, data.RandomInteger, data.Locations.Secondary, data.RandomString, databricksPrincipalID, alt.tenant_id, alt.subscription_id) +} + +func (DatabricksWorkspaceResource) altSubscriptionCmkDiskOnly(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 {} + + tenant_id = "%[5]s" + subscription_id = "%[6]s" +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-databricks-pri-sub-disk-%[1]d" + location = "West Europe" +} + +resource "azurerm_resource_group" "keyVault" { + provider = azurerm-alt + + name = "acctestRG-databricks-alt-sub-disk-%[1]d" + 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_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 + + name = "kv-altsub-%[3]s" + location = azurerm_resource_group.keyVault.location + resource_group_name = azurerm_resource_group.keyVault.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "premium" + + soft_delete_retention_days = 7 +} + +resource "azurerm_key_vault_key" "disk" { + provider = azurerm-alt + depends_on = [azurerm_key_vault_access_policy.terraform] + + name = "acctest-disk-certificate" + key_vault_id = azurerm_key_vault.keyVault.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_key_vault_access_policy" "terraform" { + provider = azurerm-alt + + key_vault_id = azurerm_key_vault.keyVault.id + tenant_id = azurerm_key_vault.keyVault.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "Get", + "List", + "Create", + "Decrypt", + "Encrypt", + "Sign", + "UnwrapKey", + "Verify", + "WrapKey", + "Delete", + "Restore", + "Recover", + "Update", + "Purge", + "GetRotationPolicy", + "SetRotationPolicy", + ] +} + +resource "azurerm_key_vault_access_policy" "managed" { + provider = azurerm-alt + + key_vault_id = azurerm_key_vault.keyVault.id + tenant_id = azurerm_key_vault.keyVault.tenant_id + object_id = "%[4]s" + + key_permissions = [ + "Get", + "UnwrapKey", + "WrapKey", + "GetRotationPolicy", + "SetRotationPolicy", + ] +} +`, data.RandomInteger, data.Locations.Secondary, data.RandomString, databricksPrincipalID, alt.tenant_id, alt.subscription_id) +} diff --git a/website/docs/r/databricks_workspace.html.markdown b/website/docs/r/databricks_workspace.html.markdown index b01783b4ba97..70c9197876b9 100644 --- a/website/docs/r/databricks_workspace.html.markdown +++ b/website/docs/r/databricks_workspace.html.markdown @@ -46,7 +46,19 @@ The following arguments are supported: * `sku` - (Required) The `sku` to use for the Databricks Workspace. Possible values are `standard`, `premium`, or `trial`. -~> **NOTE** Downgrading to a `trial sku` from a `standard` or `premium sku` will force a new resource to be created. +~> **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. + +-> **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. + +-> **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. + +-> **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). @@ -56,7 +68,7 @@ The following arguments are supported: * `managed_resource_group_name` - (Optional) The name of the resource group where Azure should place the managed Databricks resources. Changing this forces a new resource to be created. -~> **NOTE** Make sure that this field is unique if you have multiple Databrick Workspaces deployed in your subscription and choose to not have the `managed_resource_group_name` auto generated by the Azure Resource Provider. Having multiple Databrick Workspaces deployed in the same subscription with the same `manage_resource_group_name` may result in some resources that cannot be deleted. +~> **Note:** Make sure that this field is unique if you have multiple Databrick Workspaces deployed in your subscription and choose to not have the `managed_resource_group_name` auto generated by the Azure Resource Provider. Having multiple Databrick Workspaces deployed in the same subscription with the same `manage_resource_group_name` may result in some resources that cannot be deleted. * `customer_managed_key_enabled` - (Optional) Is the workspace enabled for customer managed key encryption? If `true` this enables the Managed Identity for the managed storage account. Possible values are `true` or `false`. Defaults to `false`. This field is only valid if the Databricks Workspace `sku` is set to `premium`. @@ -82,7 +94,7 @@ A `custom_parameters` block supports the following: * `no_public_ip` - (Optional) Are public IP Addresses not allowed? Possible values are `true` or `false`. Defaults to `false`. -~> **NOTE** Updating `no_public_ip` parameter is only allowed if the value is changing from `false` to `true` and and only for VNet-injected workspaces. +~> **Note:** Updating `no_public_ip` parameter is only allowed if the value is changing from `false` to `true` and and only for VNet-injected workspaces. * `public_subnet_name` - (Optional) The name of the Public Subnet within the Virtual Network. Required if `virtual_network_id` is set. Changing this forces a new resource to be created. @@ -100,7 +112,7 @@ A `custom_parameters` block supports the following: * `vnet_address_prefix` - (Optional) Address prefix for Managed virtual network. Defaults to `10.139`. Changing this forces a new resource to be created. -~> **NOTE** Databricks requires that a network security group is associated with the `public` and `private` subnets when a `virtual_network_id` has been defined. Both `public` and `private` subnets must be delegated to `Microsoft.Databricks/workspaces`. For more information about subnet delegation see the [product documentation](https://docs.microsoft.com/azure/virtual-network/subnet-delegation-overview). +~> **Note:** Databricks requires that a network security group is associated with the `public` and `private` subnets when a `virtual_network_id` has been defined. Both `public` and `private` subnets must be delegated to `Microsoft.Databricks/workspaces`. For more information about subnet delegation see the [product documentation](https://docs.microsoft.com/azure/virtual-network/subnet-delegation-overview). ## Example HCL Configurations @@ -108,8 +120,10 @@ A `custom_parameters` block supports the following: * [Databricks Workspace Secure Connectivity Cluster without Load Balancer](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/databricks/secure-connectivity-cluster/without-load-balancer) * [Databricks Workspace with Private Endpoint](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/private-endpoint/databricks/private-endpoint) * [Databricks Workspace with Private Endpoint, Customer Managed Keys for Managed Services and Databricks File System Customer Managed Keys](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/private-endpoint/databricks/managed-services) -* [Databricks Workspace with Databricks File System Customer Managed Keys](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/databricks/customer-managed-key/dbfs) +* [Databricks Workspace with Root Databricks File System Customer Managed Keys](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/databricks/customer-managed-key/dbfs) +* [Databricks Workspace with Root Databricks File System Customer Managed Keys in a Different Subscription](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/databricks/customer-managed-key/dbfs-cross-subscription) * [Databricks Workspace with Customer Managed Keys for Managed Services](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/databricks/customer-managed-key/managed-services) +* [Databricks Workspace with Customer Managed Keys for Managed Services with Key Vault and Key in a Different Subscription](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/databricks/customer-managed-key/managed-services-cross-subscription) ## Attributes Reference diff --git a/website/docs/r/databricks_workspace_root_dbfs_customer_managed_key.html.markdown b/website/docs/r/databricks_workspace_root_dbfs_customer_managed_key.html.markdown index cab45d5f2160..7f3a5ef39397 100644 --- a/website/docs/r/databricks_workspace_root_dbfs_customer_managed_key.html.markdown +++ b/website/docs/r/databricks_workspace_root_dbfs_customer_managed_key.html.markdown @@ -3,12 +3,12 @@ subcategory: "Databricks" layout: "azurerm" page_title: "Azure Resource Manager: azurerm_databricks_workspace_root_dbfs_customer_managed_key" description: |- - Manages a Customer Managed Key for the Databricks Workspaces root Databricks File System(DBFS) + Manages a Customer Managed Key for the Databricks Workspaces Root Databricks File System(DBFS) --- # azurerm_databricks_workspace_root_dbfs_customer_managed_key -Manages a Customer Managed Key for the Databricks Workspaces root Databricks File System(DBFS) +Manages a Customer Managed Key for the Databricks Workspaces Root Databricks File System(DBFS) ## Example Usage @@ -112,7 +112,7 @@ resource "azurerm_key_vault_access_policy" "databricks" { ## Example HCL Configurations * [Databricks Workspace with Root Databricks File System Customer Managed Keys](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/databricks/customer-managed-key/dbfs) -* [Databricks Workspace with Customer Managed Keys for Managed Services](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/databricks/customer-managed-key/managed-services) +* [Databricks Workspace with Root Databricks File System Customer Managed Keys in a Different Subscription](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/databricks/customer-managed-key/dbfs-cross-subscription) * [Databricks Workspace with Private Endpoint, Customer Managed Keys for Managed Services and Root Databricks File System Customer Managed Keys](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/private-endpoint/databricks/managed-services) ## Argument Reference @@ -123,6 +123,12 @@ The following arguments are supported: * `key_vault_key_id` - (Required) The resource ID of the Key Vault Key to be used. +* `key_vault_id` - (Optional) Specifies the Resource ID of the Key Vault which contains the `key_vault_key_id`. + +-> **Note:** The `key_vault_id` field only needs to be specified if the Key Vault which contains the `key_vault_key_id` exists in a different subscription than the Databricks Workspace. If the `key_vault_id` field is not specified it is assumed that the `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. + ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: