From 0896d0fab7b55110db07aed510f3aecc17341fd9 Mon Sep 17 00:00:00 2001 From: ms-henglu Date: Mon, 9 Aug 2021 13:04:45 +0800 Subject: [PATCH 1/3] add resource "azurerm_cognitive_account_customer_managed_key" --- ...e_account_customer_managed_key_resource.go | 224 ++++++++++++++++++ ...ount_customer_managed_key_resource_test.go | 217 +++++++++++++++++ .../cognitive/cognitive_account_resource.go | 4 +- internal/services/cognitive/registration.go | 3 +- .../docs/r/cognitive_account.html.markdown | 2 + ...account_customer_managed_key.html.markdown | 118 +++++++++ 6 files changed, 566 insertions(+), 2 deletions(-) create mode 100644 internal/services/cognitive/cognitive_account_customer_managed_key_resource.go create mode 100644 internal/services/cognitive/cognitive_account_customer_managed_key_resource_test.go create mode 100644 website/docs/r/cognitive_account_customer_managed_key.html.markdown diff --git a/internal/services/cognitive/cognitive_account_customer_managed_key_resource.go b/internal/services/cognitive/cognitive_account_customer_managed_key_resource.go new file mode 100644 index 000000000000..83956c482a6b --- /dev/null +++ b/internal/services/cognitive/cognitive_account_customer_managed_key_resource.go @@ -0,0 +1,224 @@ +package cognitive + +import ( + "fmt" + "time" + + "github.com/Azure/azure-sdk-for-go/services/cognitiveservices/mgmt/2021-04-30/cognitiveservices" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/locks" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cognitive/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cognitive/validate" + keyVaultParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse" + keyVaultValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceCognitiveAccountCustomerManagedKey() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceCognitiveAccountCustomerManagedKeyCreateUpdate, + Read: resourceCognitiveAccountCustomerManagedKeyRead, + Update: resourceCognitiveAccountCustomerManagedKeyCreateUpdate, + Delete: resourceCognitiveAccountCustomerManagedKeyDelete, + + 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.DefaultImporter(), + + Schema: map[string]*pluginsdk.Schema{ + "cognitive_account_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.AccountID, + }, + + "key_vault_key_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: keyVaultValidate.NestedItemIdWithOptionalVersion, + }, + + "identity_client_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.IsUUID, + }, + }, + } +} + +func resourceCognitiveAccountCustomerManagedKeyCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cognitive.AccountsClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.AccountID(d.Get("cognitive_account_id").(string)) + if err != nil { + return err + } + + locks.ByName(id.Name, "azurerm_cognitive_account") + defer locks.UnlockByName(id.Name, "azurerm_cognitive_account") + + resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + if d.IsNewResource() { + if resp.Properties != nil && resp.Properties.Encryption != nil { + return tf.ImportAsExistsError("azurerm_cognitive_account_customer_managed_key", id.ID()) + } + } + + props := cognitiveservices.Account{ + Properties: &cognitiveservices.AccountProperties{ + Encryption: &cognitiveservices.Encryption{ + KeySource: cognitiveservices.KeySourceMicrosoftKeyVault, + }, + }, + } + + keyId, err := keyVaultParse.ParseOptionallyVersionedNestedItemID(d.Get("key_vault_key_id").(string)) + if err != nil { + return err + } + props.Properties.Encryption.KeyVaultProperties = &cognitiveservices.KeyVaultProperties{ + KeyName: utils.String(keyId.Name), + KeyVersion: utils.String(keyId.Version), + KeyVaultURI: utils.String(keyId.KeyVaultBaseUrl), + IdentityClientID: utils.String(d.Get("identity_client_id").(string)), + } + + if _, err = client.Update(ctx, id.ResourceGroup, id.Name, props); err != nil { + return fmt.Errorf("updating %s: %+v", id, err) + } + + timeout, _ := ctx.Deadline() + stateConf := &pluginsdk.StateChangeConf{ + Pending: []string{"Accepted"}, + Target: []string{"Succeeded"}, + Refresh: cognitiveAccountStateRefreshFunc(ctx, client, *id), + MinTimeout: 15 * time.Second, + Timeout: time.Until(timeout), + } + + if _, err = stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for update of %s: %+v", id, err) + } + d.SetId(id.ID()) + + return resourceCognitiveAccountCustomerManagedKeyRead(d, meta) +} + +func resourceCognitiveAccountCustomerManagedKeyRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cognitive.AccountsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.AccountID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", id, err) + } + if resp.Properties == nil || resp.Properties.Encryption == nil { + d.SetId("") + return nil + } + + d.Set("cognitive_account_id", id.ID()) + if props := resp.Properties.Encryption.KeyVaultProperties; props != nil { + var keyName string + if props.KeyName != nil { + keyName = *props.KeyName + } + + var keyVaultUri string + if props.KeyVaultURI != nil { + keyVaultUri = *props.KeyVaultURI + } + + var keyVersion string + if props.KeyVersion != nil { + keyVersion = *props.KeyVersion + } + keyVaultKeyId, err := keyVaultParse.NewNestedItemID(keyVaultUri, "keys", keyName, keyVersion) + if err != nil { + return fmt.Errorf("parsing `key_vault_key_id`: %+v", err) + } + d.Set("key_vault_key_id", keyVaultKeyId.ID()) + if props.IdentityClientID != nil { + d.Set("identity_client_id", *props.IdentityClientID) + } + } else { + d.Set("key_vault_key_id", nil) + d.Set("identity_client_id", nil) + } + + return nil +} + +func resourceCognitiveAccountCustomerManagedKeyDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cognitive.AccountsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.AccountID(d.Id()) + if err != nil { + return err + } + + locks.ByName(id.Name, "azurerm_cognitive_account") + defer locks.UnlockByName(id.Name, "azurerm_cognitive_account") + + resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + if resp.Properties == nil || resp.Properties.Encryption == nil { + return fmt.Errorf("%s doesn't exist", id) + } + + // set key source to Microsoft.CognitiveServices to disable customer managed key + props := cognitiveservices.Account{ + Properties: &cognitiveservices.AccountProperties{ + Encryption: &cognitiveservices.Encryption{ + KeySource: cognitiveservices.KeySourceMicrosoftCognitiveServices, + }, + }, + } + + if _, err = client.Update(ctx, id.ResourceGroup, id.Name, props); err != nil { + return fmt.Errorf("updating %s: %+v", id, err) + } + + timeout, _ := ctx.Deadline() + stateConf := &pluginsdk.StateChangeConf{ + Pending: []string{"Accepted"}, + Target: []string{"Succeeded"}, + Refresh: cognitiveAccountStateRefreshFunc(ctx, client, *id), + MinTimeout: 15 * time.Second, + Timeout: time.Until(timeout), + } + + if _, err = stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for update of %s: %+v", id, err) + } + + return nil +} diff --git a/internal/services/cognitive/cognitive_account_customer_managed_key_resource_test.go b/internal/services/cognitive/cognitive_account_customer_managed_key_resource_test.go new file mode 100644 index 000000000000..070d438f7dda --- /dev/null +++ b/internal/services/cognitive/cognitive_account_customer_managed_key_resource_test.go @@ -0,0 +1,217 @@ +package cognitive_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/cognitive/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type CognitiveAccountCustomerManagedKeyResource struct { +} + +func TestAccCognitiveAccountCustomerManagedKey_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cognitive_account_customer_managed_key", "test") + r := CognitiveAccountCustomerManagedKeyResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCognitiveAccountCustomerManagedKey_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cognitive_account_customer_managed_key", "test") + r := CognitiveAccountCustomerManagedKeyResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccCognitiveAccountCustomerManagedKey_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cognitive_account_customer_managed_key", "test") + r := CognitiveAccountCustomerManagedKeyResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCognitiveAccountCustomerManagedKey_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cognitive_account_customer_managed_key", "test") + r := CognitiveAccountCustomerManagedKeyResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r CognitiveAccountCustomerManagedKeyResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.AccountID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.Cognitive.AccountsClient.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + return nil, fmt.Errorf("retrieving %s: %v", id.String(), err) + } + + if resp.Properties == nil || resp.Properties.Encryption == nil { + return utils.Bool(false), nil + } + + return utils.Bool(true), nil +} + +func (r CognitiveAccountCustomerManagedKeyResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s +resource "azurerm_cognitive_account_customer_managed_key" "test" { + cognitive_account_id = azurerm_cognitive_account.test.id + key_vault_key_id = azurerm_key_vault_key.test.id +} +`, r.template(data)) +} + +func (r CognitiveAccountCustomerManagedKeyResource) requiresImport(data acceptance.TestData) string { + template := CognitiveAccountCustomerManagedKeyResource{}.basic(data) + return fmt.Sprintf(` +%s +resource "azurerm_cognitive_account_customer_managed_key" "import" { + cognitive_account_id = azurerm_cognitive_account_customer_managed_key.test.cognitive_account_id + key_vault_key_id = azurerm_cognitive_account_customer_managed_key.test.key_vault_key_id +} +`, template) +} + +func (r CognitiveAccountCustomerManagedKeyResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%s +resource "azurerm_cognitive_account_customer_managed_key" "test" { + cognitive_account_id = azurerm_cognitive_account.test.id + key_vault_key_id = azurerm_key_vault_key.test.id + identity_client_id = azurerm_user_assigned_identity.test.client_id +} +`, r.template(data)) +} + +func (r CognitiveAccountCustomerManagedKeyResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + key_vault { + purge_soft_delete_on_destroy = false + } + } +} +data "azurerm_client_config" "current" {} +resource "azurerm_resource_group" "test" { + name = "acctestRG-cognitive-%d" + location = "%s" +} +resource "azurerm_user_assigned_identity" "test" { + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + name = "%s" +} +resource "azurerm_cognitive_account" "test" { + name = "acctest-cogacc-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + kind = "Face" + sku_name = "E0" + custom_subdomain_name = "acctest-cogacc-%d" + identity { + type = "SystemAssigned, UserAssigned" + identity_ids = [azurerm_user_assigned_identity.test.id] + } +} +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 = "standard" + soft_delete_enabled = true + purge_protection_enabled = true +} +resource "azurerm_key_vault_access_policy" "test" { + key_vault_id = azurerm_key_vault.test.id + tenant_id = azurerm_cognitive_account.test.identity.0.tenant_id + object_id = azurerm_cognitive_account.test.identity.0.principal_id + key_permissions = ["get", "create", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + secret_permissions = ["get"] +} +resource "azurerm_key_vault_access_policy" "test2" { + 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 = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + secret_permissions = ["get"] +} +resource "azurerm_key_vault_access_policy" "test3" { + key_vault_id = azurerm_key_vault.test.id + tenant_id = azurerm_user_assigned_identity.test.tenant_id + object_id = azurerm_user_assigned_identity.test.principal_id + key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + secret_permissions = ["get"] +} +resource "azurerm_key_vault_key" "test" { + name = "acctestkvkey%s" + key_vault_id = azurerm_key_vault.test.id + key_type = "RSA" + key_size = 2048 + key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] + depends_on = [ + azurerm_key_vault_access_policy.test, + azurerm_key_vault_access_policy.test2, + azurerm_key_vault_access_policy.test3, + ] +} +`, data.RandomInteger, data.Locations.Secondary, data.RandomString, data.RandomInteger, data.RandomInteger, data.RandomString, data.RandomString) +} diff --git a/internal/services/cognitive/cognitive_account_resource.go b/internal/services/cognitive/cognitive_account_resource.go index c18249ed61f6..6bc0d3e8ca14 100644 --- a/internal/services/cognitive/cognitive_account_resource.go +++ b/internal/services/cognitive/cognitive_account_resource.go @@ -104,7 +104,7 @@ func resourceCognitiveAccount() *pluginsdk.Resource { Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - "F0", "F1", "S0", "S", "S1", "S2", "S3", "S4", "S5", "S6", "P0", "P1", "P2", + "F0", "F1", "S0", "S", "S1", "S2", "S3", "S4", "S5", "S6", "P0", "P1", "P2", "E0", }, false), }, @@ -625,6 +625,8 @@ func expandAccountSkuName(skuName string) (*cognitiveservices.Sku, error) { tier = cognitiveservices.SkuTierStandard case "P": tier = cognitiveservices.SkuTierPremium + case "E": + tier = cognitiveservices.SkuTierEnterprise default: return nil, fmt.Errorf("sku_name %s has unknown sku tier %s", skuName, skuName[0:1]) } diff --git a/internal/services/cognitive/registration.go b/internal/services/cognitive/registration.go index f0d68b48d313..c667674de8c7 100644 --- a/internal/services/cognitive/registration.go +++ b/internal/services/cognitive/registration.go @@ -28,6 +28,7 @@ func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { // SupportedResources returns the supported Resources supported by this Service func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ - "azurerm_cognitive_account": resourceCognitiveAccount(), + "azurerm_cognitive_account": resourceCognitiveAccount(), + "azurerm_cognitive_account_customer_managed_key": resourceCognitiveAccountCustomerManagedKey(), } } diff --git a/website/docs/r/cognitive_account.html.markdown b/website/docs/r/cognitive_account.html.markdown index c85810b69298..2889b8a25406 100644 --- a/website/docs/r/cognitive_account.html.markdown +++ b/website/docs/r/cognitive_account.html.markdown @@ -46,6 +46,8 @@ The following arguments are supported: * `kind` - (Required) Specifies the type of Cognitive Service Account that should be created. Possible values are `Academic`, `AnomalyDetector`, `Bing.Autosuggest`, `Bing.Autosuggest.v7`, `Bing.CustomSearch`, `Bing.Search`, `Bing.Search.v7`, `Bing.Speech`, `Bing.SpellCheck`, `Bing.SpellCheck.v7`, `CognitiveServices`, `ComputerVision`, `ContentModerator`, `CustomSpeech`, `CustomVision.Prediction`, `CustomVision.Training`, `Emotion`, `Face`,`FormRecognizer`, `ImmersiveReader`, `LUIS`, `LUIS.Authoring`, `MetricsAdvisor`, `Personalizer`, `QnAMaker`, `Recommendations`, `SpeakerRecognition`, `Speech`, `SpeechServices`, `SpeechTranslation`, `TextAnalytics`, `TextTranslation` and `WebLM`. Changing this forces a new resource to be created. +-> **NOTE:** You must create your first Face, Text Analytics, or Computer Vision resources from the Azure portal to review and acknowledge the terms and conditions. In Azure Portal, the checkbox to accept terms and conditions is only displayed when a US region is selected. More information on [Prerequisites](https://docs.microsoft.com/en-us/azure/cognitive-services/cognitive-services-apis-create-account-cli?tabs=windows#prerequisites). + * `sku_name` - (Required) Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S`, `S0`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, and `P2`. * `custom_subdomain_name` - (Optional) The subdomain name used for token-based authentication. Changing this forces a new resource to be created. diff --git a/website/docs/r/cognitive_account_customer_managed_key.html.markdown b/website/docs/r/cognitive_account_customer_managed_key.html.markdown new file mode 100644 index 000000000000..5f1f9b708ae9 --- /dev/null +++ b/website/docs/r/cognitive_account_customer_managed_key.html.markdown @@ -0,0 +1,118 @@ +--- +subcategory: "Cognitive Services" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_cognitive_account_customer_managed_key" +description: |- + Manages a Customer Managed Key for a Cognitive Services Account. +--- + +# azurerm_cognitive_account_customer_managed_key + +Manages a Customer Managed Key for a Cognitive Services Account. + +## Example Usage + +```hcl +data "azurerm_client_config" "current" {} +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West US" +} +resource "azurerm_user_assigned_identity" "example" { + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + name = "example-identity" +} +resource "azurerm_cognitive_account" "example" { + name = "example-account" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + kind = "Face" + sku_name = "E0" + custom_subdomain_name = "example-account" + identity { + type = "SystemAssigned, UserAssigned" + identity_ids = [azurerm_user_assigned_identity.example.id] + } +} +resource "azurerm_key_vault" "example" { + name = "example-vault" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + soft_delete_enabled = true + purge_protection_enabled = true +} +resource "azurerm_key_vault_access_policy" "cognitive" { + key_vault_id = azurerm_key_vault.example.id + tenant_id = azurerm_cognitive_account.example.identity.0.tenant_id + object_id = azurerm_cognitive_account.example.identity.0.principal_id + key_permissions = ["get", "create", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + secret_permissions = ["get"] +} +resource "azurerm_key_vault_access_policy" "client" { + 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 = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + secret_permissions = ["get"] +} +resource "azurerm_key_vault_access_policy" "user" { + key_vault_id = azurerm_key_vault.test.id + tenant_id = azurerm_user_assigned_identity.example.tenant_id + object_id = azurerm_user_assigned_identity.example.principal_id + key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + secret_permissions = ["get"] +} +resource "azurerm_key_vault_key" "example" { + name = "example-key" + key_vault_id = azurerm_key_vault.test.id + key_type = "RSA" + key_size = 2048 + key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] + depends_on = [ + azurerm_key_vault_access_policy.cognitive, + azurerm_key_vault_access_policy.client, + azurerm_key_vault_access_policy.user, + ] +} +resource "azurerm_cognitive_account_customer_managed_key" "example" { + cognitive_account_id = azurerm_cognitive_account.example.id + key_vault_key_id = azurerm_key_vault_key.example.id + identity_client_id = azurerm_user_assigned_identity.example.client_id +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `cognitive_account_id` - (Required) The ID of the Cognitive Account. Changing this forces a new resource to be created. + +* `key_vault_key_id` - (Required) The ID of the Key Vault Key. + +* `identity_client_id` - (Optional) The client id of the user assigned identity that has access to the key. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Cognitive Account. + +## 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 Cognitive Account Customer Managed Key. +* `read` - (Defaults to 5 minutes) Used when retrieving the Cognitive Account Customer Managed Key. +* `update` - (Defaults to 30 minutes) Used when updating the Cognitive Account Customer Managed Key. +* `delete` - (Defaults to 30 minutes) Used when deleting the Cognitive Account Customer Managed Key. + +## Import + +Customer Managed Keys for a Cognitive Account can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_cognitive_account_customer_managed_key.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.CognitiveServices/accounts/account1 +``` From 6ccf036e6f5db436b0572a0dda560f9c6f2c39c1 Mon Sep 17 00:00:00 2001 From: henglu Date: Tue, 10 Aug 2021 11:01:05 +0800 Subject: [PATCH 2/3] update --- ...e_account_customer_managed_key_resource.go | 41 ++++++------------- ...ount_customer_managed_key_resource_test.go | 24 ++++++++--- ...account_customer_managed_key.html.markdown | 8 ++-- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/internal/services/cognitive/cognitive_account_customer_managed_key_resource.go b/internal/services/cognitive/cognitive_account_customer_managed_key_resource.go index 83956c482a6b..b4543f3242a3 100644 --- a/internal/services/cognitive/cognitive_account_customer_managed_key_resource.go +++ b/internal/services/cognitive/cognitive_account_customer_managed_key_resource.go @@ -76,7 +76,7 @@ func resourceCognitiveAccountCustomerManagedKeyCreateUpdate(d *pluginsdk.Resourc } if d.IsNewResource() { - if resp.Properties != nil && resp.Properties.Encryption != nil { + if resp.Properties != nil && resp.Properties.Encryption != nil && resp.Properties.Encryption.KeySource != cognitiveservices.KeySourceMicrosoftCognitiveServices { return tf.ImportAsExistsError("azurerm_cognitive_account_customer_managed_key", id.ID()) } } @@ -101,7 +101,7 @@ func resourceCognitiveAccountCustomerManagedKeyCreateUpdate(d *pluginsdk.Resourc } if _, err = client.Update(ctx, id.ResourceGroup, id.Name, props); err != nil { - return fmt.Errorf("updating %s: %+v", id, err) + return fmt.Errorf("adding Customer Managed Key for %s: %+v", id, err) } timeout, _ := ctx.Deadline() @@ -133,40 +133,25 @@ func resourceCognitiveAccountCustomerManagedKeyRead(d *pluginsdk.ResourceData, m resp, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { - return fmt.Errorf("retrieving %s: %+v", id, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } if resp.Properties == nil || resp.Properties.Encryption == nil { d.SetId("") return nil } + if resp.Properties.Encryption.KeySource == cognitiveservices.KeySourceMicrosoftCognitiveServices { + d.SetId("") + return nil + } d.Set("cognitive_account_id", id.ID()) if props := resp.Properties.Encryption.KeyVaultProperties; props != nil { - var keyName string - if props.KeyName != nil { - keyName = *props.KeyName - } - - var keyVaultUri string - if props.KeyVaultURI != nil { - keyVaultUri = *props.KeyVaultURI - } - - var keyVersion string - if props.KeyVersion != nil { - keyVersion = *props.KeyVersion - } - keyVaultKeyId, err := keyVaultParse.NewNestedItemID(keyVaultUri, "keys", keyName, keyVersion) + keyVaultKeyId, err := keyVaultParse.NewNestedItemID(*props.KeyVaultURI, "keys", *props.KeyName, *props.KeyVersion) if err != nil { return fmt.Errorf("parsing `key_vault_key_id`: %+v", err) } d.Set("key_vault_key_id", keyVaultKeyId.ID()) - if props.IdentityClientID != nil { - d.Set("identity_client_id", *props.IdentityClientID) - } - } else { - d.Set("key_vault_key_id", nil) - d.Set("identity_client_id", nil) + d.Set("identity_client_id", props.IdentityClientID) } return nil @@ -187,11 +172,11 @@ func resourceCognitiveAccountCustomerManagedKeyDelete(d *pluginsdk.ResourceData, resp, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { - return fmt.Errorf("retrieving %s: %+v", id, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } if resp.Properties == nil || resp.Properties.Encryption == nil { - return fmt.Errorf("%s doesn't exist", id) + return fmt.Errorf("retrieving %s: `properties` was nil", *id) } // set key source to Microsoft.CognitiveServices to disable customer managed key @@ -204,7 +189,7 @@ func resourceCognitiveAccountCustomerManagedKeyDelete(d *pluginsdk.ResourceData, } if _, err = client.Update(ctx, id.ResourceGroup, id.Name, props); err != nil { - return fmt.Errorf("updating %s: %+v", id, err) + return fmt.Errorf("removing Customer Managed Key for %s: %+v", *id, err) } timeout, _ := ctx.Deadline() @@ -217,7 +202,7 @@ func resourceCognitiveAccountCustomerManagedKeyDelete(d *pluginsdk.ResourceData, } if _, err = stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for update of %s: %+v", id, err) + return fmt.Errorf("waiting for removal of Customer Managed Key for %s: %+v", *id, err) } return nil diff --git a/internal/services/cognitive/cognitive_account_customer_managed_key_resource_test.go b/internal/services/cognitive/cognitive_account_customer_managed_key_resource_test.go index 070d438f7dda..d579f4ed2a31 100644 --- a/internal/services/cognitive/cognitive_account_customer_managed_key_resource_test.go +++ b/internal/services/cognitive/cognitive_account_customer_managed_key_resource_test.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + "github.com/Azure/azure-sdk-for-go/services/cognitiveservices/mgmt/2021-04-30/cognitiveservices" "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" @@ -98,13 +99,17 @@ func (r CognitiveAccountCustomerManagedKeyResource) Exists(ctx context.Context, resp, err := clients.Cognitive.AccountsClient.Get(ctx, id.ResourceGroup, id.Name) if err != nil { - return nil, fmt.Errorf("retrieving %s: %v", id.String(), err) + return nil, fmt.Errorf("retrieving %s: %v", *id, err) } if resp.Properties == nil || resp.Properties.Encryption == nil { return utils.Bool(false), nil } + if resp.Properties.Encryption.KeySource == cognitiveservices.KeySourceMicrosoftCognitiveServices { + return utils.Bool(false), nil + } + return utils.Bool(true), nil } @@ -119,7 +124,7 @@ resource "azurerm_cognitive_account_customer_managed_key" "test" { } func (r CognitiveAccountCustomerManagedKeyResource) requiresImport(data acceptance.TestData) string { - template := CognitiveAccountCustomerManagedKeyResource{}.basic(data) + template := r.basic(data) return fmt.Sprintf(` %s resource "azurerm_cognitive_account_customer_managed_key" "import" { @@ -149,16 +154,20 @@ provider "azurerm" { } } } + data "azurerm_client_config" "current" {} + resource "azurerm_resource_group" "test" { name = "acctestRG-cognitive-%d" location = "%s" } + resource "azurerm_user_assigned_identity" "test" { resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location name = "%s" } + resource "azurerm_cognitive_account" "test" { name = "acctest-cogacc-%d" location = azurerm_resource_group.test.location @@ -171,6 +180,7 @@ resource "azurerm_cognitive_account" "test" { identity_ids = [azurerm_user_assigned_identity.test.id] } } + resource "azurerm_key_vault" "test" { name = "acctestkv%s" location = azurerm_resource_group.test.location @@ -180,27 +190,31 @@ resource "azurerm_key_vault" "test" { soft_delete_enabled = true purge_protection_enabled = true } + resource "azurerm_key_vault_access_policy" "test" { key_vault_id = azurerm_key_vault.test.id tenant_id = azurerm_cognitive_account.test.identity.0.tenant_id object_id = azurerm_cognitive_account.test.identity.0.principal_id - key_permissions = ["get", "create", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + key_permissions = ["get", "create", "list", "restore", "recover", "unwrapKey", "wrapKey", "purge", "encrypt", "decrypt", "sign", "verify"] secret_permissions = ["get"] } + resource "azurerm_key_vault_access_policy" "test2" { 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 = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapKey", "wrapKey", "purge", "encrypt", "decrypt", "sign", "verify"] secret_permissions = ["get"] } + resource "azurerm_key_vault_access_policy" "test3" { key_vault_id = azurerm_key_vault.test.id tenant_id = azurerm_user_assigned_identity.test.tenant_id object_id = azurerm_user_assigned_identity.test.principal_id - key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapKey", "wrapKey", "purge", "encrypt", "decrypt", "sign", "verify"] secret_permissions = ["get"] } + resource "azurerm_key_vault_key" "test" { name = "acctestkvkey%s" key_vault_id = azurerm_key_vault.test.id diff --git a/website/docs/r/cognitive_account_customer_managed_key.html.markdown b/website/docs/r/cognitive_account_customer_managed_key.html.markdown index 5f1f9b708ae9..953030e067e1 100644 --- a/website/docs/r/cognitive_account_customer_managed_key.html.markdown +++ b/website/docs/r/cognitive_account_customer_managed_key.html.markdown @@ -52,14 +52,14 @@ resource "azurerm_key_vault_access_policy" "cognitive" { secret_permissions = ["get"] } resource "azurerm_key_vault_access_policy" "client" { - key_vault_id = azurerm_key_vault.test.id + 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 = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] secret_permissions = ["get"] } resource "azurerm_key_vault_access_policy" "user" { - key_vault_id = azurerm_key_vault.test.id + key_vault_id = azurerm_key_vault.example.id tenant_id = azurerm_user_assigned_identity.example.tenant_id object_id = azurerm_user_assigned_identity.example.principal_id key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] @@ -67,7 +67,7 @@ resource "azurerm_key_vault_access_policy" "user" { } resource "azurerm_key_vault_key" "example" { name = "example-key" - key_vault_id = azurerm_key_vault.test.id + key_vault_id = azurerm_key_vault.example.id key_type = "RSA" key_size = 2048 key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] @@ -92,7 +92,7 @@ The following arguments are supported: * `key_vault_key_id` - (Required) The ID of the Key Vault Key. -* `identity_client_id` - (Optional) The client id of the user assigned identity that has access to the key. +* `identity_client_id` - (Optional) The Client ID of the User Assigned Identity that has access to the key. This property only needs to be specified when there're multiple identities attached to the Cognitive Account. ## Attributes Reference From 4a7f3d0b13acffc42d64ffa40718b63dd719c9d8 Mon Sep 17 00:00:00 2001 From: ms-henglu Date: Thu, 19 Aug 2021 16:33:30 +0800 Subject: [PATCH 3/3] azurerm_key_vault_access_policy to access_policy --- ...ount_customer_managed_key_resource_test.go | 56 ++++++++-------- ...account_customer_managed_key.html.markdown | 66 +++++++++++-------- 2 files changed, 69 insertions(+), 53 deletions(-) diff --git a/internal/services/cognitive/cognitive_account_customer_managed_key_resource_test.go b/internal/services/cognitive/cognitive_account_customer_managed_key_resource_test.go index d579f4ed2a31..870ad55d7978 100644 --- a/internal/services/cognitive/cognitive_account_customer_managed_key_resource_test.go +++ b/internal/services/cognitive/cognitive_account_customer_managed_key_resource_test.go @@ -189,30 +189,39 @@ resource "azurerm_key_vault" "test" { sku_name = "standard" soft_delete_enabled = true purge_protection_enabled = true -} -resource "azurerm_key_vault_access_policy" "test" { - key_vault_id = azurerm_key_vault.test.id - tenant_id = azurerm_cognitive_account.test.identity.0.tenant_id - object_id = azurerm_cognitive_account.test.identity.0.principal_id - key_permissions = ["get", "create", "list", "restore", "recover", "unwrapKey", "wrapKey", "purge", "encrypt", "decrypt", "sign", "verify"] - secret_permissions = ["get"] -} + access_policy { + tenant_id = azurerm_cognitive_account.test.identity.0.tenant_id + object_id = azurerm_cognitive_account.test.identity.0.principal_id + key_permissions = [ + "Get", "Create", "List", "Restore", "Recover", "UnwrapKey", "WrapKey", "Purge", "Encrypt", "Decrypt", "Sign", "Verify" + ] + secret_permissions = [ + "Get", + ] + } -resource "azurerm_key_vault_access_policy" "test2" { - 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 = ["get", "create", "delete", "list", "restore", "recover", "unwrapKey", "wrapKey", "purge", "encrypt", "decrypt", "sign", "verify"] - secret_permissions = ["get"] -} + access_policy { + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + key_permissions = [ + "Get", "Create", "Delete", "List", "Restore", "Recover", "UnwrapKey", "WrapKey", "Purge", "Encrypt", "Decrypt", "Sign", "Verify" + ] + secret_permissions = [ + "Get", + ] + } -resource "azurerm_key_vault_access_policy" "test3" { - key_vault_id = azurerm_key_vault.test.id - tenant_id = azurerm_user_assigned_identity.test.tenant_id - object_id = azurerm_user_assigned_identity.test.principal_id - key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapKey", "wrapKey", "purge", "encrypt", "decrypt", "sign", "verify"] - secret_permissions = ["get"] + access_policy { + tenant_id = azurerm_user_assigned_identity.test.tenant_id + object_id = azurerm_user_assigned_identity.test.principal_id + key_permissions = [ + "Get", "Create", "Delete", "List", "Restore", "Recover", "UnwrapKey", "WrapKey", "Purge", "Encrypt", "Decrypt", "Sign", "Verify" + ] + secret_permissions = [ + "Get", + ] + } } resource "azurerm_key_vault_key" "test" { @@ -221,11 +230,6 @@ resource "azurerm_key_vault_key" "test" { key_type = "RSA" key_size = 2048 key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] - depends_on = [ - azurerm_key_vault_access_policy.test, - azurerm_key_vault_access_policy.test2, - azurerm_key_vault_access_policy.test3, - ] } `, data.RandomInteger, data.Locations.Secondary, data.RandomString, data.RandomInteger, data.RandomInteger, data.RandomString, data.RandomString) } diff --git a/website/docs/r/cognitive_account_customer_managed_key.html.markdown b/website/docs/r/cognitive_account_customer_managed_key.html.markdown index 953030e067e1..2fd12e5bf0bf 100644 --- a/website/docs/r/cognitive_account_customer_managed_key.html.markdown +++ b/website/docs/r/cognitive_account_customer_managed_key.html.markdown @@ -18,11 +18,13 @@ resource "azurerm_resource_group" "example" { name = "example-resources" location = "West US" } + resource "azurerm_user_assigned_identity" "example" { resource_group_name = azurerm_resource_group.example.name location = azurerm_resource_group.example.location name = "example-identity" } + resource "azurerm_cognitive_account" "example" { name = "example-account" location = azurerm_resource_group.example.location @@ -35,6 +37,7 @@ resource "azurerm_cognitive_account" "example" { identity_ids = [azurerm_user_assigned_identity.example.id] } } + resource "azurerm_key_vault" "example" { name = "example-vault" location = azurerm_resource_group.example.location @@ -43,40 +46,49 @@ resource "azurerm_key_vault" "example" { sku_name = "standard" soft_delete_enabled = true purge_protection_enabled = true + + access_policy { + tenant_id = azurerm_cognitive_account.test.identity.0.tenant_id + object_id = azurerm_cognitive_account.test.identity.0.principal_id + key_permissions = [ + "Get", "Create", "List", "Restore", "Recover", "UnwrapKey", "WrapKey", "Purge", "Encrypt", "Decrypt", "Sign", "Verify" + ] + secret_permissions = [ + "Get", + ] + } + + access_policy { + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + key_permissions = [ + "Get", "Create", "Delete", "List", "Restore", "Recover", "UnwrapKey", "WrapKey", "Purge", "Encrypt", "Decrypt", "Sign", "Verify" + ] + secret_permissions = [ + "Get", + ] + } + + access_policy { + tenant_id = azurerm_user_assigned_identity.test.tenant_id + object_id = azurerm_user_assigned_identity.test.principal_id + key_permissions = [ + "Get", "Create", "Delete", "List", "Restore", "Recover", "UnwrapKey", "WrapKey", "Purge", "Encrypt", "Decrypt", "Sign", "Verify" + ] + secret_permissions = [ + "Get", + ] + } } -resource "azurerm_key_vault_access_policy" "cognitive" { - key_vault_id = azurerm_key_vault.example.id - tenant_id = azurerm_cognitive_account.example.identity.0.tenant_id - object_id = azurerm_cognitive_account.example.identity.0.principal_id - key_permissions = ["get", "create", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] - secret_permissions = ["get"] -} -resource "azurerm_key_vault_access_policy" "client" { - 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 = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] - secret_permissions = ["get"] -} -resource "azurerm_key_vault_access_policy" "user" { - key_vault_id = azurerm_key_vault.example.id - tenant_id = azurerm_user_assigned_identity.example.tenant_id - object_id = azurerm_user_assigned_identity.example.principal_id - key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] - secret_permissions = ["get"] -} + resource "azurerm_key_vault_key" "example" { name = "example-key" key_vault_id = azurerm_key_vault.example.id key_type = "RSA" key_size = 2048 key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] - depends_on = [ - azurerm_key_vault_access_policy.cognitive, - azurerm_key_vault_access_policy.client, - azurerm_key_vault_access_policy.user, - ] } + resource "azurerm_cognitive_account_customer_managed_key" "example" { cognitive_account_id = azurerm_cognitive_account.example.id key_vault_key_id = azurerm_key_vault_key.example.id @@ -90,7 +102,7 @@ The following arguments are supported: * `cognitive_account_id` - (Required) The ID of the Cognitive Account. Changing this forces a new resource to be created. -* `key_vault_key_id` - (Required) The ID of the Key Vault Key. +* `key_vault_key_id` - (Required) The ID of the Key Vault Key which should be used to Encrypt the data in this Cognitive Account. * `identity_client_id` - (Optional) The Client ID of the User Assigned Identity that has access to the key. This property only needs to be specified when there're multiple identities attached to the Cognitive Account.