From 742605afcd47d1d9a703dc5319a828c596d5847c Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 26 Sep 2017 16:26:01 +0100 Subject: [PATCH] Support for Key Vault Keys Also fixes an issue where the Key Vault Secret Import Tests weren't running properly --- azurerm/config.go | 26 +- azurerm/import_arm_key_vault_key_test.go | 104 ++++ azurerm/import_arm_key_vault_secret_test.go | 4 +- azurerm/provider.go | 1 + azurerm/resource_arm_key_vault_key.go | 272 +++++++++++ azurerm/resource_arm_key_vault_key_test.go | 449 ++++++++++++++++++ azurerm/resource_arm_key_vault_secret.go | 4 +- azurerm/resource_arm_key_vault_secret_test.go | 8 +- website/azurerm.erb | 4 + website/docs/r/key_vault_key.html.markdown | 98 ++++ 10 files changed, 951 insertions(+), 19 deletions(-) create mode 100644 azurerm/import_arm_key_vault_key_test.go create mode 100644 azurerm/resource_arm_key_vault_key.go create mode 100644 azurerm/resource_arm_key_vault_key_test.go create mode 100644 website/docs/r/key_vault_key.html.markdown diff --git a/azurerm/config.go b/azurerm/config.go index d91ec81853012..e72c636127554 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -688,21 +688,25 @@ func (c *Config) getArmClient() (*ArmClient, error) { aschc.Sender = sender client.automationScheduleClient = aschc - kvc := keyvault.NewVaultsClientWithBaseURI(endpoint, c.SubscriptionID) - setUserAgent(&kvc.Client) - kvc.Authorizer = auth - kvc.Sender = sender - client.keyVaultClient = kvc - - kvmc := keyVault.New() - setUserAgent(&kvmc.Client) - kvmc.Authorizer = keyVaultAuth - kvmc.Sender = sender - client.keyVaultManagementClient = kvmc + client.registerKeyVaultClients(endpoint, c.SubscriptionID, auth, keyVaultAuth, sender) return &client, nil } +func (c *ArmClient) registerKeyVaultClients(endpoint, subscriptionId string, auth autorest.Authorizer, keyVaultAuth autorest.Authorizer, sender autorest.Sender) { + keyVaultClient := keyvault.NewVaultsClientWithBaseURI(endpoint, subscriptionId) + setUserAgent(&keyVaultClient.Client) + keyVaultClient.Authorizer = auth + keyVaultClient.Sender = sender + c.keyVaultClient = keyVaultClient + + keyVaultManagementClient := keyVault.New() + setUserAgent(&keyVaultManagementClient.Client) + keyVaultManagementClient.Authorizer = keyVaultAuth + keyVaultManagementClient.Sender = sender + c.keyVaultManagementClient = keyVaultManagementClient +} + func (armClient *ArmClient) getKeyForStorageAccount(resourceGroupName, storageAccountName string) (string, bool, error) { accountKeys, err := armClient.storageServiceClient.ListKeys(resourceGroupName, storageAccountName) if accountKeys.StatusCode == http.StatusNotFound { diff --git a/azurerm/import_arm_key_vault_key_test.go b/azurerm/import_arm_key_vault_key_test.go new file mode 100644 index 0000000000000..3e041c5e713ac --- /dev/null +++ b/azurerm/import_arm_key_vault_key_test.go @@ -0,0 +1,104 @@ +package azurerm + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAzureRMKeyVaultKey_importBasicEC(t *testing.T) { + resourceName := "azurerm_key_vault_key.test" + + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultKey_basicEC(rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"key_size"}, + }, + }, + }) +} + +func TestAccAzureRMKeyVaultKey_importBasicRSA(t *testing.T) { + resourceName := "azurerm_key_vault_key.test" + + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultKey_basicRSA(rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"key_size"}, + }, + }, + }) +} + +func TestAccAzureRMKeyVaultKey_importBasicRSAHSM(t *testing.T) { + resourceName := "azurerm_key_vault_key.test" + + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultKey_basicRSAHSM(rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"key_size"}, + }, + }, + }) +} + +func TestAccAzureRMKeyVaultKey_importComplete(t *testing.T) { + resourceName := "azurerm_key_vault_key.test" + + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultKey_complete(rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"key_size"}, + }, + }, + }) +} diff --git a/azurerm/import_arm_key_vault_secret_test.go b/azurerm/import_arm_key_vault_secret_test.go index 9ecb25c107e87..680e54b1b0b15 100644 --- a/azurerm/import_arm_key_vault_secret_test.go +++ b/azurerm/import_arm_key_vault_secret_test.go @@ -8,7 +8,7 @@ import ( ) func TestAccAzureRMKeyVaultSecret_importBasic(t *testing.T) { - resourceName := "azurerm_key_vault.test" + resourceName := "azurerm_key_vault_secret.test" rs := acctest.RandString(6) config := testAccAzureRMKeyVaultSecret_basic(rs, testLocation()) @@ -31,7 +31,7 @@ func TestAccAzureRMKeyVaultSecret_importBasic(t *testing.T) { } func TestAccAzureRMKeyVaultSecret_importComplete(t *testing.T) { - resourceName := "azurerm_key_vault.test" + resourceName := "azurerm_key_vault_secret.test" rs := acctest.RandString(6) config := testAccAzureRMKeyVaultSecret_complete(rs, testLocation()) diff --git a/azurerm/provider.go b/azurerm/provider.go index 44adc70ea21c8..b0b4294ef4478 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -101,6 +101,7 @@ func Provider() terraform.ResourceProvider { "azurerm_express_route_circuit": resourceArmExpressRouteCircuit(), "azurerm_image": resourceArmImage(), "azurerm_key_vault": resourceArmKeyVault(), + "azurerm_key_vault_key": resourceArmKeyVaultKey(), "azurerm_key_vault_secret": resourceArmKeyVaultSecret(), "azurerm_lb": resourceArmLoadBalancer(), "azurerm_lb_backend_address_pool": resourceArmLoadBalancerBackendAddressPool(), diff --git a/azurerm/resource_arm_key_vault_key.go b/azurerm/resource_arm_key_vault_key.go new file mode 100644 index 0000000000000..f73264446c56f --- /dev/null +++ b/azurerm/resource_arm_key_vault_key.go @@ -0,0 +1,272 @@ +package azurerm + +import ( + "fmt" + "log" + "net/url" + "strings" + + "github.com/Azure/azure-sdk-for-go/dataplane/keyvault" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmKeyVaultKey() *schema.Resource { + return &schema.Resource{ + Create: resourceArmKeyVaultKeyCreate, + Read: resourceArmKeyVaultKeyRead, + Update: resourceArmKeyVaultKeyUpdate, + Delete: resourceArmKeyVaultKeyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateKeyVaultChildName, + }, + + "vault_uri": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "key_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + // turns out Azure's *really* sensitive about the casing of these + // issue: https://github.com/Azure/azure-rest-api-specs/issues/1739 + ValidateFunc: validation.StringInSlice([]string{ + // TODO: add `oct` back in once this is fixed + // https://github.com/Azure/azure-rest-api-specs/issues/1739#issuecomment-332236257 + string(keyvault.EC), + string(keyvault.RSA), + string(keyvault.RSAHSM), + }, false), + }, + + "key_size": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "key_opts": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + // turns out Azure's *really* sensitive about the casing of these + // issue: https://github.com/Azure/azure-rest-api-specs/issues/1739 + ValidateFunc: validation.StringInSlice([]string{ + string(keyvault.Decrypt), + string(keyvault.Encrypt), + string(keyvault.Sign), + string(keyvault.UnwrapKey), + string(keyvault.Verify), + string(keyvault.WrapKey), + }, false), + }, + }, + + // Computed + "version": { + Type: schema.TypeString, + Computed: true, + }, + + "n": { + Type: schema.TypeString, + Computed: true, + }, + + "e": { + Type: schema.TypeString, + Computed: true, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceArmKeyVaultKeyCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).keyVaultManagementClient + + log.Print("[INFO] preparing arguments for AzureRM KeyVault Key creation.") + name := d.Get("name").(string) + keyVaultBaseUrl := d.Get("vault_uri").(string) + + keyType := d.Get("key_type").(string) + keyOptions := expandKeyVaultKeyOptions(d) + tags := d.Get("tags").(map[string]interface{}) + + // TODO: support Importing Keys once this is fixed: + // https://github.com/Azure/azure-rest-api-specs/issues/1747 + parameters := keyvault.KeyCreateParameters{ + Kty: keyvault.JSONWebKeyType(keyType), + KeyOps: keyOptions, + KeyAttributes: &keyvault.KeyAttributes{ + Enabled: utils.Bool(true), + }, + KeySize: utils.Int32(int32(d.Get("key_size").(int))), + Tags: expandTags(tags), + } + + _, err := client.CreateKey(keyVaultBaseUrl, name, parameters) + if err != nil { + return fmt.Errorf("Error Creating Key: %+v", err) + } + + // "" indicates the latest version + read, err := client.GetKey(keyVaultBaseUrl, name, "") + if err != nil { + return err + } + + d.SetId(*read.Key.Kid) + + return resourceArmKeyVaultKeyRead(d, meta) +} + +func resourceArmKeyVaultKeyUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).keyVaultManagementClient + + log.Print("[INFO] preparing arguments for AzureRM KeyVault Key update.") + id, err := parseKeyVaultKeyID(d.Id()) + if err != nil { + return err + } + + keyOptions := expandKeyVaultKeyOptions(d) + tags := d.Get("tags").(map[string]interface{}) + + parameters := keyvault.KeyUpdateParameters{ + KeyOps: keyOptions, + KeyAttributes: &keyvault.KeyAttributes{ + Enabled: utils.Bool(true), + }, + Tags: expandTags(tags), + } + + _, err = client.UpdateKey(id.KeyVaultBaseUrl, id.Name, id.Version, parameters) + if err != nil { + return err + } + + return resourceArmKeyVaultKeyRead(d, meta) +} + +func resourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).keyVaultManagementClient + + id, err := parseKeyVaultKeyID(d.Id()) + if err != nil { + return err + } + + resp, err := client.GetKey(id.KeyVaultBaseUrl, id.Name, "") + if err != nil { + return err + } + + d.Set("name", id.Name) + d.Set("vault_uri", id.KeyVaultBaseUrl) + if key := resp.Key; key != nil { + d.Set("key_type", string(key.Kty)) + + options := flattenKeyVaultKeyOptions(key.KeyOps) + if err := d.Set("key_opts", options); err != nil { + return err + } + + d.Set("n", key.N) + d.Set("e", key.E) + } + + // Computed + d.Set("version", id.Version) + + flattenAndSetTags(d, resp.Tags) + + return nil +} + +func resourceArmKeyVaultKeyDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).keyVaultManagementClient + + id, err := parseKeyVaultKeyID(d.Id()) + if err != nil { + return err + } + + _, err = client.DeleteKey(id.KeyVaultBaseUrl, id.Name) + + return err +} + +func parseKeyVaultKeyID(id string) (*KeyVaultKey, error) { + // example: https://tharvey-keyvault.vault.azure.net/keys/bird/fdf067c93bbb4b22bff4d8b7a9a56217 + idURL, err := url.ParseRequestURI(id) + if err != nil { + return nil, fmt.Errorf("Cannot parse Azure KeyVault Key Id: %q", err) + } + + path := idURL.Path + + path = strings.TrimSpace(path) + if strings.HasPrefix(path, "/") { + path = path[1:] + } + + if strings.HasSuffix(path, "/") { + path = path[:len(path)-1] + } + + components := strings.Split(path, "/") + + if len(components) != 3 { + return nil, fmt.Errorf("Azure KeyVault Key Id should have 3 segments, got %d: %q", len(components), path) + } + + key := KeyVaultKey{ + KeyVaultBaseUrl: fmt.Sprintf("%s://%s/", idURL.Scheme, idURL.Host), + Name: components[1], + Version: components[2], + } + + return &key, nil +} + +type KeyVaultKey struct { + KeyVaultBaseUrl string + Name string + Version string +} + +func expandKeyVaultKeyOptions(d *schema.ResourceData) *[]keyvault.JSONWebKeyOperation { + options := d.Get("key_opts").([]interface{}) + results := make([]keyvault.JSONWebKeyOperation, 0, len(options)) + + for _, option := range options { + results = append(results, keyvault.JSONWebKeyOperation(option.(string))) + } + + return &results +} + +func flattenKeyVaultKeyOptions(input *[]string) []interface{} { + results := make([]interface{}, 0, len(*input)) + + for _, option := range *input { + results = append(results, option) + } + + return results +} diff --git a/azurerm/resource_arm_key_vault_key_test.go b/azurerm/resource_arm_key_vault_key_test.go new file mode 100644 index 0000000000000..08d0b764b3d12 --- /dev/null +++ b/azurerm/resource_arm_key_vault_key_test.go @@ -0,0 +1,449 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMKeyVaultKey_basicEC(t *testing.T) { + resourceName := "azurerm_key_vault_key.test" + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultKey_basicEC(rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultKeyExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMKeyVaultKey_basicRSA(t *testing.T) { + resourceName := "azurerm_key_vault_key.test" + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultKey_basicRSA(rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultKeyExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMKeyVaultKey_basicRSAHSM(t *testing.T) { + resourceName := "azurerm_key_vault_key.test" + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultKey_basicRSAHSM(rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultKeyExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMKeyVaultKey_complete(t *testing.T) { + resourceName := "azurerm_key_vault_key.test" + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultKey_complete(rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultKeyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.hello", "world"), + ), + }, + }, + }) +} + +func TestAccAzureRMKeyVaultKey_update(t *testing.T) { + resourceName := "azurerm_key_vault_key.test" + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultKey_basicRSA(rs, testLocation()) + updatedConfig := testAccAzureRMKeyVaultKey_basicUpdated(rs, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultKeyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "key_opts.#", "6"), + resource.TestCheckResourceAttr(resourceName, "key_opts.0", "decrypt"), + ), + }, + { + Config: updatedConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultKeyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "key_opts.#", "5"), + resource.TestCheckResourceAttr(resourceName, "key_opts.0", "encrypt"), + ), + }, + }, + }) +} + +func testCheckAzureRMKeyVaultKeyDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).keyVaultManagementClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_key_vault_key" { + continue + } + + name := rs.Primary.Attributes["name"] + vaultBaseUrl := rs.Primary.Attributes["vault_uri"] + + // get the latest version + resp, err := client.GetKey(vaultBaseUrl, name, "") + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + return err + } + + return fmt.Errorf("Key Vault Key still exists:\n%#v", resp) + } + + return nil +} + +func testCheckAzureRMKeyVaultKeyExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + name := rs.Primary.Attributes["name"] + vaultBaseUrl := rs.Primary.Attributes["vault_uri"] + + client := testAccProvider.Meta().(*ArmClient).keyVaultManagementClient + + resp, err := client.GetKey(vaultBaseUrl, name, "") + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Key Vault Key %q (resource group: %q) does not exist", name, vaultBaseUrl) + } + + return fmt.Errorf("Bad: Get on keyVaultManagementClient: %+v", err) + } + + return nil + } +} + +func testAccAzureRMKeyVaultKey_basicEC(rString string, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%s" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "acctestkv-%s" + 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 = "premium" + } + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.service_principal_object_id}" + + key_permissions = [ + "all", + ] + + secret_permissions = [ + "all", + ] + } + + tags { + environment = "Production" + } +} + +resource "azurerm_key_vault_key" "test" { + name = "key-%s" + vault_uri = "${azurerm_key_vault.test.vault_uri}" + key_type = "EC" + key_size = 2048 + + key_opts = [ + "sign", + "verify", + ] +} +`, rString, location, rString, rString) +} + +func testAccAzureRMKeyVaultKey_basicRSA(rString string, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%s" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "acctestkv-%s" + 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 = "premium" + } + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.service_principal_object_id}" + + key_permissions = [ + "all", + ] + + secret_permissions = [ + "all", + ] + } + + tags { + environment = "Production" + } +} + +resource "azurerm_key_vault_key" "test" { + name = "key-%s" + vault_uri = "${azurerm_key_vault.test.vault_uri}" + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} +`, rString, location, rString, rString) +} + +func testAccAzureRMKeyVaultKey_basicRSAHSM(rString string, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%s" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "acctestkv-%s" + 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 = "premium" + } + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.service_principal_object_id}" + + key_permissions = [ + "all", + ] + + secret_permissions = [ + "all", + ] + } + + tags { + environment = "Production" + } +} + +resource "azurerm_key_vault_key" "test" { + name = "key-%s" + vault_uri = "${azurerm_key_vault.test.vault_uri}" + key_type = "RSA-HSM" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} +`, rString, location, rString, rString) +} + +func testAccAzureRMKeyVaultKey_complete(rString string, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%s" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "acctestkv-%s" + 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 = "premium" + } + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.service_principal_object_id}" + + key_permissions = [ + "all", + ] + + secret_permissions = [ + "all", + ] + } + + tags { + environment = "Production" + } +} + +resource "azurerm_key_vault_key" "test" { + name = "key-%s" + vault_uri = "${azurerm_key_vault.test.vault_uri}" + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] + + tags { + "hello" = "world" + } +} +`, rString, location, rString, rString) +} + +func testAccAzureRMKeyVaultKey_basicUpdated(rString string, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%s" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "acctestkv-%s" + 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 = "premium" + } + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.service_principal_object_id}" + + key_permissions = [ + "all", + ] + + secret_permissions = [ + "all", + ] + } + + tags { + environment = "Production" + } +} + +resource "azurerm_key_vault_key" "test" { + name = "key-%s" + vault_uri = "${azurerm_key_vault.test.vault_uri}" + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} +`, rString, location, rString, rString) +} diff --git a/azurerm/resource_arm_key_vault_secret.go b/azurerm/resource_arm_key_vault_secret.go index 500bbb3909226..7242871023bd0 100644 --- a/azurerm/resource_arm_key_vault_secret.go +++ b/azurerm/resource_arm_key_vault_secret.go @@ -27,7 +27,7 @@ func resourceArmKeyVaultSecret() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validateKeyVaultSecretName, + ValidateFunc: validateKeyVaultChildName, }, "vault_uri": { @@ -229,7 +229,7 @@ type SecretID struct { Version string } -func validateKeyVaultSecretName(v interface{}, k string) (ws []string, es []error) { +func validateKeyVaultChildName(v interface{}, k string) (ws []string, es []error) { value := v.(string) if matched := regexp.MustCompile(`^[0-9a-zA-Z-]+$`).Match([]byte(value)); !matched { diff --git a/azurerm/resource_arm_key_vault_secret_test.go b/azurerm/resource_arm_key_vault_secret_test.go index 23d355491d975..99e1ea2edb453 100644 --- a/azurerm/resource_arm_key_vault_secret_test.go +++ b/azurerm/resource_arm_key_vault_secret_test.go @@ -10,7 +10,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) -func TestAccAzureRMKeyVaultSecret_validateName(t *testing.T) { +func TestAccAzureRMKeyVaultChild_validateName(t *testing.T) { cases := []struct { Input string ExpectError bool @@ -50,12 +50,12 @@ func TestAccAzureRMKeyVaultSecret_validateName(t *testing.T) { } for _, tc := range cases { - _, errors := validateKeyVaultSecretName(tc.Input, "") + _, errors := validateKeyVaultChildName(tc.Input, "") hasError := len(errors) > 0 if tc.ExpectError && !hasError { - t.Fatalf("Expected the Key Vault Secret Name to trigger a validation error for '%s'", tc.Input) + t.Fatalf("Expected the Key Vault Child Name to trigger a validation error for '%s'", tc.Input) } } } @@ -197,7 +197,7 @@ func testCheckAzureRMKeyVaultSecretDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*ArmClient).keyVaultManagementClient for _, rs := range s.RootModule().Resources { - if rs.Type != "azurerm_key_vault_Secret" { + if rs.Type != "azurerm_key_vault_secret" { continue } diff --git a/website/azurerm.erb b/website/azurerm.erb index beb08b95efd74..bee78c2c240e2 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -238,6 +238,10 @@ azurerm_key_vault + > + azurerm_key_vault_key + + > azurerm_key_vault_secret diff --git a/website/docs/r/key_vault_key.html.markdown b/website/docs/r/key_vault_key.html.markdown new file mode 100644 index 0000000000000..522f3fc94031f --- /dev/null +++ b/website/docs/r/key_vault_key.html.markdown @@ -0,0 +1,98 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_key_vault_key" +sidebar_current: "docs-azurerm-resource-key-vault-key" +description: |- + Manages a Key Vault Key. + +--- + +# azurerm_key_vault_key + +Manages a Key Vault Key. + +## Example Usage + +```hcl +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "my-resource-group" + location = "West US" +} + +resource "azurerm_key_vault" "test" { + name = "my-key-vault" + 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 = "premium" + } + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.service_principal_object_id}" + + key_permissions = [ + "all", + ] + + secret_permissions = [ + "all", + ] + } + + tags { + environment = "Production" + } +} + +resource "azurerm_key_vault_key" "generated" { + name = "generated-certificate" + vault_uri = "${azurerm_key_vault.test.vault_uri}" + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the Key Vault Key. Changing this forces a new resource to be created. + +* `vault_uri` - (Required) Specifies the URI used to access the Key Vault instance, available on the `azurerm_key_vault` resource. + +* `key_type` - (Required) Specifies the Key Type to use for this Key Vault Key. Possible values are `EC` (Elliptic Curve), `Oct` (Octet), `RSA` and `RSA-HSM`. Changing this forces a new resource to be created. + +* `key_size` - (Required) Specifies the Size of the Key to create in bytes. For example, 1024 or 2048. Changing this forces a new resource to be created. + +* `key_opts` - (Required) A list of JSON web key operations. Possible values include: `decrypt`, `encrypt`, `sign`, `unwrapKey`, `verify` and `wrapKey`. Please note these values are case sensitive. + +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The Key Vault Key ID. +* `version` - The current version of the Key Vault Key. + +## Import + +Key Vault Key which is Enabled can be imported using the `resource id`, e.g. + +``` +terraform import azurerm_key_vault_key.test https://example-keyvault.vault.azure.net/keys/example/fdf067c93bbb4b22bff4d8b7a9a56217 +```