From 75c5c5574ef4f82c2b2ad5468e0cd7633b00e989 Mon Sep 17 00:00:00 2001 From: The Magician Date: Thu, 14 Mar 2024 15:24:57 -0700 Subject: [PATCH] Access Context Manager - make ingress and egress rules immutable (#10147) (#17596) * Access Context Manager - make ingress and egress rules immutable * Move immutable state to top level resource --------- [upstream:7f8a73595635f1ddd917fd6b33ce9a6d1bcf2c97] Signed-off-by: Modular Magician --- .changelog/10147.txt | 3 + ...manager_service_perimeter_egress_policy.go | 145 ++---------------- ...anager_service_perimeter_ingress_policy.go | 144 ++--------------- ...vice_perimeter_egress_policy.html.markdown | 47 +++++- ...ice_perimeter_ingress_policy.html.markdown | 50 +++++- 5 files changed, 125 insertions(+), 264 deletions(-) create mode 100644 .changelog/10147.txt diff --git a/.changelog/10147.txt b/.changelog/10147.txt new file mode 100644 index 00000000000..8f7a8359413 --- /dev/null +++ b/.changelog/10147.txt @@ -0,0 +1,3 @@ +```release-note:bug +accesscontextmanager: fixed an issue with `access_context_manager_service_perimeter_ingress_policy` and `access_context_manager_service_perimeter_egress_policy` where updates could not be applied after initial creation. Any updates applied to these resources will now involve their recreation. To ensure that new policies are added before old ones are removed, add a `lifecycle` block with `create_before_destroy = true` to your resource configuration alongside other updates. +``` \ No newline at end of file diff --git a/google/services/accesscontextmanager/resource_access_context_manager_service_perimeter_egress_policy.go b/google/services/accesscontextmanager/resource_access_context_manager_service_perimeter_egress_policy.go index ac46e8d84fa..43834955770 100644 --- a/google/services/accesscontextmanager/resource_access_context_manager_service_perimeter_egress_policy.go +++ b/google/services/accesscontextmanager/resource_access_context_manager_service_perimeter_egress_policy.go @@ -21,7 +21,6 @@ import ( "fmt" "log" "reflect" - "strings" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -35,7 +34,6 @@ func ResourceAccessContextManagerServicePerimeterEgressPolicy() *schema.Resource return &schema.Resource{ Create: resourceAccessContextManagerServicePerimeterEgressPolicyCreate, Read: resourceAccessContextManagerServicePerimeterEgressPolicyRead, - Update: resourceAccessContextManagerServicePerimeterEgressPolicyUpdate, Delete: resourceAccessContextManagerServicePerimeterEgressPolicyDelete, Importer: &schema.ResourceImporter{ @@ -44,7 +42,6 @@ func ResourceAccessContextManagerServicePerimeterEgressPolicy() *schema.Resource Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(20 * time.Minute), - Update: schema.DefaultTimeout(20 * time.Minute), Delete: schema.DefaultTimeout(20 * time.Minute), }, @@ -59,6 +56,7 @@ func ResourceAccessContextManagerServicePerimeterEgressPolicy() *schema.Resource "egress_from": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `Defines conditions on the source of a request causing this 'EgressPolicy' to apply.`, MaxItems: 1, Elem: &schema.Resource{ @@ -66,6 +64,7 @@ func ResourceAccessContextManagerServicePerimeterEgressPolicy() *schema.Resource "identities": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `A list of identities that are allowed access through this 'EgressPolicy'. Should be in the format of an email address. The email address should represent an individual user, service account, or Google group.`, @@ -76,6 +75,7 @@ represent an individual user, service account, or Google group.`, "identity_type": { Type: schema.TypeString, Optional: true, + ForceNew: true, ValidateFunc: verify.ValidateEnum([]string{"ANY_IDENTITY", "ANY_USER_ACCOUNT", "ANY_SERVICE_ACCOUNT", ""}), Description: `Specifies the type of identities that are allowed access to outside the perimeter. If left unspecified, then members of 'identities' field will @@ -84,18 +84,21 @@ be allowed access. Possible values: ["ANY_IDENTITY", "ANY_USER_ACCOUNT", "ANY_SE "source_restriction": { Type: schema.TypeString, Optional: true, + ForceNew: true, ValidateFunc: verify.ValidateEnum([]string{"SOURCE_RESTRICTION_UNSPECIFIED", "SOURCE_RESTRICTION_ENABLED", "SOURCE_RESTRICTION_DISABLED", ""}), Description: `Whether to enforce traffic restrictions based on 'sources' field. If the 'sources' field is non-empty, then this field must be set to 'SOURCE_RESTRICTION_ENABLED'. Possible values: ["SOURCE_RESTRICTION_UNSPECIFIED", "SOURCE_RESTRICTION_ENABLED", "SOURCE_RESTRICTION_DISABLED"]`, }, "sources": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `Sources that this EgressPolicy authorizes access from.`, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "access_level": { Type: schema.TypeString, Optional: true, + ForceNew: true, Description: `An AccessLevel resource name that allows resources outside the ServicePerimeter to be accessed from the inside.`, }, }, @@ -107,6 +110,7 @@ be allowed access. Possible values: ["ANY_IDENTITY", "ANY_USER_ACCOUNT", "ANY_SE "egress_to": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `Defines the conditions on the 'ApiOperation' and destination resources that cause this 'EgressPolicy' to apply.`, MaxItems: 1, @@ -115,6 +119,7 @@ cause this 'EgressPolicy' to apply.`, "external_resources": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `A list of external resources that are allowed to be accessed. A request matches if it contains an external resource in this list (Example: s3://bucket/path). Currently '*' is not allowed.`, @@ -125,6 +130,7 @@ s3://bucket/path). Currently '*' is not allowed.`, "operations": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `A list of 'ApiOperations' that this egress rule applies to. A request matches if it contains an operation/service in this list.`, Elem: &schema.Resource{ @@ -132,6 +138,7 @@ if it contains an operation/service in this list.`, "method_selectors": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `API methods or permissions to allow. Method or permission must belong to the service specified by 'serviceName' field. A single MethodSelector entry with '*' specified for the 'method' field will allow all methods @@ -141,6 +148,7 @@ AND permissions for the service specified in 'serviceName'.`, "method": { Type: schema.TypeString, Optional: true, + ForceNew: true, Description: `Value for 'method' should be a valid method name for the corresponding 'serviceName' in 'ApiOperation'. If '*' used as value for method, then ALL methods and permissions are allowed.`, @@ -148,6 +156,7 @@ then ALL methods and permissions are allowed.`, "permission": { Type: schema.TypeString, Optional: true, + ForceNew: true, Description: `Value for permission should be a valid Cloud IAM permission for the corresponding 'serviceName' in 'ApiOperation'.`, }, @@ -157,6 +166,7 @@ corresponding 'serviceName' in 'ApiOperation'.`, "service_name": { Type: schema.TypeString, Optional: true, + ForceNew: true, Description: `The name of the API whose methods or permissions the 'IngressPolicy' or 'EgressPolicy' want to allow. A single 'ApiOperation' with serviceName field set to '*' will allow all methods AND permissions for all services.`, @@ -167,6 +177,7 @@ field set to '*' will allow all methods AND permissions for all services.`, "resources": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `A list of resources, currently only projects in the form 'projects/', that match this to stanza. A request matches if it contains a resource in this list. If * is specified for resources, @@ -348,98 +359,6 @@ func resourceAccessContextManagerServicePerimeterEgressPolicyRead(d *schema.Reso return nil } -func resourceAccessContextManagerServicePerimeterEgressPolicyUpdate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*transport_tpg.Config) - userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) - if err != nil { - return err - } - - billingProject := "" - - obj := make(map[string]interface{}) - egressFromProp, err := expandNestedAccessContextManagerServicePerimeterEgressPolicyEgressFrom(d.Get("egress_from"), d, config) - if err != nil { - return err - } else if v, ok := d.GetOkExists("egress_from"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, egressFromProp)) { - obj["egressFrom"] = egressFromProp - } - egressToProp, err := expandNestedAccessContextManagerServicePerimeterEgressPolicyEgressTo(d.Get("egress_to"), d, config) - if err != nil { - return err - } else if v, ok := d.GetOkExists("egress_to"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, egressToProp)) { - obj["egressTo"] = egressToProp - } - - lockName, err := tpgresource.ReplaceVars(d, config, "{{perimeter}}") - if err != nil { - return err - } - transport_tpg.MutexStore.Lock(lockName) - defer transport_tpg.MutexStore.Unlock(lockName) - - url, err := tpgresource.ReplaceVars(d, config, "{{AccessContextManagerBasePath}}{{perimeter}}") - if err != nil { - return err - } - - log.Printf("[DEBUG] Updating ServicePerimeterEgressPolicy %q: %#v", d.Id(), obj) - updateMask := []string{} - - if d.HasChange("egress_from") { - updateMask = append(updateMask, "egressFrom") - } - - if d.HasChange("egress_to") { - updateMask = append(updateMask, "egressTo") - } - // 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 - } - - obj, err = resourceAccessContextManagerServicePerimeterEgressPolicyPatchUpdateEncoder(d, meta, obj) - 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: "PUT", - Project: billingProject, - RawURL: url, - UserAgent: userAgent, - Body: obj, - Timeout: d.Timeout(schema.TimeoutUpdate), - }) - - if err != nil { - return fmt.Errorf("Error updating ServicePerimeterEgressPolicy %q: %s", d.Id(), err) - } else { - log.Printf("[DEBUG] Finished updating ServicePerimeterEgressPolicy %q: %#v", d.Id(), res) - } - - err = AccessContextManagerOperationWaitTime( - config, res, "Updating ServicePerimeterEgressPolicy", userAgent, - d.Timeout(schema.TimeoutUpdate)) - - if err != nil { - return err - } - } - - return resourceAccessContextManagerServicePerimeterEgressPolicyRead(d, meta) -} - func resourceAccessContextManagerServicePerimeterEgressPolicyDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*transport_tpg.Config) userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) @@ -934,42 +853,6 @@ func resourceAccessContextManagerServicePerimeterEgressPolicyPatchCreateEncoder( return res, nil } -// PatchUpdateEncoder handles creating request data to PATCH parent resource -// with list including updated object. -func resourceAccessContextManagerServicePerimeterEgressPolicyPatchUpdateEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { - items, err := resourceAccessContextManagerServicePerimeterEgressPolicyListForPatch(d, meta) - if err != nil { - return nil, err - } - - idx, item, err := resourceAccessContextManagerServicePerimeterEgressPolicyFindNestedObjectInList(d, meta, items) - if err != nil { - return nil, err - } - - // Return error if item to update does not exist. - if item == nil { - return nil, fmt.Errorf("Unable to update ServicePerimeterEgressPolicy %q - not found in list", d.Id()) - } - - // Merge new object into old. - for k, v := range obj { - item[k] = v - } - items[idx] = item - - // Return list with new item added - res := map[string]interface{}{ - "egressPolicies": items, - } - wrapped := map[string]interface{}{ - "status": res, - } - res = wrapped - - return res, nil -} - // PatchDeleteEncoder handles creating request data to PATCH parent resource // with list excluding object to delete. func resourceAccessContextManagerServicePerimeterEgressPolicyPatchDeleteEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { diff --git a/google/services/accesscontextmanager/resource_access_context_manager_service_perimeter_ingress_policy.go b/google/services/accesscontextmanager/resource_access_context_manager_service_perimeter_ingress_policy.go index c7ee8091a65..c337806813e 100644 --- a/google/services/accesscontextmanager/resource_access_context_manager_service_perimeter_ingress_policy.go +++ b/google/services/accesscontextmanager/resource_access_context_manager_service_perimeter_ingress_policy.go @@ -21,7 +21,6 @@ import ( "fmt" "log" "reflect" - "strings" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -35,7 +34,6 @@ func ResourceAccessContextManagerServicePerimeterIngressPolicy() *schema.Resourc return &schema.Resource{ Create: resourceAccessContextManagerServicePerimeterIngressPolicyCreate, Read: resourceAccessContextManagerServicePerimeterIngressPolicyRead, - Update: resourceAccessContextManagerServicePerimeterIngressPolicyUpdate, Delete: resourceAccessContextManagerServicePerimeterIngressPolicyDelete, Importer: &schema.ResourceImporter{ @@ -44,7 +42,6 @@ func ResourceAccessContextManagerServicePerimeterIngressPolicy() *schema.Resourc Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(20 * time.Minute), - Update: schema.DefaultTimeout(20 * time.Minute), Delete: schema.DefaultTimeout(20 * time.Minute), }, @@ -59,6 +56,7 @@ func ResourceAccessContextManagerServicePerimeterIngressPolicy() *schema.Resourc "ingress_from": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `Defines the conditions on the source of a request causing this 'IngressPolicy' to apply.`, MaxItems: 1, @@ -67,6 +65,7 @@ to apply.`, "identities": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `A list of identities that are allowed access through this 'IngressPolicy'. Should be in the format of an email address. The email address should represent an individual user, service account, or Google group.`, @@ -77,6 +76,7 @@ an individual user, service account, or Google group.`, "identity_type": { Type: schema.TypeString, Optional: true, + ForceNew: true, ValidateFunc: verify.ValidateEnum([]string{"ANY_IDENTITY", "ANY_USER_ACCOUNT", "ANY_SERVICE_ACCOUNT", ""}), Description: `Specifies the type of identities that are allowed access from outside the perimeter. If left unspecified, then members of 'identities' field will be @@ -85,12 +85,14 @@ allowed access. Possible values: ["ANY_IDENTITY", "ANY_USER_ACCOUNT", "ANY_SERVI "sources": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `Sources that this 'IngressPolicy' authorizes access from.`, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "access_level": { Type: schema.TypeString, Optional: true, + ForceNew: true, Description: `An 'AccessLevel' resource name that allow resources within the 'ServicePerimeters' to be accessed from the internet. 'AccessLevels' listed must be in the same policy as this 'ServicePerimeter'. Referencing a nonexistent @@ -103,6 +105,7 @@ If * is specified, then all IngressSources will be allowed.`, "resource": { Type: schema.TypeString, Optional: true, + ForceNew: true, Description: `A Google Cloud resource that is allowed to ingress the perimeter. Requests from these resources will be allowed to access perimeter data. Currently only projects are allowed. Format 'projects/{project_number}' @@ -119,6 +122,7 @@ of allowing all Google Cloud resources only is not supported.`, "ingress_to": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `Defines the conditions on the 'ApiOperation' and request destination that cause this 'IngressPolicy' to apply.`, MaxItems: 1, @@ -127,6 +131,7 @@ this 'IngressPolicy' to apply.`, "operations": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `A list of 'ApiOperations' the sources specified in corresponding 'IngressFrom' are allowed to perform in this 'ServicePerimeter'.`, Elem: &schema.Resource{ @@ -134,6 +139,7 @@ are allowed to perform in this 'ServicePerimeter'.`, "method_selectors": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `API methods or permissions to allow. Method or permission must belong to the service specified by serviceName field. A single 'MethodSelector' entry with '*' specified for the method field will allow all methods AND @@ -143,6 +149,7 @@ permissions for the service specified in 'serviceName'.`, "method": { Type: schema.TypeString, Optional: true, + ForceNew: true, Description: `Value for method should be a valid method name for the corresponding serviceName in 'ApiOperation'. If '*' used as value for 'method', then ALL methods and permissions are allowed.`, @@ -150,6 +157,7 @@ ALL methods and permissions are allowed.`, "permission": { Type: schema.TypeString, Optional: true, + ForceNew: true, Description: `Value for permission should be a valid Cloud IAM permission for the corresponding 'serviceName' in 'ApiOperation'.`, }, @@ -159,6 +167,7 @@ corresponding 'serviceName' in 'ApiOperation'.`, "service_name": { Type: schema.TypeString, Optional: true, + ForceNew: true, Description: `The name of the API whose methods or permissions the 'IngressPolicy' or 'EgressPolicy' want to allow. A single 'ApiOperation' with 'serviceName' field set to '*' will allow all methods AND permissions for all services.`, @@ -169,6 +178,7 @@ field set to '*' will allow all methods AND permissions for all services.`, "resources": { Type: schema.TypeList, Optional: true, + ForceNew: true, Description: `A list of resources, currently only projects in the form 'projects/', protected by this 'ServicePerimeter' that are allowed to be accessed by sources defined in the @@ -353,98 +363,6 @@ func resourceAccessContextManagerServicePerimeterIngressPolicyRead(d *schema.Res return nil } -func resourceAccessContextManagerServicePerimeterIngressPolicyUpdate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*transport_tpg.Config) - userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) - if err != nil { - return err - } - - billingProject := "" - - obj := make(map[string]interface{}) - ingressFromProp, err := expandNestedAccessContextManagerServicePerimeterIngressPolicyIngressFrom(d.Get("ingress_from"), d, config) - if err != nil { - return err - } else if v, ok := d.GetOkExists("ingress_from"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, ingressFromProp)) { - obj["ingressFrom"] = ingressFromProp - } - ingressToProp, err := expandNestedAccessContextManagerServicePerimeterIngressPolicyIngressTo(d.Get("ingress_to"), d, config) - if err != nil { - return err - } else if v, ok := d.GetOkExists("ingress_to"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, ingressToProp)) { - obj["ingressTo"] = ingressToProp - } - - lockName, err := tpgresource.ReplaceVars(d, config, "{{perimeter}}") - if err != nil { - return err - } - transport_tpg.MutexStore.Lock(lockName) - defer transport_tpg.MutexStore.Unlock(lockName) - - url, err := tpgresource.ReplaceVars(d, config, "{{AccessContextManagerBasePath}}{{perimeter}}") - if err != nil { - return err - } - - log.Printf("[DEBUG] Updating ServicePerimeterIngressPolicy %q: %#v", d.Id(), obj) - updateMask := []string{} - - if d.HasChange("ingress_from") { - updateMask = append(updateMask, "ingressFrom") - } - - if d.HasChange("ingress_to") { - updateMask = append(updateMask, "ingressTo") - } - // 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 - } - - obj, err = resourceAccessContextManagerServicePerimeterIngressPolicyPatchUpdateEncoder(d, meta, obj) - 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: "PUT", - Project: billingProject, - RawURL: url, - UserAgent: userAgent, - Body: obj, - Timeout: d.Timeout(schema.TimeoutUpdate), - }) - - if err != nil { - return fmt.Errorf("Error updating ServicePerimeterIngressPolicy %q: %s", d.Id(), err) - } else { - log.Printf("[DEBUG] Finished updating ServicePerimeterIngressPolicy %q: %#v", d.Id(), res) - } - - err = AccessContextManagerOperationWaitTime( - config, res, "Updating ServicePerimeterIngressPolicy", userAgent, - d.Timeout(schema.TimeoutUpdate)) - - if err != nil { - return err - } - } - - return resourceAccessContextManagerServicePerimeterIngressPolicyRead(d, meta) -} - func resourceAccessContextManagerServicePerimeterIngressPolicyDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*transport_tpg.Config) userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) @@ -921,42 +839,6 @@ func resourceAccessContextManagerServicePerimeterIngressPolicyPatchCreateEncoder return res, nil } -// PatchUpdateEncoder handles creating request data to PATCH parent resource -// with list including updated object. -func resourceAccessContextManagerServicePerimeterIngressPolicyPatchUpdateEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { - items, err := resourceAccessContextManagerServicePerimeterIngressPolicyListForPatch(d, meta) - if err != nil { - return nil, err - } - - idx, item, err := resourceAccessContextManagerServicePerimeterIngressPolicyFindNestedObjectInList(d, meta, items) - if err != nil { - return nil, err - } - - // Return error if item to update does not exist. - if item == nil { - return nil, fmt.Errorf("Unable to update ServicePerimeterIngressPolicy %q - not found in list", d.Id()) - } - - // Merge new object into old. - for k, v := range obj { - item[k] = v - } - items[idx] = item - - // Return list with new item added - res := map[string]interface{}{ - "ingressPolicies": items, - } - wrapped := map[string]interface{}{ - "status": res, - } - res = wrapped - - return res, nil -} - // PatchDeleteEncoder handles creating request data to PATCH parent resource // with list excluding object to delete. func resourceAccessContextManagerServicePerimeterIngressPolicyPatchDeleteEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { diff --git a/website/docs/r/access_context_manager_service_perimeter_egress_policy.html.markdown b/website/docs/r/access_context_manager_service_perimeter_egress_policy.html.markdown index bbbb1eb0a41..4dd2220dc63 100644 --- a/website/docs/r/access_context_manager_service_perimeter_egress_policy.html.markdown +++ b/website/docs/r/access_context_manager_service_perimeter_egress_policy.html.markdown @@ -27,11 +27,57 @@ within the ServicePerimeter to access a defined set of projects outside the perimeter in certain contexts (e.g. to read data from a Cloud Storage bucket or query against a BigQuery dataset). +~> **Note:** By default, updates to this resource will remove the EgressPolicy from the +from the perimeter and add it back in a non-atomic manner. To ensure that the new EgressPolicy +is added before the old one is removed, add a `lifecycle` block with `create_before_destroy = true` to this resource. + To get more information about ServicePerimeterEgressPolicy, see: * [API documentation](https://cloud.google.com/access-context-manager/docs/reference/rest/v1/accessPolicies.servicePerimeters#egresspolicy) +## Example Usage - Access Context Manager Service Perimeter Egress Policy + + +```hcl +resource "google_access_context_manager_service_perimeter" "storage-perimeter" { + parent = "accesspolicies/${google_access_context_manager_access_policy.access-policy.name}" + name = "accesspolicies/${google_access_context_manager_access_policy.access-policy.name}/serviceperimeters/storage-perimeter" + title = "Storage Perimeter" + status { + restricted_services = ["storage.googleapis.com"] + } + lifecycle { + ignore_changes = [status[0].resources] + } +} + +resource "google_access_context_manager_service_perimeter_egress_policy" "egress_policy" { + perimeter = "${google_access_context_manager_service_perimeter.storage-perimeter.name}" + egress_from { + identity_type = "ANY_IDENTITY" + } + egress_to { + resources = ["*"] + operations { + service_name = "bigquery.googleapis.com" + method_selectors { + method = "*" + } + } + } + lifecycle { + create_before_destroy = true + } +} + + +resource "google_access_context_manager_access_policy" "access-policy" { + parent = "organizations/123456789" + title = "Storage Policy" +} +``` + ## Argument Reference The following arguments are supported: @@ -155,7 +201,6 @@ 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 diff --git a/website/docs/r/access_context_manager_service_perimeter_ingress_policy.html.markdown b/website/docs/r/access_context_manager_service_perimeter_ingress_policy.html.markdown index dcaafbe53c4..d0fe35b9445 100644 --- a/website/docs/r/access_context_manager_service_perimeter_ingress_policy.html.markdown +++ b/website/docs/r/access_context_manager_service_perimeter_ingress_policy.html.markdown @@ -28,11 +28,60 @@ For access from private networks, using the project of the hosting network is re Individual ingress policies can be limited by restricting which services and/ or actions they match using the ingressTo field. +~> **Note:** By default, updates to this resource will remove the IngressPolicy from the +from the perimeter and add it back in a non-atomic manner. To ensure that the new IngressPolicy +is added before the old one is removed, add a `lifecycle` block with `create_before_destroy = true` to this resource. + To get more information about ServicePerimeterIngressPolicy, see: * [API documentation](https://cloud.google.com/access-context-manager/docs/reference/rest/v1/accessPolicies.servicePerimeters#ingresspolicy) +## Example Usage - Access Context Manager Service Perimeter Ingress Policy + + +```hcl +resource "google_access_context_manager_service_perimeter" "storage-perimeter" { + parent = "accesspolicies/${google_access_context_manager_access_policy.access-policy.name}" + name = "accesspolicies/${google_access_context_manager_access_policy.access-policy.name}/serviceperimeters/storage-perimeter" + title = "Storage Perimeter" + status { + restricted_services = ["storage.googleapis.com"] + } + lifecycle { + ignore_changes = [status[0].resources] + } +} + +resource "google_access_context_manager_service_perimeter_ingress_policy" "ingress_policy" { + perimeter = "${google_access_context_manager_service_perimeter.storage-perimeter.name}" + ingress_from { + identity_type = "any_identity" + sources { + access_level = "*" + } + } + ingress_to { + resources = ["*"] + operations { + service_name = "bigquery.googleapis.com" + method_selectors { + method = "*" + } + } + } + lifecycle { + create_before_destroy = true + } +} + + +resource "google_access_context_manager_access_policy" "access-policy" { + parent = "organizations/123456789" + title = "Storage Policy" +} +``` + ## Argument Reference The following arguments are supported: @@ -165,7 +214,6 @@ 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