Skip to content

Commit

Permalink
Adds google_kms_secret data source (#741)
Browse files Browse the repository at this point in the history
* Create google_kms_secret datasource

* Create google_kms_secret datasource documentation

* Remove duplicated code

* Create acceptance test

* Fix indentation

* Add documentation to sidebar

* Update Cloud SDK link in docs

* Oxford comma

* Rename variable to make it clear which resource is under test

* Update test to use utils from provider_test
  • Loading branch information
mrparkers authored and rosbo committed Dec 19, 2017
1 parent 2e9933b commit 18a6255
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 0 deletions.
67 changes: 67 additions & 0 deletions google/data_source_google_kms_secret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package google

import (
"google.golang.org/api/cloudkms/v1"

"encoding/base64"
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"log"
"time"
)

func dataSourceGoogleKmsSecret() *schema.Resource {
return &schema.Resource{
Read: dataSourceGoogleKmsSecretRead,
Schema: map[string]*schema.Schema{
"crypto_key": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"ciphertext": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"plaintext": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Sensitive: true,
},
},
}
}

func dataSourceGoogleKmsSecretRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

cryptoKeyId, err := parseKmsCryptoKeyId(d.Get("crypto_key").(string), config)

if err != nil {
return err
}

ciphertext := d.Get("ciphertext").(string)

kmsDecryptRequest := &cloudkms.DecryptRequest{
Ciphertext: ciphertext,
}

decryptResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Decrypt(cryptoKeyId.cryptoKeyId(), kmsDecryptRequest).Do()

if err != nil {
return fmt.Errorf("Error decrypting ciphertext: %s", err)
}

plaintext, err := base64.StdEncoding.DecodeString(decryptResponse.Plaintext)

if err != nil {
return fmt.Errorf("Error decoding base64 response: %s", err)
}

log.Printf("[INFO] Successfully decrypted ciphertext: %s", ciphertext)

d.Set("plaintext", string(plaintext[:]))
d.SetId(time.Now().UTC().String())

return nil
}
96 changes: 96 additions & 0 deletions google/data_source_google_kms_secret_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package google

import (
"encoding/base64"
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"google.golang.org/api/cloudkms/v1"
"log"
)

func TestAccGoogleKmsSecret_basic(t *testing.T) {
t.Parallel()

projectOrg := getTestOrgFromEnv(t)
projectBillingAccount := getTestBillingAccountFromEnv(t)

projectId := "terraform-" + acctest.RandString(10)
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))

plaintext := fmt.Sprintf("secret-%s", acctest.RandString(10))

// The first test creates resources needed to encrypt plaintext and produce ciphertext
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleKmsCryptoKey_basic(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName),
Check: func(s *terraform.State) error {
ciphertext, cryptoKeyId, err := testAccEncryptSecretDataWithCryptoKey(s, "google_kms_crypto_key.crypto_key", plaintext)

if err != nil {
return err
}

// The second test asserts that the data source has the correct plaintext, given the created ciphertext
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleKmsSecret_datasource(cryptoKeyId.terraformId(), ciphertext),
Check: resource.TestCheckResourceAttr("data.google_kms_secret.acceptance", "plaintext", plaintext),
},
},
})

return nil
},
},
},
})
}

func testAccEncryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyResourceName, plaintext string) (string, *kmsCryptoKeyId, error) {
config := testAccProvider.Meta().(*Config)

rs, ok := s.RootModule().Resources[cryptoKeyResourceName]
if !ok {
return "", nil, fmt.Errorf("Resource not found: %s", cryptoKeyResourceName)
}

cryptoKeyId, err := parseKmsCryptoKeyId(rs.Primary.Attributes["id"], config)

if err != nil {
return "", nil, err
}

kmsEncryptRequest := &cloudkms.EncryptRequest{
Plaintext: base64.StdEncoding.EncodeToString([]byte(plaintext)),
}

encryptResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Encrypt(cryptoKeyId.cryptoKeyId(), kmsEncryptRequest).Do()

if err != nil {
return "", nil, fmt.Errorf("Error encrypting plaintext: %s", err)
}

log.Printf("[INFO] Successfully encrypted plaintext and got ciphertext: %s", encryptResponse.Ciphertext)

return encryptResponse.Ciphertext, cryptoKeyId, nil
}

func testGoogleKmsSecret_datasource(cryptoKeyTerraformId, ciphertext string) string {
return fmt.Sprintf(`
data "google_kms_secret" "acceptance" {
crypto_key = "%s"
ciphertext = "%s"
}
`, cryptoKeyTerraformId, ciphertext)
}
1 change: 1 addition & 0 deletions google/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func Provider() terraform.ResourceProvider {
"google_container_engine_versions": dataSourceGoogleContainerEngineVersions(),
"google_active_folder": dataSourceGoogleActiveFolder(),
"google_iam_policy": dataSourceGoogleIamPolicy(),
"google_kms_secret": dataSourceGoogleKmsSecret(),
"google_storage_object_signed_url": dataSourceGoogleSignedUrl(),
},

Expand Down
93 changes: 93 additions & 0 deletions website/docs/d/google_kms_secret.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
layout: "google"
page_title: "Google: google_kms_secret"
sidebar_current: "docs-google-kms-secret"
description: |-
Provides access to secret data encrypted with Google Cloud KMS
---

# google\_kms\_secret

This data source allows you to use data encrypted with Google Cloud KMS
within your resource definitions.

For more information see
[the official documentation](https://cloud.google.com/kms/docs/encrypt-decrypt).

~> **NOTE**: Using this data provider will allow you to conceal secret data within your
resource definitions, but it does not take care of protecting that data in the
logging output, plan output, or state output. Please take care to secure your secret
data outside of resource definitions.

## Example Usage

First, create a KMS KeyRing and CryptoKey using the resource definitions:

```hcl
resource "google_kms_key_ring" "my_key_ring" {
project = "my-project"
name = "my-key-ring"
location = "us-central1"
}
resource "google_kms_crypto_key" "my_crypto_key" {
name = "my-crypto-key"
key_ring = "${google_kms_key_ring.my_key_ring.id}"
}
```

Next, use the [Cloud SDK](https://cloud.google.com/sdk/gcloud/reference/kms/encrypt) to encrypt some
sensitive information:

```bash
$ echo -n my-secret-password | gcloud kms encrypt \
> --project my-project \
> --location us-central1 \
> --keyring my-key-ring \
> --key my-crypto-key \
> --plaintext-file - \
> --ciphertext-file - \
> | base64
CiQAqD+xX4SXOSziF4a8JYvq4spfAuWhhYSNul33H85HnVtNQW4SOgDu2UZ46dQCRFl5MF6ekabviN8xq+F+2035ZJ85B+xTYXqNf4mZs0RJitnWWuXlYQh6axnnJYu3kDU=
```

Finally, reference the encrypted ciphertext in your resource definitions:

```hcl
data "google_kms_secret" "sql_user_password" {
crypto_key = "${google_kms_crypto_key.my_crypto_key.id}"
ciphertext = "CiQAqD+xX4SXOSziF4a8JYvq4spfAuWhhYSNul33H85HnVtNQW4SOgDu2UZ46dQCRFl5MF6ekabviN8xq+F+2035ZJ85B+xTYXqNf4mZs0RJitnWWuXlYQh6axnnJYu3kDU="
}
resource "google_sql_database_instance" "master" {
name = "master-instance"
settings {
tier = "D0"
}
}
resource "google_sql_user" "users" {
name = "me"
instance = "${google_sql_database_instance.master.name}"
host = "me.com"
password = "${data.google_kms_secret.sql_user_password.plaintext}"
}
```

This will result in a Cloud SQL user being created with password `my-secret-password`.

## Argument Reference

The following arguments are supported:

* `ciphertext` (Required) - The ciphertext to be decrypted, encoded in base64
* `crypto_key` (Required) - The id of the CryptoKey that will be used to
decrypt the provided ciphertext. This is represented by the format
`{projectId}/{location}/{keyRingName}/{cryptoKeyName}`.

## Attributes Reference

The following attribute is exported:

* `plaintext` - Contains the result of decrypting the provided ciphertext.
3 changes: 3 additions & 0 deletions website/google.erb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
<li<%= sidebar_current("docs-google-datasource-iam-policy") %>>
<a href="/docs/providers/google/d/google_iam_policy.html">google_iam_policy</a>
</li>
<li<%= sidebar_current("docs-google-kms-secret") %>>
<a href="/docs/providers/google/d/google_kms_secret.html">google_kms_secret</a>
</li>
<li<%= sidebar_current("docs-google-datasource-signed_url") %>>
<a href="/docs/providers/google/d/signed_url.html">google_storage_object_signed_url</a>
</li>
Expand Down

0 comments on commit 18a6255

Please sign in to comment.