From dd8578f65d03688415ae6e7fd07f888854243b19 Mon Sep 17 00:00:00 2001 From: The Magician Date: Fri, 31 Jan 2025 16:40:43 -0800 Subject: [PATCH] Beyondcorp application (#12919) (#21199) [upstream:c0cff93bde60f8fc5d5b42a0282ef907ab3e50e3] Signed-off-by: Modular Magician --- .changelog/12919.txt | 3 + google/provider/provider_mmv1_resources.go | 11 +- .../beyondcorp/iam_beyondcorp_application.go | 240 ++++++ ...m_beyondcorp_application_generated_test.go | 691 ++++++++++++++++++ .../resource_beyondcorp_application.go | 535 ++++++++++++++ ...beyondcorp_application_generated_meta.yaml | 17 + ...e_beyondcorp_application_generated_test.go | 113 +++ .../resource_beyondcorp_application_test.go | 87 +++ ...dcorp_application_iam_policy.html.markdown | 50 ++ .../r/beyondcorp_application.html.markdown | 158 ++++ .../beyondcorp_application_iam.html.markdown | 229 ++++++ 11 files changed, 2131 insertions(+), 3 deletions(-) create mode 100644 .changelog/12919.txt create mode 100644 google/services/beyondcorp/iam_beyondcorp_application.go create mode 100644 google/services/beyondcorp/iam_beyondcorp_application_generated_test.go create mode 100644 google/services/beyondcorp/resource_beyondcorp_application.go create mode 100644 google/services/beyondcorp/resource_beyondcorp_application_generated_meta.yaml create mode 100644 google/services/beyondcorp/resource_beyondcorp_application_generated_test.go create mode 100644 google/services/beyondcorp/resource_beyondcorp_application_test.go create mode 100644 website/docs/d/beyondcorp_application_iam_policy.html.markdown create mode 100644 website/docs/r/beyondcorp_application.html.markdown create mode 100644 website/docs/r/beyondcorp_application_iam.html.markdown diff --git a/.changelog/12919.txt b/.changelog/12919.txt new file mode 100644 index 00000000000..7d16c3fa4a8 --- /dev/null +++ b/.changelog/12919.txt @@ -0,0 +1,3 @@ +```release-note:note +Reapplied `google_beyondcorp_application` +``` \ No newline at end of file diff --git a/google/provider/provider_mmv1_resources.go b/google/provider/provider_mmv1_resources.go index 36983e1382b..859c1647983 100644 --- a/google/provider/provider_mmv1_resources.go +++ b/google/provider/provider_mmv1_resources.go @@ -361,6 +361,7 @@ var generatedIAMDatasources = map[string]*schema.Resource{ "google_access_context_manager_access_policy_iam_policy": tpgiamresource.DataSourceIamPolicy(accesscontextmanager.AccessContextManagerAccessPolicyIamSchema, accesscontextmanager.AccessContextManagerAccessPolicyIamUpdaterProducer), "google_apigee_environment_iam_policy": tpgiamresource.DataSourceIamPolicy(apigee.ApigeeEnvironmentIamSchema, apigee.ApigeeEnvironmentIamUpdaterProducer), "google_artifact_registry_repository_iam_policy": tpgiamresource.DataSourceIamPolicy(artifactregistry.ArtifactRegistryRepositoryIamSchema, artifactregistry.ArtifactRegistryRepositoryIamUpdaterProducer), + "google_beyondcorp_application_iam_policy": tpgiamresource.DataSourceIamPolicy(beyondcorp.BeyondcorpApplicationIamSchema, beyondcorp.BeyondcorpApplicationIamUpdaterProducer), "google_beyondcorp_security_gateway_iam_policy": tpgiamresource.DataSourceIamPolicy(beyondcorp.BeyondcorpSecurityGatewayIamSchema, beyondcorp.BeyondcorpSecurityGatewayIamUpdaterProducer), "google_bigquery_table_iam_policy": tpgiamresource.DataSourceIamPolicy(bigquery.BigQueryTableIamSchema, bigquery.BigQueryTableIamUpdaterProducer), "google_bigquery_analytics_hub_data_exchange_iam_policy": tpgiamresource.DataSourceIamPolicy(bigqueryanalyticshub.BigqueryAnalyticsHubDataExchangeIamSchema, bigqueryanalyticshub.BigqueryAnalyticsHubDataExchangeIamUpdaterProducer), @@ -472,9 +473,9 @@ var handwrittenIAMDatasources = map[string]*schema.Resource{ } // Resources -// Generated resources: 515 -// Generated IAM resources: 273 -// Total generated resources: 788 +// Generated resources: 516 +// Generated IAM resources: 276 +// Total generated resources: 792 var generatedResources = map[string]*schema.Resource{ "google_folder_access_approval_settings": accessapproval.ResourceAccessApprovalFolderSettings(), "google_organization_access_approval_settings": accessapproval.ResourceAccessApprovalOrganizationSettings(), @@ -546,6 +547,10 @@ var generatedResources = map[string]*schema.Resource{ "google_beyondcorp_app_connection": beyondcorp.ResourceBeyondcorpAppConnection(), "google_beyondcorp_app_connector": beyondcorp.ResourceBeyondcorpAppConnector(), "google_beyondcorp_app_gateway": beyondcorp.ResourceBeyondcorpAppGateway(), + "google_beyondcorp_application": beyondcorp.ResourceBeyondcorpApplication(), + "google_beyondcorp_application_iam_binding": tpgiamresource.ResourceIamBinding(beyondcorp.BeyondcorpApplicationIamSchema, beyondcorp.BeyondcorpApplicationIamUpdaterProducer, beyondcorp.BeyondcorpApplicationIdParseFunc), + "google_beyondcorp_application_iam_member": tpgiamresource.ResourceIamMember(beyondcorp.BeyondcorpApplicationIamSchema, beyondcorp.BeyondcorpApplicationIamUpdaterProducer, beyondcorp.BeyondcorpApplicationIdParseFunc), + "google_beyondcorp_application_iam_policy": tpgiamresource.ResourceIamPolicy(beyondcorp.BeyondcorpApplicationIamSchema, beyondcorp.BeyondcorpApplicationIamUpdaterProducer, beyondcorp.BeyondcorpApplicationIdParseFunc), "google_beyondcorp_security_gateway": beyondcorp.ResourceBeyondcorpSecurityGateway(), "google_beyondcorp_security_gateway_iam_binding": tpgiamresource.ResourceIamBinding(beyondcorp.BeyondcorpSecurityGatewayIamSchema, beyondcorp.BeyondcorpSecurityGatewayIamUpdaterProducer, beyondcorp.BeyondcorpSecurityGatewayIdParseFunc), "google_beyondcorp_security_gateway_iam_member": tpgiamresource.ResourceIamMember(beyondcorp.BeyondcorpSecurityGatewayIamSchema, beyondcorp.BeyondcorpSecurityGatewayIamUpdaterProducer, beyondcorp.BeyondcorpSecurityGatewayIdParseFunc), diff --git a/google/services/beyondcorp/iam_beyondcorp_application.go b/google/services/beyondcorp/iam_beyondcorp_application.go new file mode 100644 index 00000000000..c7b2c38585a --- /dev/null +++ b/google/services/beyondcorp/iam_beyondcorp_application.go @@ -0,0 +1,240 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package beyondcorp + +import ( + "fmt" + + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "google.golang.org/api/cloudresourcemanager/v1" + + "github.com/hashicorp/terraform-provider-google/google/tpgiamresource" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +var BeyondcorpApplicationIamSchema = map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + "security_gateways_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "application_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName, + }, +} + +type BeyondcorpApplicationIamUpdater struct { + project string + securityGatewaysId string + applicationId string + d tpgresource.TerraformResourceData + Config *transport_tpg.Config +} + +func BeyondcorpApplicationIamUpdaterProducer(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (tpgiamresource.ResourceIamUpdater, error) { + values := make(map[string]string) + + project, _ := tpgresource.GetProject(d, config) + if project != "" { + if err := d.Set("project", project); err != nil { + return nil, fmt.Errorf("Error setting project: %s", err) + } + } + values["project"] = project + if v, ok := d.GetOk("security_gateways_id"); ok { + values["security_gateways_id"] = v.(string) + } + + if v, ok := d.GetOk("application_id"); ok { + values["application_id"] = v.(string) + } + + // We may have gotten either a long or short name, so attempt to parse long name if possible + m, err := tpgresource.GetImportIdQualifiers([]string{"projects/(?P[^/]+)/locations/global/securityGateways/(?P[^/]+)/applications/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Get("application_id").(string)) + if err != nil { + return nil, err + } + + for k, v := range m { + values[k] = v + } + + u := &BeyondcorpApplicationIamUpdater{ + project: values["project"], + securityGatewaysId: values["security_gateways_id"], + applicationId: values["application_id"], + d: d, + Config: config, + } + + if err := d.Set("project", u.project); err != nil { + return nil, fmt.Errorf("Error setting project: %s", err) + } + if err := d.Set("security_gateways_id", u.securityGatewaysId); err != nil { + return nil, fmt.Errorf("Error setting security_gateways_id: %s", err) + } + if err := d.Set("application_id", u.GetResourceId()); err != nil { + return nil, fmt.Errorf("Error setting application_id: %s", err) + } + + return u, nil +} + +func BeyondcorpApplicationIdParseFunc(d *schema.ResourceData, config *transport_tpg.Config) error { + values := make(map[string]string) + + project, _ := tpgresource.GetProject(d, config) + if project != "" { + values["project"] = project + } + + m, err := tpgresource.GetImportIdQualifiers([]string{"projects/(?P[^/]+)/locations/global/securityGateways/(?P[^/]+)/applications/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Id()) + if err != nil { + return err + } + + for k, v := range m { + values[k] = v + } + + u := &BeyondcorpApplicationIamUpdater{ + project: values["project"], + securityGatewaysId: values["security_gateways_id"], + applicationId: values["application_id"], + d: d, + Config: config, + } + if err := d.Set("application_id", u.GetResourceId()); err != nil { + return fmt.Errorf("Error setting application_id: %s", err) + } + d.SetId(u.GetResourceId()) + return nil +} + +func (u *BeyondcorpApplicationIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { + url, err := u.qualifyApplicationUrl("getIamPolicy") + if err != nil { + return nil, err + } + + project, err := tpgresource.GetProject(u.d, u.Config) + if err != nil { + return nil, err + } + var obj map[string]interface{} + url, err = transport_tpg.AddQueryParams(url, map[string]string{"options.requestedPolicyVersion": fmt.Sprintf("%d", tpgiamresource.IamPolicyVersion)}) + if err != nil { + return nil, err + } + + userAgent, err := tpgresource.GenerateUserAgentString(u.d, u.Config.UserAgent) + if err != nil { + return nil, err + } + + policy, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: u.Config, + Method: "GET", + Project: project, + RawURL: url, + UserAgent: userAgent, + Body: obj, + }) + if err != nil { + return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + out := &cloudresourcemanager.Policy{} + err = tpgresource.Convert(policy, out) + if err != nil { + return nil, errwrap.Wrapf("Cannot convert a policy to a resource manager policy: {{err}}", err) + } + + return out, nil +} + +func (u *BeyondcorpApplicationIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error { + json, err := tpgresource.ConvertToMap(policy) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + obj["policy"] = json + + url, err := u.qualifyApplicationUrl("setIamPolicy") + if err != nil { + return err + } + project, err := tpgresource.GetProject(u.d, u.Config) + if err != nil { + return err + } + + userAgent, err := tpgresource.GenerateUserAgentString(u.d, u.Config.UserAgent) + if err != nil { + return err + } + + _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: u.Config, + Method: "POST", + Project: project, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: u.d.Timeout(schema.TimeoutCreate), + }) + if err != nil { + return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + return nil +} + +func (u *BeyondcorpApplicationIamUpdater) qualifyApplicationUrl(methodIdentifier string) (string, error) { + urlTemplate := fmt.Sprintf("{{BeyondcorpBasePath}}%s:%s", fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s", u.project, u.securityGatewaysId, u.applicationId), methodIdentifier) + url, err := tpgresource.ReplaceVars(u.d, u.Config, urlTemplate) + if err != nil { + return "", err + } + return url, nil +} + +func (u *BeyondcorpApplicationIamUpdater) GetResourceId() string { + return fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s", u.project, u.securityGatewaysId, u.applicationId) +} + +func (u *BeyondcorpApplicationIamUpdater) GetMutexKey() string { + return fmt.Sprintf("iam-beyondcorp-application-%s", u.GetResourceId()) +} + +func (u *BeyondcorpApplicationIamUpdater) DescribeResource() string { + return fmt.Sprintf("beyondcorp application %q", u.GetResourceId()) +} diff --git a/google/services/beyondcorp/iam_beyondcorp_application_generated_test.go b/google/services/beyondcorp/iam_beyondcorp_application_generated_test.go new file mode 100644 index 00000000000..9bf0e89357d --- /dev/null +++ b/google/services/beyondcorp/iam_beyondcorp_application_generated_test.go @@ -0,0 +1,691 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package beyondcorp_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" +) + +func TestAccBeyondcorpApplicationIamBindingGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + "role": "roles/beyondcorp.securityGatewayUser", + "condition_title": "expires_after_2019_12_31", + "condition_expr": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + "condition_desc": "Expiring at midnight of 2019-12-31", + "condition_title_no_desc": "expires_after_2019_12_31-no-description", + "condition_expr_no_desc": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccBeyondcorpApplicationIamBinding_basicGenerated(context), + }, + { + ResourceName: "google_beyondcorp_application_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s roles/beyondcorp.securityGatewayUser", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Iam Binding update + Config: testAccBeyondcorpApplicationIamBinding_updateGenerated(context), + }, + { + ResourceName: "google_beyondcorp_application_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s roles/beyondcorp.securityGatewayUser", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBeyondcorpApplicationIamMemberGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + "role": "roles/beyondcorp.securityGatewayUser", + "condition_title": "expires_after_2019_12_31", + "condition_expr": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + "condition_desc": "Expiring at midnight of 2019-12-31", + "condition_title_no_desc": "expires_after_2019_12_31-no-description", + "condition_expr_no_desc": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // Test Iam Member creation (no update for member, no need to test) + Config: testAccBeyondcorpApplicationIamMember_basicGenerated(context), + }, + { + ResourceName: "google_beyondcorp_application_iam_member.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s roles/beyondcorp.securityGatewayUser user:admin@hashicorptest.com", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBeyondcorpApplicationIamPolicyGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + "role": "roles/beyondcorp.securityGatewayUser", + "condition_title": "expires_after_2019_12_31", + "condition_expr": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + "condition_desc": "Expiring at midnight of 2019-12-31", + "condition_title_no_desc": "expires_after_2019_12_31-no-description", + "condition_expr_no_desc": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccBeyondcorpApplicationIamPolicy_basicGenerated(context), + Check: resource.TestCheckResourceAttrSet("data.google_beyondcorp_application_iam_policy.foo", "policy_data"), + }, + { + ResourceName: "google_beyondcorp_application_iam_policy.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccBeyondcorpApplicationIamPolicy_emptyBinding(context), + }, + { + ResourceName: "google_beyondcorp_application_iam_policy.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBeyondcorpApplicationIamBindingGenerated_withCondition(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + "role": "roles/beyondcorp.securityGatewayUser", + "condition_title": "expires_after_2019_12_31", + "condition_expr": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + "condition_desc": "Expiring at midnight of 2019-12-31", + "condition_title_no_desc": "expires_after_2019_12_31-no-description", + "condition_expr_no_desc": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccBeyondcorpApplicationIamBinding_withConditionGenerated(context), + }, + { + ResourceName: "google_beyondcorp_application_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s roles/beyondcorp.securityGatewayUser %s", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"]), context["condition_title"]), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBeyondcorpApplicationIamBindingGenerated_withAndWithoutCondition(t *testing.T) { + // Multiple fine-grained resources + acctest.SkipIfVcr(t) + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + "role": "roles/beyondcorp.securityGatewayUser", + "condition_title": "expires_after_2019_12_31", + "condition_expr": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + "condition_desc": "Expiring at midnight of 2019-12-31", + "condition_title_no_desc": "expires_after_2019_12_31-no-description", + "condition_expr_no_desc": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccBeyondcorpApplicationIamBinding_withAndWithoutConditionGenerated(context), + }, + { + ResourceName: "google_beyondcorp_application_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s roles/beyondcorp.securityGatewayUser", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "google_beyondcorp_application_iam_binding.foo2", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s roles/beyondcorp.securityGatewayUser %s", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"]), context["condition_title"]), + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "google_beyondcorp_application_iam_binding.foo3", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s roles/beyondcorp.securityGatewayUser %s", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"]), context["condition_title_no_desc"]), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBeyondcorpApplicationIamMemberGenerated_withCondition(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + "role": "roles/beyondcorp.securityGatewayUser", + "condition_title": "expires_after_2019_12_31", + "condition_expr": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + "condition_desc": "Expiring at midnight of 2019-12-31", + "condition_title_no_desc": "expires_after_2019_12_31-no-description", + "condition_expr_no_desc": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccBeyondcorpApplicationIamMember_withConditionGenerated(context), + }, + { + ResourceName: "google_beyondcorp_application_iam_member.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s roles/beyondcorp.securityGatewayUser user:admin@hashicorptest.com %s", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"]), context["condition_title"]), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBeyondcorpApplicationIamMemberGenerated_withAndWithoutCondition(t *testing.T) { + // Multiple fine-grained resources + acctest.SkipIfVcr(t) + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + "role": "roles/beyondcorp.securityGatewayUser", + "condition_title": "expires_after_2019_12_31", + "condition_expr": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + "condition_desc": "Expiring at midnight of 2019-12-31", + "condition_title_no_desc": "expires_after_2019_12_31-no-description", + "condition_expr_no_desc": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccBeyondcorpApplicationIamMember_withAndWithoutConditionGenerated(context), + }, + { + ResourceName: "google_beyondcorp_application_iam_member.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s roles/beyondcorp.securityGatewayUser user:admin@hashicorptest.com", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "google_beyondcorp_application_iam_member.foo2", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s roles/beyondcorp.securityGatewayUser user:admin@hashicorptest.com %s", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"]), context["condition_title"]), + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "google_beyondcorp_application_iam_member.foo3", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s roles/beyondcorp.securityGatewayUser user:admin@hashicorptest.com %s", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"]), context["condition_title_no_desc"]), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBeyondcorpApplicationIamPolicyGenerated_withCondition(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + "role": "roles/beyondcorp.securityGatewayUser", + "condition_title": "expires_after_2019_12_31", + "condition_expr": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + "condition_desc": "Expiring at midnight of 2019-12-31", + "condition_title_no_desc": "expires_after_2019_12_31-no-description", + "condition_expr_no_desc": `request.time < timestamp(\"2020-01-01T00:00:00Z\")`, + } + + // Test should have 2 bindings: one with a description and one without. Any < chars are converted to a unicode character by the API. + expectedPolicyData := acctest.Nprintf(`{"bindings":[{"condition":{"description":"%{condition_desc}","expression":"%{condition_expr}","title":"%{condition_title}"},"members":["user:admin@hashicorptest.com"],"role":"%{role}"},{"condition":{"expression":"%{condition_expr}","title":"%{condition_title}-no-description"},"members":["user:admin@hashicorptest.com"],"role":"%{role}"}]}`, context) + expectedPolicyData = strings.Replace(expectedPolicyData, "<", "\\u003c", -1) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccBeyondcorpApplicationIamPolicy_withConditionGenerated(context), + Check: resource.ComposeAggregateTestCheckFunc( + // TODO(SarahFrench) - uncomment once https://github.com/GoogleCloudPlatform/magic-modules/pull/6466 merged + // resource.TestCheckResourceAttr("data.google_iam_policy.foo", "policy_data", expectedPolicyData), + resource.TestCheckResourceAttr("google_beyondcorp_application_iam_policy.foo", "policy_data", expectedPolicyData), + resource.TestCheckResourceAttrWith("data.google_iam_policy.foo", "policy_data", tpgresource.CheckGoogleIamPolicy), + ), + }, + { + ResourceName: "google_beyondcorp_application_iam_policy.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/global/securityGateways/%s/applications/%s", envvar.GetTestProjectFromEnv(), fmt.Sprintf("default%s", context["random_suffix"]), fmt.Sprintf("google%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccBeyondcorpApplicationIamMember_basicGenerated(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} + +resource "google_beyondcorp_application_iam_member" "foo" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "%{role}" + member = "user:admin@hashicorptest.com" +} +`, context) +} + +func testAccBeyondcorpApplicationIamPolicy_basicGenerated(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} + +data "google_iam_policy" "foo" { + binding { + role = "%{role}" + members = ["user:admin@hashicorptest.com"] + } +} + +resource "google_beyondcorp_application_iam_policy" "foo" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + policy_data = data.google_iam_policy.foo.policy_data +} + +data "google_beyondcorp_application_iam_policy" "foo" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + depends_on = [ + google_beyondcorp_application_iam_policy.foo + ] +} +`, context) +} + +func testAccBeyondcorpApplicationIamPolicy_emptyBinding(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} + +data "google_iam_policy" "foo" { +} + +resource "google_beyondcorp_application_iam_policy" "foo" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + policy_data = data.google_iam_policy.foo.policy_data +} +`, context) +} + +func testAccBeyondcorpApplicationIamBinding_basicGenerated(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} + +resource "google_beyondcorp_application_iam_binding" "foo" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "%{role}" + members = ["user:admin@hashicorptest.com"] +} +`, context) +} + +func testAccBeyondcorpApplicationIamBinding_updateGenerated(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} + +resource "google_beyondcorp_application_iam_binding" "foo" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "%{role}" + members = ["user:admin@hashicorptest.com", "user:gterraformtest1@gmail.com"] +} +`, context) +} + +func testAccBeyondcorpApplicationIamBinding_withConditionGenerated(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} + +resource "google_beyondcorp_application_iam_binding" "foo" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "%{role}" + members = ["user:admin@hashicorptest.com"] + condition { + title = "%{condition_title}" + description = "%{condition_desc}" + expression = "%{condition_expr}" + } +} +`, context) +} + +func testAccBeyondcorpApplicationIamBinding_withAndWithoutConditionGenerated(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} + +resource "google_beyondcorp_application_iam_binding" "foo" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "%{role}" + members = ["user:admin@hashicorptest.com"] +} + +resource "google_beyondcorp_application_iam_binding" "foo2" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "%{role}" + members = ["user:admin@hashicorptest.com"] + condition { + title = "%{condition_title}" + description = "%{condition_desc}" + expression = "%{condition_expr}" + } +} + +resource "google_beyondcorp_application_iam_binding" "foo3" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "%{role}" + members = ["user:admin@hashicorptest.com"] + condition { + # Check that lack of description doesn't cause any issues + # Relates to issue : https://github.com/hashicorp/terraform-provider-google/issues/8701 + title = "%{condition_title_no_desc}" + expression = "%{condition_expr_no_desc}" + } +} +`, context) +} + +func testAccBeyondcorpApplicationIamMember_withConditionGenerated(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} + +resource "google_beyondcorp_application_iam_member" "foo" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "%{role}" + member = "user:admin@hashicorptest.com" + condition { + title = "%{condition_title}" + description = "%{condition_desc}" + expression = "%{condition_expr}" + } +} +`, context) +} + +func testAccBeyondcorpApplicationIamMember_withAndWithoutConditionGenerated(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} + +resource "google_beyondcorp_application_iam_member" "foo" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "%{role}" + member = "user:admin@hashicorptest.com" +} + +resource "google_beyondcorp_application_iam_member" "foo2" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "%{role}" + member = "user:admin@hashicorptest.com" + condition { + title = "%{condition_title}" + description = "%{condition_desc}" + expression = "%{condition_expr}" + } +} + +resource "google_beyondcorp_application_iam_member" "foo3" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "%{role}" + member = "user:admin@hashicorptest.com" + condition { + # Check that lack of description doesn't cause any issues + # Relates to issue : https://github.com/hashicorp/terraform-provider-google/issues/8701 + title = "%{condition_title_no_desc}" + expression = "%{condition_expr_no_desc}" + } +} +`, context) +} + +func testAccBeyondcorpApplicationIamPolicy_withConditionGenerated(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} + +data "google_iam_policy" "foo" { + binding { + role = "%{role}" + members = ["user:admin@hashicorptest.com"] + condition { + # Check that lack of description doesn't cause any issues + # Relates to issue : https://github.com/hashicorp/terraform-provider-google/issues/8701 + title = "%{condition_title_no_desc}" + expression = "%{condition_expr_no_desc}" + } + } + binding { + role = "%{role}" + members = ["user:admin@hashicorptest.com"] + condition { + title = "%{condition_title}" + description = "%{condition_desc}" + expression = "%{condition_expr}" + } + } +} + +resource "google_beyondcorp_application_iam_policy" "foo" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + policy_data = data.google_iam_policy.foo.policy_data +} +`, context) +} diff --git a/google/services/beyondcorp/resource_beyondcorp_application.go b/google/services/beyondcorp/resource_beyondcorp_application.go new file mode 100644 index 00000000000..9101f6a9258 --- /dev/null +++ b/google/services/beyondcorp/resource_beyondcorp_application.go @@ -0,0 +1,535 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package beyondcorp + +import ( + "fmt" + "log" + "net/http" + "reflect" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func ResourceBeyondcorpApplication() *schema.Resource { + return &schema.Resource{ + Create: resourceBeyondcorpApplicationCreate, + Read: resourceBeyondcorpApplicationRead, + Update: resourceBeyondcorpApplicationUpdate, + Delete: resourceBeyondcorpApplicationDelete, + + Importer: &schema.ResourceImporter{ + State: resourceBeyondcorpApplicationImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(20 * time.Minute), + Update: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(20 * time.Minute), + }, + + CustomizeDiff: customdiff.All( + tpgresource.DefaultProviderProject, + ), + + Schema: map[string]*schema.Schema{ + "application_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Optional. User-settable Application resource ID. +* Must start with a letter. +* Must contain between 4-63 characters from '/a-z-/'. +* Must end with a number or letter.`, + }, + "endpoint_matchers": { + Type: schema.TypeList, + Required: true, + Description: `Required. Endpoint matchers associated with an application. +A combination of hostname and ports as endpoint matcher is used to match +the application. +Match conditions for OR logic. +An array of match conditions to allow for multiple matching criteria. +The rule is considered a match if one the conditions are met. +The conditions can be one of the following combination +(Hostname), (Hostname & Ports) + +EXAMPLES: +Hostname - ("*.abc.com"), ("xyz.abc.com") +Hostname and Ports - ("abc.com" and "22"), ("abc.com" and "22,33") etc`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "hostname": { + Type: schema.TypeString, + Required: true, + Description: `Required. Hostname of the application.`, + }, + "ports": { + Type: schema.TypeList, + Optional: true, + Description: `Optional. Ports of the application.`, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + }, + }, + }, + "security_gateways_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Part of 'parent'. See documentation of 'projectsId'.`, + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + Description: `Optional. An arbitrary user-provided name for the Application resource. +Cannot exceed 64 characters.`, + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. Timestamp when the resource was created.`, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `Identifier. Name of the resource.`, + }, + "update_time": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. Timestamp when the resource was last modified.`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceBeyondcorpApplicationCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + displayNameProp, err := expandBeyondcorpApplicationDisplayName(d.Get("display_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("display_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(displayNameProp)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { + obj["displayName"] = displayNameProp + } + endpointMatchersProp, err := expandBeyondcorpApplicationEndpointMatchers(d.Get("endpoint_matchers"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("endpoint_matchers"); !tpgresource.IsEmptyValue(reflect.ValueOf(endpointMatchersProp)) && (ok || !reflect.DeepEqual(v, endpointMatchersProp)) { + obj["endpointMatchers"] = endpointMatchersProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{BeyondcorpBasePath}}projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications?applicationId={{application_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new Application: %#v", obj) + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Application: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + headers := make(http.Header) + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutCreate), + Headers: headers, + }) + if err != nil { + return fmt.Errorf("Error creating Application: %s", err) + } + + // Store the ID now + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + // Use the resource in the operation response to populate + // identity fields and d.Id() before read + var opRes map[string]interface{} + err = BeyondcorpOperationWaitTimeWithResponse( + config, res, &opRes, project, "Creating Application", userAgent, + d.Timeout(schema.TimeoutCreate)) + if err != nil { + // The resource didn't actually create + d.SetId("") + + return fmt.Errorf("Error waiting to create Application: %s", err) + } + + if err := d.Set("name", flattenBeyondcorpApplicationName(opRes["name"], d, config)); err != nil { + return err + } + + // This may have caused the ID to update - update it if so. + id, err = tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating Application %q: %#v", d.Id(), res) + + return resourceBeyondcorpApplicationRead(d, meta) +} + +func resourceBeyondcorpApplicationRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{BeyondcorpBasePath}}projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Application: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + headers := make(http.Header) + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Headers: headers, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("BeyondcorpApplication %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading Application: %s", err) + } + + if err := d.Set("create_time", flattenBeyondcorpApplicationCreateTime(res["createTime"], d, config)); err != nil { + return fmt.Errorf("Error reading Application: %s", err) + } + if err := d.Set("display_name", flattenBeyondcorpApplicationDisplayName(res["displayName"], d, config)); err != nil { + return fmt.Errorf("Error reading Application: %s", err) + } + if err := d.Set("endpoint_matchers", flattenBeyondcorpApplicationEndpointMatchers(res["endpointMatchers"], d, config)); err != nil { + return fmt.Errorf("Error reading Application: %s", err) + } + if err := d.Set("name", flattenBeyondcorpApplicationName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading Application: %s", err) + } + if err := d.Set("update_time", flattenBeyondcorpApplicationUpdateTime(res["updateTime"], d, config)); err != nil { + return fmt.Errorf("Error reading Application: %s", err) + } + + return nil +} + +func resourceBeyondcorpApplicationUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Application: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + displayNameProp, err := expandBeyondcorpApplicationDisplayName(d.Get("display_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("display_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { + obj["displayName"] = displayNameProp + } + endpointMatchersProp, err := expandBeyondcorpApplicationEndpointMatchers(d.Get("endpoint_matchers"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("endpoint_matchers"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, endpointMatchersProp)) { + obj["endpointMatchers"] = endpointMatchersProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{BeyondcorpBasePath}}projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating Application %q: %#v", d.Id(), obj) + headers := make(http.Header) + updateMask := []string{} + + if d.HasChange("display_name") { + updateMask = append(updateMask, "displayName") + } + + if d.HasChange("endpoint_matchers") { + updateMask = append(updateMask, "endpointMatchers") + } + // updateMask is a URL parameter but not present in the schema, so ReplaceVars + // won't set it + url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + // if updateMask is empty we are not updating anything so skip the post + if len(updateMask) > 0 { + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutUpdate), + Headers: headers, + }) + + if err != nil { + return fmt.Errorf("Error updating Application %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating Application %q: %#v", d.Id(), res) + } + + err = BeyondcorpOperationWaitTime( + config, res, project, "Updating Application", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + } + + return resourceBeyondcorpApplicationRead(d, meta) +} + +func resourceBeyondcorpApplicationDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Application: %s", err) + } + billingProject = project + + url, err := tpgresource.ReplaceVars(d, config, "{{BeyondcorpBasePath}}projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}}") + if err != nil { + return err + } + + var obj map[string]interface{} + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + headers := make(http.Header) + + log.Printf("[DEBUG] Deleting Application %q", d.Id()) + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutDelete), + Headers: headers, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, "Application") + } + + err = BeyondcorpOperationWaitTime( + config, res, project, "Deleting Application", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting Application %q: %#v", d.Id(), res) + return nil +} + +func resourceBeyondcorpApplicationImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*transport_tpg.Config) + if err := tpgresource.ParseImportId([]string{ + "^projects/(?P[^/]+)/locations/global/securityGateways/(?P[^/]+)/applications/(?P[^/]+)$", + "^(?P[^/]+)/(?P[^/]+)/(?P[^/]+)$", + "^(?P[^/]+)/(?P[^/]+)$", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenBeyondcorpApplicationCreateTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenBeyondcorpApplicationDisplayName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenBeyondcorpApplicationEndpointMatchers(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "hostname": flattenBeyondcorpApplicationEndpointMatchersHostname(original["hostname"], d, config), + "ports": flattenBeyondcorpApplicationEndpointMatchersPorts(original["ports"], d, config), + }) + } + return transformed +} +func flattenBeyondcorpApplicationEndpointMatchersHostname(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenBeyondcorpApplicationEndpointMatchersPorts(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenBeyondcorpApplicationName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenBeyondcorpApplicationUpdateTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func expandBeyondcorpApplicationDisplayName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandBeyondcorpApplicationEndpointMatchers(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHostname, err := expandBeyondcorpApplicationEndpointMatchersHostname(original["hostname"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHostname); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["hostname"] = transformedHostname + } + + transformedPorts, err := expandBeyondcorpApplicationEndpointMatchersPorts(original["ports"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPorts); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["ports"] = transformedPorts + } + + req = append(req, transformed) + } + return req, nil +} + +func expandBeyondcorpApplicationEndpointMatchersHostname(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandBeyondcorpApplicationEndpointMatchersPorts(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} diff --git a/google/services/beyondcorp/resource_beyondcorp_application_generated_meta.yaml b/google/services/beyondcorp/resource_beyondcorp_application_generated_meta.yaml new file mode 100644 index 00000000000..f2e4cf694ff --- /dev/null +++ b/google/services/beyondcorp/resource_beyondcorp_application_generated_meta.yaml @@ -0,0 +1,17 @@ +resource: 'google_beyondcorp_application' +generation_type: 'mmv1' +api_service_name: 'beyondcorp.googleapis.com' +api_version: 'v1' +api_resource_type_kind: 'Application' +autogen_status: true +fields: + - field: 'application_id' + provider_only: true + - field: 'create_time' + - field: 'display_name' + - field: 'endpoint_matchers.hostname' + - field: 'endpoint_matchers.ports' + - field: 'name' + - field: 'security_gateways_id' + provider_only: true + - field: 'update_time' diff --git a/google/services/beyondcorp/resource_beyondcorp_application_generated_test.go b/google/services/beyondcorp/resource_beyondcorp_application_generated_test.go new file mode 100644 index 00000000000..638c3149564 --- /dev/null +++ b/google/services/beyondcorp/resource_beyondcorp_application_generated_test.go @@ -0,0 +1,113 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package beyondcorp_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func TestAccBeyondcorpApplication_beyondcorpSecurityGatewayApplicationBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckBeyondcorpApplicationDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccBeyondcorpApplication_beyondcorpSecurityGatewayApplicationBasicExample(context), + }, + { + ResourceName: "google_beyondcorp_application.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"application_id", "security_gateways_id"}, + }, + }, + }) +} + +func testAccBeyondcorpApplication_beyondcorpSecurityGatewayApplicationBasicExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} +`, context) +} + +func testAccCheckBeyondcorpApplicationDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_beyondcorp_application" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := acctest.GoogleProviderConfig(t) + + url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{BeyondcorpBasePath}}projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: config.UserAgent, + }) + if err == nil { + return fmt.Errorf("BeyondcorpApplication still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/services/beyondcorp/resource_beyondcorp_application_test.go b/google/services/beyondcorp/resource_beyondcorp_application_test.go new file mode 100644 index 00000000000..fd94e7c5358 --- /dev/null +++ b/google/services/beyondcorp/resource_beyondcorp_application_test.go @@ -0,0 +1,87 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package beyondcorp_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccBeyondcorpApplication_beyondcorpSecurityGatewayApplicationBasicExample_update(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccBeyondcorpApplication_beyondcorpSecurityGatewayApplicationBasicExample_basic(context), + }, + { + ResourceName: "google_beyondcorp_application.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"application_id", "security_gateways_id"}, + }, + { + Config: testAccBeyondcorpApplication_beyondcorpSecurityGatewayApplicationBasicExample_update(context), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("google_beyondcorp_application.example", plancheck.ResourceActionUpdate), + }, + }, + }, + { + ResourceName: "google_beyondcorp_application.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"application_id", "security_gateways_id"}, + }, + }, + }) +} + +func testAccBeyondcorpApplication_beyondcorpSecurityGatewayApplicationBasicExample_basic(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} +`, context) +} + +func testAccBeyondcorpApplication_beyondcorpSecurityGatewayApplicationBasicExample_update(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default%{random_suffix}" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + display_name = "Updated Name" + application_id = "google%{random_suffix}" + endpoint_matchers { + hostname = "google.com" + } +} +`, context) +} diff --git a/website/docs/d/beyondcorp_application_iam_policy.html.markdown b/website/docs/d/beyondcorp_application_iam_policy.html.markdown new file mode 100644 index 00000000000..c9a4fd8dfb3 --- /dev/null +++ b/website/docs/d/beyondcorp_application_iam_policy.html.markdown @@ -0,0 +1,50 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** Type: MMv1 *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +subcategory: "BeyondCorp" +description: |- + A datasource to retrieve the IAM policy state for BeyondCorp Application +--- + + +# `google_beyondcorp_application_iam_policy` +Retrieves the current IAM policy data for application + + +## example + +```hcl +data "google_beyondcorp_application_iam_policy" "policy" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the project will be parsed from the identifier of the parent resource. If no project is provided in the parent identifier and no project is specified, the provider project is used. + +## Attributes Reference + +The attributes are exported: + +* `etag` - (Computed) The etag of the IAM policy. + +* `policy_data` - (Required only by `google_beyondcorp_application_iam_policy`) The policy data generated by + a `google_iam_policy` data source. diff --git a/website/docs/r/beyondcorp_application.html.markdown b/website/docs/r/beyondcorp_application.html.markdown new file mode 100644 index 00000000000..23c1754207a --- /dev/null +++ b/website/docs/r/beyondcorp_application.html.markdown @@ -0,0 +1,158 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** Type: MMv1 *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +subcategory: "BeyondCorp" +description: |- + Specifies application endpoint(s) to protect behind a Security Gateway. +--- + +# google_beyondcorp_application + +Specifies application endpoint(s) to protect behind a Security Gateway. + + + + +## Example Usage - Beyondcorp Security Gateway Application Basic + + +```hcl +resource "google_beyondcorp_security_gateway" "default" { + security_gateway_id = "default" + display_name = "My Security Gateway resource" + hubs { region = "us-central1" } +} + +resource "google_beyondcorp_application" "example" { + security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id + application_id = "google" + endpoint_matchers { + hostname = "google.com" + } +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `endpoint_matchers` - + (Required) + Required. Endpoint matchers associated with an application. + A combination of hostname and ports as endpoint matcher is used to match + the application. + Match conditions for OR logic. + An array of match conditions to allow for multiple matching criteria. + The rule is considered a match if one the conditions are met. + The conditions can be one of the following combination + (Hostname), (Hostname & Ports) + EXAMPLES: + Hostname - ("*.abc.com"), ("xyz.abc.com") + Hostname and Ports - ("abc.com" and "22"), ("abc.com" and "22,33") etc + Structure is [documented below](#nested_endpoint_matchers). + +* `security_gateways_id` - + (Required) + Part of `parent`. See documentation of `projectsId`. + +* `application_id` - + (Required) + Optional. User-settable Application resource ID. + * Must start with a letter. + * Must contain between 4-63 characters from `/a-z-/`. + * Must end with a number or letter. + + +The `endpoint_matchers` block supports: + +* `hostname` - + (Required) + Required. Hostname of the application. + +* `ports` - + (Optional) + Optional. Ports of the application. + +- - - + + +* `display_name` - + (Optional) + Optional. An arbitrary user-provided name for the Application resource. + Cannot exceed 64 characters. + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}}` + +* `create_time` - + Output only. Timestamp when the resource was created. + +* `name` - + Identifier. Name of the resource. + +* `update_time` - + Output only. Timestamp when the resource was last modified. + + +## Timeouts + +This resource provides the following +[Timeouts](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/retries-and-customizable-timeouts) configuration options: + +- `create` - Default is 20 minutes. +- `update` - Default is 20 minutes. +- `delete` - Default is 20 minutes. + +## Import + + +Application can be imported using any of these accepted formats: + +* `projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}}` +* `{{project}}/{{security_gateways_id}}/{{application_id}}` +* `{{security_gateways_id}}/{{application_id}}` + + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import Application using one of the formats above. For example: + +```tf +import { + id = "projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}}" + to = google_beyondcorp_application.default +} +``` + +When using the [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import), Application can be imported using one of the formats above. For example: + +``` +$ terraform import google_beyondcorp_application.default projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}} +$ terraform import google_beyondcorp_application.default {{project}}/{{security_gateways_id}}/{{application_id}} +$ terraform import google_beyondcorp_application.default {{security_gateways_id}}/{{application_id}} +``` + +## User Project Overrides + +This resource supports [User Project Overrides](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference#user_project_override). diff --git a/website/docs/r/beyondcorp_application_iam.html.markdown b/website/docs/r/beyondcorp_application_iam.html.markdown new file mode 100644 index 00000000000..6e1ae630ad0 --- /dev/null +++ b/website/docs/r/beyondcorp_application_iam.html.markdown @@ -0,0 +1,229 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** Type: MMv1 *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +subcategory: "BeyondCorp" +description: |- + Collection of resources to manage IAM policy for BeyondCorp Application +--- + +# IAM policy for BeyondCorp Application +Three different resources help you manage your IAM policy for BeyondCorp Application. Each of these resources serves a different use case: + +* `google_beyondcorp_application_iam_policy`: Authoritative. Sets the IAM policy for the application and replaces any existing policy already attached. +* `google_beyondcorp_application_iam_binding`: Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the application are preserved. +* `google_beyondcorp_application_iam_member`: Non-authoritative. Updates the IAM policy to grant a role to a new member. Other members for the role for the application are preserved. + +A data source can be used to retrieve policy data in advent you do not need creation + +* `google_beyondcorp_application_iam_policy`: Retrieves the IAM policy for the application + +~> **Note:** `google_beyondcorp_application_iam_policy` **cannot** be used in conjunction with `google_beyondcorp_application_iam_binding` and `google_beyondcorp_application_iam_member` or they will fight over what your policy should be. + +~> **Note:** `google_beyondcorp_application_iam_binding` resources **can be** used in conjunction with `google_beyondcorp_application_iam_member` resources **only if** they do not grant privilege to the same role. + +~> **Note:** This resource supports IAM Conditions but they have some known limitations which can be found [here](https://cloud.google.com/iam/docs/conditions-overview#limitations). Please review this article if you are having issues with IAM Conditions. + + +## google_beyondcorp_application_iam_policy + +```hcl +data "google_iam_policy" "admin" { + binding { + role = "roles/beyondcorp.securityGatewayUser" + members = [ + "user:jane@example.com", + ] + } +} + +resource "google_beyondcorp_application_iam_policy" "policy" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + policy_data = data.google_iam_policy.admin.policy_data +} +``` + +With IAM Conditions: + +```hcl +data "google_iam_policy" "admin" { + binding { + role = "roles/beyondcorp.securityGatewayUser" + members = [ + "user:jane@example.com", + ] + + condition { + title = "expires_after_2019_12_31" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } + } +} + +resource "google_beyondcorp_application_iam_policy" "policy" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + policy_data = data.google_iam_policy.admin.policy_data +} +``` +## google_beyondcorp_application_iam_binding + +```hcl +resource "google_beyondcorp_application_iam_binding" "binding" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "roles/beyondcorp.securityGatewayUser" + members = [ + "user:jane@example.com", + ] +} +``` + +With IAM Conditions: + +```hcl +resource "google_beyondcorp_application_iam_binding" "binding" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "roles/beyondcorp.securityGatewayUser" + members = [ + "user:jane@example.com", + ] + + condition { + title = "expires_after_2019_12_31" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } +} +``` +## google_beyondcorp_application_iam_member + +```hcl +resource "google_beyondcorp_application_iam_member" "member" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "roles/beyondcorp.securityGatewayUser" + member = "user:jane@example.com" +} +``` + +With IAM Conditions: + +```hcl +resource "google_beyondcorp_application_iam_member" "member" { + project = google_beyondcorp_application.example.project + security_gateways_id = google_beyondcorp_application.example.security_gateways_id + application_id = google_beyondcorp_application.example.application_id + role = "roles/beyondcorp.securityGatewayUser" + member = "user:jane@example.com" + + condition { + title = "expires_after_2019_12_31" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the project will be parsed from the identifier of the parent resource. If no project is provided in the parent identifier and no project is specified, the provider project is used. + +* `member/members` - (Required) Identities that will be granted the privilege in `role`. + Each entry can have one of the following values: + * **allUsers**: A special identifier that represents anyone who is on the internet; with or without a Google account. + * **allAuthenticatedUsers**: A special identifier that represents anyone who is authenticated with a Google account or a service account. + * **user:{emailid}**: An email address that represents a specific Google account. For example, alice@gmail.com or joe@example.com. + * **serviceAccount:{emailid}**: An email address that represents a service account. For example, my-other-app@appspot.gserviceaccount.com. + * **group:{emailid}**: An email address that represents a Google group. For example, admins@example.com. + * **domain:{domain}**: A G Suite domain (primary, instead of alias) name that represents all the users of that domain. For example, google.com or example.com. + * **projectOwner:projectid**: Owners of the given project. For example, "projectOwner:my-example-project" + * **projectEditor:projectid**: Editors of the given project. For example, "projectEditor:my-example-project" + * **projectViewer:projectid**: Viewers of the given project. For example, "projectViewer:my-example-project" + +* `role` - (Required) The role that should be applied. Only one + `google_beyondcorp_application_iam_binding` can be used per role. Note that custom roles must be of the format + `[projects|organizations]/{parent-name}/roles/{role-name}`. + +* `policy_data` - (Required only by `google_beyondcorp_application_iam_policy`) The policy data generated by + a `google_iam_policy` data source. + +* `condition` - (Optional) An [IAM Condition](https://cloud.google.com/iam/docs/conditions-overview) for a given binding. + Structure is documented below. + +--- + +The `condition` block supports: + +* `expression` - (Required) Textual representation of an expression in Common Expression Language syntax. + +* `title` - (Required) A title for the expression, i.e. a short string describing its purpose. + +* `description` - (Optional) An optional description of the expression. This is a longer text which describes the expression, e.g. when hovered over it in a UI. + +~> **Warning:** Terraform considers the `role` and condition contents (`title`+`description`+`expression`) as the + identifier for the binding. This means that if any part of the condition is changed out-of-band, Terraform will + consider it to be an entirely different resource and will treat it as such. +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are +exported: + +* `etag` - (Computed) The etag of the IAM policy. + +## Import + +For all import syntaxes, the "resource in question" can take any of the following forms: + +* projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}} +* {{project}}/{{security_gateways_id}}/{{application_id}} +* {{security_gateways_id}}/{{application_id}} +* {{application_id}} + +Any variables not passed in the import command will be taken from the provider configuration. + +BeyondCorp application IAM resources can be imported using the resource identifiers, role, and member. + +IAM member imports use space-delimited identifiers: the resource in question, the role, and the member identity, e.g. +``` +$ terraform import google_beyondcorp_application_iam_member.editor "projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}} roles/beyondcorp.securityGatewayUser user:jane@example.com" +``` + +IAM binding imports use space-delimited identifiers: the resource in question and the role, e.g. +``` +$ terraform import google_beyondcorp_application_iam_binding.editor "projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}} roles/beyondcorp.securityGatewayUser" +``` + +IAM policy imports use the identifier of the resource in question, e.g. +``` +$ terraform import google_beyondcorp_application_iam_policy.editor projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}} +``` + +-> **Custom Roles** If you're importing a IAM resource with a custom role, make sure to use the + full name of the custom role, e.g. `[projects/my-project|organizations/my-org]/roles/my-custom-role`. + +## User Project Overrides + +This resource supports [User Project Overrides](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference#user_project_override).