From ec65a09f989fa15381366a8b0b4585ec6b02679a Mon Sep 17 00:00:00 2001 From: xuwu1 Date: Fri, 24 Jun 2022 15:33:58 +0800 Subject: [PATCH] add encryption in automation account --- .../automation/automation_account_resource.go | 130 ++++++++++++++++ .../automation_account_resource_test.go | 139 ++++++++++++++++++ .../docs/r/automation_account.html.markdown | 18 +++ 3 files changed, 287 insertions(+) diff --git a/internal/services/automation/automation_account_resource.go b/internal/services/automation/automation_account_resource.go index 7f300f2f5a4ef..a5e004f489f0a 100644 --- a/internal/services/automation/automation_account_resource.go +++ b/internal/services/automation/automation_account_resource.go @@ -6,10 +6,12 @@ import ( "time" "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-sdk/resource-manager/automation/2021-06-22/automationaccount" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/automation/validate" @@ -58,6 +60,48 @@ func resourceAutomationAccount() *pluginsdk.Resource { "identity": commonschema.SystemAssignedUserAssignedIdentityOptional(), + "encryption": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*schema.Schema{ + "user_identity_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: commonids.ValidateUserAssignedIdentityID, + }, + "key_source": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice( + automationaccount.PossibleValuesForEncryptionKeySourceType(), + false, + ), + }, + "key_name": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "key_version": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "key_vault_uri": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "disable_local_auth": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + "tags": tags.Schema(), "dsc_server_endpoint": { @@ -111,9 +155,17 @@ func resourceAutomationAccountCreate(d *pluginsdk.ResourceData, meta interface{} Name: automationaccount.SkuNameEnum(d.Get("sku_name").(string)), }, PublicNetworkAccess: utils.Bool(d.Get("public_network_access_enabled").(bool)), + DisableLocalAuth: utils.Bool(d.Get("disable_local_auth").(bool)), }, Location: utils.String(location.Normalize(d.Get("location").(string))), } + if encryption := d.Get("encryption").([]interface{}); len(encryption) > 0 { + enc, err := expandEncryption(encryption[0].(map[string]interface{})) + if err != nil { + return fmt.Errorf("expanding `encryption`: %v", err) + } + parameters.Properties.Encryption = enc + } // for create account do not set identity property (even TypeNone is not allowed), or api will response error if identityVal.Type != identity.TypeNone { parameters.Identity = identityVal @@ -149,11 +201,20 @@ func resourceAutomationAccountUpdate(d *pluginsdk.ResourceData, meta interface{} Name: automationaccount.SkuNameEnum(d.Get("sku_name").(string)), }, PublicNetworkAccess: utils.Bool(d.Get("public_network_access_enabled").(bool)), + DisableLocalAuth: utils.Bool(d.Get("disable_local_auth").(bool)), }, Location: utils.String(location.Normalize(d.Get("location").(string))), Identity: identity, } + if encryption := d.Get("encryption").([]interface{}); len(encryption) > 0 { + enc, err := expandEncryption(encryption[0].(map[string]interface{})) + if err != nil { + return fmt.Errorf("expanding `encryption`: %v", err) + } + parameters.Properties.Encryption = enc + } + if tagsVal := expandTags(d.Get("tags").(map[string]interface{})); tagsVal != nil { parameters.Tags = &tagsVal } @@ -217,6 +278,16 @@ func resourceAutomationAccountRead(d *pluginsdk.ResourceData, meta interface{}) } d.Set("sku_name", skuName) + if prop.DisableLocalAuth != nil { + d.Set("disable_local_auth", *prop.DisableLocalAuth) + } + + if encryption, err := flattenEncryption(prop.Encryption); err != nil { + return fmt.Errorf("flattening `encryption`: %+v", err) + } else if encryption != nil { + d.Set("encryption", encryption) + } + d.Set("dsc_server_endpoint", keysResp.Endpoint) if keys := keysResp.Keys; keys != nil { d.Set("dsc_primary_access_key", keys.Primary) @@ -258,3 +329,62 @@ func resourceAutomationAccountDelete(d *pluginsdk.ResourceData, meta interface{} return nil } + +func expandEncryption(encMap map[string]interface{}) (*automationaccount.EncryptionProperties, error) { + var id interface{} + id, ok := encMap["user_identity_id"].(string) + if !ok { + return nil, fmt.Errorf("read encryption user identity id error") + } + prop := &automationaccount.EncryptionProperties{ + Identity: &automationaccount.EncryptionPropertiesIdentity{ + UserAssignedIdentity: &id, + }, + } + if val, ok := encMap["key_source"].(string); ok && val != "" { + prop.KeySource = (*automationaccount.EncryptionKeySourceType)(&val) + } + var keyProp automationaccount.KeyVaultProperties + var hasKeyProp bool + if val, ok := encMap["key_name"].(string); ok && val != "" { + keyProp.KeyName = &val + hasKeyProp = true + } + if val, ok := encMap["key_version"].(string); ok && val != "" { + keyProp.KeyVersion = &val + hasKeyProp = true + } + if val, ok := encMap["key_vault_uri"].(string); ok && val != "" { + keyProp.KeyvaultUri = &val + hasKeyProp = true + } + if hasKeyProp { + prop.KeyVaultProperties = &keyProp + } + return prop, nil +} + +func flattenEncryption(encryption *automationaccount.EncryptionProperties) (res []interface{}, err error) { + if encryption == nil { + return + } + item := map[string]interface{}{} + if encryption.KeySource != nil { + item["key_source"] = (string)(*encryption.KeySource) + } + if encryption.Identity != nil && encryption.Identity.UserAssignedIdentity != nil { + item["user_identity_id"] = (*encryption.Identity.UserAssignedIdentity).(string) + } + if keyProp := encryption.KeyVaultProperties; keyProp != nil { + if keyProp.KeyName != nil { + item["key_name"] = *keyProp.KeyName + } + if keyProp.KeyVersion != nil { + item["key_version"] = *keyProp.KeyVersion + } + if keyProp.KeyName != nil { + item["key_vault_uri"] = *keyProp.KeyvaultUri + } + } + return []interface{}{item}, nil +} diff --git a/internal/services/automation/automation_account_resource_test.go b/internal/services/automation/automation_account_resource_test.go index 7a71212ace810..f8f6d22b819a9 100644 --- a/internal/services/automation/automation_account_resource_test.go +++ b/internal/services/automation/automation_account_resource_test.go @@ -70,6 +70,23 @@ func TestAccAutomationAccount_complete(t *testing.T) { }) } +func TestAccAutomationAccount_encryption(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_automation_account", "test") + r := AutomationAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.encryption(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku_name").HasValue("Basic"), + check.That(data.ResourceName).Key("disable_local_auth").HasValue("true"), + ), + }, + data.ImportStep(), + }) +} + func TestAccAutomationAccount_identityUpdate(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_automation_account", "test") r := AutomationAccountResource{} @@ -257,6 +274,128 @@ resource "azurerm_automation_account" "test" { `, data.RandomInteger, data.Locations.Primary) } +func (AutomationAccountResource) encryption(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + key_vault { + purge_soft_delete_on_destroy = false + purge_soft_deleted_keys_on_destroy = false + } + } +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-auto-%[1]d" + location = "%[2]s" +} + +resource "azurerm_user_assigned_identity" "test" { + name = "acctestUAI-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_key_vault" "test" { + name = "vault%[1]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" + soft_delete_retention_days = 7 + purge_protection_enabled = true + + access_policy { + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + certificate_permissions = [ + "ManageContacts", + ] + + key_permissions = [ + "Create", + "Get", + "List", + "Delete", + "Purge", + ] + + secret_permissions = [ + "Set", + ] + } + + access_policy { + tenant_id = azurerm_user_assigned_identity.test.tenant_id + object_id = azurerm_user_assigned_identity.test.principal_id + + certificate_permissions = [] + + key_permissions = [ + "Get", + "Recover", + "WrapKey", + "UnwrapKey", + ] + + secret_permissions = [] + } +} + +data "azurerm_key_vault" "test" { + name = azurerm_key_vault.test.name + resource_group_name = azurerm_key_vault.test.resource_group_name +} + +data "azurerm_key_vault_key" "test" { + name = azurerm_key_vault_key.test.name + key_vault_id = azurerm_key_vault.test.id +} + +resource "azurerm_key_vault_key" "test" { + name = "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_automation_account" "test" { + name = "acctest-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "Basic" + + identity { + type = "UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.test.id + ] + } + disable_local_auth = true + encryption { + key_source = "Microsoft.Keyvault" + user_identity_id = azurerm_user_assigned_identity.test.id + key_vault_uri = azurerm_key_vault.test.vault_uri + key_name = azurerm_key_vault_key.test.name + key_version = azurerm_key_vault_key.test.version + } +} +`, data.RandomInteger, data.Locations.Primary) +} + func (AutomationAccountResource) userAssignedIdentity(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/website/docs/r/automation_account.html.markdown b/website/docs/r/automation_account.html.markdown index b4443cd50a53f..fbca3cd0b3f06 100644 --- a/website/docs/r/automation_account.html.markdown +++ b/website/docs/r/automation_account.html.markdown @@ -44,12 +44,16 @@ The following arguments are supported: * `sku_name` - (Required) The SKU of the account - only `Basic` is supported at this time. +* `disable_local_auth` - (Optional) Whether requests using non-AAD authentication are blocked. + --- * `identity` - (Optional) An `identity` block as defined below. * `tags` - (Optional) A mapping of tags to assign to the resource. +* `encryption` - (Optional) An `encryption` block as defined below. + --- An `identity` block supports the following: @@ -60,6 +64,20 @@ An `identity` block supports the following: -> **Note:** `identity_ids` is required when `type` is set to `UserAssigned` or `SystemAssigned, UserAssigned`. +-- + +An `encryption` block supports the following: + +* `user_identity_id` - (Optional) The user identity used for CMK. It will be an ARM resource id. + +* `key_source` - (Optional) The source of the encryption key. Possible values are `Microsoft.Keyvault` and `Microsoft.Storage`. + +* `key_name` - (Optional) The name of the key used to encrypt data. + +* `key_version` - (Optional) The version of the key used to encrypt data. + +* `key_vault_uri` - (Optional) The URI of the Key Vault key used to encrypt data. + --- ## Attributes Reference