Skip to content

Commit 076b8f8

Browse files
Add support for GCF Gen2 CMEK (#8518) (#15385)
Signed-off-by: Modular Magician <[email protected]> Co-authored-by: Cameron Thornton <[email protected]>
1 parent 9bab8c6 commit 076b8f8

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

.changelog/8518.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
cloudfunctionsv2: added support for GCF Gen2 CMEK
3+
```

google/services/cloudfunctions2/resource_cloudfunctions2_function.go

+33
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,12 @@ region. If not provided, defaults to the same region as the function.`,
256256
},
257257
},
258258
},
259+
"kms_key_name": {
260+
Type: schema.TypeString,
261+
Optional: true,
262+
Description: `Resource name of a KMS crypto key (managed by the user) used to encrypt/decrypt function resources.
263+
It must match the pattern projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}.`,
264+
},
259265
"labels": {
260266
Type: schema.TypeMap,
261267
Optional: true,
@@ -548,6 +554,12 @@ func resourceCloudfunctions2functionCreate(d *schema.ResourceData, meta interfac
548554
} else if v, ok := d.GetOkExists("labels"); !tpgresource.IsEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) {
549555
obj["labels"] = labelsProp
550556
}
557+
kmsKeyNameProp, err := expandCloudfunctions2functionKmsKeyName(d.Get("kms_key_name"), d, config)
558+
if err != nil {
559+
return err
560+
} else if v, ok := d.GetOkExists("kms_key_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(kmsKeyNameProp)) && (ok || !reflect.DeepEqual(v, kmsKeyNameProp)) {
561+
obj["kmsKeyName"] = kmsKeyNameProp
562+
}
551563

552564
url, err := tpgresource.ReplaceVars(d, config, "{{Cloudfunctions2BasePath}}projects/{{project}}/locations/{{location}}/functions?functionId={{name}}")
553565
if err != nil {
@@ -687,6 +699,9 @@ func resourceCloudfunctions2functionRead(d *schema.ResourceData, meta interface{
687699
if err := d.Set("labels", flattenCloudfunctions2functionLabels(res["labels"], d, config)); err != nil {
688700
return fmt.Errorf("Error reading function: %s", err)
689701
}
702+
if err := d.Set("kms_key_name", flattenCloudfunctions2functionKmsKeyName(res["kmsKeyName"], d, config)); err != nil {
703+
return fmt.Errorf("Error reading function: %s", err)
704+
}
690705

691706
return nil
692707
}
@@ -737,6 +752,12 @@ func resourceCloudfunctions2functionUpdate(d *schema.ResourceData, meta interfac
737752
} else if v, ok := d.GetOkExists("labels"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) {
738753
obj["labels"] = labelsProp
739754
}
755+
kmsKeyNameProp, err := expandCloudfunctions2functionKmsKeyName(d.Get("kms_key_name"), d, config)
756+
if err != nil {
757+
return err
758+
} else if v, ok := d.GetOkExists("kms_key_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, kmsKeyNameProp)) {
759+
obj["kmsKeyName"] = kmsKeyNameProp
760+
}
740761

741762
url, err := tpgresource.ReplaceVars(d, config, "{{Cloudfunctions2BasePath}}projects/{{project}}/locations/{{location}}/functions/{{name}}")
742763
if err != nil {
@@ -765,6 +786,10 @@ func resourceCloudfunctions2functionUpdate(d *schema.ResourceData, meta interfac
765786
if d.HasChange("labels") {
766787
updateMask = append(updateMask, "labels")
767788
}
789+
790+
if d.HasChange("kms_key_name") {
791+
updateMask = append(updateMask, "kmsKeyName")
792+
}
768793
// updateMask is a URL parameter but not present in the schema, so ReplaceVars
769794
// won't set it
770795
url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
@@ -1429,6 +1454,10 @@ func flattenCloudfunctions2functionLabels(v interface{}, d *schema.ResourceData,
14291454
return v
14301455
}
14311456

1457+
func flattenCloudfunctions2functionKmsKeyName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
1458+
return v
1459+
}
1460+
14321461
func expandCloudfunctions2functionName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
14331462
return tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/functions/{{name}}")
14341463
}
@@ -2182,3 +2211,7 @@ func expandCloudfunctions2functionLabels(v interface{}, d tpgresource.TerraformR
21822211
}
21832212
return m, nil
21842213
}
2214+
2215+
func expandCloudfunctions2functionKmsKeyName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
2216+
return v, nil
2217+
}

website/docs/r/cloudfunctions2_function.html.markdown

+123
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,124 @@ resource "google_cloudfunctions2_function" "function" {
640640
}
641641
}
642642
```
643+
## Example Usage - Cloudfunctions2 Cmek
644+
645+
646+
```hcl
647+
locals {
648+
project = "my-project-name" # Google Cloud Platform Project ID
649+
}
650+
651+
data "google_project" "project" {
652+
provider = google-beta
653+
}
654+
655+
resource "google_storage_bucket" "bucket" {
656+
provider = google-beta
657+
658+
name = "${local.project}-gcf-source" # Every bucket name must be globally unique
659+
location = "US"
660+
uniform_bucket_level_access = true
661+
}
662+
663+
resource "google_storage_bucket_object" "object" {
664+
provider = google-beta
665+
666+
name = "function-source.zip"
667+
bucket = google_storage_bucket.bucket.name
668+
source = "function-source.zip" # Add path to the zipped function source code
669+
}
670+
671+
resource "google_project_service_identity" "ea_sa" {
672+
provider = google-beta
673+
674+
project = data.google_project.project.project_id
675+
service = "eventarc.googleapis.com"
676+
}
677+
678+
resource "google_artifact_registry_repository" "unencoded-ar-repo" {
679+
provider = google-beta
680+
681+
repository_id = "ar-repo"
682+
location = "us-central1"
683+
format = "DOCKER"
684+
}
685+
686+
resource "google_artifact_registry_repository_iam_binding" "binding" {
687+
provider = google-beta
688+
689+
location = google_artifact_registry_repository.encoded-ar-repo.location
690+
repository = google_artifact_registry_repository.encoded-ar-repo.name
691+
role = "roles/artifactregistry.admin"
692+
members = [
693+
"serviceAccount:service-${data.google_project.project.number}@gcf-admin-robot.iam.gserviceaccount.com",
694+
]
695+
}
696+
697+
resource "google_kms_crypto_key_iam_binding" "gcf_cmek_keyuser" {
698+
provider = google-beta
699+
700+
crypto_key_id = "cmek-key"
701+
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
702+
703+
members = [
704+
"serviceAccount:service-${data.google_project.project.number}@gcf-admin-robot.iam.gserviceaccount.com",
705+
"serviceAccount:service-${data.google_project.project.number}@gcp-sa-artifactregistry.iam.gserviceaccount.com",
706+
"serviceAccount:service-${data.google_project.project.number}@gs-project-accounts.iam.gserviceaccount.com",
707+
"serviceAccount:service-${data.google_project.project.number}@serverless-robot-prod.iam.gserviceaccount.com",
708+
"serviceAccount:${google_project_service_identity.ea_sa.email}",
709+
]
710+
711+
depends_on = [
712+
google_project_service_identity.ea_sa
713+
]
714+
}
715+
716+
resource "google_artifact_registry_repository" "encoded-ar-repo" {
717+
provider = google-beta
718+
719+
location = "us-central1"
720+
repository_id = "cmek-repo"
721+
format = "DOCKER"
722+
kms_key_name = "cmek-key"
723+
depends_on = [
724+
google_kms_crypto_key_iam_binding.gcf_cmek_keyuser
725+
]
726+
}
727+
728+
resource "google_cloudfunctions2_function" "function" {
729+
provider = google-beta
730+
731+
name = "function-cmek"
732+
location = "us-central1"
733+
description = "CMEK function"
734+
kms_key_name = "cmek-key"
735+
736+
build_config {
737+
runtime = "nodejs16"
738+
entry_point = "helloHttp" # Set the entry point
739+
docker_repository = google_artifact_registry_repository.encoded-ar-repo.id
740+
741+
source {
742+
storage_source {
743+
bucket = google_storage_bucket.bucket.name
744+
object = google_storage_bucket_object.object.name
745+
}
746+
}
747+
}
748+
749+
service_config {
750+
max_instance_count = 1
751+
available_memory = "256M"
752+
timeout_seconds = 60
753+
}
754+
755+
depends_on = [
756+
google_kms_crypto_key_iam_binding.gcf_cmek_keyuser
757+
]
758+
759+
}
760+
```
643761

644762
## Argument Reference
645763

@@ -680,6 +798,11 @@ The following arguments are supported:
680798
(Optional)
681799
A set of key/value label pairs associated with this Cloud Function.
682800

801+
* `kms_key_name` -
802+
(Optional)
803+
Resource name of a KMS crypto key (managed by the user) used to encrypt/decrypt function resources.
804+
It must match the pattern projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}.
805+
683806
* `location` -
684807
(Optional)
685808
The location of this cloud function.

0 commit comments

Comments
 (0)