Configure GCP Access Context Manager Service Perimeters

The gcp:accesscontextmanager/servicePerimeter:ServicePerimeter resource, part of the Pulumi GCP provider, defines VPC Service Controls perimeters that restrict which GCP services projects can access and control data movement between perimeters. This guide focuses on three capabilities: service restriction configuration, ingress and egress traffic policies, and dry-run testing for policy changes.

Service perimeters belong to Access Context Manager access policies and reference access levels for identity-based controls. The examples are intentionally small. Combine them with your own access policies, access levels, and project membership configuration.

Restrict service access with a basic perimeter

Organizations implementing zero-trust security start by defining perimeters that control which GCP services projects can access.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const access_policy = new gcp.accesscontextmanager.AccessPolicy("access-policy", {
    parent: "organizations/123456789",
    title: "my policy",
});
const service_perimeter = new gcp.accesscontextmanager.ServicePerimeter("service-perimeter", {
    parent: pulumi.interpolate`accessPolicies/${access_policy.name}`,
    name: pulumi.interpolate`accessPolicies/${access_policy.name}/servicePerimeters/restrict_storage`,
    title: "restrict_storage",
    status: {
        restrictedServices: ["storage.googleapis.com"],
    },
});
const access_level = new gcp.accesscontextmanager.AccessLevel("access-level", {
    parent: pulumi.interpolate`accessPolicies/${access_policy.name}`,
    name: pulumi.interpolate`accessPolicies/${access_policy.name}/accessLevels/chromeos_no_lock`,
    title: "chromeos_no_lock",
    basic: {
        conditions: [{
            devicePolicy: {
                requireScreenLock: false,
                osConstraints: [{
                    osType: "DESKTOP_CHROME_OS",
                }],
            },
            regions: [
                "CH",
                "IT",
                "US",
            ],
        }],
    },
});
import pulumi
import pulumi_gcp as gcp

access_policy = gcp.accesscontextmanager.AccessPolicy("access-policy",
    parent="organizations/123456789",
    title="my policy")
service_perimeter = gcp.accesscontextmanager.ServicePerimeter("service-perimeter",
    parent=access_policy.name.apply(lambda name: f"accessPolicies/{name}"),
    name=access_policy.name.apply(lambda name: f"accessPolicies/{name}/servicePerimeters/restrict_storage"),
    title="restrict_storage",
    status={
        "restricted_services": ["storage.googleapis.com"],
    })
access_level = gcp.accesscontextmanager.AccessLevel("access-level",
    parent=access_policy.name.apply(lambda name: f"accessPolicies/{name}"),
    name=access_policy.name.apply(lambda name: f"accessPolicies/{name}/accessLevels/chromeos_no_lock"),
    title="chromeos_no_lock",
    basic={
        "conditions": [{
            "device_policy": {
                "require_screen_lock": False,
                "os_constraints": [{
                    "os_type": "DESKTOP_CHROME_OS",
                }],
            },
            "regions": [
                "CH",
                "IT",
                "US",
            ],
        }],
    })
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/accesscontextmanager"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		access_policy, err := accesscontextmanager.NewAccessPolicy(ctx, "access-policy", &accesscontextmanager.AccessPolicyArgs{
			Parent: pulumi.String("organizations/123456789"),
			Title:  pulumi.String("my policy"),
		})
		if err != nil {
			return err
		}
		_, err = accesscontextmanager.NewServicePerimeter(ctx, "service-perimeter", &accesscontextmanager.ServicePerimeterArgs{
			Parent: access_policy.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("accessPolicies/%v", name), nil
			}).(pulumi.StringOutput),
			Name: access_policy.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("accessPolicies/%v/servicePerimeters/restrict_storage", name), nil
			}).(pulumi.StringOutput),
			Title: pulumi.String("restrict_storage"),
			Status: &accesscontextmanager.ServicePerimeterStatusArgs{
				RestrictedServices: pulumi.StringArray{
					pulumi.String("storage.googleapis.com"),
				},
			},
		})
		if err != nil {
			return err
		}
		_, err = accesscontextmanager.NewAccessLevel(ctx, "access-level", &accesscontextmanager.AccessLevelArgs{
			Parent: access_policy.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("accessPolicies/%v", name), nil
			}).(pulumi.StringOutput),
			Name: access_policy.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("accessPolicies/%v/accessLevels/chromeos_no_lock", name), nil
			}).(pulumi.StringOutput),
			Title: pulumi.String("chromeos_no_lock"),
			Basic: &accesscontextmanager.AccessLevelBasicArgs{
				Conditions: accesscontextmanager.AccessLevelBasicConditionArray{
					&accesscontextmanager.AccessLevelBasicConditionArgs{
						DevicePolicy: &accesscontextmanager.AccessLevelBasicConditionDevicePolicyArgs{
							RequireScreenLock: pulumi.Bool(false),
							OsConstraints: accesscontextmanager.AccessLevelBasicConditionDevicePolicyOsConstraintArray{
								&accesscontextmanager.AccessLevelBasicConditionDevicePolicyOsConstraintArgs{
									OsType: pulumi.String("DESKTOP_CHROME_OS"),
								},
							},
						},
						Regions: pulumi.StringArray{
							pulumi.String("CH"),
							pulumi.String("IT"),
							pulumi.String("US"),
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var access_policy = new Gcp.AccessContextManager.AccessPolicy("access-policy", new()
    {
        Parent = "organizations/123456789",
        Title = "my policy",
    });

    var service_perimeter = new Gcp.AccessContextManager.ServicePerimeter("service-perimeter", new()
    {
        Parent = access_policy.Name.Apply(name => $"accessPolicies/{name}"),
        Name = access_policy.Name.Apply(name => $"accessPolicies/{name}/servicePerimeters/restrict_storage"),
        Title = "restrict_storage",
        Status = new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusArgs
        {
            RestrictedServices = new[]
            {
                "storage.googleapis.com",
            },
        },
    });

    var access_level = new Gcp.AccessContextManager.AccessLevel("access-level", new()
    {
        Parent = access_policy.Name.Apply(name => $"accessPolicies/{name}"),
        Name = access_policy.Name.Apply(name => $"accessPolicies/{name}/accessLevels/chromeos_no_lock"),
        Title = "chromeos_no_lock",
        Basic = new Gcp.AccessContextManager.Inputs.AccessLevelBasicArgs
        {
            Conditions = new[]
            {
                new Gcp.AccessContextManager.Inputs.AccessLevelBasicConditionArgs
                {
                    DevicePolicy = new Gcp.AccessContextManager.Inputs.AccessLevelBasicConditionDevicePolicyArgs
                    {
                        RequireScreenLock = false,
                        OsConstraints = new[]
                        {
                            new Gcp.AccessContextManager.Inputs.AccessLevelBasicConditionDevicePolicyOsConstraintArgs
                            {
                                OsType = "DESKTOP_CHROME_OS",
                            },
                        },
                    },
                    Regions = new[]
                    {
                        "CH",
                        "IT",
                        "US",
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.accesscontextmanager.AccessPolicy;
import com.pulumi.gcp.accesscontextmanager.AccessPolicyArgs;
import com.pulumi.gcp.accesscontextmanager.ServicePerimeter;
import com.pulumi.gcp.accesscontextmanager.ServicePerimeterArgs;
import com.pulumi.gcp.accesscontextmanager.inputs.ServicePerimeterStatusArgs;
import com.pulumi.gcp.accesscontextmanager.AccessLevel;
import com.pulumi.gcp.accesscontextmanager.AccessLevelArgs;
import com.pulumi.gcp.accesscontextmanager.inputs.AccessLevelBasicArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var access_policy = new AccessPolicy("access-policy", AccessPolicyArgs.builder()
            .parent("organizations/123456789")
            .title("my policy")
            .build());

        var service_perimeter = new ServicePerimeter("service-perimeter", ServicePerimeterArgs.builder()
            .parent(access_policy.name().applyValue(_name -> String.format("accessPolicies/%s", _name)))
            .name(access_policy.name().applyValue(_name -> String.format("accessPolicies/%s/servicePerimeters/restrict_storage", _name)))
            .title("restrict_storage")
            .status(ServicePerimeterStatusArgs.builder()
                .restrictedServices("storage.googleapis.com")
                .build())
            .build());

        var access_level = new AccessLevel("access-level", AccessLevelArgs.builder()
            .parent(access_policy.name().applyValue(_name -> String.format("accessPolicies/%s", _name)))
            .name(access_policy.name().applyValue(_name -> String.format("accessPolicies/%s/accessLevels/chromeos_no_lock", _name)))
            .title("chromeos_no_lock")
            .basic(AccessLevelBasicArgs.builder()
                .conditions(AccessLevelBasicConditionArgs.builder()
                    .devicePolicy(AccessLevelBasicConditionDevicePolicyArgs.builder()
                        .requireScreenLock(false)
                        .osConstraints(AccessLevelBasicConditionDevicePolicyOsConstraintArgs.builder()
                            .osType("DESKTOP_CHROME_OS")
                            .build())
                        .build())
                    .regions(                    
                        "CH",
                        "IT",
                        "US")
                    .build())
                .build())
            .build());

    }
}
resources:
  service-perimeter:
    type: gcp:accesscontextmanager:ServicePerimeter
    properties:
      parent: accessPolicies/${["access-policy"].name}
      name: accessPolicies/${["access-policy"].name}/servicePerimeters/restrict_storage
      title: restrict_storage
      status:
        restrictedServices:
          - storage.googleapis.com
  access-level:
    type: gcp:accesscontextmanager:AccessLevel
    properties:
      parent: accessPolicies/${["access-policy"].name}
      name: accessPolicies/${["access-policy"].name}/accessLevels/chromeos_no_lock
      title: chromeos_no_lock
      basic:
        conditions:
          - devicePolicy:
              requireScreenLock: false
              osConstraints:
                - osType: DESKTOP_CHROME_OS
            regions:
              - CH
              - IT
              - US
  access-policy:
    type: gcp:accesscontextmanager:AccessPolicy
    properties:
      parent: organizations/123456789
      title: my policy

The status block defines the enforced configuration. The restrictedServices list blocks external access to specified APIs; here, Cloud Storage is restricted while projects inside the perimeter can still communicate. The parent property links the perimeter to an access policy, and the name follows the format accessPolicies/{policy_id}/servicePerimeters/{short_name}.

Control ingress and egress with traffic policies

Data exchange between perimeters requires explicit rules that define which identities can access resources and which operations they can perform.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const access_policy = new gcp.accesscontextmanager.AccessPolicy("access-policy", {
    parent: "organizations/123456789",
    title: "my policy",
});
const secure_data_exchange = new gcp.accesscontextmanager.ServicePerimeters("secure-data-exchange", {
    parent: pulumi.interpolate`accessPolicies/${access_policy.name}`,
    servicePerimeters: [
        {
            name: pulumi.interpolate`accessPolicies/${access_policy.name}/servicePerimeters/`,
            title: "",
            status: {
                restrictedServices: ["storage.googleapis.com"],
            },
        },
        {
            name: pulumi.interpolate`accessPolicies/${access_policy.name}/servicePerimeters/`,
            title: "",
            status: {
                restrictedServices: ["bigtable.googleapis.com"],
                vpcAccessibleServices: {
                    enableRestriction: true,
                    allowedServices: ["bigquery.googleapis.com"],
                },
            },
        },
    ],
});
const access_level = new gcp.accesscontextmanager.AccessLevel("access-level", {
    parent: pulumi.interpolate`accessPolicies/${access_policy.name}`,
    name: pulumi.interpolate`accessPolicies/${access_policy.name}/accessLevels/secure_data_exchange`,
    title: "secure_data_exchange",
    basic: {
        conditions: [{
            devicePolicy: {
                requireScreenLock: false,
                osConstraints: [{
                    osType: "DESKTOP_CHROME_OS",
                }],
            },
            regions: [
                "CH",
                "IT",
                "US",
            ],
        }],
    },
});
const test_access = new gcp.accesscontextmanager.ServicePerimeter("test-access", {
    parent: `accessPolicies/${test_accessGoogleAccessContextManagerAccessPolicy.name}`,
    name: `accessPolicies/${test_accessGoogleAccessContextManagerAccessPolicy.name}/servicePerimeters/%s`,
    title: "%s",
    perimeterType: "PERIMETER_TYPE_REGULAR",
    status: {
        restrictedServices: [
            "bigquery.googleapis.com",
            "storage.googleapis.com",
        ],
        accessLevels: [access_level.name],
        vpcAccessibleServices: {
            enableRestriction: true,
            allowedServices: [
                "bigquery.googleapis.com",
                "storage.googleapis.com",
            ],
        },
        ingressPolicies: [{
            ingressFrom: {
                sources: [{
                    accessLevel: test_accessGoogleAccessContextManagerAccessLevel.name,
                }],
                identityType: "ANY_IDENTITY",
            },
            ingressTo: {
                resources: ["*"],
                operations: [
                    {
                        serviceName: "bigquery.googleapis.com",
                        methodSelectors: [
                            {
                                method: "BigQueryStorage.ReadRows",
                            },
                            {
                                method: "TableService.ListTables",
                            },
                            {
                                permission: "bigquery.jobs.get",
                            },
                        ],
                    },
                    {
                        serviceName: "storage.googleapis.com",
                        methodSelectors: [{
                            method: "google.storage.objects.create",
                        }],
                    },
                ],
            },
        }],
        egressPolicies: [{
            egressFrom: {
                identityType: "ANY_USER_ACCOUNT",
            },
        }],
    },
});
import pulumi
import pulumi_gcp as gcp

access_policy = gcp.accesscontextmanager.AccessPolicy("access-policy",
    parent="organizations/123456789",
    title="my policy")
secure_data_exchange = gcp.accesscontextmanager.ServicePerimeters("secure-data-exchange",
    parent=access_policy.name.apply(lambda name: f"accessPolicies/{name}"),
    service_perimeters=[
        {
            "name": access_policy.name.apply(lambda name: f"accessPolicies/{name}/servicePerimeters/"),
            "title": "",
            "status": {
                "restricted_services": ["storage.googleapis.com"],
            },
        },
        {
            "name": access_policy.name.apply(lambda name: f"accessPolicies/{name}/servicePerimeters/"),
            "title": "",
            "status": {
                "restricted_services": ["bigtable.googleapis.com"],
                "vpc_accessible_services": {
                    "enable_restriction": True,
                    "allowed_services": ["bigquery.googleapis.com"],
                },
            },
        },
    ])
access_level = gcp.accesscontextmanager.AccessLevel("access-level",
    parent=access_policy.name.apply(lambda name: f"accessPolicies/{name}"),
    name=access_policy.name.apply(lambda name: f"accessPolicies/{name}/accessLevels/secure_data_exchange"),
    title="secure_data_exchange",
    basic={
        "conditions": [{
            "device_policy": {
                "require_screen_lock": False,
                "os_constraints": [{
                    "os_type": "DESKTOP_CHROME_OS",
                }],
            },
            "regions": [
                "CH",
                "IT",
                "US",
            ],
        }],
    })
test_access = gcp.accesscontextmanager.ServicePerimeter("test-access",
    parent=f"accessPolicies/{test_access_google_access_context_manager_access_policy['name']}",
    name=f"accessPolicies/{test_access_google_access_context_manager_access_policy['name']}/servicePerimeters/%s",
    title="%s",
    perimeter_type="PERIMETER_TYPE_REGULAR",
    status={
        "restricted_services": [
            "bigquery.googleapis.com",
            "storage.googleapis.com",
        ],
        "access_levels": [access_level.name],
        "vpc_accessible_services": {
            "enable_restriction": True,
            "allowed_services": [
                "bigquery.googleapis.com",
                "storage.googleapis.com",
            ],
        },
        "ingress_policies": [{
            "ingress_from": {
                "sources": [{
                    "access_level": test_access_google_access_context_manager_access_level["name"],
                }],
                "identity_type": "ANY_IDENTITY",
            },
            "ingress_to": {
                "resources": ["*"],
                "operations": [
                    {
                        "service_name": "bigquery.googleapis.com",
                        "method_selectors": [
                            {
                                "method": "BigQueryStorage.ReadRows",
                            },
                            {
                                "method": "TableService.ListTables",
                            },
                            {
                                "permission": "bigquery.jobs.get",
                            },
                        ],
                    },
                    {
                        "service_name": "storage.googleapis.com",
                        "method_selectors": [{
                            "method": "google.storage.objects.create",
                        }],
                    },
                ],
            },
        }],
        "egress_policies": [{
            "egress_from": {
                "identity_type": "ANY_USER_ACCOUNT",
            },
        }],
    })
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/accesscontextmanager"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		access_policy, err := accesscontextmanager.NewAccessPolicy(ctx, "access-policy", &accesscontextmanager.AccessPolicyArgs{
			Parent: pulumi.String("organizations/123456789"),
			Title:  pulumi.String("my policy"),
		})
		if err != nil {
			return err
		}
		_, err = accesscontextmanager.NewServicePerimeters(ctx, "secure-data-exchange", &accesscontextmanager.ServicePerimetersArgs{
			Parent: access_policy.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("accessPolicies/%v", name), nil
			}).(pulumi.StringOutput),
			ServicePerimeters: accesscontextmanager.ServicePerimetersServicePerimeterArray{
				&accesscontextmanager.ServicePerimetersServicePerimeterArgs{
					Name: access_policy.Name.ApplyT(func(name string) (string, error) {
						return fmt.Sprintf("accessPolicies/%v/servicePerimeters/", name), nil
					}).(pulumi.StringOutput),
					Title: pulumi.String(""),
					Status: &accesscontextmanager.ServicePerimetersServicePerimeterStatusArgs{
						RestrictedServices: pulumi.StringArray{
							pulumi.String("storage.googleapis.com"),
						},
					},
				},
				&accesscontextmanager.ServicePerimetersServicePerimeterArgs{
					Name: access_policy.Name.ApplyT(func(name string) (string, error) {
						return fmt.Sprintf("accessPolicies/%v/servicePerimeters/", name), nil
					}).(pulumi.StringOutput),
					Title: pulumi.String(""),
					Status: &accesscontextmanager.ServicePerimetersServicePerimeterStatusArgs{
						RestrictedServices: pulumi.StringArray{
							pulumi.String("bigtable.googleapis.com"),
						},
						VpcAccessibleServices: &accesscontextmanager.ServicePerimetersServicePerimeterStatusVpcAccessibleServicesArgs{
							EnableRestriction: pulumi.Bool(true),
							AllowedServices: pulumi.StringArray{
								pulumi.String("bigquery.googleapis.com"),
							},
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		access_level, err := accesscontextmanager.NewAccessLevel(ctx, "access-level", &accesscontextmanager.AccessLevelArgs{
			Parent: access_policy.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("accessPolicies/%v", name), nil
			}).(pulumi.StringOutput),
			Name: access_policy.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("accessPolicies/%v/accessLevels/secure_data_exchange", name), nil
			}).(pulumi.StringOutput),
			Title: pulumi.String("secure_data_exchange"),
			Basic: &accesscontextmanager.AccessLevelBasicArgs{
				Conditions: accesscontextmanager.AccessLevelBasicConditionArray{
					&accesscontextmanager.AccessLevelBasicConditionArgs{
						DevicePolicy: &accesscontextmanager.AccessLevelBasicConditionDevicePolicyArgs{
							RequireScreenLock: pulumi.Bool(false),
							OsConstraints: accesscontextmanager.AccessLevelBasicConditionDevicePolicyOsConstraintArray{
								&accesscontextmanager.AccessLevelBasicConditionDevicePolicyOsConstraintArgs{
									OsType: pulumi.String("DESKTOP_CHROME_OS"),
								},
							},
						},
						Regions: pulumi.StringArray{
							pulumi.String("CH"),
							pulumi.String("IT"),
							pulumi.String("US"),
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		_, err = accesscontextmanager.NewServicePerimeter(ctx, "test-access", &accesscontextmanager.ServicePerimeterArgs{
			Parent:        pulumi.Sprintf("accessPolicies/%v", test_accessGoogleAccessContextManagerAccessPolicy.Name),
			Name:          pulumi.Sprintf("accessPolicies/%v%v", test_accessGoogleAccessContextManagerAccessPolicy.Name, "/servicePerimeters/%s"),
			Title:         pulumi.String("%s"),
			PerimeterType: pulumi.String("PERIMETER_TYPE_REGULAR"),
			Status: &accesscontextmanager.ServicePerimeterStatusArgs{
				RestrictedServices: pulumi.StringArray{
					pulumi.String("bigquery.googleapis.com"),
					pulumi.String("storage.googleapis.com"),
				},
				AccessLevels: pulumi.StringArray{
					access_level.Name,
				},
				VpcAccessibleServices: &accesscontextmanager.ServicePerimeterStatusVpcAccessibleServicesArgs{
					EnableRestriction: pulumi.Bool(true),
					AllowedServices: pulumi.StringArray{
						pulumi.String("bigquery.googleapis.com"),
						pulumi.String("storage.googleapis.com"),
					},
				},
				IngressPolicies: accesscontextmanager.ServicePerimeterStatusIngressPolicyArray{
					&accesscontextmanager.ServicePerimeterStatusIngressPolicyArgs{
						IngressFrom: &accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressFromArgs{
							Sources: accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressFromSourceArray{
								&accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressFromSourceArgs{
									AccessLevel: pulumi.Any(test_accessGoogleAccessContextManagerAccessLevel.Name),
								},
							},
							IdentityType: pulumi.String("ANY_IDENTITY"),
						},
						IngressTo: &accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressToArgs{
							Resources: pulumi.StringArray{
								pulumi.String("*"),
							},
							Operations: accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressToOperationArray{
								&accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressToOperationArgs{
									ServiceName: pulumi.String("bigquery.googleapis.com"),
									MethodSelectors: accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArray{
										&accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArgs{
											Method: pulumi.String("BigQueryStorage.ReadRows"),
										},
										&accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArgs{
											Method: pulumi.String("TableService.ListTables"),
										},
										&accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArgs{
											Permission: pulumi.String("bigquery.jobs.get"),
										},
									},
								},
								&accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressToOperationArgs{
									ServiceName: pulumi.String("storage.googleapis.com"),
									MethodSelectors: accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArray{
										&accesscontextmanager.ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArgs{
											Method: pulumi.String("google.storage.objects.create"),
										},
									},
								},
							},
						},
					},
				},
				EgressPolicies: accesscontextmanager.ServicePerimeterStatusEgressPolicyArray{
					&accesscontextmanager.ServicePerimeterStatusEgressPolicyArgs{
						EgressFrom: &accesscontextmanager.ServicePerimeterStatusEgressPolicyEgressFromArgs{
							IdentityType: pulumi.String("ANY_USER_ACCOUNT"),
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var access_policy = new Gcp.AccessContextManager.AccessPolicy("access-policy", new()
    {
        Parent = "organizations/123456789",
        Title = "my policy",
    });

    var secure_data_exchange = new Gcp.AccessContextManager.ServicePerimeters("secure-data-exchange", new()
    {
        Parent = access_policy.Name.Apply(name => $"accessPolicies/{name}"),
        ServicePerimeterDetails = new[]
        {
            new Gcp.AccessContextManager.Inputs.ServicePerimetersServicePerimeterArgs
            {
                Name = access_policy.Name.Apply(name => $"accessPolicies/{name}/servicePerimeters/"),
                Title = "",
                Status = new Gcp.AccessContextManager.Inputs.ServicePerimetersServicePerimeterStatusArgs
                {
                    RestrictedServices = new[]
                    {
                        "storage.googleapis.com",
                    },
                },
            },
            new Gcp.AccessContextManager.Inputs.ServicePerimetersServicePerimeterArgs
            {
                Name = access_policy.Name.Apply(name => $"accessPolicies/{name}/servicePerimeters/"),
                Title = "",
                Status = new Gcp.AccessContextManager.Inputs.ServicePerimetersServicePerimeterStatusArgs
                {
                    RestrictedServices = new[]
                    {
                        "bigtable.googleapis.com",
                    },
                    VpcAccessibleServices = new Gcp.AccessContextManager.Inputs.ServicePerimetersServicePerimeterStatusVpcAccessibleServicesArgs
                    {
                        EnableRestriction = true,
                        AllowedServices = new[]
                        {
                            "bigquery.googleapis.com",
                        },
                    },
                },
            },
        },
    });

    var access_level = new Gcp.AccessContextManager.AccessLevel("access-level", new()
    {
        Parent = access_policy.Name.Apply(name => $"accessPolicies/{name}"),
        Name = access_policy.Name.Apply(name => $"accessPolicies/{name}/accessLevels/secure_data_exchange"),
        Title = "secure_data_exchange",
        Basic = new Gcp.AccessContextManager.Inputs.AccessLevelBasicArgs
        {
            Conditions = new[]
            {
                new Gcp.AccessContextManager.Inputs.AccessLevelBasicConditionArgs
                {
                    DevicePolicy = new Gcp.AccessContextManager.Inputs.AccessLevelBasicConditionDevicePolicyArgs
                    {
                        RequireScreenLock = false,
                        OsConstraints = new[]
                        {
                            new Gcp.AccessContextManager.Inputs.AccessLevelBasicConditionDevicePolicyOsConstraintArgs
                            {
                                OsType = "DESKTOP_CHROME_OS",
                            },
                        },
                    },
                    Regions = new[]
                    {
                        "CH",
                        "IT",
                        "US",
                    },
                },
            },
        },
    });

    var test_access = new Gcp.AccessContextManager.ServicePerimeter("test-access", new()
    {
        Parent = $"accessPolicies/{test_accessGoogleAccessContextManagerAccessPolicy.Name}",
        Name = $"accessPolicies/{test_accessGoogleAccessContextManagerAccessPolicy.Name}/servicePerimeters/%s",
        Title = "%s",
        PerimeterType = "PERIMETER_TYPE_REGULAR",
        Status = new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusArgs
        {
            RestrictedServices = new[]
            {
                "bigquery.googleapis.com",
                "storage.googleapis.com",
            },
            AccessLevels = new[]
            {
                access_level.Name,
            },
            VpcAccessibleServices = new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusVpcAccessibleServicesArgs
            {
                EnableRestriction = true,
                AllowedServices = new[]
                {
                    "bigquery.googleapis.com",
                    "storage.googleapis.com",
                },
            },
            IngressPolicies = new[]
            {
                new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusIngressPolicyArgs
                {
                    IngressFrom = new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusIngressPolicyIngressFromArgs
                    {
                        Sources = new[]
                        {
                            new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusIngressPolicyIngressFromSourceArgs
                            {
                                AccessLevel = test_accessGoogleAccessContextManagerAccessLevel.Name,
                            },
                        },
                        IdentityType = "ANY_IDENTITY",
                    },
                    IngressTo = new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusIngressPolicyIngressToArgs
                    {
                        Resources = new[]
                        {
                            "*",
                        },
                        Operations = new[]
                        {
                            new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusIngressPolicyIngressToOperationArgs
                            {
                                ServiceName = "bigquery.googleapis.com",
                                MethodSelectors = new[]
                                {
                                    new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArgs
                                    {
                                        Method = "BigQueryStorage.ReadRows",
                                    },
                                    new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArgs
                                    {
                                        Method = "TableService.ListTables",
                                    },
                                    new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArgs
                                    {
                                        Permission = "bigquery.jobs.get",
                                    },
                                },
                            },
                            new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusIngressPolicyIngressToOperationArgs
                            {
                                ServiceName = "storage.googleapis.com",
                                MethodSelectors = new[]
                                {
                                    new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArgs
                                    {
                                        Method = "google.storage.objects.create",
                                    },
                                },
                            },
                        },
                    },
                },
            },
            EgressPolicies = new[]
            {
                new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusEgressPolicyArgs
                {
                    EgressFrom = new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusEgressPolicyEgressFromArgs
                    {
                        IdentityType = "ANY_USER_ACCOUNT",
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.accesscontextmanager.AccessPolicy;
import com.pulumi.gcp.accesscontextmanager.AccessPolicyArgs;
import com.pulumi.gcp.accesscontextmanager.ServicePerimeters;
import com.pulumi.gcp.accesscontextmanager.ServicePerimetersArgs;
import com.pulumi.gcp.accesscontextmanager.inputs.ServicePerimetersServicePerimeterArgs;
import com.pulumi.gcp.accesscontextmanager.inputs.ServicePerimetersServicePerimeterStatusArgs;
import com.pulumi.gcp.accesscontextmanager.inputs.ServicePerimetersServicePerimeterStatusVpcAccessibleServicesArgs;
import com.pulumi.gcp.accesscontextmanager.AccessLevel;
import com.pulumi.gcp.accesscontextmanager.AccessLevelArgs;
import com.pulumi.gcp.accesscontextmanager.inputs.AccessLevelBasicArgs;
import com.pulumi.gcp.accesscontextmanager.ServicePerimeter;
import com.pulumi.gcp.accesscontextmanager.ServicePerimeterArgs;
import com.pulumi.gcp.accesscontextmanager.inputs.ServicePerimeterStatusArgs;
import com.pulumi.gcp.accesscontextmanager.inputs.ServicePerimeterStatusVpcAccessibleServicesArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var access_policy = new AccessPolicy("access-policy", AccessPolicyArgs.builder()
            .parent("organizations/123456789")
            .title("my policy")
            .build());

        var secure_data_exchange = new ServicePerimeters("secure-data-exchange", ServicePerimetersArgs.builder()
            .parent(access_policy.name().applyValue(_name -> String.format("accessPolicies/%s", _name)))
            .servicePerimeters(            
                ServicePerimetersServicePerimeterArgs.builder()
                    .name(access_policy.name().applyValue(_name -> String.format("accessPolicies/%s/servicePerimeters/", _name)))
                    .title("")
                    .status(ServicePerimetersServicePerimeterStatusArgs.builder()
                        .restrictedServices("storage.googleapis.com")
                        .build())
                    .build(),
                ServicePerimetersServicePerimeterArgs.builder()
                    .name(access_policy.name().applyValue(_name -> String.format("accessPolicies/%s/servicePerimeters/", _name)))
                    .title("")
                    .status(ServicePerimetersServicePerimeterStatusArgs.builder()
                        .restrictedServices("bigtable.googleapis.com")
                        .vpcAccessibleServices(ServicePerimetersServicePerimeterStatusVpcAccessibleServicesArgs.builder()
                            .enableRestriction(true)
                            .allowedServices("bigquery.googleapis.com")
                            .build())
                        .build())
                    .build())
            .build());

        var access_level = new AccessLevel("access-level", AccessLevelArgs.builder()
            .parent(access_policy.name().applyValue(_name -> String.format("accessPolicies/%s", _name)))
            .name(access_policy.name().applyValue(_name -> String.format("accessPolicies/%s/accessLevels/secure_data_exchange", _name)))
            .title("secure_data_exchange")
            .basic(AccessLevelBasicArgs.builder()
                .conditions(AccessLevelBasicConditionArgs.builder()
                    .devicePolicy(AccessLevelBasicConditionDevicePolicyArgs.builder()
                        .requireScreenLock(false)
                        .osConstraints(AccessLevelBasicConditionDevicePolicyOsConstraintArgs.builder()
                            .osType("DESKTOP_CHROME_OS")
                            .build())
                        .build())
                    .regions(                    
                        "CH",
                        "IT",
                        "US")
                    .build())
                .build())
            .build());

        var test_access = new ServicePerimeter("test-access", ServicePerimeterArgs.builder()
            .parent(String.format("accessPolicies/%s", test_accessGoogleAccessContextManagerAccessPolicy.name()))
            .name(String.format("accessPolicies/%s/servicePerimeters/%s", test_accessGoogleAccessContextManagerAccessPolicy.name()))
            .title("%s")
            .perimeterType("PERIMETER_TYPE_REGULAR")
            .status(ServicePerimeterStatusArgs.builder()
                .restrictedServices(                
                    "bigquery.googleapis.com",
                    "storage.googleapis.com")
                .accessLevels(access_level.name())
                .vpcAccessibleServices(ServicePerimeterStatusVpcAccessibleServicesArgs.builder()
                    .enableRestriction(true)
                    .allowedServices(                    
                        "bigquery.googleapis.com",
                        "storage.googleapis.com")
                    .build())
                .ingressPolicies(ServicePerimeterStatusIngressPolicyArgs.builder()
                    .ingressFrom(ServicePerimeterStatusIngressPolicyIngressFromArgs.builder()
                        .sources(ServicePerimeterStatusIngressPolicyIngressFromSourceArgs.builder()
                            .accessLevel(test_accessGoogleAccessContextManagerAccessLevel.name())
                            .build())
                        .identityType("ANY_IDENTITY")
                        .build())
                    .ingressTo(ServicePerimeterStatusIngressPolicyIngressToArgs.builder()
                        .resources("*")
                        .operations(                        
                            ServicePerimeterStatusIngressPolicyIngressToOperationArgs.builder()
                                .serviceName("bigquery.googleapis.com")
                                .methodSelectors(                                
                                    ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArgs.builder()
                                        .method("BigQueryStorage.ReadRows")
                                        .build(),
                                    ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArgs.builder()
                                        .method("TableService.ListTables")
                                        .build(),
                                    ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArgs.builder()
                                        .permission("bigquery.jobs.get")
                                        .build())
                                .build(),
                            ServicePerimeterStatusIngressPolicyIngressToOperationArgs.builder()
                                .serviceName("storage.googleapis.com")
                                .methodSelectors(ServicePerimeterStatusIngressPolicyIngressToOperationMethodSelectorArgs.builder()
                                    .method("google.storage.objects.create")
                                    .build())
                                .build())
                        .build())
                    .build())
                .egressPolicies(ServicePerimeterStatusEgressPolicyArgs.builder()
                    .egressFrom(ServicePerimeterStatusEgressPolicyEgressFromArgs.builder()
                        .identityType("ANY_USER_ACCOUNT")
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  secure-data-exchange:
    type: gcp:accesscontextmanager:ServicePerimeters
    properties:
      parent: accessPolicies/${["access-policy"].name}
      servicePerimeters:
        - name: accessPolicies/${["access-policy"].name}/servicePerimeters/
          title: ""
          status:
            restrictedServices:
              - storage.googleapis.com
        - name: accessPolicies/${["access-policy"].name}/servicePerimeters/
          title: ""
          status:
            restrictedServices:
              - bigtable.googleapis.com
            vpcAccessibleServices:
              enableRestriction: true
              allowedServices:
                - bigquery.googleapis.com
  access-level:
    type: gcp:accesscontextmanager:AccessLevel
    properties:
      parent: accessPolicies/${["access-policy"].name}
      name: accessPolicies/${["access-policy"].name}/accessLevels/secure_data_exchange
      title: secure_data_exchange
      basic:
        conditions:
          - devicePolicy:
              requireScreenLock: false
              osConstraints:
                - osType: DESKTOP_CHROME_OS
            regions:
              - CH
              - IT
              - US
  access-policy:
    type: gcp:accesscontextmanager:AccessPolicy
    properties:
      parent: organizations/123456789
      title: my policy
  test-access:
    type: gcp:accesscontextmanager:ServicePerimeter
    properties:
      parent: accessPolicies/${["test-accessGoogleAccessContextManagerAccessPolicy"].name}
      name: accessPolicies/${["test-accessGoogleAccessContextManagerAccessPolicy"].name}/servicePerimeters/%s
      title: '%s'
      perimeterType: PERIMETER_TYPE_REGULAR
      status:
        restrictedServices:
          - bigquery.googleapis.com
          - storage.googleapis.com
        accessLevels:
          - ${["access-level"].name}
        vpcAccessibleServices:
          enableRestriction: true
          allowedServices:
            - bigquery.googleapis.com
            - storage.googleapis.com
        ingressPolicies:
          - ingressFrom:
              sources:
                - accessLevel: ${["test-accessGoogleAccessContextManagerAccessLevel"].name}
              identityType: ANY_IDENTITY
            ingressTo:
              resources:
                - '*'
              operations:
                - serviceName: bigquery.googleapis.com
                  methodSelectors:
                    - method: BigQueryStorage.ReadRows
                    - method: TableService.ListTables
                    - permission: bigquery.jobs.get
                - serviceName: storage.googleapis.com
                  methodSelectors:
                    - method: google.storage.objects.create
        egressPolicies:
          - egressFrom:
              identityType: ANY_USER_ACCOUNT

The ingressPolicies array controls inbound traffic. Each policy’s ingressFrom block specifies source access levels and identity types, while ingressTo defines target resources and allowed operations. The operations array uses serviceName to identify APIs and methodSelectors to permit specific methods or permissions. The egressPolicies array controls outbound traffic with similar structure. The vpcAccessibleServices block further restricts which services VPC resources can reach, even within the perimeter.

Test perimeter changes with dry-run mode

Before enforcing new restrictions, you can validate that policies won’t break existing workflows by testing a proposed configuration alongside the active one.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const access_policy = new gcp.accesscontextmanager.AccessPolicy("access-policy", {
    parent: "organizations/123456789",
    title: "my policy",
});
const service_perimeter = new gcp.accesscontextmanager.ServicePerimeter("service-perimeter", {
    parent: pulumi.interpolate`accessPolicies/${access_policy.name}`,
    name: pulumi.interpolate`accessPolicies/${access_policy.name}/servicePerimeters/restrict_bigquery_dryrun_storage`,
    title: "restrict_bigquery_dryrun_storage",
    status: {
        restrictedServices: ["bigquery.googleapis.com"],
    },
    spec: {
        restrictedServices: ["storage.googleapis.com"],
    },
    useExplicitDryRunSpec: true,
});
import pulumi
import pulumi_gcp as gcp

access_policy = gcp.accesscontextmanager.AccessPolicy("access-policy",
    parent="organizations/123456789",
    title="my policy")
service_perimeter = gcp.accesscontextmanager.ServicePerimeter("service-perimeter",
    parent=access_policy.name.apply(lambda name: f"accessPolicies/{name}"),
    name=access_policy.name.apply(lambda name: f"accessPolicies/{name}/servicePerimeters/restrict_bigquery_dryrun_storage"),
    title="restrict_bigquery_dryrun_storage",
    status={
        "restricted_services": ["bigquery.googleapis.com"],
    },
    spec={
        "restricted_services": ["storage.googleapis.com"],
    },
    use_explicit_dry_run_spec=True)
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/accesscontextmanager"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		access_policy, err := accesscontextmanager.NewAccessPolicy(ctx, "access-policy", &accesscontextmanager.AccessPolicyArgs{
			Parent: pulumi.String("organizations/123456789"),
			Title:  pulumi.String("my policy"),
		})
		if err != nil {
			return err
		}
		_, err = accesscontextmanager.NewServicePerimeter(ctx, "service-perimeter", &accesscontextmanager.ServicePerimeterArgs{
			Parent: access_policy.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("accessPolicies/%v", name), nil
			}).(pulumi.StringOutput),
			Name: access_policy.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("accessPolicies/%v/servicePerimeters/restrict_bigquery_dryrun_storage", name), nil
			}).(pulumi.StringOutput),
			Title: pulumi.String("restrict_bigquery_dryrun_storage"),
			Status: &accesscontextmanager.ServicePerimeterStatusArgs{
				RestrictedServices: pulumi.StringArray{
					pulumi.String("bigquery.googleapis.com"),
				},
			},
			Spec: &accesscontextmanager.ServicePerimeterSpecArgs{
				RestrictedServices: pulumi.StringArray{
					pulumi.String("storage.googleapis.com"),
				},
			},
			UseExplicitDryRunSpec: pulumi.Bool(true),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var access_policy = new Gcp.AccessContextManager.AccessPolicy("access-policy", new()
    {
        Parent = "organizations/123456789",
        Title = "my policy",
    });

    var service_perimeter = new Gcp.AccessContextManager.ServicePerimeter("service-perimeter", new()
    {
        Parent = access_policy.Name.Apply(name => $"accessPolicies/{name}"),
        Name = access_policy.Name.Apply(name => $"accessPolicies/{name}/servicePerimeters/restrict_bigquery_dryrun_storage"),
        Title = "restrict_bigquery_dryrun_storage",
        Status = new Gcp.AccessContextManager.Inputs.ServicePerimeterStatusArgs
        {
            RestrictedServices = new[]
            {
                "bigquery.googleapis.com",
            },
        },
        Spec = new Gcp.AccessContextManager.Inputs.ServicePerimeterSpecArgs
        {
            RestrictedServices = new[]
            {
                "storage.googleapis.com",
            },
        },
        UseExplicitDryRunSpec = true,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.accesscontextmanager.AccessPolicy;
import com.pulumi.gcp.accesscontextmanager.AccessPolicyArgs;
import com.pulumi.gcp.accesscontextmanager.ServicePerimeter;
import com.pulumi.gcp.accesscontextmanager.ServicePerimeterArgs;
import com.pulumi.gcp.accesscontextmanager.inputs.ServicePerimeterStatusArgs;
import com.pulumi.gcp.accesscontextmanager.inputs.ServicePerimeterSpecArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var access_policy = new AccessPolicy("access-policy", AccessPolicyArgs.builder()
            .parent("organizations/123456789")
            .title("my policy")
            .build());

        var service_perimeter = new ServicePerimeter("service-perimeter", ServicePerimeterArgs.builder()
            .parent(access_policy.name().applyValue(_name -> String.format("accessPolicies/%s", _name)))
            .name(access_policy.name().applyValue(_name -> String.format("accessPolicies/%s/servicePerimeters/restrict_bigquery_dryrun_storage", _name)))
            .title("restrict_bigquery_dryrun_storage")
            .status(ServicePerimeterStatusArgs.builder()
                .restrictedServices("bigquery.googleapis.com")
                .build())
            .spec(ServicePerimeterSpecArgs.builder()
                .restrictedServices("storage.googleapis.com")
                .build())
            .useExplicitDryRunSpec(true)
            .build());

    }
}
resources:
  service-perimeter:
    type: gcp:accesscontextmanager:ServicePerimeter
    properties:
      parent: accessPolicies/${["access-policy"].name}
      name: accessPolicies/${["access-policy"].name}/servicePerimeters/restrict_bigquery_dryrun_storage
      title: restrict_bigquery_dryrun_storage
      status:
        restrictedServices:
          - bigquery.googleapis.com
      spec:
        restrictedServices:
          - storage.googleapis.com
      useExplicitDryRunSpec: true
  access-policy:
    type: gcp:accesscontextmanager:AccessPolicy
    properties:
      parent: organizations/123456789
      title: my policy

The spec block defines the dry-run configuration while status remains enforced. Setting useExplicitDryRunSpec to true activates this mode, allowing you to analyze the impact of proposed changes without affecting production traffic. Here, the perimeter enforces BigQuery restrictions in status while testing Storage restrictions in spec.

Beyond these examples

These snippets focus on specific perimeter-level features: service restriction and VPC-accessible services, ingress and egress traffic policies, and dry-run testing with spec/status separation. They’re intentionally minimal rather than full VPC Service Controls deployments.

The examples reference pre-existing infrastructure such as Access Context Manager access policies, access levels for identity-based controls, and GCP projects to protect within perimeters. They focus on configuring the perimeter rather than provisioning the surrounding access control hierarchy.

To keep things focused, common perimeter patterns are omitted, including:

  • Bridge perimeters for cross-perimeter resource sharing (perimeterType)
  • Project and resource membership (resources property in status/spec)
  • Access level integration beyond ingress policies
  • VPC Service Controls for hybrid connectivity

These omissions are intentional: the goal is to illustrate how each perimeter feature is wired, not provide drop-in security modules. See the Service Perimeter resource reference for all available configuration options.

Let's configure GCP Access Context Manager Service Perimeters

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Authentication & Permissions
Why am I getting a 403 error when creating a service perimeter?
When using User Application Default Credentials, you must specify billingProject and set userProjectOverride to true in your provider configuration. Your account also needs the serviceusage.services.use permission on the billing project.
Perimeter Types & Overlap Rules
What's the difference between regular and bridge perimeters?
Regular perimeters (the default) contain resources, access levels, and restricted services. Bridge perimeters contain only resources and are used for cross-project operations. Regular perimeters govern access controls, while bridges enable resource sharing between perimeters.
Can a project belong to multiple service perimeters?
A project can belong to only ONE regular service perimeter, but it can belong to multiple bridge perimeters. This allows complex topologies where projects share data with a common perimeter without sharing among themselves.
Configuration & Dry-Run Testing
What's the difference between status and spec?
status contains the enforced configuration that actively restricts access. spec contains a dry-run configuration for testing changes without enforcement. To use spec, you must set useExplicitDryRunSpec to true.
How do I test perimeter changes without enforcing them?
Configure the spec property with your proposed changes and set useExplicitDryRunSpec to true. This lets you analyze differences between enforced and suggested restrictions without actually enforcing them.
What properties can't I change after creating a perimeter?
The name, parent, and perimeterType properties are immutable. You cannot change which access policy a perimeter belongs to, its resource name, or convert between regular and bridge types.
Service Restrictions & Access Control
How do I restrict which services are accessible within a perimeter?
Configure restrictedServices in the status block with an array of service names like storage.googleapis.com or bigquery.googleapis.com. These services will only be accessible from within the perimeter.
How do I configure ingress and egress policies for a perimeter?
Use ingressPolicies and egressPolicies within the status block. Ingress policies define allowed inbound traffic with sources, identity types, target resources, and allowed operations. Egress policies control outbound traffic with identity types and destinations.

Using a different cloud?

Explore security guides for other cloud providers: