Adds support for creating KMS CryptoKeys resources (#692)

* Adds support for creating KMS CryptoKeys resources

* Destroy extant CryptoKeyVersions on CryptoKey destroy

* Inherit project, location etc from KeyRing in CryptoKey

* Add function to calculate next rotation

* Implement RotationPeriod parameter on CryptoKey

* Import CryptoKey state

* Uncommit my local acceptance test hacks

* Docs for google_kms_crypto_key

* Clear id at the end of CryptoKey deletion

Also add more detail to warning message.

* Fix parseCryptoKeyId error messages

* Use correct naming in CryptoKeyIdParsing test

* Check RotationPeriod is present in acceptance test

* Rename variable in test function for consistency

* Fix wrong resource name in cryptokey docs

* Add KeyRing to CryptoKey doc example

* Run test CryptoKey configs through terraform fmt

* Don't set CryptoKey purpose in terraform state on import

* Fix indentation in CryptoKey test

* Parallelise CryptoKey tests

* Set rotation_key on CryptoKey read

* Move RotationPeriod validation to planning phase

* Use import state passthrough for CryptoKey

* Correct casing issues in test case names

* Remove redundant CheckDestroy calls in CryptoKey tests

* Add explanatory comment about extra test steps

* More explicit error handling in CryptoKey tests

* Explicit dependency on project services in test keyring configs

* Clean up comments in cryptokey resource

* Do not repeat in cryptokey id regexes
This commit is contained in:
Andrew Farrell 2017-11-14 21:21:25 +00:00 committed by Dana Hoffman
parent a6d50b0c87
commit 56d633a8d4
7 changed files with 763 additions and 0 deletions

0
google/encryptor-pod.yml Normal file
View File

View File

@ -0,0 +1,45 @@
package google
import (
"testing"
"fmt"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"os"
)
func TestAccGoogleKmsCryptoKey_importBasic(t *testing.T) {
t.Parallel()
skipIfEnvNotSet(t,
[]string{
"GOOGLE_ORG",
"GOOGLE_BILLING_ACCOUNT",
}...,
)
resourceName := "google_kms_crypto_key.crypto_key"
projectId := "terraform-" + acctest.RandString(10)
projectOrg := os.Getenv("GOOGLE_ORG")
projectBillingAccount := os.Getenv("GOOGLE_BILLING_ACCOUNT")
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleKmsCryptoKey_basic(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName),
},
resource.TestStep{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -118,6 +118,7 @@ func Provider() terraform.ResourceProvider {
"google_logging_folder_sink": resourceLoggingFolderSink(),
"google_logging_project_sink": resourceLoggingProjectSink(),
"google_kms_key_ring": resourceKmsKeyRing(),
"google_kms_crypto_key": resourceKmsCryptoKey(),
"google_sourcerepo_repository": resourceSourceRepoRepository(),
"google_spanner_instance": resourceSpannerInstance(),
"google_spanner_database": resourceSpannerDatabase(),

View File

@ -0,0 +1,248 @@
package google
import (
"fmt"
"log"
"regexp"
"strconv"
"strings"
"time"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/cloudkms/v1"
)
func resourceKmsCryptoKey() *schema.Resource {
return &schema.Resource{
Create: resourceKmsCryptoKeyCreate,
Read: resourceKmsCryptoKeyRead,
Delete: resourceKmsCryptoKeyDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"key_ring": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"rotation_period": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateKmsCryptoKeyRotationPeriod,
},
},
}
}
type kmsCryptoKeyId struct {
KeyRingId kmsKeyRingId
Name string
}
func (s *kmsCryptoKeyId) cryptoKeyId() string {
return fmt.Sprintf("%s/cryptoKeys/%s", s.KeyRingId.keyRingId(), s.Name)
}
func (s *kmsCryptoKeyId) parentId() string {
return s.KeyRingId.keyRingId()
}
func (s *kmsCryptoKeyId) terraformId() string {
return fmt.Sprintf("%s/%s", s.KeyRingId.terraformId(), s.Name)
}
func resourceKmsCryptoKeyCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
keyRingId, err := parseKmsKeyRingId(d.Get("key_ring").(string), config)
if err != nil {
return err
}
cryptoKeyId := &kmsCryptoKeyId{
KeyRingId: *keyRingId,
Name: d.Get("name").(string),
}
key := cloudkms.CryptoKey{Purpose: "ENCRYPT_DECRYPT"}
if d.Get("rotation_period") != "" {
rotationPeriod := d.Get("rotation_period").(string)
nextRotation, err := kmsCryptoKeyNextRotation(time.Now(), rotationPeriod)
if err != nil {
return fmt.Errorf("Error setting CryptoKey rotation period: %s", err.Error())
}
key.NextRotationTime = nextRotation
key.RotationPeriod = rotationPeriod
}
cryptoKey, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Create(cryptoKeyId.KeyRingId.keyRingId(), &key).CryptoKeyId(cryptoKeyId.Name).Do()
if err != nil {
return fmt.Errorf("Error creating CryptoKey: %s", err.Error())
}
log.Printf("[DEBUG] Created CryptoKey %s", cryptoKey.Name)
d.SetId(cryptoKeyId.terraformId())
return resourceKmsCryptoKeyRead(d, meta)
}
func resourceKmsCryptoKeyRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
cryptoKeyId, err := parseKmsCryptoKeyId(d.Id(), config)
if err != nil {
return err
}
log.Printf("[DEBUG] Executing read for KMS CryptoKey %s", cryptoKeyId.cryptoKeyId())
if err != nil {
return fmt.Errorf("Error reading CryptoKey: %s", err)
}
d.Set("key_ring", cryptoKeyId.KeyRingId.terraformId())
d.Set("name", cryptoKeyId.Name)
d.Set("rotation_period", d.Get("rotation_period"))
d.SetId(cryptoKeyId.terraformId())
return nil
}
func clearCryptoKeyVersions(cryptoKeyId *kmsCryptoKeyId, config *Config) error {
versionsClient := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.CryptoKeyVersions
versionsResponse, err := versionsClient.List(cryptoKeyId.cryptoKeyId()).Do()
if err != nil {
return err
}
for _, version := range versionsResponse.CryptoKeyVersions {
request := &cloudkms.DestroyCryptoKeyVersionRequest{}
_, err = versionsClient.Destroy(version.Name, request).Do()
if err != nil {
return err
}
}
return nil
}
/*
Because KMS CryptoKey resources cannot be deleted on GCP, we are only going to remove it from state
and destroy all its versions, rendering the key useless for encryption and decryption of data.
Re-creation of this resource through Terraform will produce an error.
*/
func resourceKmsCryptoKeyDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
cryptoKeyId, err := parseKmsCryptoKeyId(d.Id(), config)
if err != nil {
return err
}
log.Printf(`
[WARNING] KMS CryptoKey resources cannot be deleted from GCP. The CryptoKey %s will be removed from Terraform state,
and all its CryptoKeyVersions will be destroyed, but it will still be present on the server.`, cryptoKeyId.cryptoKeyId())
err = clearCryptoKeyVersions(cryptoKeyId, config)
if err != nil {
return err
}
d.SetId("")
return nil
}
func validateKmsCryptoKeyRotationPeriod(value interface{}, _ string) (ws []string, errors []error) {
period := value.(string)
pattern := regexp.MustCompile("^([0-9.]*\\d)s$")
match := pattern.FindStringSubmatch(period)
if len(match) == 0 {
errors = append(errors, fmt.Errorf("Invalid period format: %s", period))
}
number := match[1]
seconds, err := strconv.ParseFloat(number, 64)
if err != nil {
errors = append(errors, err)
} else {
if seconds < 86400.0 {
errors = append(errors, fmt.Errorf("Rotation period must be greater than one day"))
}
parts := strings.Split(number, ".")
if len(parts) > 1 && len(parts[1]) > 9 {
errors = append(errors, fmt.Errorf("Rotation period cannot have more than 9 fractional digits"))
}
}
return
}
func kmsCryptoKeyNextRotation(now time.Time, period string) (result string, err error) {
var duration time.Duration
duration, err = time.ParseDuration(period)
if err == nil {
result = now.UTC().Add(duration).Format(time.RFC3339Nano)
}
return
}
func parseKmsCryptoKeyId(id string, config *Config) (*kmsCryptoKeyId, error) {
parts := strings.Split(id, "/")
cryptoKeyIdRegex := regexp.MustCompile("^([a-z0-9-]+)/([a-z0-9-])+/([a-zA-Z0-9_-]{1,63})/([a-zA-Z0-9_-]{1,63})$")
cryptoKeyIdWithoutProjectRegex := regexp.MustCompile("^([a-z0-9-])+/([a-zA-Z0-9_-]{1,63})/([a-zA-Z0-9_-]{1,63})$")
if cryptoKeyIdRegex.MatchString(id) {
return &kmsCryptoKeyId{
KeyRingId: kmsKeyRingId{
Project: parts[0],
Location: parts[1],
Name: parts[2],
},
Name: parts[3],
}, nil
}
if cryptoKeyIdWithoutProjectRegex.MatchString(id) {
if config.Project == "" {
return nil, fmt.Errorf("The default project for the provider must be set when using the `{location}/{keyRingName}/{cryptoKeyName}` id format.")
}
return &kmsCryptoKeyId{
KeyRingId: kmsKeyRingId{
Project: config.Project,
Location: parts[0],
Name: parts[1],
},
Name: parts[2],
}, nil
}
return nil, fmt.Errorf("Invalid CryptoKey id format, expecting `{projectId}/{locationId}/{KeyringName}/{cryptoKeyName}` or `{locationId}/{keyRingName}/{cryptoKeyName}.`")
}

View File

@ -0,0 +1,397 @@
package google
import (
"fmt"
"log"
"os"
"testing"
"time"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestCryptoKeyIdParsing(t *testing.T) {
t.Parallel()
cases := map[string]struct {
ImportId string
ExpectedError bool
ExpectedTerraformId string
ExpectedCryptoKeyId string
Config *Config
}{
"id is in project/location/keyRingName/cryptoKeyName format": {
ImportId: "test-project/us-central1/test-key-ring/test-key-name",
ExpectedError: false,
ExpectedTerraformId: "test-project/us-central1/test-key-ring/test-key-name",
ExpectedCryptoKeyId: "projects/test-project/locations/us-central1/keyRings/test-key-ring/cryptoKeys/test-key-name",
},
"id contains name that is longer than 63 characters": {
ImportId: "test-project/us-central1/test-key-ring/can-you-believe-that-this-cryptokey-name-is-this-extravagantly-long",
ExpectedError: true,
},
"id is in location/keyRingName/cryptoKeyName format": {
ImportId: "us-central1/test-key-ring/test-key-name",
ExpectedError: false,
ExpectedTerraformId: "test-project/us-central1/test-key-ring/test-key-name",
ExpectedCryptoKeyId: "projects/test-project/locations/us-central1/keyRings/test-key-ring/cryptoKeys/test-key-name",
Config: &Config{Project: "test-project"},
},
"id is in location/keyRingName/cryptoKeyName format without project in config": {
ImportId: "us-central1/test-key-ring/test-key-name",
ExpectedError: true,
Config: &Config{Project: ""},
},
}
for tn, tc := range cases {
cryptoKeyId, err := parseKmsCryptoKeyId(tc.ImportId, tc.Config)
if tc.ExpectedError && err == nil {
t.Fatalf("bad: %s, expected an error", tn)
}
if err != nil {
if tc.ExpectedError {
continue
}
t.Fatalf("bad: %s, err: %#v", tn, err)
}
if cryptoKeyId.terraformId() != tc.ExpectedTerraformId {
t.Fatalf("bad: %s, expected Terraform ID to be `%s` but is `%s`", tn, tc.ExpectedTerraformId, cryptoKeyId.terraformId())
}
if cryptoKeyId.cryptoKeyId() != tc.ExpectedCryptoKeyId {
t.Fatalf("bad: %s, expected CryptoKey ID to be `%s` but is `%s`", tn, tc.ExpectedCryptoKeyId, cryptoKeyId.cryptoKeyId())
}
}
}
func TestCryptoKeyNextRotationCalculation(t *testing.T) {
t.Parallel()
now := time.Now().UTC()
period, _ := time.ParseDuration("1000000s")
expected := now.Add(period).Format(time.RFC3339Nano)
timestamp, err := kmsCryptoKeyNextRotation(now, "1000000s")
if err != nil {
t.Fatalf("unexpected failure parsing time %s and duration 1000s: %s", now, err.Error())
}
if expected != timestamp {
t.Fatalf("expected %s to equal %s", timestamp, expected)
}
}
func TestCryptoKeyNextRotationCalculation_validation(t *testing.T) {
t.Parallel()
_, errs := validateKmsCryptoKeyRotationPeriod("86399s", "rotation_period")
if len(errs) == 0 {
t.Fatalf("Periods of less than a day should be invalid")
}
_, errs = validateKmsCryptoKeyRotationPeriod("100000.0000000001s", "rotation_period")
if len(errs) == 0 {
t.Fatalf("Numbers with more than 9 fractional digits are invalid")
}
}
func TestAccGoogleKmsCryptoKey_basic(t *testing.T) {
t.Parallel()
skipIfEnvNotSet(t,
[]string{
"GOOGLE_ORG",
"GOOGLE_BILLING_ACCOUNT",
}...,
)
projectId := "terraform-" + acctest.RandString(10)
projectOrg := os.Getenv("GOOGLE_ORG")
location := os.Getenv("GOOGLE_REGION")
projectBillingAccount := os.Getenv("GOOGLE_BILLING_ACCOUNT")
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
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: resource.ComposeTestCheckFunc(
testAccCheckGoogleKmsCryptoKeyExists("google_kms_crypto_key.crypto_key"),
),
},
// Use a separate TestStep rather than a CheckDestroy because we need the project to still exist.
resource.TestStep{
Config: testGoogleKmsCryptoKey_removed(projectId, projectOrg, projectBillingAccount, keyRingName),
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleKmsCryptoKeyWasRemovedFromState("google_kms_crypto_key.crypto_key"),
testAccCheckGoogleKmsCryptoKeyVersionsDestroyed(projectId, location, keyRingName, cryptoKeyName),
),
},
},
})
}
func TestAccGoogleKmsCryptoKey_rotation(t *testing.T) {
t.Parallel()
skipIfEnvNotSet(t,
[]string{
"GOOGLE_ORG",
"GOOGLE_BILLING_ACCOUNT",
}...,
)
projectId := "terraform-" + acctest.RandString(10)
projectOrg := os.Getenv("GOOGLE_ORG")
location := os.Getenv("GOOGLE_REGION")
projectBillingAccount := os.Getenv("GOOGLE_BILLING_ACCOUNT")
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
rotationPeriod := "100000s"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleKmsCryptoKey_rotation(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName, rotationPeriod),
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleKmsCryptoKeyExists("google_kms_crypto_key.crypto_key"),
testAccCheckGoogleKmsCryptoKeyHasRotationParams(rotationPeriod, "google_kms_crypto_key.crypto_key"),
),
},
// Use a separate TestStep rather than a CheckDestroy because we need the project to still exist.
resource.TestStep{
Config: testGoogleKmsCryptoKey_removed(projectId, projectOrg, projectBillingAccount, keyRingName),
Check: resource.ComposeTestCheckFunc(
testAccCheckGoogleKmsCryptoKeyWasRemovedFromState("google_kms_crypto_key.crypto_key"),
testAccCheckGoogleKmsCryptoKeyVersionsDestroyed(projectId, location, keyRingName, cryptoKeyName),
),
},
},
})
}
func testAccCheckGoogleKmsCryptoKeyExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("Resource not found: %s", resourceName)
}
keyRingId, err := parseKmsKeyRingId(rs.Primary.Attributes["key_ring"], config)
if err != nil {
return err
}
cryptoKeyId := &kmsCryptoKeyId{
KeyRingId: *keyRingId,
Name: rs.Primary.Attributes["name"],
}
listCryptoKeysResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.List(cryptoKeyId.parentId()).Do()
if err != nil {
return fmt.Errorf("Error listing KeyRings: %s", err)
}
for _, cryptoKey := range listCryptoKeysResponse.CryptoKeys {
log.Printf("[DEBUG] Found CryptoKey: %s", cryptoKey.Name)
if cryptoKey.Name == cryptoKeyId.cryptoKeyId() {
return nil
}
}
return fmt.Errorf("CryptoKey not found: %s", cryptoKeyId.cryptoKeyId())
}
}
func testAccCheckGoogleKmsCryptoKeyHasRotationParams(rotationPeriod, resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("Resource not found: %s", resourceName)
}
keyRingId, err := parseKmsKeyRingId(rs.Primary.Attributes["key_ring"], config)
if err != nil {
return err
}
cryptoKeyId := &kmsCryptoKeyId{
KeyRingId: *keyRingId,
Name: rs.Primary.Attributes["name"],
}
getCryptoKeyResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Get(cryptoKeyId.cryptoKeyId()).Do()
if err != nil {
return err
}
if rotationPeriod != getCryptoKeyResponse.RotationPeriod {
return fmt.Errorf("Expected rotation period %s to match input %s", getCryptoKeyResponse.RotationPeriod, rotationPeriod)
}
_, err = time.Parse(time.RFC3339Nano, getCryptoKeyResponse.NextRotationTime)
if err != nil {
return fmt.Errorf("Failed to parse NextRotationTime timestamp: %s", err)
}
return nil
}
}
/*
KMS KeyRings cannot be deleted. This ensures that the CryptoKey resource was removed from state,
even though the server-side resource was not removed.
*/
func testAccCheckGoogleKmsCryptoKeyWasRemovedFromState(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
_, ok := s.RootModule().Resources[resourceName]
if ok {
return fmt.Errorf("Resource was not removed from state: %s", resourceName)
}
return nil
}
}
/*
KMS KeyRings cannot be deleted. This ensures that the CryptoKey resource's CryptoKeyVersion
sub-resources were scheduled to be destroyed, rendering the key itself inoperable.
*/
func testAccCheckGoogleKmsCryptoKeyVersionsDestroyed(projectId, location, keyRingName, cryptoKeyName string) resource.TestCheckFunc {
return func(_ *terraform.State) error {
config := testAccProvider.Meta().(*Config)
gcpResourceUri := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", projectId, location, keyRingName, cryptoKeyName)
response, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.CryptoKeyVersions.List(gcpResourceUri).Do()
if err != nil {
return fmt.Errorf("Unexpected failure to list versions: %s", err)
}
versions := response.CryptoKeyVersions
for _, v := range versions {
if v.State != "DESTROY_SCHEDULED" && v.State != "DESTROYED" {
return fmt.Errorf("CryptoKey %s should have no versions, but version %s has state %s", cryptoKeyName, v.Name, v.State)
}
}
return nil
}
}
/*
This test runs in its own project, otherwise the test project would start to get filled
with undeletable resources
*/
func testGoogleKmsCryptoKey_basic(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName string) string {
return fmt.Sprintf(`
resource "google_project" "acceptance" {
name = "%s"
project_id = "%s"
org_id = "%s"
billing_account = "%s"
}
resource "google_project_services" "acceptance" {
project = "${google_project.acceptance.project_id}"
services = [
"cloudkms.googleapis.com",
]
}
resource "google_kms_key_ring" "key_ring" {
project = "${google_project_services.acceptance.project}"
name = "%s"
location = "us-central1"
}
resource "google_kms_crypto_key" "crypto_key" {
name = "%s"
key_ring = "${google_kms_key_ring.key_ring.id}"
}
`, projectId, projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName)
}
func testGoogleKmsCryptoKey_rotation(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName, rotationPeriod string) string {
return fmt.Sprintf(`
resource "google_project" "acceptance" {
name = "%s"
project_id = "%s"
org_id = "%s"
billing_account = "%s"
}
resource "google_project_services" "acceptance" {
project = "${google_project.acceptance.project_id}"
services = [
"cloudkms.googleapis.com",
]
}
resource "google_kms_key_ring" "key_ring" {
project = "${google_project_services.acceptance.project}"
name = "%s"
location = "us-central1"
}
resource "google_kms_crypto_key" "crypto_key" {
name = "%s"
key_ring = "${google_kms_key_ring.key_ring.id}"
rotation_period = "%s"
}
`, projectId, projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName, rotationPeriod)
}
func testGoogleKmsCryptoKey_removed(projectId, projectOrg, projectBillingAccount, keyRingName string) string {
return fmt.Sprintf(`
resource "google_project" "acceptance" {
name = "%s"
project_id = "%s"
org_id = "%s"
billing_account = "%s"
}
resource "google_project_services" "acceptance" {
project = "${google_project.acceptance.project_id}"
services = [
"cloudkms.googleapis.com",
]
}
resource "google_kms_key_ring" "key_ring" {
project = "${google_project_services.acceptance.project}"
name = "%s"
location = "us-central1"
}
`, projectId, projectId, projectOrg, projectBillingAccount, keyRingName)
}

View File

@ -0,0 +1,69 @@
---
layout: "google"
page_title: "Google: google_kms_crypto_key"
sidebar_current: "docs-google-kms-crypto-key"
description: |-
Allows creation of a Google Cloud Platform KMS CryptoKey.
---
# google\_kms\_crypto\_key
Allows creation of a Google Cloud Platform KMS CryptoKey. For more information see
[the official documentation](https://cloud.google.com/kms/docs/object-hierarchy#cryptokey)
and
[API](https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys).
A CryptoKey is an interface to key material which can be used to encrypt and decrypt data. A CryptoKey belongs to a
Google Cloud KMS KeyRing.
~> Note: CryptoKeys cannot be deleted from Google Cloud Platform. Destroying a Terraform-managed CryptoKey will remove it
from state and delete all CryptoKeyVersions, rendering the key unusable, but **will not delete the resource on the server**.
## Example Usage
```hcl
resource "google_kms_key_ring" "my_key_ring" {
name = "my-key-ring"
project = "my-project"
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}"
rotation_period = "100000s"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The CryptoKey's name.
A CryptoKeys name must be unique within a location and match the regular expression `[a-zA-Z0-9_-]{1,63}`
* `key_ring` - (Required) The id of the Google Cloud Platform KeyRing to which the key shall belong.
- - -
* `rotation_period` - (Optional) Every time this period passes, generate a new CryptoKeyVersion and set it as
the primary. The first rotation will take place after the specified period. The rotation period has the format
of a decimal number with up to 9 fractional digits, followed by the letter s (seconds). It must be greater than
a day (ie, 83400).
## Attributes Reference
In addition to the arguments listed above, the following computed attributes are
exported:
* `id` - The ID of the created CryptoKey. Its format is `{projectId}/{location}/{keyRingName}/{cryptoKeyName}`.
## Import
CryptoKeys can be imported using the CryptoKey autogenerated `id`, e.g.
```
$ terraform import google_kms_crypto_key.my_crypto_key my-gcp-project/us-central1/my-key-ring/my-crypto-key
$ terraform import google_kms_crypto_key.my_crypto_key us-central1/my-key-ring/my-crypto-key
```

View File

@ -80,6 +80,9 @@
<li<%= sidebar_current("docs-google-kms-key-ring") %>>
<a href="/docs/providers/google/r/google_kms_key_ring.html">google_kms_key_ring</a>
</li>
<li<%= sidebar_current("docs-google-kms-crypto-key") %>>
<a href="/docs/providers/google/r/google_kms_crypto_key.html">google_kms_crypto_key</a>
</li>
<li<%= sidebar_current("docs-google-organization-policy") %>>
<a href="/docs/providers/google/r/google_organization_policy.html">google_organization_policy</a>
</li>