Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

azurerm_databricks - add 100% coverage for 2018-04-01 API #12331

Merged
merged 67 commits into from
Jul 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
40fe726
Current progress
WodansSon Jun 15, 2021
aca1936
Enable CMK working
WodansSon Jun 22, 2021
ca938bd
Add validation for cmk
WodansSon Jun 23, 2021
2c2a699
Add nil check for amlWorkspaceID
WodansSon Jun 23, 2021
69f3ef2
Attempt to fix complete test case
WodansSon Jun 23, 2021
3396f7c
remove maxitems from storage_account_identity
WodansSon Jun 23, 2021
b37a7d3
Fix lint errors
WodansSon Jun 24, 2021
2669161
Modify test TestAccDatabricksWorkspace_update
WodansSon Jun 24, 2021
e2c0329
Remove the set for ui_definition_uri
WodansSon Jun 24, 2021
dc1fdcf
Update test cases
WodansSon Jun 24, 2021
351c80d
Add test cases for new attributes
WodansSon Jun 24, 2021
852e1f1
Fixing test again
WodansSon Jun 24, 2021
ba2ccd4
Commenting out unused test code for now
WodansSon Jun 24, 2021
5f40200
Update azurerm/internal/services/databricks/databricks_workspace_reso…
WodansSon Jun 25, 2021
c0ebe1b
Update azurerm/internal/services/databricks/databricks_workspace_reso…
WodansSon Jun 25, 2021
e5d5d46
Update azurerm/internal/services/databricks/databricks_workspace_reso…
WodansSon Jun 25, 2021
671e676
Address PR comments...
WodansSon Jun 25, 2021
f035dbf
Update validation checks
WodansSon Jun 26, 2021
608d838
Fix lint error
WodansSon Jun 26, 2021
5eba929
Refactoring validation for readability
WodansSon Jun 26, 2021
ae58f3f
Refactor the world
WodansSon Jun 26, 2021
60ded22
Fixing my dyslexia in my uber comment...
WodansSon Jun 26, 2021
b739fa8
Update tests
WodansSon Jun 27, 2021
fa47caa
Bug fix
WodansSon Jun 27, 2021
1f167fd
Moved managed CMK code
WodansSon Jun 27, 2021
00920aa
Remove computed from custom_parameters
WodansSon Jun 27, 2021
fbb3a50
Revert update to custom_parameters
WodansSon Jun 27, 2021
08427c1
Enable CMK tests
WodansSon Jun 27, 2021
0abc254
Middle of massive refactor to new resource
WodansSon Jun 30, 2021
691d66a
Moved to new resource
WodansSon Jul 1, 2021
7c35ce8
terrafmt documentation
WodansSon Jul 1, 2021
9ed763e
Fix test case
WodansSon Jul 1, 2021
ef52e0d
Add new step to test case
WodansSon Jul 1, 2021
bf426a9
Update import test
WodansSon Jul 1, 2021
8e86ed6
Update import test
WodansSon Jul 1, 2021
3b220c2
Update importer setId
WodansSon Jul 1, 2021
3c6f2d9
Additional test updates
WodansSon Jul 1, 2021
7b975d4
Fix lint errors
WodansSon Jul 1, 2021
ef95831
Import fix
WodansSon Jul 1, 2021
e94cbc3
moved cmk out of custom params
WodansSon Jul 2, 2021
bfb7597
terrafmt
WodansSon Jul 2, 2021
e884d33
Another terrafmt
WodansSon Jul 2, 2021
cff7631
Update tests to validate parent resource
WodansSon Jul 2, 2021
948334a
Update CMK to always pass all custom params
WodansSon Jul 2, 2021
c4e0d98
Fixing my dyslexia again
WodansSon Jul 2, 2021
468f1f1
Update delete to pass all params
WodansSon Jul 3, 2021
1be5f4e
Add dependency on CMK for access policy
WodansSon Jul 3, 2021
3a11585
Wait a minute... Strike that. Reverse it.
WodansSon Jul 3, 2021
a7a3032
no_public_ip cannot be changed once set
WodansSon Jul 3, 2021
e00e09c
Fix invalid update and infra tests
WodansSon Jul 3, 2021
0d2a19d
Fix test cleanup configurations
WodansSon Jul 3, 2021
3044529
Fix update test as everything is force new
WodansSon Jul 3, 2021
f93964b
Trying unhook the associations so destroy works
WodansSon Jul 3, 2021
b5878d4
Remove just the subnet delegation
WodansSon Jul 3, 2021
f4acaf2
Delete the NSG first the the delegation
WodansSon Jul 3, 2021
d368e68
Remove assoc order
WodansSon Jul 3, 2021
763b184
Modify step one of cleanup
WodansSon Jul 3, 2021
114cfe8
Disable no public ip in clean up
WodansSon Jul 3, 2021
81597e0
remove deprecated attributes from tests
WodansSon Jul 3, 2021
ddd1c19
Documentation update only...
WodansSon Jul 4, 2021
2802269
Remove the local specific URL from the docs...
WodansSon Jul 4, 2021
d230c17
Address PR comments
WodansSon Jul 7, 2021
4291b13
Force databricks deletion first
WodansSon Jul 7, 2021
76ba47b
Remove fix for broken tests
WodansSon Jul 8, 2021
cb8108e
add workspace dependency on nsg
WodansSon Jul 8, 2021
ad817b9
Strike that, reverse it...
WodansSon Jul 8, 2021
1711bea
Not the NSG the NSGA
WodansSon Jul 8, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
package databricks

import (
"context"
"fmt"
"log"
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/services/databricks/mgmt/2018-04-01/databricks"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/databricks/parse"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/databricks/validate"
keyVaultParse "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/keyvault/parse"
keyVaultValidate "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/keyvault/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceDatabricksWorkspaceCustomerManagedKey() *pluginsdk.Resource {
return &pluginsdk.Resource{
Create: DatabricksWorkspaceCustomerManagedKeyCreateUpdate,
Read: DatabricksWorkspaceCustomerManagedKeyRead,
Update: DatabricksWorkspaceCustomerManagedKeyCreateUpdate,
Delete: DatabricksWorkspaceCustomerManagedKeyDelete,

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.ImporterValidatingResourceIdThen(func(id string) error {
_, err := parse.CustomerManagedKeyID(id)
return err
}, func(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) ([]*pluginsdk.ResourceData, error) {
client := meta.(*clients.Client).DataBricks.WorkspacesClient

// validate that the passed ID is a valid CMK configuration ID
customManagedKey, err := parse.CustomerManagedKeyID(d.Id())
if err != nil {
return []*pluginsdk.ResourceData{d}, fmt.Errorf("parsing Databricks workspace customer managed key ID %q for import: %v", d.Id(), err)
}

// convert the passed custom Managed Key ID to a valid workspace ID
workspace := parse.NewWorkspaceID(customManagedKey.SubscriptionId, customManagedKey.ResourceGroup, customManagedKey.CustomerMangagedKeyName)

// validate that the workspace exists
if _, err = client.Get(ctx, workspace.ResourceGroup, workspace.Name); err != nil {
return []*pluginsdk.ResourceData{d}, fmt.Errorf("retrieving the Databricks workspace customer managed key configuration(ID: %q) for workspace (ID: %q): %s", customManagedKey.ID(), workspace.ID(), err)
}

// set the new values for the CMK resource
d.SetId(customManagedKey.ID())
d.Set("workspace_id", workspace.ID())

return []*pluginsdk.ResourceData{d}, nil
}),

Schema: map[string]*pluginsdk.Schema{
"workspace_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validate.WorkspaceID,
},

// Make this key vault key id and abstract everything from the string...
"key_vault_key_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: keyVaultValidate.KeyVaultChildID,
},
},
}
}

func DatabricksWorkspaceCustomerManagedKeyCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error {
workspaceClient := meta.(*clients.Client).DataBricks.WorkspacesClient
keyVaultsClient := meta.(*clients.Client).KeyVault
subscriptionId := meta.(*clients.Client).Account.SubscriptionId
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()

workspaceIDRaw := d.Get("workspace_id").(string)
workspaceID, err := parse.WorkspaceID(workspaceIDRaw)
if err != nil {
return err
}

keyIdRaw := d.Get("key_vault_key_id").(string)
key, err := keyVaultParse.ParseNestedItemID(keyIdRaw)
if err != nil {
return err
}

// Not sure if I should also lock the key vault here too
// or at the very least the key?
locks.ByName(workspaceID.Name, "azurerm_databricks_workspace")
defer locks.UnlockByName(workspaceID.Name, "azurerm_databricks_workspace")
var encryptionEnabled, infrastructureEnabled bool

workspace, err := workspaceClient.Get(ctx, workspaceID.ResourceGroup, workspaceID.Name)
if err != nil {
return fmt.Errorf("retrieving Databricks Workspace %q (Resource Group %q): %+v", workspaceID.Name, workspaceID.ResourceGroup, err)
}
if workspace.Parameters != nil {
if workspace.Parameters.RequireInfrastructureEncryption != nil {
infrastructureEnabled = *workspace.Parameters.RequireInfrastructureEncryption.Value
}
if workspace.Parameters.PrepareEncryption != nil {
encryptionEnabled = *workspace.Parameters.PrepareEncryption.Value
}
} else {
return fmt.Errorf("retrieving Databricks Workspace %q (Resource Group %q): `WorkspaceCustomParameters` was nil", workspaceID.Name, workspaceID.ResourceGroup)
}

if infrastructureEnabled {
return fmt.Errorf("Databricks Workspace %q (Resource Group %q): `infrastructure_encryption_enabled` must be set to `false`", workspaceID.Name, workspaceID.ResourceGroup)
}
if !encryptionEnabled {
return fmt.Errorf("Databricks Workspace %q (Resource Group %q): `customer_managed_key_enabled` must be set to `true`", workspaceID.Name, workspaceID.ResourceGroup)
}

// make sure the key vault exists
keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, meta.(*clients.Client).Resource, 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)
}

resourceID := parse.NewCustomerManagedKeyID(subscriptionId, workspaceID.ResourceGroup, workspaceID.Name)

if d.IsNewResource() {
if workspace.Parameters.Encryption != nil {
return tf.ImportAsExistsError("azurerm_databricks_workspace_customer_managed_key", resourceID.ID())
}
}

// We need to pull all of the custom params from the parent
// workspace resource and then add our new encryption values into the
// structure, else the other values set in the parent workspace
// resource will be lost and overwritten as nil. ¯\_(ツ)_/¯
// NOTE: 'workspace.Parameters' will never be nil as 'customer_managed_key_enabled' and 'infrastructure_encryption_enabled'
// fields have a default value in the parent workspace resource.
params := workspace.Parameters
params.Encryption = &databricks.WorkspaceEncryptionParameter{
Value: &databricks.Encryption{
KeySource: databricks.MicrosoftKeyvault,
KeyName: &key.Name,
KeyVersion: &key.Version,
KeyVaultURI: &key.KeyVaultBaseUrl,
},
}

props := databricks.Workspace{
Location: workspace.Location,
Sku: workspace.Sku,
WorkspaceProperties: &databricks.WorkspaceProperties{
ManagedResourceGroupID: workspace.WorkspaceProperties.ManagedResourceGroupID,
Parameters: params,
},
Tags: workspace.Tags,
}

future, err := workspaceClient.CreateOrUpdate(ctx, props, resourceID.ResourceGroup, resourceID.CustomerMangagedKeyName)
if err != nil {
return fmt.Errorf("creating/updating %s: %+v", resourceID, err)
}

if err = future.WaitForCompletionRef(ctx, workspaceClient.Client); err != nil {
return fmt.Errorf("waiting for create/update of %s: %+v", resourceID, err)
}

d.SetId(resourceID.ID())
return DatabricksWorkspaceCustomerManagedKeyRead(d, meta)
}

func DatabricksWorkspaceCustomerManagedKeyRead(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).DataBricks.WorkspacesClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.CustomerManagedKeyID(d.Id())
if err != nil {
return err
}

workspaceId := parse.NewWorkspaceID(id.SubscriptionId, id.ResourceGroup, id.CustomerMangagedKeyName)

resp, err := client.Get(ctx, id.ResourceGroup, id.CustomerMangagedKeyName)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[DEBUG] %s was not found - removing from state", *id)
d.SetId("")
return nil
}

return fmt.Errorf("retrieving %s: %+v", *id, err)
}

keySource := ""
keyName := ""
keyVersion := ""
keyVaultURI := ""

if resp.WorkspaceProperties.Parameters != nil {
if props := resp.WorkspaceProperties.Parameters.Encryption; props != nil {
if props.Value.KeySource != "" {
keySource = string(props.Value.KeySource)
}
if props.Value.KeyName != nil {
keyName = *props.Value.KeyName
}
if props.Value.KeyVersion != nil {
keyVersion = *props.Value.KeyVersion
}
if props.Value.KeyVaultURI != nil {
keyVaultURI = *props.Value.KeyVaultURI
}
}
}

// I have to get rid of this check due to import if you want to re-cmk your DBFS.
// This is because when you delete this it sets the key source to default
// if !strings.EqualFold(keySource, string(databricks.MicrosoftKeyvault)) {
// return fmt.Errorf("retrieving Databricks Workspace %q (Resource Group %q): `Workspace.WorkspaceProperties.Encryption.Value.KeySource` was expected to be %q, got %q", id.CustomerMangagedKeyName, id.ResourceGroup, string(databricks.MicrosoftKeyvault), keySource)
// }

if strings.EqualFold(keySource, string(databricks.MicrosoftKeyvault)) && (keyName == "" || keyVersion == "" || keyVaultURI == "") {
return fmt.Errorf("Databricks Workspace %q (Resource Group %q): `Workspace.WorkspaceProperties.Encryption.Value(s)` were nil", id.CustomerMangagedKeyName, id.ResourceGroup)
}

d.SetId(id.ID())
d.Set("workspace_id", workspaceId.ID())
if keyVaultURI != "" {
key, err := keyVaultParse.NewNestedItemID(keyVaultURI, "keys", keyName, keyVersion)
if err == nil {
d.Set("key_vault_key_id", key.ID())
}
}

return nil
}

func DatabricksWorkspaceCustomerManagedKeyDelete(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).DataBricks.WorkspacesClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.CustomerManagedKeyID(d.Id())
if err != nil {
return err
}

workspaceID := parse.NewWorkspaceID(id.SubscriptionId, id.ResourceGroup, id.CustomerMangagedKeyName)

// Not sure if I should also lock the key vault here too
locks.ByName(workspaceID.Name, "azurerm_databricks_workspace")
defer locks.UnlockByName(workspaceID.Name, "azurerm_databricks_workspace")

workspace, err := client.Get(ctx, id.ResourceGroup, id.CustomerMangagedKeyName)
if err != nil {
if utils.ResponseWasNotFound(workspace.Response) {
log.Printf("[DEBUG] %s was not found - removing from state", *id)
d.SetId("")
return nil
}

return fmt.Errorf("retrieving %s: %+v", *id, err)
}

// Since this isn't real and you cannot turn off CMK without destroying the
// workspace and recreating it the best I can do is to set the workspace
// back to using Microsoft managed keys and removing the CMK fields
// also need to pull all of the custom params from the parent
// workspace resource and then add our new encryption values into the
// structure, else the other values set in the parent workspace
// resource will be lost and overwritten as nil. ¯\_(ツ)_/¯
params := workspace.Parameters
params.Encryption = &databricks.WorkspaceEncryptionParameter{
Value: &databricks.Encryption{
KeySource: databricks.Default,
},
}

props := databricks.Workspace{
Location: workspace.Location,
Sku: workspace.Sku,
WorkspaceProperties: &databricks.WorkspaceProperties{
ManagedResourceGroupID: workspace.WorkspaceProperties.ManagedResourceGroupID,
Parameters: params,
},
Tags: workspace.Tags,
}

future, err := client.CreateOrUpdate(ctx, props, workspaceID.ResourceGroup, workspaceID.Name)
if err != nil {
return fmt.Errorf("creating/updating %s: %+v", workspaceID, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("waiting for create/update of %s: %+v", workspaceID, err)
}

return nil
}
Loading