Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add google_kms_secret_ciphertext resource, deprecate datasource #5314

Merged
merged 1 commit into from
Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion google/data_source_google_kms_secret_ciphertext.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import (

func dataSourceGoogleKmsSecretCiphertext() *schema.Resource {
return &schema.Resource{
Read: dataSourceGoogleKmsSecretCiphertextRead,
DeprecationMessage: "Use the google_kms_secret_ciphertext resource instead.",
Read: dataSourceGoogleKmsSecretCiphertextRead,
Schema: map[string]*schema.Schema{
"crypto_key": {
Type: schema.TypeString,
Expand Down
82 changes: 5 additions & 77 deletions google/data_source_google_kms_secret_ciphertext_test.go
Original file line number Diff line number Diff line change
@@ -1,113 +1,41 @@
package google

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

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

func TestAccKmsSecretCiphertext_basic(t *testing.T) {
func TestAccDataKmsSecretCiphertext_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))
kms := BootstrapKMSKey(t)

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{
{
Config: testGoogleKmsCryptoKey_basic(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName),
Config: testGoogleKmsSecretCiphertext_datasource(kms.CryptoKey.Name, plaintext),
Check: func(s *terraform.State) error {
cryptoKeyId, err := getCryptoKeyId(s, "google_kms_crypto_key.crypto_key")
plaintext, err := testAccDecryptSecretDataWithCryptoKey(s, kms.CryptoKey.Name, "data.google_kms_secret_ciphertext.acceptance")

if err != nil {
return err
}

// The second test asserts that the data source created a ciphertext that can be decrypted to the correct plaintext
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testGoogleKmsSecretCiphertext_datasource(cryptoKeyId.terraformId(), plaintext),
Check: func(s *terraform.State) error {
plaintext, err := testAccDecryptSecretDataWithCryptoKey(s, cryptoKeyId, "data.google_kms_secret_ciphertext.acceptance")

if err != nil {
return err
}

return resource.TestCheckResourceAttr("data.google_kms_secret_ciphertext.acceptance", "plaintext", plaintext)(s)
},
},
},
})

return nil
return resource.TestCheckResourceAttr("data.google_kms_secret_ciphertext.acceptance", "plaintext", plaintext)(s)
},
},
},
})
}

func getCryptoKeyId(s *terraform.State, cryptoKeyResourceName string) (*kmsCryptoKeyId, error) {
config := testAccProvider.Meta().(*Config)
rs, ok := s.RootModule().Resources[cryptoKeyResourceName]
if !ok {
return nil, fmt.Errorf("Resource not found: %s", cryptoKeyResourceName)
}

return parseKmsCryptoKeyId(rs.Primary.Attributes["id"], config)
}

func testAccDecryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyId *kmsCryptoKeyId, secretCiphertextResourceName string) (string, error) {
config := testAccProvider.Meta().(*Config)
rs, ok := s.RootModule().Resources[secretCiphertextResourceName]
if !ok {
return "", fmt.Errorf("Resource not found: %s", secretCiphertextResourceName)
}
ciphertext, ok := rs.Primary.Attributes["ciphertext"]
if !ok {
return "", fmt.Errorf("Attribute 'ciphertext' not found in resource '%s'", secretCiphertextResourceName)
}

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)
}

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

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

plaintext := string(plaintextBytes)
log.Printf("[INFO] Successfully decrypted ciphertext and got plaintext: %s", plaintext)

return plaintext, nil
}

func testGoogleKmsSecretCiphertext_datasource(cryptoKeyTerraformId, plaintext string) string {
return fmt.Sprintf(`
data "google_kms_secret_ciphertext" "acceptance" {
Expand Down
5 changes: 3 additions & 2 deletions google/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,9 +477,9 @@ func Provider() terraform.ResourceProvider {
return provider
}

// Generated resources: 95
// Generated resources: 96
// Generated IAM resources: 45
// Total generated resources: 140
// Total generated resources: 141
func ResourceMap() map[string]*schema.Resource {
resourceMap, _ := ResourceMapWithErrors()
return resourceMap
Expand Down Expand Up @@ -597,6 +597,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) {
"google_identity_platform_tenant": resourceIdentityPlatformTenant(),
"google_kms_key_ring": resourceKMSKeyRing(),
"google_kms_crypto_key": resourceKMSCryptoKey(),
"google_kms_secret_ciphertext": resourceKMSSecretCiphertext(),
"google_logging_metric": resourceLoggingMetric(),
"google_ml_engine_model": resourceMLEngineModel(),
"google_monitoring_alert_policy": resourceMonitoringAlertPolicy(),
Expand Down
165 changes: 165 additions & 0 deletions google/resource_kms_secret_ciphertext.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// ----------------------------------------------------------------------------
//
// *** AUTO GENERATED CODE *** AUTO GENERATED CODE ***
//
// ----------------------------------------------------------------------------
//
// This file is automatically generated by Magic Modules and manual
// changes will be clobbered when the file is regenerated.
//
// Please read more about how to change this file in
// .github/CONTRIBUTING.md.
//
// ----------------------------------------------------------------------------

package google

import (
"encoding/base64"
"fmt"
"log"
"reflect"
"regexp"
"time"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func resourceKMSSecretCiphertext() *schema.Resource {
return &schema.Resource{
Create: resourceKMSSecretCiphertextCreate,
Read: resourceKMSSecretCiphertextRead,
Delete: resourceKMSSecretCiphertextDelete,

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(4 * time.Minute),
Delete: schema.DefaultTimeout(4 * time.Minute),
},

Schema: map[string]*schema.Schema{
"crypto_key": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: `The full name of the CryptoKey that will be used to encrypt the provided plaintext.
Format: ''projects/{{project}}/locations/{{location}}/keyRings/{{keyRing}}/cryptoKeys/{{cryptoKey}}''`,
},
"plaintext": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: `The plaintext to be encrypted.`,
Sensitive: true,
},
"ciphertext": {
Type: schema.TypeString,
Computed: true,
Description: `Contains the result of encrypting the provided plaintext, encoded in base64.`,
},
},
}
}

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

obj := make(map[string]interface{})
plaintextProp, err := expandKMSSecretCiphertextPlaintext(d.Get("plaintext"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("plaintext"); !isEmptyValue(reflect.ValueOf(plaintextProp)) && (ok || !reflect.DeepEqual(v, plaintextProp)) {
obj["plaintext"] = plaintextProp
}

url, err := replaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}:encrypt")
if err != nil {
return err
}

log.Printf("[DEBUG] Creating new SecretCiphertext: %#v", obj)
var project string
if parts := regexp.MustCompile(`projects\/([^\/]+)\/`).FindStringSubmatch(url); parts != nil {
project = parts[1]
}
res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutCreate))
if err != nil {
return fmt.Errorf("Error creating SecretCiphertext: %s", err)
}

// Store the ID now
id, err := replaceVars(d, config, "{{crypto_key}}/{{ciphertext}}")
if err != nil {
return fmt.Errorf("Error constructing id: %s", err)
}
d.SetId(id)

log.Printf("[DEBUG] Finished creating SecretCiphertext %q: %#v", d.Id(), res)

// we don't set anything on read and instead do it all in create
ciphertext, ok := res["ciphertext"]
if !ok {
return fmt.Errorf("Create response didn't contain critical fields. Create may not have succeeded.")
}
d.Set("ciphertext", ciphertext.(string))

id, err = replaceVars(d, config, "{{crypto_key}}/{{ciphertext}}")
if err != nil {
return fmt.Errorf("Error constructing id: %s", err)
}
d.SetId(id)

return resourceKMSSecretCiphertextRead(d, meta)
}

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

url, err := replaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}")
if err != nil {
return err
}

var project string
if parts := regexp.MustCompile(`projects\/([^\/]+)\/`).FindStringSubmatch(url); parts != nil {
project = parts[1]
}
res, err := sendRequest(config, "GET", project, url, nil)
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("KMSSecretCiphertext %q", d.Id()))
}

res, err = resourceKMSSecretCiphertextDecoder(d, meta, res)
if err != nil {
return err
}

if res == nil {
// Decoding the object has resulted in it being gone. It may be marked deleted
log.Printf("[DEBUG] Removing KMSSecretCiphertext because it no longer exists.")
d.SetId("")
return nil
}

return nil
}

func resourceKMSSecretCiphertextDelete(d *schema.ResourceData, meta interface{}) error {
log.Printf("[WARNING] KMS SecretCiphertext resources"+
" cannot be deleted from GCP. The resource %s will be removed from Terraform"+
" state, but will still be present on the server.", d.Id())
d.SetId("")

return nil
}

func expandKMSSecretCiphertextPlaintext(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
if v == nil {
return nil, nil
}

return base64.StdEncoding.EncodeToString([]byte(v.(string))), nil
}

func resourceKMSSecretCiphertextDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) {
return res, nil
}
82 changes: 82 additions & 0 deletions google/resource_kms_secret_ciphertext_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package google

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

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

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

kms := BootstrapKMSKey(t)

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

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testGoogleKmsSecretCiphertext(kms.CryptoKey.Name, plaintext),
Check: func(s *terraform.State) error {
plaintext, err := testAccDecryptSecretDataWithCryptoKey(s, kms.CryptoKey.Name, "google_kms_secret_ciphertext.acceptance")

if err != nil {
return err
}

return resource.TestCheckResourceAttr("google_kms_secret_ciphertext.acceptance", "plaintext", plaintext)(s)
},
},
},
})
}

func testAccDecryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyId string, secretCiphertextResourceName string) (string, error) {
config := testAccProvider.Meta().(*Config)
rs, ok := s.RootModule().Resources[secretCiphertextResourceName]
if !ok {
return "", fmt.Errorf("Resource not found: %s", secretCiphertextResourceName)
}
ciphertext, ok := rs.Primary.Attributes["ciphertext"]
if !ok {
return "", fmt.Errorf("Attribute 'ciphertext' not found in resource '%s'", secretCiphertextResourceName)
}

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

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

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

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

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

plaintext := string(plaintextBytes)
log.Printf("[INFO] Successfully decrypted ciphertext and got plaintext: %s", plaintext)

return plaintext, nil
}

func testGoogleKmsSecretCiphertext(cryptoKeyTerraformId, plaintext string) string {
return fmt.Sprintf(`
resource "google_kms_secret_ciphertext" "acceptance" {
crypto_key = "%s"
plaintext = "%s"
}
`, cryptoKeyTerraformId, plaintext)
}
Loading