Skip to content

Commit

Permalink
New Data Source: azurerm_key_vault_encrypted_value
Browse files Browse the repository at this point in the history
```
$ TF_ACC=1 go test -v ./internal/services/keyvault -run=TestAccEncryptedValueDataSource_
=== RUN   TestAccEncryptedValueDataSource_encryptAndDecrypt
=== PAUSE TestAccEncryptedValueDataSource_encryptAndDecrypt
=== CONT  TestAccEncryptedValueDataSource_encryptAndDecrypt
--- PASS: TestAccEncryptedValueDataSource_encryptAndDecrypt (274.42s)
PASS
ok  	github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault	276.443s
```

This commit supersedes #8895 by converting this into a Data Source rather than a Resource which handles the bi-directional
conversion of encrypted/decrypted values as necessary.
  • Loading branch information
tombuildsstuff committed Mar 18, 2022
1 parent 7110726 commit 24377d9
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 0 deletions.
1 change: 1 addition & 0 deletions internal/provider/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ func SupportedTypedServices() []sdk.TypedServiceRegistration {
costmanagement.Registration{},
disks.Registration{},
eventhub.Registration{},
keyvault.Registration{},
loadbalancer.Registration{},
loadtest.Registration{},
mssql.Registration{},
Expand Down
128 changes: 128 additions & 0 deletions internal/services/keyvault/encrypted_value_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package keyvault

import (
"context"
"crypto/sha1"
"fmt"
"time"

"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
"github.com/hashicorp/terraform-provider-azurerm/internal/timeouts"
"github.com/hashicorp/terraform-provider-azurerm/utils"
)

var _ sdk.DataSource = EncryptedValueDataSource{}

type EncryptedValueDataSource struct{}

type EncryptedValueDataSourceModel struct {
KeyVaultKeyId string `tfschema:"key_vault_key_id"`
Algorithm string `tfschema:"algorithm"`
EncryptedData string `tfschema:"encrypted_data"`
PlainTextValue string `tfschema:"plain_text_value"`
}

func (EncryptedValueDataSource) Arguments() map[string]*schema.Schema {
return map[string]*schema.Schema{
"key_vault_key_id": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validate.NestedItemId,
},
"algorithm": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
string(keyvault.RSA15),
string(keyvault.RSAOAEP),
string(keyvault.RSAOAEP256),
}, false),
},
"encrypted_data": {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
},
"plain_text_value": {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
},
}
}

func (EncryptedValueDataSource) Attributes() map[string]*schema.Schema {
return map[string]*schema.Schema{}
}

func (EncryptedValueDataSource) ModelObject() interface{} {
return &EncryptedValueDataSourceModel{}
}

func (e EncryptedValueDataSource) ResourceType() string {
return "azurerm_key_vault_encrypted_value"
}

func (EncryptedValueDataSource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.KeyVault.ManagementClient
ctx, cancel := timeouts.ForCreate(metadata.Client.StopContext, metadata.ResourceData)
defer cancel()

var model EncryptedValueDataSourceModel
if err := metadata.Decode(&model); err != nil {
return fmt.Errorf("decoding: %+v", err)
}

if model.EncryptedData == "" && model.PlainTextValue == "" {
return fmt.Errorf("one of `encrypted_data` or `plain_text_value` must be specified - both were empty")
}
if model.EncryptedData != "" && model.PlainTextValue != "" {
return fmt.Errorf("only one of `encrypted_data` or `plain_text_value` must be specified - both were specified")
}

keyVaultKeyId, err := parse.ParseNestedItemID(model.KeyVaultKeyId)
if err != nil {
return err
}

if model.EncryptedData != "" {
params := keyvault.KeyOperationsParameters{
Algorithm: keyvault.JSONWebKeyEncryptionAlgorithm(model.Algorithm),
Value: utils.String(model.EncryptedData),
}
result, err := client.Decrypt(ctx, keyVaultKeyId.KeyVaultBaseUrl, keyVaultKeyId.Name, keyVaultKeyId.Version, params)
if err != nil {
return fmt.Errorf("decrypting plain-text value using Key Vault Key ID %q: %+v", model.KeyVaultKeyId, err)
}
if result.Result == nil {
return fmt.Errorf("decrypting plain-text value using Key Vault Key ID %q: `result` was nil", model.KeyVaultKeyId)
}
model.PlainTextValue = *result.Result
} else {
params := keyvault.KeyOperationsParameters{
Algorithm: keyvault.JSONWebKeyEncryptionAlgorithm(model.Algorithm),
Value: utils.String(model.PlainTextValue),
}
result, err := client.Encrypt(ctx, keyVaultKeyId.KeyVaultBaseUrl, keyVaultKeyId.Name, keyVaultKeyId.Version, params)
if err != nil {
return fmt.Errorf("encrypting plain-text value using Key Vault Key ID %q: %+v", model.KeyVaultKeyId, err)
}
if result.Result == nil {
return fmt.Errorf("encrypting plain-text value using Key Vault Key ID %q: `result` was nil", model.KeyVaultKeyId)
}
model.EncryptedData = *result.Result
}

metadata.ResourceData.SetId(fmt.Sprintf("%s-%s-%s", model.KeyVaultKeyId, model.Algorithm, sha1.Sum([]byte(model.EncryptedData))))
return metadata.Encode(&model)
},
Timeout: 5 * time.Minute,
}
}
98 changes: 98 additions & 0 deletions internal/services/keyvault/encrypted_value_data_source_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package keyvault_test

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
)

type EncryptedValueDataSourceTest struct{}

func TestAccEncryptedValueDataSource_encryptAndDecrypt(t *testing.T) {
// since this config includes both Encrypted and Decrypted we're testing both use-cases (and comparing the values below)
// so we only need a single test here
data := acceptance.BuildTestData(t, "data.azurerm_key_vault_encrypted_value", "decrypted")
r := EncryptedValueDataSourceTest{}
data.DataSourceTest(t, []acceptance.TestStep{
{
Config: r.decrypt(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).Key("encrypted_value").MatchesOtherKey(check.That("data.azurerm_key_vault_encrypted_value.encrypted").Key("encrypted_value")),
check.That(data.ResourceName).Key("plain_text_value").MatchesOtherKey(check.That("data.azurerm_key_vault_encrypted_value.encrypted").Key("plain_text_value")),
),
},
})
}

func (t EncryptedValueDataSourceTest) decrypt(data acceptance.TestData) string {
template := t.template(data)
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
%s
data "azurerm_key_vault_encrypted_value" "encrypted" {
key_vault_key_id = azurerm_key_vault_key.test.id
algorithm = "RSA1_5"
plain_text_value = "some-encrypted-value"
}
data "azurerm_key_vault_encrypted_value" "decrypted" {
key_vault_key_id = azurerm_key_vault_key.test.id
algorithm = "RSA1_5"
encrypted_data = data.azurerm_key_vault_encrypted_value.encrypted.encrypted_data
}
`, template)
}

func (t EncryptedValueDataSourceTest) template(data acceptance.TestData) string {
return fmt.Sprintf(`
data "azurerm_client_config" "current" {}
resource "azurerm_resource_group" "test" {
name = "acctestRG-%[1]d"
location = "%[2]s"
}
resource "azurerm_key_vault" "test" {
name = "acctestkv-%[3]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"
soft_delete_retention_days = 7
access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id
key_permissions = [
"Create",
"Delete",
"Decrypt",
"Encrypt",
"Get",
"Purge",
"Recover",
"Update",
]
}
}
resource "azurerm_key_vault_key" "test" {
name = "key-%[3]s"
key_vault_id = azurerm_key_vault.test.id
key_type = "RSA"
key_size = 2048
key_opts = [
"decrypt",
"encrypt",
]
}
`, data.RandomInteger, data.Locations.Primary, data.RandomString)
}
11 changes: 11 additions & 0 deletions internal/services/keyvault/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

type Registration struct{}

var _ sdk.TypedServiceRegistrationWithAGitHubLabel = Registration{}
var _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{}

func (r Registration) AssociatedGitHubLabel() string {
Expand Down Expand Up @@ -54,3 +55,13 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource {
"azurerm_key_vault_managed_storage_account_sas_token_definition": resourceKeyVaultManagedStorageAccountSasTokenDefinition(),
}
}

func (r Registration) DataSources() []sdk.DataSource {
return []sdk.DataSource{
EncryptedValueDataSource{},
}
}

func (r Registration) Resources() []sdk.Resource {
return []sdk.Resource{}
}
63 changes: 63 additions & 0 deletions website/docs/d/key_vault_encrypted_value.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
subcategory: "Key Vault"
layout: "azurerm"
page_title: "Azure Resource Manager: Data Source: azurerm_key_vault_encrypted_value"
description: |-
Encrypts or Decrypts a value using a Key Vault Key.
---

# Data Source: azurerm_key_vault_encrypted_value

Encrypts or Decrypts a value using a Key Vault Key.

## Example Usage

```hcl
data "azurerm_key_vault" "example" {
name = "mykeyvault"
resource_group_name = "some-resource-group"
}
data "azurerm_key_vault_key" "example" {
name = "some-key"
key_vault_id = data.azurerm_key_vault.example.id
}
data "azurerm_key_vault_encrypted_value" "encrypted" {
key_vault_key_id = azurerm_key_vault_key.test.id
algorithm = "RSA1_5"
plain_text_value = "some-encrypted-value"
}
output "id" {
value = data.azurerm_key_vault_encrypted_value.example.encrypted_data
}
```

## Arguments Reference

The following arguments are supported:

* `algorithm` - (Required) The Algorithm which should be used to Decrypt/Encrypt this Value. Possible values are `RSA1_5`, `RSA-OAEP` and `RSA-OAEP-256`.

* `key_vault_key_id` - (Required) The ID of the Key Vault Key which should be used to Decrypt/Encrypt this Value.

---

* `encrypted_data` - (Optional) The Base64 URL Encoded Encrypted Data which should be decrypted into `plain_text_value`.

* `plain_text_value` - (Optional) The plain-text value which should be Encrypted into `encrypted_data`.

-> **Note:** One of either `encrypted_data` or `plain_text_value` must be specified and is used to populate the encrypted/decrypted value for the other field.

## Attributes Reference

The following attributes are exported:

* `id` - The ID of this Encrypted Value

## Timeouts

The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions:

* `read` - (Defaults to 5 minutes) Used when encrypting/decrypting this value.

0 comments on commit 24377d9

Please sign in to comment.