From bf54f6a907b6209bdb42b1bec100244d297bea2b Mon Sep 17 00:00:00 2001 From: Mo Ahuja Date: Wed, 25 Aug 2021 10:41:10 -0400 Subject: [PATCH 1/4] Adding ability to activate a synapse workspace protected by a CMK --- internal/services/synapse/client/client.go | 4 + .../services/synapse/parse/workspace_keys.go | 75 +++++++ .../synapse/parse/workspace_keys_test.go | 128 +++++++++++ internal/services/synapse/registration.go | 1 + internal/services/synapse/resourceids.go | 1 + .../synapse/synapse_workspace_key_resource.go | 202 ++++++++++++++++++ .../synapse_workspace_key_resource_test.go | 159 ++++++++++++++ .../synapse/synapse_workspace_resource.go | 31 ++- .../synapse_workspace_resource_test.go | 2 +- .../synapse/validate/workspace_keys_id.go | 23 ++ .../validate/workspace_keys_id_test.go | 88 ++++++++ .../r/synapse_workspace_keys.html.markdown | 144 +++++++++++++ 12 files changed, 847 insertions(+), 11 deletions(-) create mode 100644 internal/services/synapse/parse/workspace_keys.go create mode 100644 internal/services/synapse/parse/workspace_keys_test.go create mode 100644 internal/services/synapse/synapse_workspace_key_resource.go create mode 100644 internal/services/synapse/synapse_workspace_key_resource_test.go create mode 100644 internal/services/synapse/validate/workspace_keys_id.go create mode 100644 internal/services/synapse/validate/workspace_keys_id_test.go create mode 100644 website/docs/r/synapse_workspace_keys.html.markdown diff --git a/internal/services/synapse/client/client.go b/internal/services/synapse/client/client.go index 534eb920cd95..8957d4c9d0e0 100644 --- a/internal/services/synapse/client/client.go +++ b/internal/services/synapse/client/client.go @@ -26,6 +26,7 @@ type Client struct { WorkspaceManagedIdentitySQLControlSettingsClient *synapse.WorkspaceManagedIdentitySQLControlSettingsClient WorkspaceSecurityAlertPolicyClient *synapse.WorkspaceManagedSQLServerSecurityAlertPolicyClient WorkspaceVulnerabilityAssessmentsClient *synapse.WorkspaceManagedSQLServerVulnerabilityAssessmentsClient + KeysClient *synapse.KeysClient synapseAuthorizer autorest.Authorizer } @@ -73,6 +74,8 @@ func NewClient(o *common.ClientOptions) *Client { workspaceVulnerabilityAssessmentsClient := synapse.NewWorkspaceManagedSQLServerVulnerabilityAssessmentsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&workspaceVulnerabilityAssessmentsClient.Client, o.ResourceManagerAuthorizer) + keysClient := synapse.NewKeysClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&keysClient.Client, o.ResourceManagerAuthorizer) return &Client{ FirewallRulesClient: &firewallRuleClient, @@ -89,6 +92,7 @@ func NewClient(o *common.ClientOptions) *Client { WorkspaceManagedIdentitySQLControlSettingsClient: &workspaceManagedIdentitySQLControlSettingsClient, WorkspaceSecurityAlertPolicyClient: &workspaceSecurityAlertPolicyClient, WorkspaceVulnerabilityAssessmentsClient: &workspaceVulnerabilityAssessmentsClient, + KeysClient: &keysClient, synapseAuthorizer: o.SynapseAuthorizer, } diff --git a/internal/services/synapse/parse/workspace_keys.go b/internal/services/synapse/parse/workspace_keys.go new file mode 100644 index 000000000000..2f1b5a0c463a --- /dev/null +++ b/internal/services/synapse/parse/workspace_keys.go @@ -0,0 +1,75 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" +) + +type WorkspaceKeysId struct { + SubscriptionId string + ResourceGroup string + WorkspaceName string + KeyName string +} + +func NewWorkspaceKeysID(subscriptionId, resourceGroup, workspaceName, keyName string) WorkspaceKeysId { + return WorkspaceKeysId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + WorkspaceName: workspaceName, + KeyName: keyName, + } +} + +func (id WorkspaceKeysId) String() string { + segments := []string{ + fmt.Sprintf("Key Name %q", id.KeyName), + fmt.Sprintf("Workspace Name %q", id.WorkspaceName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Workspace Keys", segmentsStr) +} + +func (id WorkspaceKeysId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Synapse/workspaces/%s/keys/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.WorkspaceName, id.KeyName) +} + +// WorkspaceKeysID parses a WorkspaceKeys ID into an WorkspaceKeysId struct +func WorkspaceKeysID(input string) (*WorkspaceKeysId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := WorkspaceKeysId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.WorkspaceName, err = id.PopSegment("workspaces"); err != nil { + return nil, err + } + if resourceId.KeyName, err = id.PopSegment("keys"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/synapse/parse/workspace_keys_test.go b/internal/services/synapse/parse/workspace_keys_test.go new file mode 100644 index 000000000000..dfa0574f37ea --- /dev/null +++ b/internal/services/synapse/parse/workspace_keys_test.go @@ -0,0 +1,128 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/resourceid" +) + +var _ resourceid.Formatter = WorkspaceKeysId{} + +func TestWorkspaceKeysIDFormatter(t *testing.T) { + actual := NewWorkspaceKeysID("12345678-1234-9876-4563-123456789012", "resGroup1", "workspace1", "key1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/keys/key1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestWorkspaceKeysID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *WorkspaceKeysId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing WorkspaceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/", + Error: true, + }, + + { + // missing value for WorkspaceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/", + Error: true, + }, + + { + // missing KeyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/", + Error: true, + }, + + { + // missing value for KeyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/keys/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/keys/key1", + Expected: &WorkspaceKeysId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + WorkspaceName: "workspace1", + KeyName: "key1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.SYNAPSE/WORKSPACES/WORKSPACE1/KEYS/KEY1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := WorkspaceKeysID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.WorkspaceName != v.Expected.WorkspaceName { + t.Fatalf("Expected %q but got %q for WorkspaceName", v.Expected.WorkspaceName, actual.WorkspaceName) + } + if actual.KeyName != v.Expected.KeyName { + t.Fatalf("Expected %q but got %q for KeyName", v.Expected.KeyName, actual.KeyName) + } + } +} diff --git a/internal/services/synapse/registration.go b/internal/services/synapse/registration.go index 98dae8c119c8..ecba4ed8b2bb 100644 --- a/internal/services/synapse/registration.go +++ b/internal/services/synapse/registration.go @@ -40,5 +40,6 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { "azurerm_synapse_workspace": resourceSynapseWorkspace(), "azurerm_synapse_workspace_security_alert_policy": resourceSynapseWorkspaceSecurityAlertPolicy(), "azurerm_synapse_workspace_vulnerability_assessment": resourceSynapseWorkspaceVulnerabilityAssessment(), + "azurerm_synapse_workspace_key": resourceSynapseWorkspaceKey(), } } diff --git a/internal/services/synapse/resourceids.go b/internal/services/synapse/resourceids.go index 85b171a9a2f5..9d70b98a5633 100644 --- a/internal/services/synapse/resourceids.go +++ b/internal/services/synapse/resourceids.go @@ -13,3 +13,4 @@ package synapse //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=PrivateLinkHub -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/privateLinkHubs/privateLinkHub1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=LinkedService -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/linkedservices/linkedservice1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=IntegrationRuntime -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/integrationruntimes/IntegrationRuntime1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=WorkspaceKeys -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/keys/key1 diff --git a/internal/services/synapse/synapse_workspace_key_resource.go b/internal/services/synapse/synapse_workspace_key_resource.go new file mode 100644 index 000000000000..49ed43c06e80 --- /dev/null +++ b/internal/services/synapse/synapse_workspace_key_resource.go @@ -0,0 +1,202 @@ +package synapse + +import ( + "context" + "fmt" + "log" + "strconv" + "time" + + "github.com/Azure/azure-sdk-for-go/services/synapse/mgmt/2021-03-01/synapse" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + keyVaultValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/synapse/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/synapse/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceSynapseWorkspaceKey() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceSynapseWorkspaceKeysCreateUpdate, + Read: resourceSynapseWorkspaceKeyRead, + Update: resourceSynapseWorkspaceKeysCreateUpdate, + Delete: resourceSynapseWorkspaceKeysDelete, + + 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 := parse.WorkspaceKeysID(id) + return err + }), + + Schema: map[string]*pluginsdk.Schema{ + "synapse_workspace_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.WorkspaceID, + }, + + "key_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "customer_managed_key_versionless_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: keyVaultValidate.VersionlessNestedItemId, + }, + + "is_active_cmk": { + Type: pluginsdk.TypeBool, + Required: true, + }, + }, + } +} + +func resourceSynapseWorkspaceKeysCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Synapse.KeysClient + // workspaceClient := meta.(*clients.Client).Synapse.WorkspaceClient + + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + workspaceId, err := parse.WorkspaceID(d.Get("synapse_workspace_id").(string)) + if err != nil { + return err + } + + key := d.Get("customer_managed_key_versionless_id") + keyName := d.Get("key_name").(string) + isActiveCMK := d.Get("is_active_cmk").(bool) + + log.Printf("[INFO] Is active CMK: %t", isActiveCMK) + + keyProperties := synapse.KeyProperties{ + IsActiveCMK: &isActiveCMK, + KeyVaultURL: utils.String(key.(string)), + } + + synapseKey := synapse.Key{ + KeyProperties: &keyProperties, + } + + keyresult, err := client.CreateOrUpdate(ctx, workspaceId.ResourceGroup, workspaceId.Name, keyName, synapseKey) + if err != nil { + return fmt.Errorf("creating Synapse Workspace Key %q (Workspace %q): %+v", workspaceId.Name, workspaceId.Name, err) + } + + if keyresult.ID == nil || *keyresult.ID == "" { + return fmt.Errorf("empty or nil ID returned for Synapse Key 'cmk'") + } + + // If the state of the key in the response (from Azure) is not equal to the desired target state (from plan/config), we'll wait until that change is complete + if isActiveCMK != *keyresult.KeyProperties.IsActiveCMK { + updateWait := synapseKeysWaitForStateChange(ctx, meta, d.Timeout(pluginsdk.TimeoutUpdate), workspaceId.ResourceGroup, workspaceId.Name, keyName, strconv.FormatBool(*keyresult.KeyProperties.IsActiveCMK), strconv.FormatBool(isActiveCMK)) + + if _, err := updateWait.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for Synapse Keys to finish updating '%q' (Workspace Group %q): %v", keyName, workspaceId.Name, err) + } + } + + id := parse.NewWorkspaceKeysID(workspaceId.SubscriptionId, workspaceId.ResourceGroup, workspaceId.Name, keyName) + d.SetId(id.ID()) + + return resourceSynapseWorkspaceKeyRead(d, meta) +} + +func resourceSynapseWorkspaceKeyRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Synapse.KeysClient + + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.WorkspaceKeysID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.KeyName) + if err != nil { + return fmt.Errorf("retrieving Synapse Workspace Key %q (Workspace %q): %+v", id.KeyName, id.WorkspaceName, err) + } + + workspaceID := parse.NewWorkspaceID(id.SubscriptionId, id.ResourceGroup, id.WorkspaceName) + + // Set the properties + d.Set("synapse_workspace_id", workspaceID.ID()) + d.Set("is_active_cmk", resp.KeyProperties.IsActiveCMK) + d.Set("key_name", id.KeyName) + d.Set("customer_managed_key_versionless_id", resp.KeyProperties.KeyVaultURL) + + return nil +} + +func resourceSynapseWorkspaceKeysDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Synapse.KeysClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.WorkspaceKeysID(d.Id()) + if err != nil { + return err + } + + // Fetch the key and check if it's an active key + keyresult, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.KeyName) + if err != nil { + return fmt.Errorf("Unable to fetch key %s in workspace %s: %v", id.KeyName, id.WorkspaceName, err) + } + + // Azure only lets you delete keys that are not active + if !*keyresult.KeyProperties.IsActiveCMK { + keyresult, err := client.Delete(ctx, id.ResourceGroup, id.WorkspaceName, id.KeyName) + if err != nil { + return fmt.Errorf("Unable to delete key %s in workspace %s: %v", id.KeyName, id.WorkspaceName, err) + } + + if keyresult.ID == nil || *keyresult.ID == "" { + return fmt.Errorf("empty or nil ID returned for Synapse Key %q during delete", id.KeyName) + } + } + + return nil +} + +func synapseKeysWaitForStateChange(ctx context.Context, meta interface{}, timeout time.Duration, resourceGroup string, workspaceName string, keyName string, pendingState string, targetState string) *pluginsdk.StateChangeConf { + return &pluginsdk.StateChangeConf{ + Pending: []string{pendingState}, + Target: []string{targetState}, + MinTimeout: 1 * time.Minute, + Timeout: timeout, + Refresh: synapseKeysRefresh(ctx, meta, resourceGroup, workspaceName, keyName), + } +} + +func synapseKeysRefresh(ctx context.Context, meta interface{}, resourceGroup string, workspaceName string, keyName string) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + client := meta.(*clients.Client).Synapse.KeysClient + + log.Printf("[INFO] checking on state of encryption key '%q' (Workspace %q)", workspaceName, keyName) + + resp, err := client.Get(ctx, resourceGroup, workspaceName, keyName) + if err != nil { + return nil, "nil", fmt.Errorf("polling for the status of encryption key '%q' (Workspace %q): %v", keyName, workspaceName, err) + } + + if resp.KeyProperties != nil && resp.KeyProperties.IsActiveCMK != nil { + return resp, strconv.FormatBool(*resp.KeyProperties.IsActiveCMK), nil + } + + // I am not returning an error here as this might have just been a bad get + return resp, "nil", nil + } +} diff --git a/internal/services/synapse/synapse_workspace_key_resource_test.go b/internal/services/synapse/synapse_workspace_key_resource_test.go new file mode 100644 index 000000000000..da562672f630 --- /dev/null +++ b/internal/services/synapse/synapse_workspace_key_resource_test.go @@ -0,0 +1,159 @@ +package synapse_test + +import ( + "context" + "fmt" + "testing" + + "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/services/synapse/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type SynapseWorkspaceKeysResource struct{} + +func TestAccSynapseWorkspaceKeys_customerManagedKey(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_synapse_workspace_keys", "test") + r := SynapseWorkspaceKeysResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.customerManagedKey(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // CMK takes a while to activate, so validation against the plan tends to fail. + data.ImportStep("is_active_cmk"), + }) +} + +func (r SynapseWorkspaceKeysResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.WorkspaceKeysID(state.ID) + if err != nil { + return nil, err + } + + resp, err := client.Synapse.KeysClient.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.KeyName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving Synapse Workspace Key %q (Workspace %q): %+v", id.KeyName, id.WorkspaceName, err) + } + + return utils.Bool(true), nil +} + +func (r SynapseWorkspaceKeysResource) customerManagedKey(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +data "azurerm_client_config" "current" {} + +resource "azurerm_key_vault" "test" { + name = "acckv%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + purge_protection_enabled = true +} + +resource "azurerm_key_vault_access_policy" "deployer" { + key_vault_id = azurerm_key_vault.test.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "create", "get", "delete", "purge" + ] +} + +resource "azurerm_key_vault_key" "test" { + name = "key" + key_vault_id = azurerm_key_vault.test.id + key_type = "RSA" + key_size = 2048 + key_opts = [ + "unwrapKey", + "wrapKey" + ] + depends_on = [ + azurerm_key_vault_access_policy.deployer + ] +} + +resource "azurerm_synapse_workspace" "test" { + name = "acctestsw%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + storage_data_lake_gen2_filesystem_id = azurerm_storage_data_lake_gen2_filesystem.test.id + sql_administrator_login = "sqladminuser" + sql_administrator_login_password = "H@Sh1CoR3!" + customer_managed_key_versionless_id = azurerm_key_vault_key.test.versionless_id + customer_managed_key_name = "test_key" +} + + + + +resource "azurerm_key_vault_access_policy" "workspace_policy" { + key_vault_id = azurerm_key_vault.test.id + tenant_id = azurerm_synapse_workspace.test.identity[0].tenant_id + object_id = azurerm_synapse_workspace.test.identity[0].principal_id + + key_permissions = [ + "Get", "WrapKey", "UnwrapKey" + ] +} + +resource "azurerm_synapse_workspace_key" "test" { + customer_managed_key_versionless_id = azurerm_key_vault_key.test.versionless_id + synapse_workspace_id = azurerm_synapse_workspace.test.id + is_active_cmk = true + key_name = "test_key" + depends_on = [azurerm_key_vault_access_policy.workspace_policy] +} + + + + + +`, template, data.RandomInteger, data.RandomInteger) +} + +func (r SynapseWorkspaceKeysResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + key_vault { + purge_soft_delete_on_destroy = false + } + } +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-synapse-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestacc%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_kind = "BlobStorage" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_data_lake_gen2_filesystem" "test" { + name = "acctest-%d" + storage_account_id = azurerm_storage_account.test.id +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomInteger) +} diff --git a/internal/services/synapse/synapse_workspace_resource.go b/internal/services/synapse/synapse_workspace_resource.go index 9eb3ed190fe0..ef82656cfa62 100644 --- a/internal/services/synapse/synapse_workspace_resource.go +++ b/internal/services/synapse/synapse_workspace_resource.go @@ -240,6 +240,13 @@ func resourceSynapseWorkspace() *pluginsdk.Resource { ValidateFunc: keyVaultValidate.VersionlessNestedItemId, }, + // Default to cmk to ensure backwards compatibility with previous version that hardcoded the key name to cmk + "customer_managed_key_name": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "cmk", + }, + "tags": tags.Schema(), }, } @@ -381,7 +388,9 @@ func resourceSynapseWorkspaceRead(d *pluginsdk.ResourceData, meta interface{}) e d.Set("sql_administrator_login", props.SQLAdministratorLogin) d.Set("managed_resource_group_name", props.ManagedResourceGroupName) d.Set("connectivity_endpoints", utils.FlattenMapStringPtrString(props.ConnectivityEndpoints)) - d.Set("customer_managed_key_versionless_id", flattenEncryptionDetails(props.Encryption)) + keyName, keyId := flattenEncryptionDetails(props.Encryption) + d.Set("customer_managed_key_versionless_id", keyId) + d.Set("customer_managed_key_name", keyName) repoType, repo := flattenWorkspaceRepositoryConfiguration(props.WorkspaceRepositoryConfiguration) if repoType == workspaceVSTSConfiguration { @@ -568,13 +577,15 @@ func expandIdentityControlSQLSettings(enabled bool) *synapse.ManagedIdentitySQLC func expandEncryptionDetails(d *pluginsdk.ResourceData) *synapse.EncryptionDetails { if key, ok := d.GetOk("customer_managed_key_versionless_id"); ok { - return &synapse.EncryptionDetails{ - Cmk: &synapse.CustomerManagedKeyDetails{ - Key: &synapse.WorkspaceKeyDetails{ - Name: utils.String("cmk"), - KeyVaultURL: utils.String(key.(string)), + if keyName, ok := d.GetOk("customer_managed_key_name"); ok { + return &synapse.EncryptionDetails{ + Cmk: &synapse.CustomerManagedKeyDetails{ + Key: &synapse.WorkspaceKeyDetails{ + Name: utils.String(keyName.(string)), + KeyVaultURL: utils.String(key.(string)), + }, }, - }, + } } } return nil @@ -684,11 +695,11 @@ func flattenIdentityControlSQLSettings(settings synapse.ManagedIdentitySQLContro return false } -func flattenEncryptionDetails(encryption *synapse.EncryptionDetails) *string { +func flattenEncryptionDetails(encryption *synapse.EncryptionDetails) (*string, *string) { if cmk := encryption.Cmk; cmk != nil { if key := cmk.Key; key != nil { - return key.KeyVaultURL + return key.Name, key.KeyVaultURL } } - return nil + return nil, nil } diff --git a/internal/services/synapse/synapse_workspace_resource_test.go b/internal/services/synapse/synapse_workspace_resource_test.go index b6aefcbcf114..438405319dd8 100644 --- a/internal/services/synapse/synapse_workspace_resource_test.go +++ b/internal/services/synapse/synapse_workspace_resource_test.go @@ -151,7 +151,7 @@ func TestAccSynapseWorkspace_github(t *testing.T) { }) } -func TestAccSynapseWorkspace_customerManagedKey(t *testing.T) { +func TestAccSynapseWorkspace_customerManagedKeyActivation(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_synapse_workspace", "test") r := SynapseWorkspaceResource{} diff --git a/internal/services/synapse/validate/workspace_keys_id.go b/internal/services/synapse/validate/workspace_keys_id.go new file mode 100644 index 000000000000..2be18eabbd7a --- /dev/null +++ b/internal/services/synapse/validate/workspace_keys_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/synapse/parse" +) + +func WorkspaceKeysID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.WorkspaceKeysID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/synapse/validate/workspace_keys_id_test.go b/internal/services/synapse/validate/workspace_keys_id_test.go new file mode 100644 index 000000000000..ca7a1aca5f65 --- /dev/null +++ b/internal/services/synapse/validate/workspace_keys_id_test.go @@ -0,0 +1,88 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestWorkspaceKeysID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing WorkspaceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/", + Valid: false, + }, + + { + // missing value for WorkspaceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/", + Valid: false, + }, + + { + // missing KeyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/", + Valid: false, + }, + + { + // missing value for KeyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/keys/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/keys/key1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.SYNAPSE/WORKSPACES/WORKSPACE1/KEYS/KEY1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := WorkspaceKeysID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/website/docs/r/synapse_workspace_keys.html.markdown b/website/docs/r/synapse_workspace_keys.html.markdown new file mode 100644 index 000000000000..f21e27a3d98d --- /dev/null +++ b/website/docs/r/synapse_workspace_keys.html.markdown @@ -0,0 +1,144 @@ +--- +subcategory: "Synapse" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_synapse_workspace_keys" +description: |- + Manages Synapse Workspace Keys +--- + +# azurerm_synapse_workspace + +Manages Synapse Workspace keys + +-> **Note:** Keys that are actively protecting a workspace cannot be deleted. When the keys resource is deleted, if the key is inactive it will be deleted, if it is active it will not be deleted. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_storage_account" "example" { + name = "examplestorageacc" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + account_tier = "Standard" + account_replication_type = "LRS" + account_kind = "StorageV2" + is_hns_enabled = "true" +} + +resource "azurerm_storage_data_lake_gen2_filesystem" "example" { + name = "example" + storage_account_id = azurerm_storage_account.example.id +} + + + +data "azurerm_client_config" "current" {} + +resource "azurerm_key_vault" "example" { + name = "example" + location = azurerm_resource_group.exampl.location + resource_group_name = azurerm_resource_group.example.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + purge_protection_enabled = true +} + +resource "azurerm_key_vault_access_policy" "deployer" { + key_vault_id = azurerm_key_vault.example.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "create", "get", "delete", "purge" + ] +} + +resource "azurerm_key_vault_key" "example" { + name = "workspace_encryption_key" + key_vault_id = azurerm_key_vault.example.id + key_type = "RSA" + key_size = 2048 + key_opts = [ + "unwrapKey", + "wrapKey" + ] + depends_on = [ + azurerm_key_vault_access_policy.deployer + ] +} + +resource "azurerm_synapse_workspace" "example" { + name = "example" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + storage_data_lake_gen2_filesystem_id = azurerm_storage_data_lake_gen2_filesystem.example.id + sql_administrator_login = "sqladminuser" + sql_administrator_login_password = "H@Sh1CoR3!" + customer_managed_key_versionless_id = azurerm_key_vault_key.example.versionless_id + customer_managed_key_name = "enckey" + + aad_admin { + login = "AzureAD Admin" + object_id = "00000000-0000-0000-0000-000000000000" + tenant_id = "00000000-0000-0000-0000-000000000000" + } + + tags = { + Env = "production" + } +} + +resource "azurerm_key_vault_access_policy" "workspace_policy" { + key_vault_id = azurerm_key_vault.example.id + tenant_id = azurerm_synapse_workspace.example.identity[0].tenant_id + object_id = azurerm_synapse_workspace.example.identity[0].principal_id + + key_permissions = [ + "Get", "WrapKey", "UnwrapKey" + ] +} + +resource "azurerm_synapse_workspace_key" "example" { + customer_managed_key_versionless_id = azurerm_key_vault_key.example.versionless_id + synapse_workspace_id = azurerm_synapse_workspace.example.id + is_active_cmk = true + key_name = "enckey" + depends_on = [azurerm_key_vault_access_policy.workspace_policy] +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `key_name` - (Required) Specifies the name of the workspace key. Should match the name of the key in the synapse workspace. + +* `customer_managed_key_versionless_id` - (Required) The Azure Key Vault Key Versionless ID to be used as the Customer Managed Key (CMK) for double encryption + +* `synapse_workspace_id` - (Required) The ID of the Synapse Workspace where the encryption key should be configured. + +* `is_active_cmk` - (Required) Specifies if the workspace should be encrypted with this key. + +-> **Note:** Only one key can actively encrypt a workspace. When performing a key rotation, setting a new key as the active key will disable existing keys. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Synapse Workspace. +* `read` - (Defaults to 5 minutes) Used when retrieving the Synapse Workspace. +* `update` - (Defaults to 30 minutes) Used when updating the Synapse Workspace. +* `delete` - (Defaults to 30 minutes) Used when deleting the Synapse Workspace. + +## Import + +Synapse Workspace can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_synapse_workspace_key.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Synapse/workspaces/workspace1/keys/key1 +``` From fa4ddd764fa4938505f4c83aa844e9e8a0c9f51e Mon Sep 17 00:00:00 2001 From: Mo Ahuja Date: Mon, 30 Aug 2021 14:12:25 -0400 Subject: [PATCH 2/4] Updated Synapse workspace to support named keys. This allows for key rotation scenarios. --- website/docs/r/synapse_workspace.html.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/docs/r/synapse_workspace.html.markdown b/website/docs/r/synapse_workspace.html.markdown index 874c07e853fa..24b90ad94eb7 100644 --- a/website/docs/r/synapse_workspace.html.markdown +++ b/website/docs/r/synapse_workspace.html.markdown @@ -85,6 +85,8 @@ The following arguments are supported: * `customer_managed_key_versionless_id` - (Optional) The Azure Key Vault Key Versionless ID to be used as the Customer Managed Key (CMK) for double encryption (e.g. `https://example-keyvault.vault.azure.net/type/cmk/`). +* `customer_managed_key_name` - (Optional) An identifier for the key. Name needs to match the name of the key used with the `azurerm_synapse_workspace_key` resource. Defaults to "cmk" if not specified. + * `tags` - (Optional) A mapping of tags which should be assigned to the Synapse Workspace. --- From 72d45fb448408bfd4d2cf7bb1c9dc8017fcd9d17 Mon Sep 17 00:00:00 2001 From: Mo Ahuja Date: Fri, 10 Sep 2021 11:10:29 -0400 Subject: [PATCH 3/4] Modified parameter structure for workspace to support user specified key name --- .../synapse/synapse_workspace_key_resource.go | 12 +-- .../synapse_workspace_key_resource_test.go | 16 ++-- .../synapse/synapse_workspace_resource.go | 79 ++++++++++++------- .../synapse_workspace_resource_test.go | 7 +- .../docs/r/synapse_workspace.html.markdown | 12 ++- .../r/synapse_workspace_keys.html.markdown | 4 +- 6 files changed, 83 insertions(+), 47 deletions(-) diff --git a/internal/services/synapse/synapse_workspace_key_resource.go b/internal/services/synapse/synapse_workspace_key_resource.go index 49ed43c06e80..ea1da7bedc2e 100644 --- a/internal/services/synapse/synapse_workspace_key_resource.go +++ b/internal/services/synapse/synapse_workspace_key_resource.go @@ -43,7 +43,7 @@ func resourceSynapseWorkspaceKey() *pluginsdk.Resource { ValidateFunc: validate.WorkspaceID, }, - "key_name": { + "cusomter_managed_key_name": { Type: pluginsdk.TypeString, Required: true, }, @@ -54,7 +54,7 @@ func resourceSynapseWorkspaceKey() *pluginsdk.Resource { ValidateFunc: keyVaultValidate.VersionlessNestedItemId, }, - "is_active_cmk": { + "active": { Type: pluginsdk.TypeBool, Required: true, }, @@ -75,8 +75,8 @@ func resourceSynapseWorkspaceKeysCreateUpdate(d *pluginsdk.ResourceData, meta in } key := d.Get("customer_managed_key_versionless_id") - keyName := d.Get("key_name").(string) - isActiveCMK := d.Get("is_active_cmk").(bool) + keyName := d.Get("cusomter_managed_key_name").(string) + isActiveCMK := d.Get("active").(bool) log.Printf("[INFO] Is active CMK: %t", isActiveCMK) @@ -133,8 +133,8 @@ func resourceSynapseWorkspaceKeyRead(d *pluginsdk.ResourceData, meta interface{} // Set the properties d.Set("synapse_workspace_id", workspaceID.ID()) - d.Set("is_active_cmk", resp.KeyProperties.IsActiveCMK) - d.Set("key_name", id.KeyName) + d.Set("active", resp.KeyProperties.IsActiveCMK) + d.Set("cusomter_managed_key_name", id.KeyName) d.Set("customer_managed_key_versionless_id", resp.KeyProperties.KeyVaultURL) return nil diff --git a/internal/services/synapse/synapse_workspace_key_resource_test.go b/internal/services/synapse/synapse_workspace_key_resource_test.go index da562672f630..472a69f7fac5 100644 --- a/internal/services/synapse/synapse_workspace_key_resource_test.go +++ b/internal/services/synapse/synapse_workspace_key_resource_test.go @@ -16,7 +16,7 @@ import ( type SynapseWorkspaceKeysResource struct{} func TestAccSynapseWorkspaceKeys_customerManagedKey(t *testing.T) { - data := acceptance.BuildTestData(t, "azurerm_synapse_workspace_keys", "test") + data := acceptance.BuildTestData(t, "azurerm_synapse_workspace_key", "test") r := SynapseWorkspaceKeysResource{} data.ResourceTest(t, r, []acceptance.TestStep{ @@ -27,7 +27,7 @@ func TestAccSynapseWorkspaceKeys_customerManagedKey(t *testing.T) { ), }, // CMK takes a while to activate, so validation against the plan tends to fail. - data.ImportStep("is_active_cmk"), + data.ImportStep(), }) } @@ -95,8 +95,12 @@ resource "azurerm_synapse_workspace" "test" { storage_data_lake_gen2_filesystem_id = azurerm_storage_data_lake_gen2_filesystem.test.id sql_administrator_login = "sqladminuser" sql_administrator_login_password = "H@Sh1CoR3!" - customer_managed_key_versionless_id = azurerm_key_vault_key.test.versionless_id - customer_managed_key_name = "test_key" + customer_managed_key { + key_versionless_id = azurerm_key_vault_key.test.versionless_id + key_name = "test_key" + + } + } @@ -115,8 +119,8 @@ resource "azurerm_key_vault_access_policy" "workspace_policy" { resource "azurerm_synapse_workspace_key" "test" { customer_managed_key_versionless_id = azurerm_key_vault_key.test.versionless_id synapse_workspace_id = azurerm_synapse_workspace.test.id - is_active_cmk = true - key_name = "test_key" + active = true + cusomter_managed_key_name = "test_key" depends_on = [azurerm_key_vault_access_policy.workspace_policy] } diff --git a/internal/services/synapse/synapse_workspace_resource.go b/internal/services/synapse/synapse_workspace_resource.go index ef82656cfa62..ddfa61c6c5b1 100644 --- a/internal/services/synapse/synapse_workspace_resource.go +++ b/internal/services/synapse/synapse_workspace_resource.go @@ -234,17 +234,26 @@ func resourceSynapseWorkspace() *pluginsdk.Resource { Optional: true, }, - "customer_managed_key_versionless_id": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: keyVaultValidate.VersionlessNestedItemId, - }, - - // Default to cmk to ensure backwards compatibility with previous version that hardcoded the key name to cmk - "customer_managed_key_name": { - Type: pluginsdk.TypeString, + "customer_managed_key": { + Type: pluginsdk.TypeList, Optional: true, - Default: "cmk", + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "key_versionless_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: keyVaultValidate.VersionlessNestedItemId, + }, + + // Default to cmk to ensure backwards compatibility with previous version that hardcoded the key name to cmk + "key_name": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "cmk", + }, + }, + }, }, "tags": tags.Schema(), @@ -388,9 +397,10 @@ func resourceSynapseWorkspaceRead(d *pluginsdk.ResourceData, meta interface{}) e d.Set("sql_administrator_login", props.SQLAdministratorLogin) d.Set("managed_resource_group_name", props.ManagedResourceGroupName) d.Set("connectivity_endpoints", utils.FlattenMapStringPtrString(props.ConnectivityEndpoints)) - keyName, keyId := flattenEncryptionDetails(props.Encryption) - d.Set("customer_managed_key_versionless_id", keyId) - d.Set("customer_managed_key_name", keyName) + cmk := flattenEncryptionDetails(props.Encryption) + if err := d.Set("customer_managed_key", cmk); err != nil { + return fmt.Errorf("setting `customer_managed_key`: %+v", err) + } repoType, repo := flattenWorkspaceRepositoryConfiguration(props.WorkspaceRepositoryConfiguration) if repoType == workspaceVSTSConfiguration { @@ -576,18 +586,19 @@ func expandIdentityControlSQLSettings(enabled bool) *synapse.ManagedIdentitySQLC } func expandEncryptionDetails(d *pluginsdk.ResourceData) *synapse.EncryptionDetails { - if key, ok := d.GetOk("customer_managed_key_versionless_id"); ok { - if keyName, ok := d.GetOk("customer_managed_key_name"); ok { - return &synapse.EncryptionDetails{ - Cmk: &synapse.CustomerManagedKeyDetails{ - Key: &synapse.WorkspaceKeyDetails{ - Name: utils.String(keyName.(string)), - KeyVaultURL: utils.String(key.(string)), - }, + + if cmkList, ok := d.GetOk("customer_managed_key"); ok { + cmk := cmkList.([]interface{})[0].(map[string]interface{}) + return &synapse.EncryptionDetails{ + Cmk: &synapse.CustomerManagedKeyDetails{ + Key: &synapse.WorkspaceKeyDetails{ + Name: utils.String(cmk["key_name"].(string)), + KeyVaultURL: utils.String(cmk["key_versionless_id"].(string)), }, - } + }, } } + return nil } @@ -695,11 +706,25 @@ func flattenIdentityControlSQLSettings(settings synapse.ManagedIdentitySQLContro return false } -func flattenEncryptionDetails(encryption *synapse.EncryptionDetails) (*string, *string) { - if cmk := encryption.Cmk; cmk != nil { - if key := cmk.Key; key != nil { - return key.Name, key.KeyVaultURL +func flattenEncryptionDetails(encryption *synapse.EncryptionDetails) []interface{} { + + if encryption != nil { + if cmk := encryption.Cmk; cmk != nil { + if cmk.Key != nil { + resultMap := map[string]interface{}{} + resultMap["key_name"] = *cmk.Key.Name + resultMap["key_versionless_id"] = *cmk.Key.KeyVaultURL + return []interface{}{resultMap} + } } + + // if cmk := encryption.Cmk; cmk != nil { + // if key := cmk.Key; key != nil { + // return key.Name, key.KeyVaultURL + // } + // } + } - return nil, nil + + return make([]interface{}, 0) } diff --git a/internal/services/synapse/synapse_workspace_resource_test.go b/internal/services/synapse/synapse_workspace_resource_test.go index 438405319dd8..a3263490f4eb 100644 --- a/internal/services/synapse/synapse_workspace_resource_test.go +++ b/internal/services/synapse/synapse_workspace_resource_test.go @@ -160,7 +160,7 @@ func TestAccSynapseWorkspace_customerManagedKeyActivation(t *testing.T) { Config: r.customerManagedKey(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("customer_managed_key_versionless_id").Exists(), + check.That(data.ResourceName).Key("customer_managed_key.0.key_versionless_id").Exists(), ), }, data.ImportStep("sql_administrator_login_password"), @@ -390,7 +390,10 @@ resource "azurerm_synapse_workspace" "test" { storage_data_lake_gen2_filesystem_id = azurerm_storage_data_lake_gen2_filesystem.test.id sql_administrator_login = "sqladminuser" sql_administrator_login_password = "H@Sh1CoR3!" - customer_managed_key_versionless_id = azurerm_key_vault_key.test.versionless_id + customer_managed_key { + key_versionless_id = azurerm_key_vault_key.test.versionless_id + } + } `, template, data.RandomInteger, data.RandomInteger) } diff --git a/website/docs/r/synapse_workspace.html.markdown b/website/docs/r/synapse_workspace.html.markdown index 24b90ad94eb7..bc4a692c1b87 100644 --- a/website/docs/r/synapse_workspace.html.markdown +++ b/website/docs/r/synapse_workspace.html.markdown @@ -83,10 +83,6 @@ The following arguments are supported: * `github_repo` - (Optional) A `github_repo` block as defined below. -* `customer_managed_key_versionless_id` - (Optional) The Azure Key Vault Key Versionless ID to be used as the Customer Managed Key (CMK) for double encryption (e.g. `https://example-keyvault.vault.azure.net/type/cmk/`). - -* `customer_managed_key_name` - (Optional) An identifier for the key. Name needs to match the name of the key used with the `azurerm_synapse_workspace_key` resource. Defaults to "cmk" if not specified. - * `tags` - (Optional) A mapping of tags which should be assigned to the Synapse Workspace. --- @@ -131,6 +127,14 @@ A `github_repo` block supports the following: -> **Note:** You must log in to the Synapse UI to complete the authentication to the GitHub repository. +--- + +A `customer_managed_key` block supports the following: + +* `key_versionless_id` - (Optional) The Azure Key Vault Key Versionless ID to be used as the Customer Managed Key (CMK) for double encryption (e.g. `https://example-keyvault.vault.azure.net/type/cmk/`). + +* `key_name` - (Optional) An identifier for the key. Name needs to match the name of the key used with the `azurerm_synapse_workspace_key` resource. Defaults to "cmk" if not specified. + ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: diff --git a/website/docs/r/synapse_workspace_keys.html.markdown b/website/docs/r/synapse_workspace_keys.html.markdown index f21e27a3d98d..bb9bba4fc23b 100644 --- a/website/docs/r/synapse_workspace_keys.html.markdown +++ b/website/docs/r/synapse_workspace_keys.html.markdown @@ -106,7 +106,7 @@ resource "azurerm_key_vault_access_policy" "workspace_policy" { resource "azurerm_synapse_workspace_key" "example" { customer_managed_key_versionless_id = azurerm_key_vault_key.example.versionless_id synapse_workspace_id = azurerm_synapse_workspace.example.id - is_active_cmk = true + active = true key_name = "enckey" depends_on = [azurerm_key_vault_access_policy.workspace_policy] } @@ -122,7 +122,7 @@ The following arguments are supported: * `synapse_workspace_id` - (Required) The ID of the Synapse Workspace where the encryption key should be configured. -* `is_active_cmk` - (Required) Specifies if the workspace should be encrypted with this key. +* `active` - (Required) Specifies if the workspace should be encrypted with this key. -> **Note:** Only one key can actively encrypt a workspace. When performing a key rotation, setting a new key as the active key will disable existing keys. From 83ea742190be8fa521583c0c0e318abb53eccf2d Mon Sep 17 00:00:00 2001 From: Mo Ahuja Date: Fri, 10 Sep 2021 11:17:24 -0400 Subject: [PATCH 4/4] Fixed website linting --- website/docs/r/synapse_workspace.html.markdown | 4 +++- website/docs/r/synapse_workspace_keys.html.markdown | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/website/docs/r/synapse_workspace.html.markdown b/website/docs/r/synapse_workspace.html.markdown index bc4a692c1b87..d3e65b87b685 100644 --- a/website/docs/r/synapse_workspace.html.markdown +++ b/website/docs/r/synapse_workspace.html.markdown @@ -83,6 +83,8 @@ The following arguments are supported: * `github_repo` - (Optional) A `github_repo` block as defined below. +* `customer_managed_key` - (Optional) A `customer_managed_key` block as defined below. + * `tags` - (Optional) A mapping of tags which should be assigned to the Synapse Workspace. --- @@ -131,7 +133,7 @@ A `github_repo` block supports the following: A `customer_managed_key` block supports the following: -* `key_versionless_id` - (Optional) The Azure Key Vault Key Versionless ID to be used as the Customer Managed Key (CMK) for double encryption (e.g. `https://example-keyvault.vault.azure.net/type/cmk/`). +* `key_versionless_id` - (Required) The Azure Key Vault Key Versionless ID to be used as the Customer Managed Key (CMK) for double encryption (e.g. `https://example-keyvault.vault.azure.net/type/cmk/`). * `key_name` - (Optional) An identifier for the key. Name needs to match the name of the key used with the `azurerm_synapse_workspace_key` resource. Defaults to "cmk" if not specified. diff --git a/website/docs/r/synapse_workspace_keys.html.markdown b/website/docs/r/synapse_workspace_keys.html.markdown index bb9bba4fc23b..d194a76c7256 100644 --- a/website/docs/r/synapse_workspace_keys.html.markdown +++ b/website/docs/r/synapse_workspace_keys.html.markdown @@ -106,7 +106,7 @@ resource "azurerm_key_vault_access_policy" "workspace_policy" { resource "azurerm_synapse_workspace_key" "example" { customer_managed_key_versionless_id = azurerm_key_vault_key.example.versionless_id synapse_workspace_id = azurerm_synapse_workspace.example.id - active = true + active = true key_name = "enckey" depends_on = [azurerm_key_vault_access_policy.workspace_policy] }