Skip to content

Commit 18a6255

Browse files
mrparkersrosbo
authored andcommitted
Adds google_kms_secret data source (#741)
* 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
1 parent 2e9933b commit 18a6255

5 files changed

+260
-0
lines changed
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package google
2+
3+
import (
4+
"google.golang.org/api/cloudkms/v1"
5+
6+
"encoding/base64"
7+
"fmt"
8+
"github.com/hashicorp/terraform/helper/schema"
9+
"log"
10+
"time"
11+
)
12+
13+
func dataSourceGoogleKmsSecret() *schema.Resource {
14+
return &schema.Resource{
15+
Read: dataSourceGoogleKmsSecretRead,
16+
Schema: map[string]*schema.Schema{
17+
"crypto_key": &schema.Schema{
18+
Type: schema.TypeString,
19+
Required: true,
20+
},
21+
"ciphertext": &schema.Schema{
22+
Type: schema.TypeString,
23+
Required: true,
24+
},
25+
"plaintext": &schema.Schema{
26+
Type: schema.TypeString,
27+
Computed: true,
28+
Sensitive: true,
29+
},
30+
},
31+
}
32+
}
33+
34+
func dataSourceGoogleKmsSecretRead(d *schema.ResourceData, meta interface{}) error {
35+
config := meta.(*Config)
36+
37+
cryptoKeyId, err := parseKmsCryptoKeyId(d.Get("crypto_key").(string), config)
38+
39+
if err != nil {
40+
return err
41+
}
42+
43+
ciphertext := d.Get("ciphertext").(string)
44+
45+
kmsDecryptRequest := &cloudkms.DecryptRequest{
46+
Ciphertext: ciphertext,
47+
}
48+
49+
decryptResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Decrypt(cryptoKeyId.cryptoKeyId(), kmsDecryptRequest).Do()
50+
51+
if err != nil {
52+
return fmt.Errorf("Error decrypting ciphertext: %s", err)
53+
}
54+
55+
plaintext, err := base64.StdEncoding.DecodeString(decryptResponse.Plaintext)
56+
57+
if err != nil {
58+
return fmt.Errorf("Error decoding base64 response: %s", err)
59+
}
60+
61+
log.Printf("[INFO] Successfully decrypted ciphertext: %s", ciphertext)
62+
63+
d.Set("plaintext", string(plaintext[:]))
64+
d.SetId(time.Now().UTC().String())
65+
66+
return nil
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package google
2+
3+
import (
4+
"encoding/base64"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform/helper/acctest"
9+
"github.com/hashicorp/terraform/helper/resource"
10+
"github.com/hashicorp/terraform/terraform"
11+
"google.golang.org/api/cloudkms/v1"
12+
"log"
13+
)
14+
15+
func TestAccGoogleKmsSecret_basic(t *testing.T) {
16+
t.Parallel()
17+
18+
projectOrg := getTestOrgFromEnv(t)
19+
projectBillingAccount := getTestBillingAccountFromEnv(t)
20+
21+
projectId := "terraform-" + acctest.RandString(10)
22+
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
23+
cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
24+
25+
plaintext := fmt.Sprintf("secret-%s", acctest.RandString(10))
26+
27+
// The first test creates resources needed to encrypt plaintext and produce ciphertext
28+
resource.Test(t, resource.TestCase{
29+
PreCheck: func() { testAccPreCheck(t) },
30+
Providers: testAccProviders,
31+
Steps: []resource.TestStep{
32+
resource.TestStep{
33+
Config: testGoogleKmsCryptoKey_basic(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName),
34+
Check: func(s *terraform.State) error {
35+
ciphertext, cryptoKeyId, err := testAccEncryptSecretDataWithCryptoKey(s, "google_kms_crypto_key.crypto_key", plaintext)
36+
37+
if err != nil {
38+
return err
39+
}
40+
41+
// The second test asserts that the data source has the correct plaintext, given the created ciphertext
42+
resource.Test(t, resource.TestCase{
43+
PreCheck: func() { testAccPreCheck(t) },
44+
Providers: testAccProviders,
45+
Steps: []resource.TestStep{
46+
resource.TestStep{
47+
Config: testGoogleKmsSecret_datasource(cryptoKeyId.terraformId(), ciphertext),
48+
Check: resource.TestCheckResourceAttr("data.google_kms_secret.acceptance", "plaintext", plaintext),
49+
},
50+
},
51+
})
52+
53+
return nil
54+
},
55+
},
56+
},
57+
})
58+
}
59+
60+
func testAccEncryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyResourceName, plaintext string) (string, *kmsCryptoKeyId, error) {
61+
config := testAccProvider.Meta().(*Config)
62+
63+
rs, ok := s.RootModule().Resources[cryptoKeyResourceName]
64+
if !ok {
65+
return "", nil, fmt.Errorf("Resource not found: %s", cryptoKeyResourceName)
66+
}
67+
68+
cryptoKeyId, err := parseKmsCryptoKeyId(rs.Primary.Attributes["id"], config)
69+
70+
if err != nil {
71+
return "", nil, err
72+
}
73+
74+
kmsEncryptRequest := &cloudkms.EncryptRequest{
75+
Plaintext: base64.StdEncoding.EncodeToString([]byte(plaintext)),
76+
}
77+
78+
encryptResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Encrypt(cryptoKeyId.cryptoKeyId(), kmsEncryptRequest).Do()
79+
80+
if err != nil {
81+
return "", nil, fmt.Errorf("Error encrypting plaintext: %s", err)
82+
}
83+
84+
log.Printf("[INFO] Successfully encrypted plaintext and got ciphertext: %s", encryptResponse.Ciphertext)
85+
86+
return encryptResponse.Ciphertext, cryptoKeyId, nil
87+
}
88+
89+
func testGoogleKmsSecret_datasource(cryptoKeyTerraformId, ciphertext string) string {
90+
return fmt.Sprintf(`
91+
data "google_kms_secret" "acceptance" {
92+
crypto_key = "%s"
93+
ciphertext = "%s"
94+
}
95+
`, cryptoKeyTerraformId, ciphertext)
96+
}

google/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ func Provider() terraform.ResourceProvider {
7474
"google_container_engine_versions": dataSourceGoogleContainerEngineVersions(),
7575
"google_active_folder": dataSourceGoogleActiveFolder(),
7676
"google_iam_policy": dataSourceGoogleIamPolicy(),
77+
"google_kms_secret": dataSourceGoogleKmsSecret(),
7778
"google_storage_object_signed_url": dataSourceGoogleSignedUrl(),
7879
},
7980

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
layout: "google"
3+
page_title: "Google: google_kms_secret"
4+
sidebar_current: "docs-google-kms-secret"
5+
description: |-
6+
Provides access to secret data encrypted with Google Cloud KMS
7+
---
8+
9+
# google\_kms\_secret
10+
11+
This data source allows you to use data encrypted with Google Cloud KMS
12+
within your resource definitions.
13+
14+
For more information see
15+
[the official documentation](https://cloud.google.com/kms/docs/encrypt-decrypt).
16+
17+
~> **NOTE**: Using this data provider will allow you to conceal secret data within your
18+
resource definitions, but it does not take care of protecting that data in the
19+
logging output, plan output, or state output. Please take care to secure your secret
20+
data outside of resource definitions.
21+
22+
## Example Usage
23+
24+
First, create a KMS KeyRing and CryptoKey using the resource definitions:
25+
26+
```hcl
27+
resource "google_kms_key_ring" "my_key_ring" {
28+
project = "my-project"
29+
name = "my-key-ring"
30+
location = "us-central1"
31+
}
32+
33+
resource "google_kms_crypto_key" "my_crypto_key" {
34+
name = "my-crypto-key"
35+
key_ring = "${google_kms_key_ring.my_key_ring.id}"
36+
}
37+
```
38+
39+
Next, use the [Cloud SDK](https://cloud.google.com/sdk/gcloud/reference/kms/encrypt) to encrypt some
40+
sensitive information:
41+
42+
```bash
43+
$ echo -n my-secret-password | gcloud kms encrypt \
44+
> --project my-project \
45+
> --location us-central1 \
46+
> --keyring my-key-ring \
47+
> --key my-crypto-key \
48+
> --plaintext-file - \
49+
> --ciphertext-file - \
50+
> | base64
51+
CiQAqD+xX4SXOSziF4a8JYvq4spfAuWhhYSNul33H85HnVtNQW4SOgDu2UZ46dQCRFl5MF6ekabviN8xq+F+2035ZJ85B+xTYXqNf4mZs0RJitnWWuXlYQh6axnnJYu3kDU=
52+
```
53+
54+
Finally, reference the encrypted ciphertext in your resource definitions:
55+
56+
```hcl
57+
data "google_kms_secret" "sql_user_password" {
58+
crypto_key = "${google_kms_crypto_key.my_crypto_key.id}"
59+
ciphertext = "CiQAqD+xX4SXOSziF4a8JYvq4spfAuWhhYSNul33H85HnVtNQW4SOgDu2UZ46dQCRFl5MF6ekabviN8xq+F+2035ZJ85B+xTYXqNf4mZs0RJitnWWuXlYQh6axnnJYu3kDU="
60+
}
61+
62+
resource "google_sql_database_instance" "master" {
63+
name = "master-instance"
64+
65+
settings {
66+
tier = "D0"
67+
}
68+
}
69+
70+
resource "google_sql_user" "users" {
71+
name = "me"
72+
instance = "${google_sql_database_instance.master.name}"
73+
host = "me.com"
74+
password = "${data.google_kms_secret.sql_user_password.plaintext}"
75+
}
76+
```
77+
78+
This will result in a Cloud SQL user being created with password `my-secret-password`.
79+
80+
## Argument Reference
81+
82+
The following arguments are supported:
83+
84+
* `ciphertext` (Required) - The ciphertext to be decrypted, encoded in base64
85+
* `crypto_key` (Required) - The id of the CryptoKey that will be used to
86+
decrypt the provided ciphertext. This is represented by the format
87+
`{projectId}/{location}/{keyRingName}/{cryptoKeyName}`.
88+
89+
## Attributes Reference
90+
91+
The following attribute is exported:
92+
93+
* `plaintext` - Contains the result of decrypting the provided ciphertext.

website/google.erb

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@
5555
<li<%= sidebar_current("docs-google-datasource-iam-policy") %>>
5656
<a href="/docs/providers/google/d/google_iam_policy.html">google_iam_policy</a>
5757
</li>
58+
<li<%= sidebar_current("docs-google-kms-secret") %>>
59+
<a href="/docs/providers/google/d/google_kms_secret.html">google_kms_secret</a>
60+
</li>
5861
<li<%= sidebar_current("docs-google-datasource-signed_url") %>>
5962
<a href="/docs/providers/google/d/signed_url.html">google_storage_object_signed_url</a>
6063
</li>

0 commit comments

Comments
 (0)