The gcp:beyondcorp/securityGatewayApplication:SecurityGatewayApplication resource, part of the Pulumi GCP provider, defines application endpoints protected by a BeyondCorp Security Gateway: hostname matching, upstream routing, and protocol configuration. This guide focuses on four capabilities: endpoint matching for public and private services, VPC network routing with egress policies, API Gateway and Proxy Gateway schemas, and contextual header forwarding for identity.
Security Gateway Applications belong to Security Gateway resources and route traffic to VPC networks or external endpoints. The examples are intentionally small. Combine them with your own Security Gateways, VPC infrastructure, and upstream services.
Route traffic to public endpoints by hostname
Teams protecting access to public SaaS applications define which hostnames and ports the Security Gateway intercepts, creating a zero-trust boundary for external services.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const _default = new gcp.beyondcorp.SecurityGateway("default", {
securityGatewayId: "default-sg",
displayName: "My Security Gateway resource",
hubs: [{
region: "us-central1",
}],
});
const example = new gcp.beyondcorp.SecurityGatewayApplication("example", {
securityGatewayId: _default.securityGatewayId,
applicationId: "google-sga",
endpointMatchers: [{
hostname: "google.com",
ports: [
80,
443,
],
}],
});
import pulumi
import pulumi_gcp as gcp
default = gcp.beyondcorp.SecurityGateway("default",
security_gateway_id="default-sg",
display_name="My Security Gateway resource",
hubs=[{
"region": "us-central1",
}])
example = gcp.beyondcorp.SecurityGatewayApplication("example",
security_gateway_id=default.security_gateway_id,
application_id="google-sga",
endpoint_matchers=[{
"hostname": "google.com",
"ports": [
80,
443,
],
}])
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/beyondcorp"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_default, err := beyondcorp.NewSecurityGateway(ctx, "default", &beyondcorp.SecurityGatewayArgs{
SecurityGatewayId: pulumi.String("default-sg"),
DisplayName: pulumi.String("My Security Gateway resource"),
Hubs: beyondcorp.SecurityGatewayHubArray{
&beyondcorp.SecurityGatewayHubArgs{
Region: pulumi.String("us-central1"),
},
},
})
if err != nil {
return err
}
_, err = beyondcorp.NewSecurityGatewayApplication(ctx, "example", &beyondcorp.SecurityGatewayApplicationArgs{
SecurityGatewayId: _default.SecurityGatewayId,
ApplicationId: pulumi.String("google-sga"),
EndpointMatchers: beyondcorp.SecurityGatewayApplicationEndpointMatcherArray{
&beyondcorp.SecurityGatewayApplicationEndpointMatcherArgs{
Hostname: pulumi.String("google.com"),
Ports: pulumi.IntArray{
pulumi.Int(80),
pulumi.Int(443),
},
},
},
})
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 @default = new Gcp.Beyondcorp.SecurityGateway("default", new()
{
SecurityGatewayId = "default-sg",
DisplayName = "My Security Gateway resource",
Hubs = new[]
{
new Gcp.Beyondcorp.Inputs.SecurityGatewayHubArgs
{
Region = "us-central1",
},
},
});
var example = new Gcp.Beyondcorp.SecurityGatewayApplication("example", new()
{
SecurityGatewayId = @default.SecurityGatewayId,
ApplicationId = "google-sga",
EndpointMatchers = new[]
{
new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationEndpointMatcherArgs
{
Hostname = "google.com",
Ports = new[]
{
80,
443,
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.beyondcorp.SecurityGateway;
import com.pulumi.gcp.beyondcorp.SecurityGatewayArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayHubArgs;
import com.pulumi.gcp.beyondcorp.SecurityGatewayApplication;
import com.pulumi.gcp.beyondcorp.SecurityGatewayApplicationArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationEndpointMatcherArgs;
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 default_ = new SecurityGateway("default", SecurityGatewayArgs.builder()
.securityGatewayId("default-sg")
.displayName("My Security Gateway resource")
.hubs(SecurityGatewayHubArgs.builder()
.region("us-central1")
.build())
.build());
var example = new SecurityGatewayApplication("example", SecurityGatewayApplicationArgs.builder()
.securityGatewayId(default_.securityGatewayId())
.applicationId("google-sga")
.endpointMatchers(SecurityGatewayApplicationEndpointMatcherArgs.builder()
.hostname("google.com")
.ports(
80,
443)
.build())
.build());
}
}
resources:
default:
type: gcp:beyondcorp:SecurityGateway
properties:
securityGatewayId: default-sg
displayName: My Security Gateway resource
hubs:
- region: us-central1
example:
type: gcp:beyondcorp:SecurityGatewayApplication
properties:
securityGatewayId: ${default.securityGatewayId}
applicationId: google-sga
endpointMatchers:
- hostname: google.com
ports:
- 80
- 443
The endpointMatchers property defines which traffic to intercept. Each matcher combines a hostname (supporting wildcards like “*.abc.com”) with optional port numbers. The Security Gateway evaluates matchers with OR logic: traffic matching any matcher is routed through the gateway. The securityGatewayId links this application to its parent Security Gateway resource.
Forward traffic to services in a VPC network
Applications running in private VPC networks need upstream configuration to route traffic from the Security Gateway into the correct network and region.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const project = gcp.organizations.getProject({});
const _default = new gcp.beyondcorp.SecurityGateway("default", {
securityGatewayId: "default-sg",
displayName: "My Security Gateway resource",
hubs: [{
region: "us-central1",
}],
});
const example = new gcp.beyondcorp.SecurityGatewayApplication("example", {
securityGatewayId: _default.securityGatewayId,
applicationId: "my-vm-service2",
endpointMatchers: [{
hostname: "my-vm-service.com",
ports: [
80,
443,
],
}],
upstreams: [{
egressPolicy: {
regions: ["us-central1"],
},
network: {
name: project.then(project => `projects/${project.projectId}/global/networks/default`),
},
}],
});
import pulumi
import pulumi_gcp as gcp
project = gcp.organizations.get_project()
default = gcp.beyondcorp.SecurityGateway("default",
security_gateway_id="default-sg",
display_name="My Security Gateway resource",
hubs=[{
"region": "us-central1",
}])
example = gcp.beyondcorp.SecurityGatewayApplication("example",
security_gateway_id=default.security_gateway_id,
application_id="my-vm-service2",
endpoint_matchers=[{
"hostname": "my-vm-service.com",
"ports": [
80,
443,
],
}],
upstreams=[{
"egress_policy": {
"regions": ["us-central1"],
},
"network": {
"name": f"projects/{project.project_id}/global/networks/default",
},
}])
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/beyondcorp"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
if err != nil {
return err
}
_default, err := beyondcorp.NewSecurityGateway(ctx, "default", &beyondcorp.SecurityGatewayArgs{
SecurityGatewayId: pulumi.String("default-sg"),
DisplayName: pulumi.String("My Security Gateway resource"),
Hubs: beyondcorp.SecurityGatewayHubArray{
&beyondcorp.SecurityGatewayHubArgs{
Region: pulumi.String("us-central1"),
},
},
})
if err != nil {
return err
}
_, err = beyondcorp.NewSecurityGatewayApplication(ctx, "example", &beyondcorp.SecurityGatewayApplicationArgs{
SecurityGatewayId: _default.SecurityGatewayId,
ApplicationId: pulumi.String("my-vm-service2"),
EndpointMatchers: beyondcorp.SecurityGatewayApplicationEndpointMatcherArray{
&beyondcorp.SecurityGatewayApplicationEndpointMatcherArgs{
Hostname: pulumi.String("my-vm-service.com"),
Ports: pulumi.IntArray{
pulumi.Int(80),
pulumi.Int(443),
},
},
},
Upstreams: beyondcorp.SecurityGatewayApplicationUpstreamArray{
&beyondcorp.SecurityGatewayApplicationUpstreamArgs{
EgressPolicy: &beyondcorp.SecurityGatewayApplicationUpstreamEgressPolicyArgs{
Regions: pulumi.StringArray{
pulumi.String("us-central1"),
},
},
Network: &beyondcorp.SecurityGatewayApplicationUpstreamNetworkArgs{
Name: pulumi.Sprintf("projects/%v/global/networks/default", project.ProjectId),
},
},
},
})
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 project = Gcp.Organizations.GetProject.Invoke();
var @default = new Gcp.Beyondcorp.SecurityGateway("default", new()
{
SecurityGatewayId = "default-sg",
DisplayName = "My Security Gateway resource",
Hubs = new[]
{
new Gcp.Beyondcorp.Inputs.SecurityGatewayHubArgs
{
Region = "us-central1",
},
},
});
var example = new Gcp.Beyondcorp.SecurityGatewayApplication("example", new()
{
SecurityGatewayId = @default.SecurityGatewayId,
ApplicationId = "my-vm-service2",
EndpointMatchers = new[]
{
new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationEndpointMatcherArgs
{
Hostname = "my-vm-service.com",
Ports = new[]
{
80,
443,
},
},
},
Upstreams = new[]
{
new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamArgs
{
EgressPolicy = new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamEgressPolicyArgs
{
Regions = new[]
{
"us-central1",
},
},
Network = new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamNetworkArgs
{
Name = $"projects/{project.Apply(getProjectResult => getProjectResult.ProjectId)}/global/networks/default",
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.beyondcorp.SecurityGateway;
import com.pulumi.gcp.beyondcorp.SecurityGatewayArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayHubArgs;
import com.pulumi.gcp.beyondcorp.SecurityGatewayApplication;
import com.pulumi.gcp.beyondcorp.SecurityGatewayApplicationArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationEndpointMatcherArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamEgressPolicyArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamNetworkArgs;
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) {
final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
.build());
var default_ = new SecurityGateway("default", SecurityGatewayArgs.builder()
.securityGatewayId("default-sg")
.displayName("My Security Gateway resource")
.hubs(SecurityGatewayHubArgs.builder()
.region("us-central1")
.build())
.build());
var example = new SecurityGatewayApplication("example", SecurityGatewayApplicationArgs.builder()
.securityGatewayId(default_.securityGatewayId())
.applicationId("my-vm-service2")
.endpointMatchers(SecurityGatewayApplicationEndpointMatcherArgs.builder()
.hostname("my-vm-service.com")
.ports(
80,
443)
.build())
.upstreams(SecurityGatewayApplicationUpstreamArgs.builder()
.egressPolicy(SecurityGatewayApplicationUpstreamEgressPolicyArgs.builder()
.regions("us-central1")
.build())
.network(SecurityGatewayApplicationUpstreamNetworkArgs.builder()
.name(String.format("projects/%s/global/networks/default", project.projectId()))
.build())
.build())
.build());
}
}
resources:
default:
type: gcp:beyondcorp:SecurityGateway
properties:
securityGatewayId: default-sg
displayName: My Security Gateway resource
hubs:
- region: us-central1
example:
type: gcp:beyondcorp:SecurityGatewayApplication
properties:
securityGatewayId: ${default.securityGatewayId}
applicationId: my-vm-service2
endpointMatchers:
- hostname: my-vm-service.com
ports:
- 80
- 443
upstreams:
- egressPolicy:
regions:
- us-central1
network:
name: projects/${project.projectId}/global/networks/default
variables:
project:
fn::invoke:
function: gcp:organizations:getProject
arguments: {}
The upstreams property defines where to forward matched traffic. For VPC routing, the network property references your VPC by full resource path, and egressPolicy specifies which regions can handle egress. This configuration extends the basic endpoint matching pattern by adding VPC-aware routing.
Configure API Gateway schema for discovery services
API-based applications that don’t require hostname matching use the API_GATEWAY schema with external endpoints and proxy protocol configuration.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const _default = new gcp.beyondcorp.SecurityGateway("default", {
securityGatewayId: "default-sg-spa-api",
displayName: "My SPA Security Gateway resource",
});
const example_spa = new gcp.beyondcorp.SecurityGatewayApplication("example-spa", {
securityGatewayId: _default.securityGatewayId,
applicationId: "app-discovery",
upstreams: [{
external: {
endpoints: [{
hostname: "my.discovery.service.com",
port: 443,
}],
},
proxyProtocol: {
allowedClientHeaders: ["header"],
},
}],
schema: "API_GATEWAY",
});
import pulumi
import pulumi_gcp as gcp
default = gcp.beyondcorp.SecurityGateway("default",
security_gateway_id="default-sg-spa-api",
display_name="My SPA Security Gateway resource")
example_spa = gcp.beyondcorp.SecurityGatewayApplication("example-spa",
security_gateway_id=default.security_gateway_id,
application_id="app-discovery",
upstreams=[{
"external": {
"endpoints": [{
"hostname": "my.discovery.service.com",
"port": 443,
}],
},
"proxy_protocol": {
"allowed_client_headers": ["header"],
},
}],
schema="API_GATEWAY")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/beyondcorp"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_default, err := beyondcorp.NewSecurityGateway(ctx, "default", &beyondcorp.SecurityGatewayArgs{
SecurityGatewayId: pulumi.String("default-sg-spa-api"),
DisplayName: pulumi.String("My SPA Security Gateway resource"),
})
if err != nil {
return err
}
_, err = beyondcorp.NewSecurityGatewayApplication(ctx, "example-spa", &beyondcorp.SecurityGatewayApplicationArgs{
SecurityGatewayId: _default.SecurityGatewayId,
ApplicationId: pulumi.String("app-discovery"),
Upstreams: beyondcorp.SecurityGatewayApplicationUpstreamArray{
&beyondcorp.SecurityGatewayApplicationUpstreamArgs{
External: &beyondcorp.SecurityGatewayApplicationUpstreamExternalArgs{
Endpoints: beyondcorp.SecurityGatewayApplicationUpstreamExternalEndpointArray{
&beyondcorp.SecurityGatewayApplicationUpstreamExternalEndpointArgs{
Hostname: pulumi.String("my.discovery.service.com"),
Port: pulumi.Int(443),
},
},
},
ProxyProtocol: &beyondcorp.SecurityGatewayApplicationUpstreamProxyProtocolArgs{
AllowedClientHeaders: pulumi.StringArray{
pulumi.String("header"),
},
},
},
},
Schema: pulumi.String("API_GATEWAY"),
})
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 @default = new Gcp.Beyondcorp.SecurityGateway("default", new()
{
SecurityGatewayId = "default-sg-spa-api",
DisplayName = "My SPA Security Gateway resource",
});
var example_spa = new Gcp.Beyondcorp.SecurityGatewayApplication("example-spa", new()
{
SecurityGatewayId = @default.SecurityGatewayId,
ApplicationId = "app-discovery",
Upstreams = new[]
{
new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamArgs
{
External = new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamExternalArgs
{
Endpoints = new[]
{
new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamExternalEndpointArgs
{
Hostname = "my.discovery.service.com",
Port = 443,
},
},
},
ProxyProtocol = new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamProxyProtocolArgs
{
AllowedClientHeaders = new[]
{
"header",
},
},
},
},
Schema = "API_GATEWAY",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.beyondcorp.SecurityGateway;
import com.pulumi.gcp.beyondcorp.SecurityGatewayArgs;
import com.pulumi.gcp.beyondcorp.SecurityGatewayApplication;
import com.pulumi.gcp.beyondcorp.SecurityGatewayApplicationArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamExternalArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamProxyProtocolArgs;
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 default_ = new SecurityGateway("default", SecurityGatewayArgs.builder()
.securityGatewayId("default-sg-spa-api")
.displayName("My SPA Security Gateway resource")
.build());
var example_spa = new SecurityGatewayApplication("example-spa", SecurityGatewayApplicationArgs.builder()
.securityGatewayId(default_.securityGatewayId())
.applicationId("app-discovery")
.upstreams(SecurityGatewayApplicationUpstreamArgs.builder()
.external(SecurityGatewayApplicationUpstreamExternalArgs.builder()
.endpoints(SecurityGatewayApplicationUpstreamExternalEndpointArgs.builder()
.hostname("my.discovery.service.com")
.port(443)
.build())
.build())
.proxyProtocol(SecurityGatewayApplicationUpstreamProxyProtocolArgs.builder()
.allowedClientHeaders("header")
.build())
.build())
.schema("API_GATEWAY")
.build());
}
}
resources:
default:
type: gcp:beyondcorp:SecurityGateway
properties:
securityGatewayId: default-sg-spa-api
displayName: My SPA Security Gateway resource
example-spa:
type: gcp:beyondcorp:SecurityGatewayApplication
properties:
securityGatewayId: ${default.securityGatewayId}
applicationId: app-discovery
upstreams:
- external:
endpoints:
- hostname: my.discovery.service.com
port: 443
proxyProtocol:
allowedClientHeaders:
- header
schema: API_GATEWAY
The schema property switches from default hostname-based routing to API Gateway mode. In this mode, upstreams.external.endpoints defines the backend service by hostname and port, while proxyProtocol.allowedClientHeaders controls which HTTP headers pass through to the upstream service. This pattern suits API discovery services that don’t need endpoint matchers.
Add contextual headers for proxy gateway routing
Proxy gateways that need to forward user identity, group membership, and device information use contextual headers to pass BeyondCorp context to upstream services.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const _default = new gcp.beyondcorp.SecurityGateway("default", {
securityGatewayId: "default-sg-spa-proxy",
displayName: "My SPA Security Gateway resource",
});
const example_spa = new gcp.beyondcorp.SecurityGatewayApplication("example-spa", {
securityGatewayId: _default.securityGatewayId,
applicationId: "app-proxy",
endpointMatchers: [{
hostname: "a.site.com",
ports: [443],
}],
upstreams: [{
external: {
endpoints: [{
hostname: "my.proxy.service.com",
port: 443,
}],
},
proxyProtocol: {
allowedClientHeaders: [
"header1",
"header2",
],
contextualHeaders: {
userInfo: {
outputType: "PROTOBUF",
},
groupInfo: {
outputType: "JSON",
},
deviceInfo: {
outputType: "NONE",
},
outputType: "JSON",
},
metadataHeaders: {
"metadata-header1": "value1",
"metadata-header2": "value2",
},
gatewayIdentity: "RESOURCE_NAME",
clientIp: true,
},
}],
schema: "PROXY_GATEWAY",
});
import pulumi
import pulumi_gcp as gcp
default = gcp.beyondcorp.SecurityGateway("default",
security_gateway_id="default-sg-spa-proxy",
display_name="My SPA Security Gateway resource")
example_spa = gcp.beyondcorp.SecurityGatewayApplication("example-spa",
security_gateway_id=default.security_gateway_id,
application_id="app-proxy",
endpoint_matchers=[{
"hostname": "a.site.com",
"ports": [443],
}],
upstreams=[{
"external": {
"endpoints": [{
"hostname": "my.proxy.service.com",
"port": 443,
}],
},
"proxy_protocol": {
"allowed_client_headers": [
"header1",
"header2",
],
"contextual_headers": {
"user_info": {
"output_type": "PROTOBUF",
},
"group_info": {
"output_type": "JSON",
},
"device_info": {
"output_type": "NONE",
},
"output_type": "JSON",
},
"metadata_headers": {
"metadata-header1": "value1",
"metadata-header2": "value2",
},
"gateway_identity": "RESOURCE_NAME",
"client_ip": True,
},
}],
schema="PROXY_GATEWAY")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/beyondcorp"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_default, err := beyondcorp.NewSecurityGateway(ctx, "default", &beyondcorp.SecurityGatewayArgs{
SecurityGatewayId: pulumi.String("default-sg-spa-proxy"),
DisplayName: pulumi.String("My SPA Security Gateway resource"),
})
if err != nil {
return err
}
_, err = beyondcorp.NewSecurityGatewayApplication(ctx, "example-spa", &beyondcorp.SecurityGatewayApplicationArgs{
SecurityGatewayId: _default.SecurityGatewayId,
ApplicationId: pulumi.String("app-proxy"),
EndpointMatchers: beyondcorp.SecurityGatewayApplicationEndpointMatcherArray{
&beyondcorp.SecurityGatewayApplicationEndpointMatcherArgs{
Hostname: pulumi.String("a.site.com"),
Ports: pulumi.IntArray{
pulumi.Int(443),
},
},
},
Upstreams: beyondcorp.SecurityGatewayApplicationUpstreamArray{
&beyondcorp.SecurityGatewayApplicationUpstreamArgs{
External: &beyondcorp.SecurityGatewayApplicationUpstreamExternalArgs{
Endpoints: beyondcorp.SecurityGatewayApplicationUpstreamExternalEndpointArray{
&beyondcorp.SecurityGatewayApplicationUpstreamExternalEndpointArgs{
Hostname: pulumi.String("my.proxy.service.com"),
Port: pulumi.Int(443),
},
},
},
ProxyProtocol: &beyondcorp.SecurityGatewayApplicationUpstreamProxyProtocolArgs{
AllowedClientHeaders: pulumi.StringArray{
pulumi.String("header1"),
pulumi.String("header2"),
},
ContextualHeaders: &beyondcorp.SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersArgs{
UserInfo: &beyondcorp.SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersUserInfoArgs{
OutputType: pulumi.String("PROTOBUF"),
},
GroupInfo: &beyondcorp.SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersGroupInfoArgs{
OutputType: pulumi.String("JSON"),
},
DeviceInfo: &beyondcorp.SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersDeviceInfoArgs{
OutputType: pulumi.String("NONE"),
},
OutputType: pulumi.String("JSON"),
},
MetadataHeaders: pulumi.StringMap{
"metadata-header1": pulumi.String("value1"),
"metadata-header2": pulumi.String("value2"),
},
GatewayIdentity: pulumi.String("RESOURCE_NAME"),
ClientIp: pulumi.Bool(true),
},
},
},
Schema: pulumi.String("PROXY_GATEWAY"),
})
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 @default = new Gcp.Beyondcorp.SecurityGateway("default", new()
{
SecurityGatewayId = "default-sg-spa-proxy",
DisplayName = "My SPA Security Gateway resource",
});
var example_spa = new Gcp.Beyondcorp.SecurityGatewayApplication("example-spa", new()
{
SecurityGatewayId = @default.SecurityGatewayId,
ApplicationId = "app-proxy",
EndpointMatchers = new[]
{
new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationEndpointMatcherArgs
{
Hostname = "a.site.com",
Ports = new[]
{
443,
},
},
},
Upstreams = new[]
{
new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamArgs
{
External = new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamExternalArgs
{
Endpoints = new[]
{
new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamExternalEndpointArgs
{
Hostname = "my.proxy.service.com",
Port = 443,
},
},
},
ProxyProtocol = new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamProxyProtocolArgs
{
AllowedClientHeaders = new[]
{
"header1",
"header2",
},
ContextualHeaders = new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersArgs
{
UserInfo = new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersUserInfoArgs
{
OutputType = "PROTOBUF",
},
GroupInfo = new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersGroupInfoArgs
{
OutputType = "JSON",
},
DeviceInfo = new Gcp.Beyondcorp.Inputs.SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersDeviceInfoArgs
{
OutputType = "NONE",
},
OutputType = "JSON",
},
MetadataHeaders =
{
{ "metadata-header1", "value1" },
{ "metadata-header2", "value2" },
},
GatewayIdentity = "RESOURCE_NAME",
ClientIp = true,
},
},
},
Schema = "PROXY_GATEWAY",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.beyondcorp.SecurityGateway;
import com.pulumi.gcp.beyondcorp.SecurityGatewayArgs;
import com.pulumi.gcp.beyondcorp.SecurityGatewayApplication;
import com.pulumi.gcp.beyondcorp.SecurityGatewayApplicationArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationEndpointMatcherArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamExternalArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamProxyProtocolArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersUserInfoArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersGroupInfoArgs;
import com.pulumi.gcp.beyondcorp.inputs.SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersDeviceInfoArgs;
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 default_ = new SecurityGateway("default", SecurityGatewayArgs.builder()
.securityGatewayId("default-sg-spa-proxy")
.displayName("My SPA Security Gateway resource")
.build());
var example_spa = new SecurityGatewayApplication("example-spa", SecurityGatewayApplicationArgs.builder()
.securityGatewayId(default_.securityGatewayId())
.applicationId("app-proxy")
.endpointMatchers(SecurityGatewayApplicationEndpointMatcherArgs.builder()
.hostname("a.site.com")
.ports(443)
.build())
.upstreams(SecurityGatewayApplicationUpstreamArgs.builder()
.external(SecurityGatewayApplicationUpstreamExternalArgs.builder()
.endpoints(SecurityGatewayApplicationUpstreamExternalEndpointArgs.builder()
.hostname("my.proxy.service.com")
.port(443)
.build())
.build())
.proxyProtocol(SecurityGatewayApplicationUpstreamProxyProtocolArgs.builder()
.allowedClientHeaders(
"header1",
"header2")
.contextualHeaders(SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersArgs.builder()
.userInfo(SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersUserInfoArgs.builder()
.outputType("PROTOBUF")
.build())
.groupInfo(SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersGroupInfoArgs.builder()
.outputType("JSON")
.build())
.deviceInfo(SecurityGatewayApplicationUpstreamProxyProtocolContextualHeadersDeviceInfoArgs.builder()
.outputType("NONE")
.build())
.outputType("JSON")
.build())
.metadataHeaders(Map.ofEntries(
Map.entry("metadata-header1", "value1"),
Map.entry("metadata-header2", "value2")
))
.gatewayIdentity("RESOURCE_NAME")
.clientIp(true)
.build())
.build())
.schema("PROXY_GATEWAY")
.build());
}
}
resources:
default:
type: gcp:beyondcorp:SecurityGateway
properties:
securityGatewayId: default-sg-spa-proxy
displayName: My SPA Security Gateway resource
example-spa:
type: gcp:beyondcorp:SecurityGatewayApplication
properties:
securityGatewayId: ${default.securityGatewayId}
applicationId: app-proxy
endpointMatchers:
- hostname: a.site.com
ports:
- 443
upstreams:
- external:
endpoints:
- hostname: my.proxy.service.com
port: 443
proxyProtocol:
allowedClientHeaders:
- header1
- header2
contextualHeaders:
userInfo:
outputType: PROTOBUF
groupInfo:
outputType: JSON
deviceInfo:
outputType: NONE
outputType: JSON
metadataHeaders:
metadata-header1: value1
metadata-header2: value2
gatewayIdentity: RESOURCE_NAME
clientIp: true
schema: PROXY_GATEWAY
The PROXY_GATEWAY schema enables full proxy protocol configuration. The contextualHeaders block forwards BeyondCorp identity data: userInfo, groupInfo, and deviceInfo can each output as PROTOBUF, JSON, or NONE. The metadataHeaders property adds custom key-value pairs, gatewayIdentity includes the gateway resource name, and clientIp forwards the original client IP address. This gives upstream services complete visibility into the BeyondCorp security context.
Beyond these examples
These snippets focus on specific Security Gateway Application features: endpoint matching and port filtering, VPC network routing and egress policies, API Gateway and Proxy Gateway schemas, and contextual headers and identity forwarding. They’re intentionally minimal rather than full zero-trust deployments.
The examples reference pre-existing infrastructure such as BeyondCorp Security Gateway resources, VPC networks and subnets, and external services like discovery endpoints or proxy services. They focus on application configuration rather than provisioning the surrounding infrastructure.
To keep things focused, common application patterns are omitted, including:
- Display names and resource descriptions (displayName)
- Multiple upstream configurations per application
- Advanced egress policy rules beyond region selection
- Error handling and retry configuration
These omissions are intentional: the goal is to illustrate how each Security Gateway Application feature is wired, not provide drop-in zero-trust modules. See the BeyondCorp Security Gateway Application resource reference for all available configuration options.
Let's configure GCP BeyondCorp Security Gateway Applications
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration & Setup
applicationId, securityGatewayId, and project properties are immutable and cannot be changed after resource creation.applicationId must start with a letter, contain 4-63 characters from /a-z-/, and end with a number or letter.PROXY_GATEWAY is used with endpointMatchers for proxy scenarios (matching hostnames/ports). API_GATEWAY is used without endpointMatchers for API discovery scenarios where traffic is routed directly to external endpoints.Endpoint Matching & Routing
*.abc.com, or (Hostname & Ports) like abc.com with ports 22,33.upstreams with a network property (VPC reference) and egressPolicy specifying regions, as shown in the VPC example.upstreams.external.endpoints with hostname and port properties, as shown in the SPA API and SPA Proxy examples.