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

New Resource: 'azurerm_storage_account_encryption_settings' to enable storage account encryption using key vault customer-managed keys #2046

Closed
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
90 changes: 89 additions & 1 deletion azurerm/resource_arm_storage_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,28 @@ func resourceArmStorageAccount() *schema.Resource {
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
},

"key_vault_properties": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key_name": {
Type: schema.TypeString,
Required: true,
},
"key_version": {
Type: schema.TypeString,
Required: true,
},
"key_vault_uri": {
Type: schema.TypeString,
Required: true,
},
},
},
},

"custom_domain": {
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -341,6 +363,7 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e
storageAccountEncryptionSource := d.Get("account_encryption_source").(string)

networkRules := expandStorageAccountNetworkRules(d)
keyVaultProperties := expandAzureRmStorageAccountKeyVaultProperties(d)

parameters := storage.AccountCreateParameters{
Location: &location,
Expand All @@ -358,7 +381,8 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e
File: &storage.EncryptionService{
Enabled: utils.Bool(enableFileEncryption),
}},
KeySource: storage.KeySource(storageAccountEncryptionSource),
KeySource: storage.KeySource(storageAccountEncryptionSource),
KeyVaultProperties: keyVaultProperties,
},
EnableHTTPSTrafficOnly: &enableHTTPSTrafficOnly,
NetworkRuleSet: networkRules,
Expand Down Expand Up @@ -582,6 +606,27 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e
d.SetPartial("network_rules")
}

if d.HasChange("account_encryption_source") {
accountEncryptionSource := d.Get("account_encryption_source").(string)
keyVaultProperties := expandAzureRmStorageAccountKeyVaultProperties(d)
opts := storage.AccountUpdateParameters{
AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{
Encryption: &storage.Encryption{
KeySource: storage.KeySource(accountEncryptionSource),
KeyVaultProperties: keyVaultProperties,
},
},
}

_, err := client.Update(ctx, resourceGroupName, storageAccountName, opts)
if err != nil {
return fmt.Errorf("Error updating Azure Storage Account account_encryption_source and key_vault_properties %q: %+v", storageAccountName, err)
}

d.SetPartial("account_encryption_source")
d.SetPartial("key_vault_properties")
}

d.Partial(false)
return resourceArmStorageAccountRead(d, meta)
}
Expand Down Expand Up @@ -646,6 +691,12 @@ func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) err
}
}
d.Set("account_encryption_source", string(encryption.KeySource))

if keyVaultProperties := encryption.KeyVaultProperties; keyVaultProperties != nil {
if err := d.Set("key_vault_properties", flattenAzureRmStorageAccountKeyVaultProperties(keyVaultProperties)); err != nil {
return fmt.Errorf("Error flattening `key_vault_properties`: %+v", err)
}
}
}

// Computed
Expand Down Expand Up @@ -963,3 +1014,40 @@ func flattenAzureRmStorageAccountIdentity(identity *storage.Identity) []interfac

return []interface{}{result}
}

func expandAzureRmStorageAccountKeyVaultProperties(d *schema.ResourceData) *storage.KeyVaultProperties {
vs := d.Get("key_vault_properties").([]interface{})
if vs == nil || len(vs) == 0 {
return &storage.KeyVaultProperties{}
}

v := vs[0].(map[string]interface{})
keyName := v["key_name"].(string)
keyVersion := v["key_version"].(string)
keyVaultURI := v["key_vault_uri"].(string)

return &storage.KeyVaultProperties{
KeyName: utils.String(keyName),
KeyVersion: utils.String(keyVersion),
KeyVaultURI: utils.String(keyVaultURI),
}
}

func flattenAzureRmStorageAccountKeyVaultProperties(keyVaultProperties *storage.KeyVaultProperties) []interface{} {
if keyVaultProperties == nil {
return make([]interface{}, 0)
}

result := make(map[string]interface{})
if keyVaultProperties.KeyName != nil {
result["key_name"] = *keyVaultProperties.KeyName
}
if keyVaultProperties.KeyVersion != nil {
result["key_version"] = *keyVaultProperties.KeyVersion
}
if keyVaultProperties.KeyVaultURI != nil {
result["key_vault_uri"] = *keyVaultProperties.KeyVaultURI
}

return []interface{}{result}
}
130 changes: 130 additions & 0 deletions azurerm/resource_arm_storage_account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,41 @@ func TestAccAzureRMStorageAccount_networkRulesDeleted(t *testing.T) {
})
}

func TestAccAzureRMStorageAccount_keyVault(t *testing.T) {
resourceName := "azurerm_storage_account.testsa"
ri := acctest.RandInt()
rs := acctest.RandString(4)
location := testLocation()
preConfig := testAccAzureRMStorageAccount_identity(ri, rs, location)
postConfig := testAccAzureRMStorageAccount_keyvault(ri, rs, location)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMStorageAccountDestroy,
Steps: []resource.TestStep{
{
Config: preConfig,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMStorageAccountExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "account_encryption_source", "Microsoft.Storage"),
),
},
{
Config: postConfig,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMStorageAccountExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "account_encryption_source", "Microsoft.KeyVault"),
resource.TestCheckResourceAttr(resourceName, "key_vault_properties.#", "1"),
resource.TestCheckResourceAttrSet(resourceName, "key_vault_properties.0.key_name"),
resource.TestCheckResourceAttrSet(resourceName, "key_vault_properties.0.key_version"),
resource.TestCheckResourceAttrSet(resourceName, "key_vault_properties.0.key_vault_uri"),
),
},
},
})
}

func testCheckAzureRMStorageAccountExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
// Ensure we have enough information in state to look up in API
Expand Down Expand Up @@ -1033,3 +1068,98 @@ resource "azurerm_storage_account" "testsa" {
}
`, rInt, location, rInt, rInt, rString)
}

func testAccAzureRMStorageAccount_keyvault(rInt int, rString string, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "testrg" {
name = "testAccAzureRMSA-%d"
location = "%s"
}

data "azurerm_client_config" "current" {}

resource "azurerm_key_vault" "test" {
name = "testAccKv-%s"
location = "${azurerm_resource_group.testrg.location}"
resource_group_name = "${azurerm_resource_group.testrg.name}"

sku {
name = "standard"
}

tenant_id = "${data.azurerm_client_config.current.tenant_id}"
}

resource "azurerm_key_vault_access_policy" "sp" {
vault_name = "${azurerm_key_vault.test.name}"
resource_group_name = "${azurerm_resource_group.testrg.name}"

tenant_id = "${data.azurerm_client_config.current.tenant_id}"
object_id = "${data.azurerm_client_config.current.service_principal_object_id}"

key_permissions = [
"get",
"create",
"list",
"delete",
]

secret_permissions = []
}

resource "azurerm_key_vault_access_policy" "storage" {
vault_name = "${azurerm_key_vault.test.name}"
resource_group_name = "${azurerm_resource_group.testrg.name}"

tenant_id = "${azurerm_storage_account.testsa.identity.0.tenant_id}"
object_id = "${azurerm_storage_account.testsa.identity.0.principal_id}"

key_permissions = [
"get",
"wrapKey",
"unwrapKey",
]

secret_permissions = []
}

resource "azurerm_key_vault_key" "test" {
depends_on = ["azurerm_key_vault_access_policy.sp"]

name = "testAccKey-%d"
vault_uri = "${azurerm_key_vault.test.vault_uri}"
key_type = "RSA"
key_size = 2048

key_opts = [
"decrypt",
"encrypt",
"sign",
"unwrapKey",
"verify",
"wrapKey",
]
}

resource "azurerm_storage_account" "testsa" {
name = "unlikely23exst2acct%s"
resource_group_name = "${azurerm_resource_group.testrg.name}"
location = "${azurerm_resource_group.testrg.location}"

account_tier = "Standard"
account_replication_type = "LRS"

account_encryption_source = "Microsoft.KeyVault"

key_vault_properties {
key_name = "${azurerm_key_vault_key.test.name}"
key_version = "${azurerm_key_vault_key.test.version}"
key_vault_uri = "${azurerm_key_vault.test.vault_uri}"
}

tags {
environment = "production"
}
}
`, rInt, location, rString, rInt, rString)
}
10 changes: 10 additions & 0 deletions website/docs/r/storage_account.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ The following arguments are supported:

* `account_encryption_source` - (Optional) The Encryption Source for this Storage Account. Possible values are `Microsoft.Keyvault` and `Microsoft.Storage`. Defaults to `Microsoft.Storage`.

* `key_vault_properties` - (Optional) A `key_vault_properties` block as documented below.

* `custom_domain` - (Optional) A `custom_domain` block as documented below.

* `network_rules` - (Optional) A `network_rules` block as documented below.
Expand All @@ -116,6 +118,14 @@ The following arguments are supported:

---

* `key_vault_properties` supports the following:

* `key_name` - (Required) The name of Key Vault key.
* `key_version` - (Required) The version of Key Vault key.
* `key_vault_uri` - (Required) The Uri of Key Vault.

---

* `custom_domain` supports the following:

* `name` - (Optional) The Custom Domain Name to use for the Storage Account, which will be validated by Azure.
Expand Down