From eeb378f424f39410d3023366c2e2b5d90cf0c275 Mon Sep 17 00:00:00 2001 From: johnethomas Date: Tue, 29 Mar 2022 15:55:35 +0000 Subject: [PATCH 1/4] Add API fields and data sources to support using custom keys with Access Approval --- mmv1/products/accessapproval/api.yaml | 55 +++++++++++ mmv1/products/accessapproval/terraform.yaml | 21 +++++ ...ear_folder_access_approval_settings.go.erb | 2 + ...ganization_access_approval_settings.go.erb | 2 + ...ar_project_access_approval_settings.go.erb | 2 + ..._access_approval_active_key_version.tf.erb | 51 ++++++++++ ..._access_approval_active_key_version.tf.erb | 46 +++++++++ ..._access_approval_active_key_version.tf.erb | 40 ++++++++ ..._access_approval_folder_service_account.go | 62 +++++++++++++ ...s_approval_organization_service_account.go | 62 +++++++++++++ ...access_approval_project_service_account.go | 62 +++++++++++++ ...ss_approval_folder_service_account_test.go | 55 +++++++++++ ...roval_organization_service_account_test.go | 38 ++++++++ ...s_approval_project_service_account_test.go | 38 ++++++++ ...ce_access_approval_folder_settings_test.go | 93 ++++++++++++++++++- ...ess_approval_organization_settings_test.go | 57 ++++++++++++ ...e_access_approval_project_settings_test.go | 56 +++++++++++ .../terraform/utils/provider.go.erb | 3 + ...roval_folder_service_account.html.markdown | 47 ++++++++++ ...organization_service_account.html.markdown | 47 ++++++++++ ...oval_project_service_account.html.markdown | 47 ++++++++++ 21 files changed, 884 insertions(+), 2 deletions(-) create mode 100644 mmv1/templates/terraform/examples/folder_access_approval_active_key_version.tf.erb create mode 100644 mmv1/templates/terraform/examples/organization_access_approval_active_key_version.tf.erb create mode 100644 mmv1/templates/terraform/examples/project_access_approval_active_key_version.tf.erb create mode 100644 mmv1/third_party/terraform/data_sources/data_source_access_approval_folder_service_account.go create mode 100644 mmv1/third_party/terraform/data_sources/data_source_access_approval_organization_service_account.go create mode 100644 mmv1/third_party/terraform/data_sources/data_source_access_approval_project_service_account.go create mode 100644 mmv1/third_party/terraform/tests/data_source_access_approval_folder_service_account_test.go create mode 100644 mmv1/third_party/terraform/tests/data_source_access_approval_organization_service_account_test.go create mode 100644 mmv1/third_party/terraform/tests/data_source_access_approval_project_service_account_test.go create mode 100644 mmv1/third_party/terraform/website/docs/d/access_approval_folder_service_account.html.markdown create mode 100644 mmv1/third_party/terraform/website/docs/d/access_approval_organization_service_account.html.markdown create mode 100644 mmv1/third_party/terraform/website/docs/d/access_approval_project_service_account.html.markdown diff --git a/mmv1/products/accessapproval/api.yaml b/mmv1/products/accessapproval/api.yaml index a54c89cdd6ed..830471638585 100644 --- a/mmv1/products/accessapproval/api.yaml +++ b/mmv1/products/accessapproval/api.yaml @@ -109,6 +109,25 @@ objects: output: true description: | If the field is true, that indicates that at least one service is enrolled for Access Approval in one or more ancestors of the Folder. + - !ruby/object:Api::Type::String + name: activeKeyVersion + description: | + The asymmetric crypto key version to use for signing approval requests. + Empty active_key_version indicates that a Google-managed key should be used for signing. + This property will be ignored if set by an ancestor of the resource, and new non-empty values may not be set. + - !ruby/object:Api::Type::Boolean + name: ancestorHasActiveKeyVersion + output: true + description: | + If the field is true, that indicates that an ancestor of this Folder has set active_key_version. + - !ruby/object:Api::Type::Boolean + name: invalidKeyVersion + output: true + description: | + If the field is true, that indicates that there is some configuration issue with the active_key_version + configured on this Folder (e.g. it doesn't exist or the Access Approval service account doesn't have the + correct permissions on it, etc.) This key version is not necessarily the effective key version at this level, + as key versions are inherited top-down. - !ruby/object:Api::Resource name: ProjectSettings base_url: "projects/{{project_id}}/accessApprovalSettings" @@ -180,6 +199,25 @@ objects: output: true description: | If the field is true, that indicates that at least one service is enrolled for Access Approval in one or more ancestors of the Project. + - !ruby/object:Api::Type::String + name: activeKeyVersion + description: | + The asymmetric crypto key version to use for signing approval requests. + Empty active_key_version indicates that a Google-managed key should be used for signing. + This property will be ignored if set by an ancestor of the resource, and new non-empty values may not be set. + - !ruby/object:Api::Type::Boolean + name: ancestorHasActiveKeyVersion + output: true + description: | + If the field is true, that indicates that an ancestor of this Project has set active_key_version. + - !ruby/object:Api::Type::Boolean + name: invalidKeyVersion + output: true + description: | + If the field is true, that indicates that there is some configuration issue with the active_key_version + configured on this Project (e.g. it doesn't exist or the Access Approval service account doesn't have the + correct permissions on it, etc.) This key version is not necessarily the effective key version at this level, + as key versions are inherited top-down. - !ruby/object:Api::Type::String name: project description: | @@ -256,3 +294,20 @@ objects: output: true description: | This field will always be unset for the organization since organizations do not have ancestors. + - !ruby/object:Api::Type::String + name: activeKeyVersion + description: | + The asymmetric crypto key version to use for signing approval requests. + Empty active_key_version indicates that a Google-managed key should be used for signing. + - !ruby/object:Api::Type::Boolean + name: ancestorHasActiveKeyVersion + output: true + description: | + This field will always be unset for the organization since organizations do not have ancestors. + - !ruby/object:Api::Type::Boolean + name: invalidKeyVersion + output: true + description: | + If the field is true, that indicates that there is some configuration issue with the active_key_version + configured on this Organization (e.g. it doesn't exist or the Access Approval service account doesn't have the + correct permissions on it, etc.). diff --git a/mmv1/products/accessapproval/terraform.yaml b/mmv1/products/accessapproval/terraform.yaml index 1febec8064dc..0dc78cf4a37b 100644 --- a/mmv1/products/accessapproval/terraform.yaml +++ b/mmv1/products/accessapproval/terraform.yaml @@ -25,6 +25,14 @@ overrides: !ruby/object:Overrides::ResourceOverrides folder_name: 'my-folder' test_env_vars: org_id: :ORG_ID + - !ruby/object:Provider::Terraform::Examples + skip_test: true + name: 'folder_access_approval_active_key_version' + primary_resource_id: 'folder_access_approval' + vars: + folder_name: 'my-folder' + test_env_vars: + org_id: :ORG_ID custom_code: !ruby/object:Provider::Terraform::CustomCode custom_delete: templates/terraform/custom_delete/clear_folder_access_approval_settings.go.erb pre_create: templates/terraform/update_mask.erb @@ -47,6 +55,13 @@ overrides: !ruby/object:Overrides::ResourceOverrides test_env_vars: project: :PROJECT_NAME org_id: :ORG_ID + - !ruby/object:Provider::Terraform::Examples + skip_test: true + name: 'project_access_approval_active_key_version' + primary_resource_id: 'project_access_approval' + test_env_vars: + project: :PROJECT_NAME + org_id: :ORG_ID custom_code: !ruby/object:Provider::Terraform::CustomCode custom_delete: templates/terraform/custom_delete/clear_project_access_approval_settings.go.erb pre_create: templates/terraform/update_mask.erb @@ -67,6 +82,12 @@ overrides: !ruby/object:Overrides::ResourceOverrides primary_resource_id: 'organization_access_approval' test_env_vars: org_id: :ORG_ID + - !ruby/object:Provider::Terraform::Examples + skip_test: true + name: 'organization_access_approval_active_key_version' + primary_resource_id: 'organization_access_approval' + test_env_vars: + org_id: :ORG_ID custom_code: !ruby/object:Provider::Terraform::CustomCode custom_delete: templates/terraform/custom_delete/clear_organization_access_approval_settings.go.erb pre_create: templates/terraform/update_mask.erb diff --git a/mmv1/templates/terraform/custom_delete/clear_folder_access_approval_settings.go.erb b/mmv1/templates/terraform/custom_delete/clear_folder_access_approval_settings.go.erb index ec376af022fe..6dca42f29e31 100644 --- a/mmv1/templates/terraform/custom_delete/clear_folder_access_approval_settings.go.erb +++ b/mmv1/templates/terraform/custom_delete/clear_folder_access_approval_settings.go.erb @@ -1,6 +1,7 @@ obj := make(map[string]interface{}) obj["notificationEmails"] = []string{} obj["enrolledServices"] = []string{} +obj["activeKeyVersion"] = "" url, err := replaceVars(d, config, "{{AccessApprovalBasePath}}folders/{{folder_id}}/accessApprovalSettings") if err != nil { @@ -12,6 +13,7 @@ updateMask := []string{} updateMask = append(updateMask, "notificationEmails") updateMask = append(updateMask, "enrolledServices") +updateMask = append(updateMask, "activeKeyVersion") // updateMask is a URL parameter but not present in the schema, so replaceVars // won't set it diff --git a/mmv1/templates/terraform/custom_delete/clear_organization_access_approval_settings.go.erb b/mmv1/templates/terraform/custom_delete/clear_organization_access_approval_settings.go.erb index 7f0299d6c869..5689b763a681 100644 --- a/mmv1/templates/terraform/custom_delete/clear_organization_access_approval_settings.go.erb +++ b/mmv1/templates/terraform/custom_delete/clear_organization_access_approval_settings.go.erb @@ -1,6 +1,7 @@ obj := make(map[string]interface{}) obj["notificationEmails"] = []string{} obj["enrolledServices"] = []string{} +obj["activeKeyVersion"] = "" url, err := replaceVars(d, config, "{{AccessApprovalBasePath}}organizations/{{organization_id}}/accessApprovalSettings") if err != nil { @@ -12,6 +13,7 @@ updateMask := []string{} updateMask = append(updateMask, "notificationEmails") updateMask = append(updateMask, "enrolledServices") +updateMask = append(updateMask, "activeKeyVersion") // updateMask is a URL parameter but not present in the schema, so replaceVars // won't set it diff --git a/mmv1/templates/terraform/custom_delete/clear_project_access_approval_settings.go.erb b/mmv1/templates/terraform/custom_delete/clear_project_access_approval_settings.go.erb index 30cc67243ab4..6c5d505a6a21 100644 --- a/mmv1/templates/terraform/custom_delete/clear_project_access_approval_settings.go.erb +++ b/mmv1/templates/terraform/custom_delete/clear_project_access_approval_settings.go.erb @@ -1,6 +1,7 @@ obj := make(map[string]interface{}) obj["notificationEmails"] = []string{} obj["enrolledServices"] = []string{} +obj["activeKeyVersion"] = "" url, err := replaceVars(d, config, "{{AccessApprovalBasePath}}projects/{{project_id}}/accessApprovalSettings") if err != nil { @@ -12,6 +13,7 @@ updateMask := []string{} updateMask = append(updateMask, "notificationEmails") updateMask = append(updateMask, "enrolledServices") +updateMask = append(updateMask, "activeKeyVersion") // updateMask is a URL parameter but not present in the schema, so replaceVars // won't set it diff --git a/mmv1/templates/terraform/examples/folder_access_approval_active_key_version.tf.erb b/mmv1/templates/terraform/examples/folder_access_approval_active_key_version.tf.erb new file mode 100644 index 000000000000..474dc36b1b21 --- /dev/null +++ b/mmv1/templates/terraform/examples/folder_access_approval_active_key_version.tf.erb @@ -0,0 +1,51 @@ +resource "google_folder" "my_folder" { + display_name = "<%= ctx[:vars]['folder_name'] %>" + parent = "organizations/<%= ctx[:test_env_vars]['org_id'] %>" +} + +resource "google_project" "my_project" { + name = "My Project" + project_id = "your-project-id" + folder_id = google_folder.my_folder.name +} + +resource "google_kms_key_ring" "key_ring" { + name = "key-ring" + location = "global" + project = google_project.my_project.project_id +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "crypto-key" + key_ring = google_kms_key_ring.key_ring.id + purpose = "ASYMMETRIC_SIGN" + + version_template { + algorithm = "EC_SIGN_P384_SHA384" + } +} + +data "google_access_approval_folder_service_account" "service_account" { + folder_id = google_folder.my_folder.folder_id +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_folder_service_account.service_account.account_email}" +} + +data "google_kms_crypto_key_version" "crypto_key_version" { + crypto_key = google_kms_crypto_key.crypto_key.id +} + +resource "google_folder_access_approval_settings" "<%= ctx[:primary_resource_id] %>" { + folder_id = google_folder.my_folder.folder_id + active_key_version = data.google_kms_crypto_key_version.crypto_key_version.name + + enrolled_services { + cloud_product = "all" + } + + depends_on = [google_kms_crypto_key_iam_member.iam] +} diff --git a/mmv1/templates/terraform/examples/organization_access_approval_active_key_version.tf.erb b/mmv1/templates/terraform/examples/organization_access_approval_active_key_version.tf.erb new file mode 100644 index 000000000000..c100453e7d35 --- /dev/null +++ b/mmv1/templates/terraform/examples/organization_access_approval_active_key_version.tf.erb @@ -0,0 +1,46 @@ +resource "google_project" "my_project" { + name = "My Project" + project_id = "your-project-id" + org_id = "<%= ctx[:test_env_vars]['org_id'] %>" +} + +resource "google_kms_key_ring" "key_ring" { + name = "key-ring" + location = "global" + project = google_project.my_project.project_id +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "crypto-key" + key_ring = google_kms_key_ring.key_ring.id + purpose = "ASYMMETRIC_SIGN" + + version_template { + algorithm = "EC_SIGN_P384_SHA384" + } +} + +data "google_access_approval_organization_service_account" "service_account" { + organization_id = "<%= ctx[:test_env_vars]['org_id'] %>" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_organization_service_account.service_account.account_email}" +} + +data "google_kms_crypto_key_version" "crypto_key_version" { + crypto_key = google_kms_crypto_key.crypto_key.id +} + +resource "google_organization_access_approval_settings" "<%= ctx[:primary_resource_id] %>" { + organization_id = "<%= ctx[:test_env_vars]['org_id'] %>" + active_key_version = data.google_kms_crypto_key_version.crypto_key_version.name + + enrolled_services { + cloud_product = "all" + } + + depends_on = [google_kms_crypto_key_iam_member.iam] +} diff --git a/mmv1/templates/terraform/examples/project_access_approval_active_key_version.tf.erb b/mmv1/templates/terraform/examples/project_access_approval_active_key_version.tf.erb new file mode 100644 index 000000000000..7230464e2161 --- /dev/null +++ b/mmv1/templates/terraform/examples/project_access_approval_active_key_version.tf.erb @@ -0,0 +1,40 @@ +resource "google_kms_key_ring" "key_ring" { + name = "key-ring" + location = "global" + project = "<%= ctx[:test_env_vars]['project'] %>" +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "crypto-key" + key_ring = google_kms_key_ring.key_ring.id + purpose = "ASYMMETRIC_SIGN" + + version_template { + algorithm = "EC_SIGN_P384_SHA384" + } +} + +data "google_access_approval_project_service_account" "service_account" { + project_id = "<%= ctx[:test_env_vars]['project'] %>" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_project_service_account.service_account.account_email}" +} + +data "google_kms_crypto_key_version" "crypto_key_version" { + crypto_key = google_kms_crypto_key.crypto_key.id +} + +resource "google_project_access_approval_settings" "<%= ctx[:primary_resource_id] %>" { + project_id = "<%= ctx[:test_env_vars]['project'] %>" + active_key_version = data.google_kms_crypto_key_version.crypto_key_version.name + + enrolled_services { + cloud_product = "all" + } + + depends_on = [google_kms_crypto_key_iam_member.iam] +} diff --git a/mmv1/third_party/terraform/data_sources/data_source_access_approval_folder_service_account.go b/mmv1/third_party/terraform/data_sources/data_source_access_approval_folder_service_account.go new file mode 100644 index 000000000000..8d258e9b0835 --- /dev/null +++ b/mmv1/third_party/terraform/data_sources/data_source_access_approval_folder_service_account.go @@ -0,0 +1,62 @@ +package google + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceAccessApprovalFolderServiceAccount() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAccessApprovalFolderServiceAccountRead, + Schema: map[string]*schema.Schema{ + "folder_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "account_email": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAccessApprovalFolderServiceAccountRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{AccessApprovalBasePath}}folders/{{folder_id}}/serviceAccount") + if err != nil { + return err + } + + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("AccessApprovalFolderServiceAccount %q", d.Id())) + } + + if err := d.Set("name", res["name"]); err != nil { + return fmt.Errorf("Error setting name: %s", err) + } + if err := d.Set("account_email", res["accountEmail"]); err != nil { + return fmt.Errorf("Error setting account_email: %s", err) + } + d.SetId(res["name"].(string)) + + return nil +} diff --git a/mmv1/third_party/terraform/data_sources/data_source_access_approval_organization_service_account.go b/mmv1/third_party/terraform/data_sources/data_source_access_approval_organization_service_account.go new file mode 100644 index 000000000000..b7991fa952ab --- /dev/null +++ b/mmv1/third_party/terraform/data_sources/data_source_access_approval_organization_service_account.go @@ -0,0 +1,62 @@ +package google + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceAccessApprovalOrganizationServiceAccount() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAccessApprovalOrganizationServiceAccountRead, + Schema: map[string]*schema.Schema{ + "organization_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "account_email": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAccessApprovalOrganizationServiceAccountRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{AccessApprovalBasePath}}organizations/{{organization_id}}/serviceAccount") + if err != nil { + return err + } + + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("AccessApprovalOrganizationServiceAccount %q", d.Id())) + } + + if err := d.Set("name", res["name"]); err != nil { + return fmt.Errorf("Error setting name: %s", err) + } + if err := d.Set("account_email", res["accountEmail"]); err != nil { + return fmt.Errorf("Error setting account_email: %s", err) + } + d.SetId(res["name"].(string)) + + return nil +} diff --git a/mmv1/third_party/terraform/data_sources/data_source_access_approval_project_service_account.go b/mmv1/third_party/terraform/data_sources/data_source_access_approval_project_service_account.go new file mode 100644 index 000000000000..ee4865218ebc --- /dev/null +++ b/mmv1/third_party/terraform/data_sources/data_source_access_approval_project_service_account.go @@ -0,0 +1,62 @@ +package google + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceAccessApprovalProjectServiceAccount() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAccessApprovalProjectServiceAccountRead, + Schema: map[string]*schema.Schema{ + "project_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "account_email": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAccessApprovalProjectServiceAccountRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{AccessApprovalBasePath}}projects/{{project_id}}/serviceAccount") + if err != nil { + return err + } + + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("AccessApprovalProjectServiceAccount %q", d.Id())) + } + + if err := d.Set("name", res["name"]); err != nil { + return fmt.Errorf("Error setting name: %s", err) + } + if err := d.Set("account_email", res["accountEmail"]); err != nil { + return fmt.Errorf("Error setting account_email: %s", err) + } + d.SetId(res["name"].(string)) + + return nil +} diff --git a/mmv1/third_party/terraform/tests/data_source_access_approval_folder_service_account_test.go b/mmv1/third_party/terraform/tests/data_source_access_approval_folder_service_account_test.go new file mode 100644 index 000000000000..96e1bf7ff8d4 --- /dev/null +++ b/mmv1/third_party/terraform/tests/data_source_access_approval_folder_service_account_test.go @@ -0,0 +1,55 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceAccessApprovalFolderServiceAccount_basic(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": getTestOrgFromEnv(t), + "random_suffix": randString(t, 10), + } + + resourceName := "data.google_access_approval_folder_service_account.aa_account" + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + ExternalProviders: map[string]resource.ExternalProvider{ + "time": {}, + }, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAccessApprovalFolderServiceAccount_basic(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "account_email"), + ), + }, + }, + }) +} + +func testAccDataSourceAccessApprovalFolderServiceAccount_basic(context map[string]interface{}) string { + return Nprintf(` +resource "google_folder" "my_folder" { + display_name = "tf-test-my-folder%{random_suffix}" + parent = "organizations/%{org_id}" +} + +resource "time_sleep" "wait_120_seconds" { + depends_on = [google_folder.my_folder] + + create_duration = "120s" +} + +data "google_access_approval_folder_service_account" "aa_account" { + folder_id = google_folder.my_folder.folder_id + + depends_on = [time_sleep.wait_120_seconds] +} +`, context) +} diff --git a/mmv1/third_party/terraform/tests/data_source_access_approval_organization_service_account_test.go b/mmv1/third_party/terraform/tests/data_source_access_approval_organization_service_account_test.go new file mode 100644 index 000000000000..11756d25bb29 --- /dev/null +++ b/mmv1/third_party/terraform/tests/data_source_access_approval_organization_service_account_test.go @@ -0,0 +1,38 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceAccessApprovalOrganizationServiceAccount_basic(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": getTestOrgFromEnv(t), + } + + resourceName := "data.google_access_approval_organization_service_account.aa_account" + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAccessApprovalOrganizationServiceAccount_basic(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "account_email"), + ), + }, + }, + }) +} + +func testAccDataSourceAccessApprovalOrganizationServiceAccount_basic(context map[string]interface{}) string { + return Nprintf(` +data "google_access_approval_organization_service_account" "aa_account" { + organization_id = "%{org_id}" +} +`, context) +} diff --git a/mmv1/third_party/terraform/tests/data_source_access_approval_project_service_account_test.go b/mmv1/third_party/terraform/tests/data_source_access_approval_project_service_account_test.go new file mode 100644 index 000000000000..e0713a910090 --- /dev/null +++ b/mmv1/third_party/terraform/tests/data_source_access_approval_project_service_account_test.go @@ -0,0 +1,38 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceAccessApprovalProjectServiceAccount_basic(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "project_id": getTestProjectFromEnv(), + } + + resourceName := "data.google_access_approval_project_service_account.aa_account" + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAccessApprovalProjectServiceAccount_basic(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "account_email"), + ), + }, + }, + }) +} + +func testAccDataSourceAccessApprovalProjectServiceAccount_basic(context map[string]interface{}) string { + return Nprintf(` +data "google_access_approval_project_service_account" "aa_account" { + project_id = "%{project_id}" +} +`, context) +} diff --git a/mmv1/third_party/terraform/tests/resource_access_approval_folder_settings_test.go b/mmv1/third_party/terraform/tests/resource_access_approval_folder_settings_test.go index bd9cfacf4f37..f5f081c19080 100644 --- a/mmv1/third_party/terraform/tests/resource_access_approval_folder_settings_test.go +++ b/mmv1/third_party/terraform/tests/resource_access_approval_folder_settings_test.go @@ -14,13 +14,18 @@ import ( // See AccessApprovalOrganizationSettings for the test runner. func testAccAccessApprovalFolderSettings(t *testing.T) { context := map[string]interface{}{ + "project": getTestProjectFromEnv(), "org_id": getTestOrgFromEnv(t), + "location": getTestRegionFromEnv(), "random_suffix": randString(t, 10), } vcrTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + ExternalProviders: map[string]resource.ExternalProvider{ + "time": {}, + }, CheckDestroy: testAccCheckAccessApprovalFolderSettingsDestroyProducer(t), Steps: []resource.TestStep{ { @@ -41,6 +46,15 @@ func testAccAccessApprovalFolderSettings(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{"folder_id"}, }, + { + Config: testAccAccessApprovalFolderSettings_activeKeyVersion(context), + }, + { + ResourceName: "google_folder_access_approval_settings.folder_access_approval", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"folder_id"}, + }, }, }) } @@ -52,6 +66,12 @@ resource "google_folder" "my_folder" { parent = "organizations/%{org_id}" } +resource "time_sleep" "wait_120_seconds" { + depends_on = [google_folder.my_folder] + + create_duration = "120s" +} + resource "google_folder_access_approval_settings" "folder_access_approval" { folder_id = google_folder.my_folder.folder_id notification_emails = ["testuser@example.com"] @@ -59,6 +79,8 @@ resource "google_folder_access_approval_settings" "folder_access_approval" { enrolled_services { cloud_product = "all" } + + depends_on = [time_sleep.wait_120_seconds] } `, context) } @@ -70,6 +92,12 @@ resource "google_folder" "my_folder" { parent = "organizations/%{org_id}" } +resource "time_sleep" "wait_120_seconds" { + depends_on = [google_folder.my_folder] + + create_duration = "120s" +} + resource "google_folder_access_approval_settings" "folder_access_approval" { folder_id = google_folder.my_folder.folder_id notification_emails = ["testuser@example.com", "example.user@example.com"] @@ -77,6 +105,67 @@ resource "google_folder_access_approval_settings" "folder_access_approval" { enrolled_services { cloud_product = "all" } + + depends_on = [time_sleep.wait_120_seconds] +} +`, context) +} + +func testAccAccessApprovalFolderSettings_activeKeyVersion(context map[string]interface{}) string { + return Nprintf(` +resource "google_folder" "my_folder" { + display_name = "tf-test-my-folder%{random_suffix}" + parent = "organizations/%{org_id}" +} + +resource "time_sleep" "wait_120_seconds" { + depends_on = [google_folder.my_folder] + + create_duration = "120s" +} + +resource "google_kms_key_ring" "key_ring" { + name = "tf-test-%{random_suffix}" + project = "%{project}" + location = "%{location}" +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "tf-test-%{random_suffix}" + key_ring = google_kms_key_ring.key_ring.id + purpose = "ASYMMETRIC_SIGN" + + version_template { + algorithm = "EC_SIGN_P384_SHA384" + } +} + +data "google_access_approval_folder_service_account" "aa_account" { + folder_id = google_folder.my_folder.folder_id + + depends_on = [time_sleep.wait_120_seconds] +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_folder_service_account.aa_account.account_email}" +} + +data "google_kms_crypto_key_version" "crypto_key_version" { + crypto_key = google_kms_crypto_key.crypto_key.id +} + +resource "google_folder_access_approval_settings" "folder_access_approval" { + folder_id = google_folder.my_folder.folder_id + + enrolled_services { + cloud_product = "all" + } + + active_key_version = data.google_kms_crypto_key_version.crypto_key_version.name + + depends_on = [google_kms_crypto_key_iam_member.iam] } `, context) } diff --git a/mmv1/third_party/terraform/tests/resource_access_approval_organization_settings_test.go b/mmv1/third_party/terraform/tests/resource_access_approval_organization_settings_test.go index aac0111c586b..57b7a90c5748 100644 --- a/mmv1/third_party/terraform/tests/resource_access_approval_organization_settings_test.go +++ b/mmv1/third_party/terraform/tests/resource_access_approval_organization_settings_test.go @@ -32,7 +32,9 @@ func TestAccAccessApprovalSettings(t *testing.T) { func testAccAccessApprovalOrganizationSettings(t *testing.T) { context := map[string]interface{}{ + "project": getTestProjectFromEnv(), "org_id": getTestOrgFromEnv(t), + "location": getTestRegionFromEnv(), "random_suffix": randString(t, 10), } @@ -59,6 +61,15 @@ func testAccAccessApprovalOrganizationSettings(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{"organization_id"}, }, + { + Config: testAccAccessApprovalOrganizationSettings_activeKeyVersion(context), + }, + { + ResourceName: "google_organization_access_approval_settings.organization_access_approval", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"organization_id"}, + }, }, }) } @@ -95,6 +106,52 @@ resource "google_organization_access_approval_settings" "organization_access_app `, context) } +func testAccAccessApprovalOrganizationSettings_activeKeyVersion(context map[string]interface{}) string { + return Nprintf(` +resource "google_kms_key_ring" "key_ring" { + name = "tf-test-%{random_suffix}" + project = "%{project}" + location = "%{location}" +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "tf-test-%{random_suffix}" + key_ring = google_kms_key_ring.key_ring.id + purpose = "ASYMMETRIC_SIGN" + + version_template { + algorithm = "EC_SIGN_P384_SHA384" + } +} + +data "google_access_approval_organization_service_account" "aa_account" { + organization_id = "%{org_id}" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_organization_service_account.aa_account.account_email}" +} + +data "google_kms_crypto_key_version" "crypto_key_version" { + crypto_key = google_kms_crypto_key.crypto_key.id +} + +resource "google_organization_access_approval_settings" "organization_access_approval" { + organization_id = "%{org_id}" + + enrolled_services { + cloud_product = "all" + } + + active_key_version = data.google_kms_crypto_key_version.crypto_key_version.name + + depends_on = [google_kms_crypto_key_iam_member.iam] +} +`, context) +} + func testAccCheckAccessApprovalOrganizationSettingsDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/mmv1/third_party/terraform/tests/resource_access_approval_project_settings_test.go b/mmv1/third_party/terraform/tests/resource_access_approval_project_settings_test.go index f95512ba4f3b..64e5ff51d408 100644 --- a/mmv1/third_party/terraform/tests/resource_access_approval_project_settings_test.go +++ b/mmv1/third_party/terraform/tests/resource_access_approval_project_settings_test.go @@ -16,6 +16,7 @@ func testAccAccessApprovalProjectSettings(t *testing.T) { context := map[string]interface{}{ "project": getTestProjectFromEnv(), "org_id": getTestOrgFromEnv(t), + "location": getTestRegionFromEnv(), "random_suffix": randString(t, 10), } @@ -42,6 +43,15 @@ func testAccAccessApprovalProjectSettings(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{"project_id"}, }, + { + Config: testAccAccessApprovalProjectSettings_activeKeyVersion(context), + }, + { + ResourceName: "google_project_access_approval_settings.project_access_approval", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"project_id"}, + }, }, }) } @@ -73,6 +83,52 @@ resource "google_project_access_approval_settings" "project_access_approval" { `, context) } +func testAccAccessApprovalProjectSettings_activeKeyVersion(context map[string]interface{}) string { + return Nprintf(` +resource "google_kms_key_ring" "key_ring" { + name = "tf-test-%{random_suffix}" + project = "%{project}" + location = "%{location}" +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "tf-test-%{random_suffix}" + key_ring = google_kms_key_ring.key_ring.id + purpose = "ASYMMETRIC_SIGN" + + version_template { + algorithm = "EC_SIGN_P384_SHA384" + } +} + +data "google_access_approval_project_service_account" "aa_account" { + project_id = "%{project}" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_project_service_account.aa_account.account_email}" +} + +data "google_kms_crypto_key_version" "crypto_key_version" { + crypto_key = google_kms_crypto_key.crypto_key.id +} + +resource "google_project_access_approval_settings" "project_access_approval" { + project_id = "%{project}" + + enrolled_services { + cloud_product = "all" + } + + active_key_version = data.google_kms_crypto_key_version.crypto_key_version.name + + depends_on = [google_kms_crypto_key_iam_member.iam] +} +`, context) +} + func testAccCheckAccessApprovalProjectSettingsDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/mmv1/third_party/terraform/utils/provider.go.erb b/mmv1/third_party/terraform/utils/provider.go.erb index 7eb206e2d6ec..4dd46ade59dc 100644 --- a/mmv1/third_party/terraform/utils/provider.go.erb +++ b/mmv1/third_party/terraform/utils/provider.go.erb @@ -204,6 +204,9 @@ func Provider() *schema.Provider { }, DataSourcesMap: map[string]*schema.Resource{ + "google_access_approval_project_service_account": dataSourceAccessApprovalProjectServiceAccount(), + "google_access_approval_folder_service_account": dataSourceAccessApprovalFolderServiceAccount(), + "google_access_approval_organization_service_account": dataSourceAccessApprovalOrganizationServiceAccount(), "google_active_folder": dataSourceGoogleActiveFolder(), "google_app_engine_default_service_account": dataSourceGoogleAppEngineDefaultServiceAccount(), "google_billing_account": dataSourceGoogleBillingAccount(), diff --git a/mmv1/third_party/terraform/website/docs/d/access_approval_folder_service_account.html.markdown b/mmv1/third_party/terraform/website/docs/d/access_approval_folder_service_account.html.markdown new file mode 100644 index 000000000000..1394ca46a63b --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/access_approval_folder_service_account.html.markdown @@ -0,0 +1,47 @@ +--- +subcategory: "Access Approval" +layout: "google" +page_title: "Google: google_access_approval_folder_service_account" +sidebar_current: "docs-google-datasource-access-approval-folder-service-account" +description: |- + Get the email address of a folder's Access Approval service account. +--- + +# google\_access\_approval\_folder\_service\_account + +Get the email address of a folder's Access Approval service account. + +Each Google Cloud folder has a unique service account used by Access Approval. +When using Access Approval with a +[custom signing key](https://cloud.google.com/cloud-provider-access-management/access-approval/docs/review-approve-access-requests-custom-keys), +this account needs to be granted the `cloudkms.signerVerifier` IAM role on the +Cloud KMS key used to sign approvals. + +## Example Usage + +```hcl +data "google_access_approval_folder_service_account" "service_account" { + folder_id = "my-folder" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_folder_service_account.service_account.account_email}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `folder_id` - (Required) The folder ID the service account was created for. + +## Attributes Reference + +The following attributes are exported: + +* `name` - The Access Approval service account resource name. Format is "folders/{folder_id}/serviceAccount". + +* `account_email` - The email address of the service account. This value is +often used to refer to the service account in order to grant IAM permissions. diff --git a/mmv1/third_party/terraform/website/docs/d/access_approval_organization_service_account.html.markdown b/mmv1/third_party/terraform/website/docs/d/access_approval_organization_service_account.html.markdown new file mode 100644 index 000000000000..590c0b7d01e6 --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/access_approval_organization_service_account.html.markdown @@ -0,0 +1,47 @@ +--- +subcategory: "Access Approval" +layout: "google" +page_title: "Google: google_access_approval_organization_service_account" +sidebar_current: "docs-google-datasource-access-approval-organization-service-account" +description: |- + Get the email address of an organization's Access Approval service account. +--- + +# google\_access\_approval\_organization\_service\_account + +Get the email address of an organization's Access Approval service account. + +Each Google Cloud organization has a unique service account used by Access Approval. +When using Access Approval with a +[custom signing key](https://cloud.google.com/cloud-provider-access-management/access-approval/docs/review-approve-access-requests-custom-keys), +this account needs to be granted the `cloudkms.signerVerifier` IAM role on the +Cloud KMS key used to sign approvals. + +## Example Usage + +```hcl +data "google_access_approval_organization_service_account" "service_account" { + organization_id = "my-organization" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_organization_service_account.service_account.account_email}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `organization_id` - (Required) The organization ID the service account was created for. + +## Attributes Reference + +The following attributes are exported: + +* `name` - The Access Approval service account resource name. Format is "organizations/{organization_id}/serviceAccount". + +* `account_email` - The email address of the service account. This value is +often used to refer to the service account in order to grant IAM permissions. diff --git a/mmv1/third_party/terraform/website/docs/d/access_approval_project_service_account.html.markdown b/mmv1/third_party/terraform/website/docs/d/access_approval_project_service_account.html.markdown new file mode 100644 index 000000000000..e36798a2c330 --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/access_approval_project_service_account.html.markdown @@ -0,0 +1,47 @@ +--- +subcategory: "Access Approval" +layout: "google" +page_title: "Google: google_access_approval_project_service_account" +sidebar_current: "docs-google-datasource-access-approval-project-service-account" +description: |- + Get the email address of a project's Access Approval service account. +--- + +# google\_access\_approval\_project\_service\_account + +Get the email address of a project's Access Approval service account. + +Each Google Cloud project has a unique service account used by Access Approval. +When using Access Approval with a +[custom signing key](https://cloud.google.com/cloud-provider-access-management/access-approval/docs/review-approve-access-requests-custom-keys), +this account needs to be granted the `cloudkms.signerVerifier` IAM role on the +Cloud KMS key used to sign approvals. + +## Example Usage + +```hcl +data "google_access_approval_project_service_account" "service_account" { + project_id = "my-project" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_project_service_account.service_account.account_email}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `project_id` - (Required) The project ID the service account was created for. + +## Attributes Reference + +The following attributes are exported: + +* `name` - The Access Approval service account resource name. Format is "projects/{project_id}/serviceAccount". + +* `account_email` - The email address of the service account. This value is +often used to refer to the service account in order to grant IAM permissions. From 41e464a33e64b32ab4674f73b8185678b8084e6c Mon Sep 17 00:00:00 2001 From: johnethomas Date: Tue, 29 Mar 2022 15:55:35 +0000 Subject: [PATCH 2/4] Add API fields and data sources to support using custom keys with Access Approval --- mmv1/products/accessapproval/api.yaml | 55 +++++++++++ mmv1/products/accessapproval/terraform.yaml | 21 +++++ ...ear_folder_access_approval_settings.go.erb | 2 + ...ganization_access_approval_settings.go.erb | 2 + ...ar_project_access_approval_settings.go.erb | 2 + ..._access_approval_active_key_version.tf.erb | 51 ++++++++++ ..._access_approval_active_key_version.tf.erb | 46 +++++++++ ..._access_approval_active_key_version.tf.erb | 40 ++++++++ ..._access_approval_folder_service_account.go | 62 +++++++++++++ ...s_approval_organization_service_account.go | 62 +++++++++++++ ...access_approval_project_service_account.go | 62 +++++++++++++ ...ss_approval_folder_service_account_test.go | 55 +++++++++++ ...roval_organization_service_account_test.go | 38 ++++++++ ...s_approval_project_service_account_test.go | 38 ++++++++ ...ce_access_approval_folder_settings_test.go | 93 ++++++++++++++++++- ...ess_approval_organization_settings_test.go | 57 ++++++++++++ ...e_access_approval_project_settings_test.go | 56 +++++++++++ .../terraform/utils/provider.go.erb | 3 + ...roval_folder_service_account.html.markdown | 47 ++++++++++ ...organization_service_account.html.markdown | 47 ++++++++++ ...oval_project_service_account.html.markdown | 47 ++++++++++ 21 files changed, 884 insertions(+), 2 deletions(-) create mode 100644 mmv1/templates/terraform/examples/folder_access_approval_active_key_version.tf.erb create mode 100644 mmv1/templates/terraform/examples/organization_access_approval_active_key_version.tf.erb create mode 100644 mmv1/templates/terraform/examples/project_access_approval_active_key_version.tf.erb create mode 100644 mmv1/third_party/terraform/data_sources/data_source_access_approval_folder_service_account.go create mode 100644 mmv1/third_party/terraform/data_sources/data_source_access_approval_organization_service_account.go create mode 100644 mmv1/third_party/terraform/data_sources/data_source_access_approval_project_service_account.go create mode 100644 mmv1/third_party/terraform/tests/data_source_access_approval_folder_service_account_test.go create mode 100644 mmv1/third_party/terraform/tests/data_source_access_approval_organization_service_account_test.go create mode 100644 mmv1/third_party/terraform/tests/data_source_access_approval_project_service_account_test.go create mode 100644 mmv1/third_party/terraform/website/docs/d/access_approval_folder_service_account.html.markdown create mode 100644 mmv1/third_party/terraform/website/docs/d/access_approval_organization_service_account.html.markdown create mode 100644 mmv1/third_party/terraform/website/docs/d/access_approval_project_service_account.html.markdown diff --git a/mmv1/products/accessapproval/api.yaml b/mmv1/products/accessapproval/api.yaml index a54c89cdd6ed..830471638585 100644 --- a/mmv1/products/accessapproval/api.yaml +++ b/mmv1/products/accessapproval/api.yaml @@ -109,6 +109,25 @@ objects: output: true description: | If the field is true, that indicates that at least one service is enrolled for Access Approval in one or more ancestors of the Folder. + - !ruby/object:Api::Type::String + name: activeKeyVersion + description: | + The asymmetric crypto key version to use for signing approval requests. + Empty active_key_version indicates that a Google-managed key should be used for signing. + This property will be ignored if set by an ancestor of the resource, and new non-empty values may not be set. + - !ruby/object:Api::Type::Boolean + name: ancestorHasActiveKeyVersion + output: true + description: | + If the field is true, that indicates that an ancestor of this Folder has set active_key_version. + - !ruby/object:Api::Type::Boolean + name: invalidKeyVersion + output: true + description: | + If the field is true, that indicates that there is some configuration issue with the active_key_version + configured on this Folder (e.g. it doesn't exist or the Access Approval service account doesn't have the + correct permissions on it, etc.) This key version is not necessarily the effective key version at this level, + as key versions are inherited top-down. - !ruby/object:Api::Resource name: ProjectSettings base_url: "projects/{{project_id}}/accessApprovalSettings" @@ -180,6 +199,25 @@ objects: output: true description: | If the field is true, that indicates that at least one service is enrolled for Access Approval in one or more ancestors of the Project. + - !ruby/object:Api::Type::String + name: activeKeyVersion + description: | + The asymmetric crypto key version to use for signing approval requests. + Empty active_key_version indicates that a Google-managed key should be used for signing. + This property will be ignored if set by an ancestor of the resource, and new non-empty values may not be set. + - !ruby/object:Api::Type::Boolean + name: ancestorHasActiveKeyVersion + output: true + description: | + If the field is true, that indicates that an ancestor of this Project has set active_key_version. + - !ruby/object:Api::Type::Boolean + name: invalidKeyVersion + output: true + description: | + If the field is true, that indicates that there is some configuration issue with the active_key_version + configured on this Project (e.g. it doesn't exist or the Access Approval service account doesn't have the + correct permissions on it, etc.) This key version is not necessarily the effective key version at this level, + as key versions are inherited top-down. - !ruby/object:Api::Type::String name: project description: | @@ -256,3 +294,20 @@ objects: output: true description: | This field will always be unset for the organization since organizations do not have ancestors. + - !ruby/object:Api::Type::String + name: activeKeyVersion + description: | + The asymmetric crypto key version to use for signing approval requests. + Empty active_key_version indicates that a Google-managed key should be used for signing. + - !ruby/object:Api::Type::Boolean + name: ancestorHasActiveKeyVersion + output: true + description: | + This field will always be unset for the organization since organizations do not have ancestors. + - !ruby/object:Api::Type::Boolean + name: invalidKeyVersion + output: true + description: | + If the field is true, that indicates that there is some configuration issue with the active_key_version + configured on this Organization (e.g. it doesn't exist or the Access Approval service account doesn't have the + correct permissions on it, etc.). diff --git a/mmv1/products/accessapproval/terraform.yaml b/mmv1/products/accessapproval/terraform.yaml index 1febec8064dc..0dc78cf4a37b 100644 --- a/mmv1/products/accessapproval/terraform.yaml +++ b/mmv1/products/accessapproval/terraform.yaml @@ -25,6 +25,14 @@ overrides: !ruby/object:Overrides::ResourceOverrides folder_name: 'my-folder' test_env_vars: org_id: :ORG_ID + - !ruby/object:Provider::Terraform::Examples + skip_test: true + name: 'folder_access_approval_active_key_version' + primary_resource_id: 'folder_access_approval' + vars: + folder_name: 'my-folder' + test_env_vars: + org_id: :ORG_ID custom_code: !ruby/object:Provider::Terraform::CustomCode custom_delete: templates/terraform/custom_delete/clear_folder_access_approval_settings.go.erb pre_create: templates/terraform/update_mask.erb @@ -47,6 +55,13 @@ overrides: !ruby/object:Overrides::ResourceOverrides test_env_vars: project: :PROJECT_NAME org_id: :ORG_ID + - !ruby/object:Provider::Terraform::Examples + skip_test: true + name: 'project_access_approval_active_key_version' + primary_resource_id: 'project_access_approval' + test_env_vars: + project: :PROJECT_NAME + org_id: :ORG_ID custom_code: !ruby/object:Provider::Terraform::CustomCode custom_delete: templates/terraform/custom_delete/clear_project_access_approval_settings.go.erb pre_create: templates/terraform/update_mask.erb @@ -67,6 +82,12 @@ overrides: !ruby/object:Overrides::ResourceOverrides primary_resource_id: 'organization_access_approval' test_env_vars: org_id: :ORG_ID + - !ruby/object:Provider::Terraform::Examples + skip_test: true + name: 'organization_access_approval_active_key_version' + primary_resource_id: 'organization_access_approval' + test_env_vars: + org_id: :ORG_ID custom_code: !ruby/object:Provider::Terraform::CustomCode custom_delete: templates/terraform/custom_delete/clear_organization_access_approval_settings.go.erb pre_create: templates/terraform/update_mask.erb diff --git a/mmv1/templates/terraform/custom_delete/clear_folder_access_approval_settings.go.erb b/mmv1/templates/terraform/custom_delete/clear_folder_access_approval_settings.go.erb index ec376af022fe..6dca42f29e31 100644 --- a/mmv1/templates/terraform/custom_delete/clear_folder_access_approval_settings.go.erb +++ b/mmv1/templates/terraform/custom_delete/clear_folder_access_approval_settings.go.erb @@ -1,6 +1,7 @@ obj := make(map[string]interface{}) obj["notificationEmails"] = []string{} obj["enrolledServices"] = []string{} +obj["activeKeyVersion"] = "" url, err := replaceVars(d, config, "{{AccessApprovalBasePath}}folders/{{folder_id}}/accessApprovalSettings") if err != nil { @@ -12,6 +13,7 @@ updateMask := []string{} updateMask = append(updateMask, "notificationEmails") updateMask = append(updateMask, "enrolledServices") +updateMask = append(updateMask, "activeKeyVersion") // updateMask is a URL parameter but not present in the schema, so replaceVars // won't set it diff --git a/mmv1/templates/terraform/custom_delete/clear_organization_access_approval_settings.go.erb b/mmv1/templates/terraform/custom_delete/clear_organization_access_approval_settings.go.erb index 7f0299d6c869..5689b763a681 100644 --- a/mmv1/templates/terraform/custom_delete/clear_organization_access_approval_settings.go.erb +++ b/mmv1/templates/terraform/custom_delete/clear_organization_access_approval_settings.go.erb @@ -1,6 +1,7 @@ obj := make(map[string]interface{}) obj["notificationEmails"] = []string{} obj["enrolledServices"] = []string{} +obj["activeKeyVersion"] = "" url, err := replaceVars(d, config, "{{AccessApprovalBasePath}}organizations/{{organization_id}}/accessApprovalSettings") if err != nil { @@ -12,6 +13,7 @@ updateMask := []string{} updateMask = append(updateMask, "notificationEmails") updateMask = append(updateMask, "enrolledServices") +updateMask = append(updateMask, "activeKeyVersion") // updateMask is a URL parameter but not present in the schema, so replaceVars // won't set it diff --git a/mmv1/templates/terraform/custom_delete/clear_project_access_approval_settings.go.erb b/mmv1/templates/terraform/custom_delete/clear_project_access_approval_settings.go.erb index 30cc67243ab4..6c5d505a6a21 100644 --- a/mmv1/templates/terraform/custom_delete/clear_project_access_approval_settings.go.erb +++ b/mmv1/templates/terraform/custom_delete/clear_project_access_approval_settings.go.erb @@ -1,6 +1,7 @@ obj := make(map[string]interface{}) obj["notificationEmails"] = []string{} obj["enrolledServices"] = []string{} +obj["activeKeyVersion"] = "" url, err := replaceVars(d, config, "{{AccessApprovalBasePath}}projects/{{project_id}}/accessApprovalSettings") if err != nil { @@ -12,6 +13,7 @@ updateMask := []string{} updateMask = append(updateMask, "notificationEmails") updateMask = append(updateMask, "enrolledServices") +updateMask = append(updateMask, "activeKeyVersion") // updateMask is a URL parameter but not present in the schema, so replaceVars // won't set it diff --git a/mmv1/templates/terraform/examples/folder_access_approval_active_key_version.tf.erb b/mmv1/templates/terraform/examples/folder_access_approval_active_key_version.tf.erb new file mode 100644 index 000000000000..474dc36b1b21 --- /dev/null +++ b/mmv1/templates/terraform/examples/folder_access_approval_active_key_version.tf.erb @@ -0,0 +1,51 @@ +resource "google_folder" "my_folder" { + display_name = "<%= ctx[:vars]['folder_name'] %>" + parent = "organizations/<%= ctx[:test_env_vars]['org_id'] %>" +} + +resource "google_project" "my_project" { + name = "My Project" + project_id = "your-project-id" + folder_id = google_folder.my_folder.name +} + +resource "google_kms_key_ring" "key_ring" { + name = "key-ring" + location = "global" + project = google_project.my_project.project_id +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "crypto-key" + key_ring = google_kms_key_ring.key_ring.id + purpose = "ASYMMETRIC_SIGN" + + version_template { + algorithm = "EC_SIGN_P384_SHA384" + } +} + +data "google_access_approval_folder_service_account" "service_account" { + folder_id = google_folder.my_folder.folder_id +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_folder_service_account.service_account.account_email}" +} + +data "google_kms_crypto_key_version" "crypto_key_version" { + crypto_key = google_kms_crypto_key.crypto_key.id +} + +resource "google_folder_access_approval_settings" "<%= ctx[:primary_resource_id] %>" { + folder_id = google_folder.my_folder.folder_id + active_key_version = data.google_kms_crypto_key_version.crypto_key_version.name + + enrolled_services { + cloud_product = "all" + } + + depends_on = [google_kms_crypto_key_iam_member.iam] +} diff --git a/mmv1/templates/terraform/examples/organization_access_approval_active_key_version.tf.erb b/mmv1/templates/terraform/examples/organization_access_approval_active_key_version.tf.erb new file mode 100644 index 000000000000..c100453e7d35 --- /dev/null +++ b/mmv1/templates/terraform/examples/organization_access_approval_active_key_version.tf.erb @@ -0,0 +1,46 @@ +resource "google_project" "my_project" { + name = "My Project" + project_id = "your-project-id" + org_id = "<%= ctx[:test_env_vars]['org_id'] %>" +} + +resource "google_kms_key_ring" "key_ring" { + name = "key-ring" + location = "global" + project = google_project.my_project.project_id +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "crypto-key" + key_ring = google_kms_key_ring.key_ring.id + purpose = "ASYMMETRIC_SIGN" + + version_template { + algorithm = "EC_SIGN_P384_SHA384" + } +} + +data "google_access_approval_organization_service_account" "service_account" { + organization_id = "<%= ctx[:test_env_vars]['org_id'] %>" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_organization_service_account.service_account.account_email}" +} + +data "google_kms_crypto_key_version" "crypto_key_version" { + crypto_key = google_kms_crypto_key.crypto_key.id +} + +resource "google_organization_access_approval_settings" "<%= ctx[:primary_resource_id] %>" { + organization_id = "<%= ctx[:test_env_vars]['org_id'] %>" + active_key_version = data.google_kms_crypto_key_version.crypto_key_version.name + + enrolled_services { + cloud_product = "all" + } + + depends_on = [google_kms_crypto_key_iam_member.iam] +} diff --git a/mmv1/templates/terraform/examples/project_access_approval_active_key_version.tf.erb b/mmv1/templates/terraform/examples/project_access_approval_active_key_version.tf.erb new file mode 100644 index 000000000000..7230464e2161 --- /dev/null +++ b/mmv1/templates/terraform/examples/project_access_approval_active_key_version.tf.erb @@ -0,0 +1,40 @@ +resource "google_kms_key_ring" "key_ring" { + name = "key-ring" + location = "global" + project = "<%= ctx[:test_env_vars]['project'] %>" +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "crypto-key" + key_ring = google_kms_key_ring.key_ring.id + purpose = "ASYMMETRIC_SIGN" + + version_template { + algorithm = "EC_SIGN_P384_SHA384" + } +} + +data "google_access_approval_project_service_account" "service_account" { + project_id = "<%= ctx[:test_env_vars]['project'] %>" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_project_service_account.service_account.account_email}" +} + +data "google_kms_crypto_key_version" "crypto_key_version" { + crypto_key = google_kms_crypto_key.crypto_key.id +} + +resource "google_project_access_approval_settings" "<%= ctx[:primary_resource_id] %>" { + project_id = "<%= ctx[:test_env_vars]['project'] %>" + active_key_version = data.google_kms_crypto_key_version.crypto_key_version.name + + enrolled_services { + cloud_product = "all" + } + + depends_on = [google_kms_crypto_key_iam_member.iam] +} diff --git a/mmv1/third_party/terraform/data_sources/data_source_access_approval_folder_service_account.go b/mmv1/third_party/terraform/data_sources/data_source_access_approval_folder_service_account.go new file mode 100644 index 000000000000..8d258e9b0835 --- /dev/null +++ b/mmv1/third_party/terraform/data_sources/data_source_access_approval_folder_service_account.go @@ -0,0 +1,62 @@ +package google + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceAccessApprovalFolderServiceAccount() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAccessApprovalFolderServiceAccountRead, + Schema: map[string]*schema.Schema{ + "folder_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "account_email": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAccessApprovalFolderServiceAccountRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{AccessApprovalBasePath}}folders/{{folder_id}}/serviceAccount") + if err != nil { + return err + } + + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("AccessApprovalFolderServiceAccount %q", d.Id())) + } + + if err := d.Set("name", res["name"]); err != nil { + return fmt.Errorf("Error setting name: %s", err) + } + if err := d.Set("account_email", res["accountEmail"]); err != nil { + return fmt.Errorf("Error setting account_email: %s", err) + } + d.SetId(res["name"].(string)) + + return nil +} diff --git a/mmv1/third_party/terraform/data_sources/data_source_access_approval_organization_service_account.go b/mmv1/third_party/terraform/data_sources/data_source_access_approval_organization_service_account.go new file mode 100644 index 000000000000..b7991fa952ab --- /dev/null +++ b/mmv1/third_party/terraform/data_sources/data_source_access_approval_organization_service_account.go @@ -0,0 +1,62 @@ +package google + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceAccessApprovalOrganizationServiceAccount() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAccessApprovalOrganizationServiceAccountRead, + Schema: map[string]*schema.Schema{ + "organization_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "account_email": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAccessApprovalOrganizationServiceAccountRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{AccessApprovalBasePath}}organizations/{{organization_id}}/serviceAccount") + if err != nil { + return err + } + + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("AccessApprovalOrganizationServiceAccount %q", d.Id())) + } + + if err := d.Set("name", res["name"]); err != nil { + return fmt.Errorf("Error setting name: %s", err) + } + if err := d.Set("account_email", res["accountEmail"]); err != nil { + return fmt.Errorf("Error setting account_email: %s", err) + } + d.SetId(res["name"].(string)) + + return nil +} diff --git a/mmv1/third_party/terraform/data_sources/data_source_access_approval_project_service_account.go b/mmv1/third_party/terraform/data_sources/data_source_access_approval_project_service_account.go new file mode 100644 index 000000000000..ee4865218ebc --- /dev/null +++ b/mmv1/third_party/terraform/data_sources/data_source_access_approval_project_service_account.go @@ -0,0 +1,62 @@ +package google + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceAccessApprovalProjectServiceAccount() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAccessApprovalProjectServiceAccountRead, + Schema: map[string]*schema.Schema{ + "project_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "account_email": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAccessApprovalProjectServiceAccountRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{AccessApprovalBasePath}}projects/{{project_id}}/serviceAccount") + if err != nil { + return err + } + + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("AccessApprovalProjectServiceAccount %q", d.Id())) + } + + if err := d.Set("name", res["name"]); err != nil { + return fmt.Errorf("Error setting name: %s", err) + } + if err := d.Set("account_email", res["accountEmail"]); err != nil { + return fmt.Errorf("Error setting account_email: %s", err) + } + d.SetId(res["name"].(string)) + + return nil +} diff --git a/mmv1/third_party/terraform/tests/data_source_access_approval_folder_service_account_test.go b/mmv1/third_party/terraform/tests/data_source_access_approval_folder_service_account_test.go new file mode 100644 index 000000000000..96e1bf7ff8d4 --- /dev/null +++ b/mmv1/third_party/terraform/tests/data_source_access_approval_folder_service_account_test.go @@ -0,0 +1,55 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceAccessApprovalFolderServiceAccount_basic(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": getTestOrgFromEnv(t), + "random_suffix": randString(t, 10), + } + + resourceName := "data.google_access_approval_folder_service_account.aa_account" + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + ExternalProviders: map[string]resource.ExternalProvider{ + "time": {}, + }, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAccessApprovalFolderServiceAccount_basic(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "account_email"), + ), + }, + }, + }) +} + +func testAccDataSourceAccessApprovalFolderServiceAccount_basic(context map[string]interface{}) string { + return Nprintf(` +resource "google_folder" "my_folder" { + display_name = "tf-test-my-folder%{random_suffix}" + parent = "organizations/%{org_id}" +} + +resource "time_sleep" "wait_120_seconds" { + depends_on = [google_folder.my_folder] + + create_duration = "120s" +} + +data "google_access_approval_folder_service_account" "aa_account" { + folder_id = google_folder.my_folder.folder_id + + depends_on = [time_sleep.wait_120_seconds] +} +`, context) +} diff --git a/mmv1/third_party/terraform/tests/data_source_access_approval_organization_service_account_test.go b/mmv1/third_party/terraform/tests/data_source_access_approval_organization_service_account_test.go new file mode 100644 index 000000000000..11756d25bb29 --- /dev/null +++ b/mmv1/third_party/terraform/tests/data_source_access_approval_organization_service_account_test.go @@ -0,0 +1,38 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceAccessApprovalOrganizationServiceAccount_basic(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": getTestOrgFromEnv(t), + } + + resourceName := "data.google_access_approval_organization_service_account.aa_account" + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAccessApprovalOrganizationServiceAccount_basic(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "account_email"), + ), + }, + }, + }) +} + +func testAccDataSourceAccessApprovalOrganizationServiceAccount_basic(context map[string]interface{}) string { + return Nprintf(` +data "google_access_approval_organization_service_account" "aa_account" { + organization_id = "%{org_id}" +} +`, context) +} diff --git a/mmv1/third_party/terraform/tests/data_source_access_approval_project_service_account_test.go b/mmv1/third_party/terraform/tests/data_source_access_approval_project_service_account_test.go new file mode 100644 index 000000000000..e0713a910090 --- /dev/null +++ b/mmv1/third_party/terraform/tests/data_source_access_approval_project_service_account_test.go @@ -0,0 +1,38 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceAccessApprovalProjectServiceAccount_basic(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "project_id": getTestProjectFromEnv(), + } + + resourceName := "data.google_access_approval_project_service_account.aa_account" + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAccessApprovalProjectServiceAccount_basic(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "account_email"), + ), + }, + }, + }) +} + +func testAccDataSourceAccessApprovalProjectServiceAccount_basic(context map[string]interface{}) string { + return Nprintf(` +data "google_access_approval_project_service_account" "aa_account" { + project_id = "%{project_id}" +} +`, context) +} diff --git a/mmv1/third_party/terraform/tests/resource_access_approval_folder_settings_test.go b/mmv1/third_party/terraform/tests/resource_access_approval_folder_settings_test.go index bd9cfacf4f37..f5f081c19080 100644 --- a/mmv1/third_party/terraform/tests/resource_access_approval_folder_settings_test.go +++ b/mmv1/third_party/terraform/tests/resource_access_approval_folder_settings_test.go @@ -14,13 +14,18 @@ import ( // See AccessApprovalOrganizationSettings for the test runner. func testAccAccessApprovalFolderSettings(t *testing.T) { context := map[string]interface{}{ + "project": getTestProjectFromEnv(), "org_id": getTestOrgFromEnv(t), + "location": getTestRegionFromEnv(), "random_suffix": randString(t, 10), } vcrTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + ExternalProviders: map[string]resource.ExternalProvider{ + "time": {}, + }, CheckDestroy: testAccCheckAccessApprovalFolderSettingsDestroyProducer(t), Steps: []resource.TestStep{ { @@ -41,6 +46,15 @@ func testAccAccessApprovalFolderSettings(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{"folder_id"}, }, + { + Config: testAccAccessApprovalFolderSettings_activeKeyVersion(context), + }, + { + ResourceName: "google_folder_access_approval_settings.folder_access_approval", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"folder_id"}, + }, }, }) } @@ -52,6 +66,12 @@ resource "google_folder" "my_folder" { parent = "organizations/%{org_id}" } +resource "time_sleep" "wait_120_seconds" { + depends_on = [google_folder.my_folder] + + create_duration = "120s" +} + resource "google_folder_access_approval_settings" "folder_access_approval" { folder_id = google_folder.my_folder.folder_id notification_emails = ["testuser@example.com"] @@ -59,6 +79,8 @@ resource "google_folder_access_approval_settings" "folder_access_approval" { enrolled_services { cloud_product = "all" } + + depends_on = [time_sleep.wait_120_seconds] } `, context) } @@ -70,6 +92,12 @@ resource "google_folder" "my_folder" { parent = "organizations/%{org_id}" } +resource "time_sleep" "wait_120_seconds" { + depends_on = [google_folder.my_folder] + + create_duration = "120s" +} + resource "google_folder_access_approval_settings" "folder_access_approval" { folder_id = google_folder.my_folder.folder_id notification_emails = ["testuser@example.com", "example.user@example.com"] @@ -77,6 +105,67 @@ resource "google_folder_access_approval_settings" "folder_access_approval" { enrolled_services { cloud_product = "all" } + + depends_on = [time_sleep.wait_120_seconds] +} +`, context) +} + +func testAccAccessApprovalFolderSettings_activeKeyVersion(context map[string]interface{}) string { + return Nprintf(` +resource "google_folder" "my_folder" { + display_name = "tf-test-my-folder%{random_suffix}" + parent = "organizations/%{org_id}" +} + +resource "time_sleep" "wait_120_seconds" { + depends_on = [google_folder.my_folder] + + create_duration = "120s" +} + +resource "google_kms_key_ring" "key_ring" { + name = "tf-test-%{random_suffix}" + project = "%{project}" + location = "%{location}" +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "tf-test-%{random_suffix}" + key_ring = google_kms_key_ring.key_ring.id + purpose = "ASYMMETRIC_SIGN" + + version_template { + algorithm = "EC_SIGN_P384_SHA384" + } +} + +data "google_access_approval_folder_service_account" "aa_account" { + folder_id = google_folder.my_folder.folder_id + + depends_on = [time_sleep.wait_120_seconds] +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_folder_service_account.aa_account.account_email}" +} + +data "google_kms_crypto_key_version" "crypto_key_version" { + crypto_key = google_kms_crypto_key.crypto_key.id +} + +resource "google_folder_access_approval_settings" "folder_access_approval" { + folder_id = google_folder.my_folder.folder_id + + enrolled_services { + cloud_product = "all" + } + + active_key_version = data.google_kms_crypto_key_version.crypto_key_version.name + + depends_on = [google_kms_crypto_key_iam_member.iam] } `, context) } diff --git a/mmv1/third_party/terraform/tests/resource_access_approval_organization_settings_test.go b/mmv1/third_party/terraform/tests/resource_access_approval_organization_settings_test.go index aac0111c586b..57b7a90c5748 100644 --- a/mmv1/third_party/terraform/tests/resource_access_approval_organization_settings_test.go +++ b/mmv1/third_party/terraform/tests/resource_access_approval_organization_settings_test.go @@ -32,7 +32,9 @@ func TestAccAccessApprovalSettings(t *testing.T) { func testAccAccessApprovalOrganizationSettings(t *testing.T) { context := map[string]interface{}{ + "project": getTestProjectFromEnv(), "org_id": getTestOrgFromEnv(t), + "location": getTestRegionFromEnv(), "random_suffix": randString(t, 10), } @@ -59,6 +61,15 @@ func testAccAccessApprovalOrganizationSettings(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{"organization_id"}, }, + { + Config: testAccAccessApprovalOrganizationSettings_activeKeyVersion(context), + }, + { + ResourceName: "google_organization_access_approval_settings.organization_access_approval", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"organization_id"}, + }, }, }) } @@ -95,6 +106,52 @@ resource "google_organization_access_approval_settings" "organization_access_app `, context) } +func testAccAccessApprovalOrganizationSettings_activeKeyVersion(context map[string]interface{}) string { + return Nprintf(` +resource "google_kms_key_ring" "key_ring" { + name = "tf-test-%{random_suffix}" + project = "%{project}" + location = "%{location}" +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "tf-test-%{random_suffix}" + key_ring = google_kms_key_ring.key_ring.id + purpose = "ASYMMETRIC_SIGN" + + version_template { + algorithm = "EC_SIGN_P384_SHA384" + } +} + +data "google_access_approval_organization_service_account" "aa_account" { + organization_id = "%{org_id}" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_organization_service_account.aa_account.account_email}" +} + +data "google_kms_crypto_key_version" "crypto_key_version" { + crypto_key = google_kms_crypto_key.crypto_key.id +} + +resource "google_organization_access_approval_settings" "organization_access_approval" { + organization_id = "%{org_id}" + + enrolled_services { + cloud_product = "all" + } + + active_key_version = data.google_kms_crypto_key_version.crypto_key_version.name + + depends_on = [google_kms_crypto_key_iam_member.iam] +} +`, context) +} + func testAccCheckAccessApprovalOrganizationSettingsDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/mmv1/third_party/terraform/tests/resource_access_approval_project_settings_test.go b/mmv1/third_party/terraform/tests/resource_access_approval_project_settings_test.go index f95512ba4f3b..64e5ff51d408 100644 --- a/mmv1/third_party/terraform/tests/resource_access_approval_project_settings_test.go +++ b/mmv1/third_party/terraform/tests/resource_access_approval_project_settings_test.go @@ -16,6 +16,7 @@ func testAccAccessApprovalProjectSettings(t *testing.T) { context := map[string]interface{}{ "project": getTestProjectFromEnv(), "org_id": getTestOrgFromEnv(t), + "location": getTestRegionFromEnv(), "random_suffix": randString(t, 10), } @@ -42,6 +43,15 @@ func testAccAccessApprovalProjectSettings(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{"project_id"}, }, + { + Config: testAccAccessApprovalProjectSettings_activeKeyVersion(context), + }, + { + ResourceName: "google_project_access_approval_settings.project_access_approval", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"project_id"}, + }, }, }) } @@ -73,6 +83,52 @@ resource "google_project_access_approval_settings" "project_access_approval" { `, context) } +func testAccAccessApprovalProjectSettings_activeKeyVersion(context map[string]interface{}) string { + return Nprintf(` +resource "google_kms_key_ring" "key_ring" { + name = "tf-test-%{random_suffix}" + project = "%{project}" + location = "%{location}" +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "tf-test-%{random_suffix}" + key_ring = google_kms_key_ring.key_ring.id + purpose = "ASYMMETRIC_SIGN" + + version_template { + algorithm = "EC_SIGN_P384_SHA384" + } +} + +data "google_access_approval_project_service_account" "aa_account" { + project_id = "%{project}" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_project_service_account.aa_account.account_email}" +} + +data "google_kms_crypto_key_version" "crypto_key_version" { + crypto_key = google_kms_crypto_key.crypto_key.id +} + +resource "google_project_access_approval_settings" "project_access_approval" { + project_id = "%{project}" + + enrolled_services { + cloud_product = "all" + } + + active_key_version = data.google_kms_crypto_key_version.crypto_key_version.name + + depends_on = [google_kms_crypto_key_iam_member.iam] +} +`, context) +} + func testAccCheckAccessApprovalProjectSettingsDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/mmv1/third_party/terraform/utils/provider.go.erb b/mmv1/third_party/terraform/utils/provider.go.erb index 7eb206e2d6ec..641f4d1d1891 100644 --- a/mmv1/third_party/terraform/utils/provider.go.erb +++ b/mmv1/third_party/terraform/utils/provider.go.erb @@ -204,6 +204,9 @@ func Provider() *schema.Provider { }, DataSourcesMap: map[string]*schema.Resource{ + "google_access_approval_folder_service_account": dataSourceAccessApprovalFolderServiceAccount(), + "google_access_approval_organization_service_account": dataSourceAccessApprovalOrganizationServiceAccount(), + "google_access_approval_project_service_account": dataSourceAccessApprovalProjectServiceAccount(), "google_active_folder": dataSourceGoogleActiveFolder(), "google_app_engine_default_service_account": dataSourceGoogleAppEngineDefaultServiceAccount(), "google_billing_account": dataSourceGoogleBillingAccount(), diff --git a/mmv1/third_party/terraform/website/docs/d/access_approval_folder_service_account.html.markdown b/mmv1/third_party/terraform/website/docs/d/access_approval_folder_service_account.html.markdown new file mode 100644 index 000000000000..1394ca46a63b --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/access_approval_folder_service_account.html.markdown @@ -0,0 +1,47 @@ +--- +subcategory: "Access Approval" +layout: "google" +page_title: "Google: google_access_approval_folder_service_account" +sidebar_current: "docs-google-datasource-access-approval-folder-service-account" +description: |- + Get the email address of a folder's Access Approval service account. +--- + +# google\_access\_approval\_folder\_service\_account + +Get the email address of a folder's Access Approval service account. + +Each Google Cloud folder has a unique service account used by Access Approval. +When using Access Approval with a +[custom signing key](https://cloud.google.com/cloud-provider-access-management/access-approval/docs/review-approve-access-requests-custom-keys), +this account needs to be granted the `cloudkms.signerVerifier` IAM role on the +Cloud KMS key used to sign approvals. + +## Example Usage + +```hcl +data "google_access_approval_folder_service_account" "service_account" { + folder_id = "my-folder" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_folder_service_account.service_account.account_email}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `folder_id` - (Required) The folder ID the service account was created for. + +## Attributes Reference + +The following attributes are exported: + +* `name` - The Access Approval service account resource name. Format is "folders/{folder_id}/serviceAccount". + +* `account_email` - The email address of the service account. This value is +often used to refer to the service account in order to grant IAM permissions. diff --git a/mmv1/third_party/terraform/website/docs/d/access_approval_organization_service_account.html.markdown b/mmv1/third_party/terraform/website/docs/d/access_approval_organization_service_account.html.markdown new file mode 100644 index 000000000000..590c0b7d01e6 --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/access_approval_organization_service_account.html.markdown @@ -0,0 +1,47 @@ +--- +subcategory: "Access Approval" +layout: "google" +page_title: "Google: google_access_approval_organization_service_account" +sidebar_current: "docs-google-datasource-access-approval-organization-service-account" +description: |- + Get the email address of an organization's Access Approval service account. +--- + +# google\_access\_approval\_organization\_service\_account + +Get the email address of an organization's Access Approval service account. + +Each Google Cloud organization has a unique service account used by Access Approval. +When using Access Approval with a +[custom signing key](https://cloud.google.com/cloud-provider-access-management/access-approval/docs/review-approve-access-requests-custom-keys), +this account needs to be granted the `cloudkms.signerVerifier` IAM role on the +Cloud KMS key used to sign approvals. + +## Example Usage + +```hcl +data "google_access_approval_organization_service_account" "service_account" { + organization_id = "my-organization" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_organization_service_account.service_account.account_email}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `organization_id` - (Required) The organization ID the service account was created for. + +## Attributes Reference + +The following attributes are exported: + +* `name` - The Access Approval service account resource name. Format is "organizations/{organization_id}/serviceAccount". + +* `account_email` - The email address of the service account. This value is +often used to refer to the service account in order to grant IAM permissions. diff --git a/mmv1/third_party/terraform/website/docs/d/access_approval_project_service_account.html.markdown b/mmv1/third_party/terraform/website/docs/d/access_approval_project_service_account.html.markdown new file mode 100644 index 000000000000..e36798a2c330 --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/access_approval_project_service_account.html.markdown @@ -0,0 +1,47 @@ +--- +subcategory: "Access Approval" +layout: "google" +page_title: "Google: google_access_approval_project_service_account" +sidebar_current: "docs-google-datasource-access-approval-project-service-account" +description: |- + Get the email address of a project's Access Approval service account. +--- + +# google\_access\_approval\_project\_service\_account + +Get the email address of a project's Access Approval service account. + +Each Google Cloud project has a unique service account used by Access Approval. +When using Access Approval with a +[custom signing key](https://cloud.google.com/cloud-provider-access-management/access-approval/docs/review-approve-access-requests-custom-keys), +this account needs to be granted the `cloudkms.signerVerifier` IAM role on the +Cloud KMS key used to sign approvals. + +## Example Usage + +```hcl +data "google_access_approval_project_service_account" "service_account" { + project_id = "my-project" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = google_kms_crypto_key.crypto_key.id + role = "roles/cloudkms.signerVerifier" + member = "serviceAccount:${data.google_access_approval_project_service_account.service_account.account_email}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `project_id` - (Required) The project ID the service account was created for. + +## Attributes Reference + +The following attributes are exported: + +* `name` - The Access Approval service account resource name. Format is "projects/{project_id}/serviceAccount". + +* `account_email` - The email address of the service account. This value is +often used to refer to the service account in order to grant IAM permissions. From e8736debc08b2f2f9fdca2714dcad01da55357bb Mon Sep 17 00:00:00 2001 From: johnethomas Date: Tue, 29 Mar 2022 19:17:25 +0000 Subject: [PATCH 3/4] Add comments explaining why time_sleep is used in tests with folders (to avoid eventually consistency errors caused by creating a folder and then using it) --- .../data_source_access_approval_folder_service_account_test.go | 1 + .../tests/resource_access_approval_folder_settings_test.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/mmv1/third_party/terraform/tests/data_source_access_approval_folder_service_account_test.go b/mmv1/third_party/terraform/tests/data_source_access_approval_folder_service_account_test.go index 96e1bf7ff8d4..0e39b9a71642 100644 --- a/mmv1/third_party/terraform/tests/data_source_access_approval_folder_service_account_test.go +++ b/mmv1/third_party/terraform/tests/data_source_access_approval_folder_service_account_test.go @@ -40,6 +40,7 @@ resource "google_folder" "my_folder" { parent = "organizations/%{org_id}" } +# Wait after folder creation to limit eventual consistency errors. resource "time_sleep" "wait_120_seconds" { depends_on = [google_folder.my_folder] diff --git a/mmv1/third_party/terraform/tests/resource_access_approval_folder_settings_test.go b/mmv1/third_party/terraform/tests/resource_access_approval_folder_settings_test.go index f5f081c19080..f1d11f5fe2c7 100644 --- a/mmv1/third_party/terraform/tests/resource_access_approval_folder_settings_test.go +++ b/mmv1/third_party/terraform/tests/resource_access_approval_folder_settings_test.go @@ -66,6 +66,7 @@ resource "google_folder" "my_folder" { parent = "organizations/%{org_id}" } +# Wait after folder creation to limit eventual consistency errors. resource "time_sleep" "wait_120_seconds" { depends_on = [google_folder.my_folder] @@ -92,6 +93,7 @@ resource "google_folder" "my_folder" { parent = "organizations/%{org_id}" } +# Wait after folder creation to limit eventual consistency errors. resource "time_sleep" "wait_120_seconds" { depends_on = [google_folder.my_folder] @@ -118,6 +120,7 @@ resource "google_folder" "my_folder" { parent = "organizations/%{org_id}" } +# Wait after folder creation to limit eventual consistency errors. resource "time_sleep" "wait_120_seconds" { depends_on = [google_folder.my_folder] From d6fc0766e1643a6d9d8aaf327368bc6773384492 Mon Sep 17 00:00:00 2001 From: johnethomas Date: Tue, 29 Mar 2022 20:22:57 +0000 Subject: [PATCH 4/4] Fix spacing in data source provider definition --- mmv1/third_party/terraform/utils/provider.go.erb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mmv1/third_party/terraform/utils/provider.go.erb b/mmv1/third_party/terraform/utils/provider.go.erb index caf9d28ca267..be3cab97934a 100644 --- a/mmv1/third_party/terraform/utils/provider.go.erb +++ b/mmv1/third_party/terraform/utils/provider.go.erb @@ -208,9 +208,9 @@ func Provider() *schema.Provider { DataSourcesMap: map[string]*schema.Resource{ // ####### START datasources ########### - "google_access_approval_folder_service_account": dataSourceAccessApprovalFolderServiceAccount(), - "google_access_approval_organization_service_account": dataSourceAccessApprovalOrganizationServiceAccount(), - "google_access_approval_project_service_account": dataSourceAccessApprovalProjectServiceAccount(), + "google_access_approval_folder_service_account": dataSourceAccessApprovalFolderServiceAccount(), + "google_access_approval_organization_service_account": dataSourceAccessApprovalOrganizationServiceAccount(), + "google_access_approval_project_service_account": dataSourceAccessApprovalProjectServiceAccount(), "google_active_folder": dataSourceGoogleActiveFolder(), "google_app_engine_default_service_account": dataSourceGoogleAppEngineDefaultServiceAccount(), "google_billing_account": dataSourceGoogleBillingAccount(),