Skip to content

Commit

Permalink
Added support of customer_managed_key into azurerm_cognitive_account
Browse files Browse the repository at this point in the history
  • Loading branch information
dkuzmenok committed Sep 23, 2022
1 parent eaa801c commit 54114a4
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ resource "azurerm_cognitive_account" "test" {
type = "SystemAssigned, UserAssigned"
identity_ids = [azurerm_user_assigned_identity.test.id]
}
lifecycle {
ignore_changes = ["customer_managed_key"]
}
}
resource "azurerm_key_vault" "test" {
Expand Down
85 changes: 85 additions & 0 deletions internal/services/cognitive/cognitive_account_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"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/services/network"
networkParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse"
storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate"
Expand Down Expand Up @@ -118,6 +120,27 @@ func resourceCognitiveAccount() *pluginsdk.Resource {
ValidateFunc: validation.StringIsNotEmpty,
},

"customer_managed_key": {
Type: pluginsdk.TypeList,
Optional: true,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"key_vault_key_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: keyVaultValidate.NestedItemIdWithOptionalVersion,
},

"identity_client_id": {
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: validation.IsUUID,
},
},
},
},

"fqdns": {
Type: pluginsdk.TypeList,
Optional: true,
Expand Down Expand Up @@ -352,6 +375,7 @@ func resourceCognitiveAccountCreate(d *pluginsdk.ResourceData, meta interface{})
UserOwnedStorage: expandCognitiveAccountStorage(d.Get("storage").([]interface{})),
RestrictOutboundNetworkAccess: utils.Bool(d.Get("outbound_network_access_restricted").(bool)),
DisableLocalAuth: utils.Bool(!d.Get("local_auth_enabled").(bool)),
Encryption: expandCognitiveAccountCustomerManagedKey(d.Get("customer_managed_key").([]interface{})),
},
Tags: tags.Expand(d.Get("tags").(map[string]interface{})),
}
Expand Down Expand Up @@ -435,6 +459,7 @@ func resourceCognitiveAccountUpdate(d *pluginsdk.ResourceData, meta interface{})
UserOwnedStorage: expandCognitiveAccountStorage(d.Get("storage").([]interface{})),
RestrictOutboundNetworkAccess: utils.Bool(d.Get("outbound_network_access_restricted").(bool)),
DisableLocalAuth: utils.Bool(!d.Get("local_auth_enabled").(bool)),
Encryption: expandCognitiveAccountCustomerManagedKey(d.Get("customer_managed_key").([]interface{})),
},
Tags: tags.Expand(d.Get("tags").(map[string]interface{})),
}
Expand Down Expand Up @@ -549,6 +574,15 @@ func resourceCognitiveAccountRead(d *pluginsdk.ResourceData, meta interface{}) e
localAuthEnabled = !*props.DisableLocalAuth
}
d.Set("local_auth_enabled", localAuthEnabled)

customerManagedKey, err := flattenCognitiveAccountCustomerManagedKey(id, props.Encryption)
if err != nil {
return err
}

if err := d.Set("customer_managed_key", customerManagedKey); err != nil {
return fmt.Errorf("setting `customer_managed_key`: %+v", err)
}
}

return tags.FlattenAndSet(d, model.Tags)
Expand Down Expand Up @@ -797,3 +831,54 @@ func flattenCognitiveAccountStorage(input *[]cognitiveservicesaccounts.UserOwned
}
return results
}

func expandCognitiveAccountCustomerManagedKey(input []interface{}) *cognitiveservicesaccounts.Encryption {
if len(input) == 0 || input[0] == nil {
return nil
}

v := input[0].(map[string]interface{})
keyId, _ := keyVaultParse.ParseOptionallyVersionedNestedItemID(v["key_vault_key_id"].(string))
keySource := cognitiveservicesaccounts.KeySourceMicrosoftPointKeyVault

var identity string
if value := v["identity_client_id"]; value != nil && value != "" {
identity = value.(string)
}

return &cognitiveservicesaccounts.Encryption{
KeySource: &keySource,
KeyVaultProperties: &cognitiveservicesaccounts.KeyVaultProperties{
KeyName: utils.String(keyId.Name),
KeyVersion: utils.String(keyId.Version),
KeyVaultUri: utils.String(keyId.KeyVaultBaseUrl),
IdentityClientId: utils.String(identity),
},
}
}

func flattenCognitiveAccountCustomerManagedKey(cognitiveAccountId *cognitiveservicesaccounts.AccountId, input *cognitiveservicesaccounts.Encryption) ([]interface{}, error) {
if input == nil {
return []interface{}{}, nil
}

var keyId string
var identityClientId string
if props := input.KeyVaultProperties; props != nil {
keyVaultKeyId, err := keyVaultParse.NewNestedItemID(*props.KeyVaultUri, "keys", *props.KeyName, *props.KeyVersion)
if err != nil {
return nil, fmt.Errorf("parsing `key_vault_key_id`: %+v", err)
}
keyId = keyVaultKeyId.ID()
if props.IdentityClientId != nil {
identityClientId = *props.IdentityClientId
}
}

return []interface{}{
map[string]interface{}{
"key_vault_key_id": keyId,
"identity_client_id": identityClientId,
},
}, nil
}
103 changes: 103 additions & 0 deletions internal/services/cognitive/cognitive_account_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,23 @@ func TestAccCognitiveAccount_metricsAdvisor(t *testing.T) {
})
}

func TestAccCognitiveAccount_customerManagedKey(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_cognitive_account", "test")
r := CognitiveAccountResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.customerManagedKey(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("customer_managed_key.0.key_vault_key_id").Exists(),
check.That(data.ResourceName).Key("customer_managed_key.0.identity_client_id").IsUUID(),
),
},
data.ImportStep(),
})
}

func (t CognitiveAccountResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := cognitiveservicesaccounts.ParseAccountID(state.ID)
if err != nil {
Expand Down Expand Up @@ -934,3 +951,89 @@ resource "azurerm_subnet" "test_b" {
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger)
}

func (CognitiveAccountResource) customerManagedKey(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-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_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"
purge_protection_enabled = true
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_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"]
}
resource "azurerm_cognitive_account" "test" {
name = "acctest-cogacc-%d"
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
kind = "SpeechServices"
sku_name = "S0"
custom_subdomain_name = "acctest-cogacc-%d"
identity {
type = "SystemAssigned, UserAssigned"
identity_ids = [
azurerm_user_assigned_identity.test.id
]
}
customer_managed_key {
key_vault_key_id = azurerm_key_vault_key.test.id
identity_client_id = azurerm_user_assigned_identity.test.client_id
}
}
`, data.RandomInteger, data.Locations.Secondary, data.RandomString, data.RandomString, data.RandomString, data.RandomInteger, data.RandomInteger)
}
10 changes: 10 additions & 0 deletions website/docs/r/cognitive_account.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ The following arguments are supported:

* `custom_subdomain_name` - (Required) The subdomain name used for token-based authentication. Changing this forces a new resource to be created.

* `customer_managed_key` (Optional) A `customer_managed_key` block as documented below.

* `fqdns` - (Optional) List of FQDNs allowed for the Cognitive Account.

* `identity` - (Optional) An `identity` block as defined below.
Expand Down Expand Up @@ -106,6 +108,14 @@ A `virtual_network_rules` block supports the following:

---

A `customer_managed_key` block supports the following:

* `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.

---

A `identity` block supports the following:

* `type` - (Required) Specifies the type of Managed Service Identity that should be configured on this Cognitive Account. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned` (to enable both).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ description: |-

Manages a Customer Managed Key for a Cognitive Services Account.

~> **NOTE:** It's possible to define a Customer Managed Key both within [the `azurerm_cognitive_account` resource](cognitive_account.html) via the `customer_managed_key` block and by using [the `azurerm_cognitive_account_customer_managed_key` resource](cognitive_account_customer_managed_key.html). However it's not possible to use both methods to manage a Customer Managed Key for a Cognitive Account, since there'll be conflicts.

## Example Usage

```hcl
Expand Down

0 comments on commit 54114a4

Please sign in to comment.