Configure GCP Cloud Armor Region Security Policies

The gcp:compute/regionSecurityPolicy:RegionSecurityPolicy resource, part of the Pulumi GCP provider, defines a Cloud Armor security policy that filters traffic at the HTTP or network layer within a specific GCP region. This guide focuses on four capabilities: policy type selection, rule-based request filtering with reCAPTCHA, DDoS protection, and custom packet field extraction.

Security policies are created independently but must be attached to backend services or load balancers to filter traffic. The examples are intentionally small. Combine them with your own backend services and attach policies using separate resources.

Create a basic Cloud Armor security policy

Most deployments start with a minimal policy that establishes the foundation for request filtering.

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

const region_sec_policy_basic = new gcp.compute.RegionSecurityPolicy("region-sec-policy-basic", {
    name: "my-sec-policy-basic",
    description: "basic region security policy",
    type: "CLOUD_ARMOR",
});
import pulumi
import pulumi_gcp as gcp

region_sec_policy_basic = gcp.compute.RegionSecurityPolicy("region-sec-policy-basic",
    name="my-sec-policy-basic",
    description="basic region security policy",
    type="CLOUD_ARMOR")
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewRegionSecurityPolicy(ctx, "region-sec-policy-basic", &compute.RegionSecurityPolicyArgs{
			Name:        pulumi.String("my-sec-policy-basic"),
			Description: pulumi.String("basic region security policy"),
			Type:        pulumi.String("CLOUD_ARMOR"),
		})
		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 region_sec_policy_basic = new Gcp.Compute.RegionSecurityPolicy("region-sec-policy-basic", new()
    {
        Name = "my-sec-policy-basic",
        Description = "basic region security policy",
        Type = "CLOUD_ARMOR",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.RegionSecurityPolicy;
import com.pulumi.gcp.compute.RegionSecurityPolicyArgs;
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 region_sec_policy_basic = new RegionSecurityPolicy("region-sec-policy-basic", RegionSecurityPolicyArgs.builder()
            .name("my-sec-policy-basic")
            .description("basic region security policy")
            .type("CLOUD_ARMOR")
            .build());

    }
}
resources:
  region-sec-policy-basic:
    type: gcp:compute:RegionSecurityPolicy
    properties:
      name: my-sec-policy-basic
      description: basic region security policy
      type: CLOUD_ARMOR

The type property determines where the policy operates. CLOUD_ARMOR policies filter HTTP requests before they reach backend services. The policy itself doesn’t include rules yet; you can add them later or let Cloud Armor create a default allow rule.

Filter requests with reCAPTCHA and IP-based rules

Security policies enforce access control through rules that evaluate request properties and block suspicious traffic.

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

const region_sec_policy_with_rules = new gcp.compute.RegionSecurityPolicy("region-sec-policy-with-rules", {
    name: "my-sec-policy-with-rules",
    description: "basic region security policy with multiple rules",
    type: "CLOUD_ARMOR",
    rules: [
        {
            action: "deny",
            priority: 1000,
            match: {
                expr: {
                    expression: "request.path.matches(\"/login.html\") && token.recaptcha_session.score < 0.2",
                },
            },
        },
        {
            action: "deny",
            priority: 2147483647,
            match: {
                versionedExpr: "SRC_IPS_V1",
                config: {
                    srcIpRanges: ["*"],
                },
            },
            description: "default rule",
        },
    ],
});
import pulumi
import pulumi_gcp as gcp

region_sec_policy_with_rules = gcp.compute.RegionSecurityPolicy("region-sec-policy-with-rules",
    name="my-sec-policy-with-rules",
    description="basic region security policy with multiple rules",
    type="CLOUD_ARMOR",
    rules=[
        {
            "action": "deny",
            "priority": 1000,
            "match": {
                "expr": {
                    "expression": "request.path.matches(\"/login.html\") && token.recaptcha_session.score < 0.2",
                },
            },
        },
        {
            "action": "deny",
            "priority": 2147483647,
            "match": {
                "versioned_expr": "SRC_IPS_V1",
                "config": {
                    "src_ip_ranges": ["*"],
                },
            },
            "description": "default rule",
        },
    ])
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewRegionSecurityPolicy(ctx, "region-sec-policy-with-rules", &compute.RegionSecurityPolicyArgs{
			Name:        pulumi.String("my-sec-policy-with-rules"),
			Description: pulumi.String("basic region security policy with multiple rules"),
			Type:        pulumi.String("CLOUD_ARMOR"),
			Rules: compute.RegionSecurityPolicyRuleTypeArray{
				&compute.RegionSecurityPolicyRuleTypeArgs{
					Action:   pulumi.String("deny"),
					Priority: pulumi.Int(1000),
					Match: &compute.RegionSecurityPolicyRuleMatchArgs{
						Expr: &compute.RegionSecurityPolicyRuleMatchExprArgs{
							Expression: pulumi.String("request.path.matches(\"/login.html\") && token.recaptcha_session.score < 0.2"),
						},
					},
				},
				&compute.RegionSecurityPolicyRuleTypeArgs{
					Action:   pulumi.String("deny"),
					Priority: pulumi.Int(2147483647),
					Match: &compute.RegionSecurityPolicyRuleMatchArgs{
						VersionedExpr: pulumi.String("SRC_IPS_V1"),
						Config: &compute.RegionSecurityPolicyRuleMatchConfigArgs{
							SrcIpRanges: pulumi.StringArray{
								pulumi.String("*"),
							},
						},
					},
					Description: pulumi.String("default rule"),
				},
			},
		})
		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 region_sec_policy_with_rules = new Gcp.Compute.RegionSecurityPolicy("region-sec-policy-with-rules", new()
    {
        Name = "my-sec-policy-with-rules",
        Description = "basic region security policy with multiple rules",
        Type = "CLOUD_ARMOR",
        Rules = new[]
        {
            new Gcp.Compute.Inputs.RegionSecurityPolicyRuleArgs
            {
                Action = "deny",
                Priority = 1000,
                Match = new Gcp.Compute.Inputs.RegionSecurityPolicyRuleMatchArgs
                {
                    Expr = new Gcp.Compute.Inputs.RegionSecurityPolicyRuleMatchExprArgs
                    {
                        Expression = "request.path.matches(\"/login.html\") && token.recaptcha_session.score < 0.2",
                    },
                },
            },
            new Gcp.Compute.Inputs.RegionSecurityPolicyRuleArgs
            {
                Action = "deny",
                Priority = 2147483647,
                Match = new Gcp.Compute.Inputs.RegionSecurityPolicyRuleMatchArgs
                {
                    VersionedExpr = "SRC_IPS_V1",
                    Config = new Gcp.Compute.Inputs.RegionSecurityPolicyRuleMatchConfigArgs
                    {
                        SrcIpRanges = new[]
                        {
                            "*",
                        },
                    },
                },
                Description = "default rule",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.RegionSecurityPolicy;
import com.pulumi.gcp.compute.RegionSecurityPolicyArgs;
import com.pulumi.gcp.compute.inputs.RegionSecurityPolicyRuleArgs;
import com.pulumi.gcp.compute.inputs.RegionSecurityPolicyRuleMatchArgs;
import com.pulumi.gcp.compute.inputs.RegionSecurityPolicyRuleMatchExprArgs;
import com.pulumi.gcp.compute.inputs.RegionSecurityPolicyRuleMatchConfigArgs;
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 region_sec_policy_with_rules = new RegionSecurityPolicy("region-sec-policy-with-rules", RegionSecurityPolicyArgs.builder()
            .name("my-sec-policy-with-rules")
            .description("basic region security policy with multiple rules")
            .type("CLOUD_ARMOR")
            .rules(            
                RegionSecurityPolicyRuleArgs.builder()
                    .action("deny")
                    .priority(1000)
                    .match(RegionSecurityPolicyRuleMatchArgs.builder()
                        .expr(RegionSecurityPolicyRuleMatchExprArgs.builder()
                            .expression("request.path.matches(\"/login.html\") && token.recaptcha_session.score < 0.2")
                            .build())
                        .build())
                    .build(),
                RegionSecurityPolicyRuleArgs.builder()
                    .action("deny")
                    .priority(2147483647)
                    .match(RegionSecurityPolicyRuleMatchArgs.builder()
                        .versionedExpr("SRC_IPS_V1")
                        .config(RegionSecurityPolicyRuleMatchConfigArgs.builder()
                            .srcIpRanges("*")
                            .build())
                        .build())
                    .description("default rule")
                    .build())
            .build());

    }
}
resources:
  region-sec-policy-with-rules:
    type: gcp:compute:RegionSecurityPolicy
    properties:
      name: my-sec-policy-with-rules
      description: basic region security policy with multiple rules
      type: CLOUD_ARMOR
      rules:
        - action: deny
          priority: '1000'
          match:
            expr:
              expression: request.path.matches("/login.html") && token.recaptcha_session.score < 0.2
        - action: deny
          priority: '2147483647'
          match:
            versionedExpr: SRC_IPS_V1
            config:
              srcIpRanges:
                - '*'
          description: default rule

Rules execute in priority order (lower numbers first). The first rule blocks requests to /login.html when reCAPTCHA scores fall below 0.2. The expression property uses Common Expression Language (CEL) to combine path matching with reCAPTCHA session scores. The second rule (priority 2147483647) acts as a default deny, blocking all traffic that doesn’t match earlier rules. The versionedExpr field uses predefined expressions; SRC_IPS_V1 matches source IP ranges.

Enable DDoS protection for network policies

Network-layer policies protect load balancing resources from volumetric attacks by analyzing traffic patterns.

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

const region_sec_policy_ddos_protection = new gcp.compute.RegionSecurityPolicy("region-sec-policy-ddos-protection", {
    name: "my-sec-policy-ddos-protection",
    description: "with ddos protection config",
    type: "CLOUD_ARMOR_NETWORK",
    ddosProtectionConfig: {
        ddosProtection: "ADVANCED_PREVIEW",
    },
});
import pulumi
import pulumi_gcp as gcp

region_sec_policy_ddos_protection = gcp.compute.RegionSecurityPolicy("region-sec-policy-ddos-protection",
    name="my-sec-policy-ddos-protection",
    description="with ddos protection config",
    type="CLOUD_ARMOR_NETWORK",
    ddos_protection_config={
        "ddos_protection": "ADVANCED_PREVIEW",
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewRegionSecurityPolicy(ctx, "region-sec-policy-ddos-protection", &compute.RegionSecurityPolicyArgs{
			Name:        pulumi.String("my-sec-policy-ddos-protection"),
			Description: pulumi.String("with ddos protection config"),
			Type:        pulumi.String("CLOUD_ARMOR_NETWORK"),
			DdosProtectionConfig: &compute.RegionSecurityPolicyDdosProtectionConfigArgs{
				DdosProtection: pulumi.String("ADVANCED_PREVIEW"),
			},
		})
		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 region_sec_policy_ddos_protection = new Gcp.Compute.RegionSecurityPolicy("region-sec-policy-ddos-protection", new()
    {
        Name = "my-sec-policy-ddos-protection",
        Description = "with ddos protection config",
        Type = "CLOUD_ARMOR_NETWORK",
        DdosProtectionConfig = new Gcp.Compute.Inputs.RegionSecurityPolicyDdosProtectionConfigArgs
        {
            DdosProtection = "ADVANCED_PREVIEW",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.RegionSecurityPolicy;
import com.pulumi.gcp.compute.RegionSecurityPolicyArgs;
import com.pulumi.gcp.compute.inputs.RegionSecurityPolicyDdosProtectionConfigArgs;
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 region_sec_policy_ddos_protection = new RegionSecurityPolicy("region-sec-policy-ddos-protection", RegionSecurityPolicyArgs.builder()
            .name("my-sec-policy-ddos-protection")
            .description("with ddos protection config")
            .type("CLOUD_ARMOR_NETWORK")
            .ddosProtectionConfig(RegionSecurityPolicyDdosProtectionConfigArgs.builder()
                .ddosProtection("ADVANCED_PREVIEW")
                .build())
            .build());

    }
}
resources:
  region-sec-policy-ddos-protection:
    type: gcp:compute:RegionSecurityPolicy
    properties:
      name: my-sec-policy-ddos-protection
      description: with ddos protection config
      type: CLOUD_ARMOR_NETWORK
      ddosProtectionConfig:
        ddosProtection: ADVANCED_PREVIEW

The CLOUD_ARMOR_NETWORK type operates at the network layer, filtering packets before they reach backend services or instances. The ddosProtectionConfig enables advanced DDoS mitigation. ADVANCED_PREVIEW provides enhanced protection features during the preview period.

Extract custom packet fields for network filtering

Network policies can inspect arbitrary packet data beyond standard headers for specialized protocol filtering.

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

const region_sec_policy_user_defined_fields = new gcp.compute.RegionSecurityPolicy("region-sec-policy-user-defined-fields", {
    name: "my-sec-policy-user-defined-fields",
    description: "with user defined fields",
    type: "CLOUD_ARMOR_NETWORK",
    userDefinedFields: [
        {
            name: "SIG1_AT_0",
            base: "UDP",
            offset: 8,
            size: 2,
            mask: "0x8F00",
        },
        {
            name: "SIG2_AT_8",
            base: "UDP",
            offset: 16,
            size: 4,
            mask: "0xFFFFFFFF",
        },
    ],
});
import pulumi
import pulumi_gcp as gcp

region_sec_policy_user_defined_fields = gcp.compute.RegionSecurityPolicy("region-sec-policy-user-defined-fields",
    name="my-sec-policy-user-defined-fields",
    description="with user defined fields",
    type="CLOUD_ARMOR_NETWORK",
    user_defined_fields=[
        {
            "name": "SIG1_AT_0",
            "base": "UDP",
            "offset": 8,
            "size": 2,
            "mask": "0x8F00",
        },
        {
            "name": "SIG2_AT_8",
            "base": "UDP",
            "offset": 16,
            "size": 4,
            "mask": "0xFFFFFFFF",
        },
    ])
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewRegionSecurityPolicy(ctx, "region-sec-policy-user-defined-fields", &compute.RegionSecurityPolicyArgs{
			Name:        pulumi.String("my-sec-policy-user-defined-fields"),
			Description: pulumi.String("with user defined fields"),
			Type:        pulumi.String("CLOUD_ARMOR_NETWORK"),
			UserDefinedFields: compute.RegionSecurityPolicyUserDefinedFieldArray{
				&compute.RegionSecurityPolicyUserDefinedFieldArgs{
					Name:   pulumi.String("SIG1_AT_0"),
					Base:   pulumi.String("UDP"),
					Offset: pulumi.Int(8),
					Size:   pulumi.Int(2),
					Mask:   pulumi.String("0x8F00"),
				},
				&compute.RegionSecurityPolicyUserDefinedFieldArgs{
					Name:   pulumi.String("SIG2_AT_8"),
					Base:   pulumi.String("UDP"),
					Offset: pulumi.Int(16),
					Size:   pulumi.Int(4),
					Mask:   pulumi.String("0xFFFFFFFF"),
				},
			},
		})
		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 region_sec_policy_user_defined_fields = new Gcp.Compute.RegionSecurityPolicy("region-sec-policy-user-defined-fields", new()
    {
        Name = "my-sec-policy-user-defined-fields",
        Description = "with user defined fields",
        Type = "CLOUD_ARMOR_NETWORK",
        UserDefinedFields = new[]
        {
            new Gcp.Compute.Inputs.RegionSecurityPolicyUserDefinedFieldArgs
            {
                Name = "SIG1_AT_0",
                Base = "UDP",
                Offset = 8,
                Size = 2,
                Mask = "0x8F00",
            },
            new Gcp.Compute.Inputs.RegionSecurityPolicyUserDefinedFieldArgs
            {
                Name = "SIG2_AT_8",
                Base = "UDP",
                Offset = 16,
                Size = 4,
                Mask = "0xFFFFFFFF",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.RegionSecurityPolicy;
import com.pulumi.gcp.compute.RegionSecurityPolicyArgs;
import com.pulumi.gcp.compute.inputs.RegionSecurityPolicyUserDefinedFieldArgs;
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 region_sec_policy_user_defined_fields = new RegionSecurityPolicy("region-sec-policy-user-defined-fields", RegionSecurityPolicyArgs.builder()
            .name("my-sec-policy-user-defined-fields")
            .description("with user defined fields")
            .type("CLOUD_ARMOR_NETWORK")
            .userDefinedFields(            
                RegionSecurityPolicyUserDefinedFieldArgs.builder()
                    .name("SIG1_AT_0")
                    .base("UDP")
                    .offset(8)
                    .size(2)
                    .mask("0x8F00")
                    .build(),
                RegionSecurityPolicyUserDefinedFieldArgs.builder()
                    .name("SIG2_AT_8")
                    .base("UDP")
                    .offset(16)
                    .size(4)
                    .mask("0xFFFFFFFF")
                    .build())
            .build());

    }
}
resources:
  region-sec-policy-user-defined-fields:
    type: gcp:compute:RegionSecurityPolicy
    properties:
      name: my-sec-policy-user-defined-fields
      description: with user defined fields
      type: CLOUD_ARMOR_NETWORK
      userDefinedFields:
        - name: SIG1_AT_0
          base: UDP
          offset: 8
          size: 2
          mask: 0x8F00
        - name: SIG2_AT_8
          base: UDP
          offset: 16
          size: 4
          mask: 0xFFFFFFFF

User-defined fields extract specific byte ranges from packets. Each field specifies a base protocol (UDP or TCP), an offset in bytes from the protocol header, and a size. The mask property selects specific bits from the extracted bytes. SIG1_AT_0 extracts 2 bytes starting at offset 8 in the UDP header, applying mask 0x8F00. Rules can then match against these extracted values.

Beyond these examples

These snippets focus on specific security policy features: policy types, rule-based request filtering, and DDoS protection and custom packet inspection. They’re intentionally minimal rather than complete security configurations.

The examples assume pre-existing infrastructure such as GCP project and region configuration, backend services or load balancers to attach policies to, and reCAPTCHA Enterprise keys for reCAPTCHA-based rules. They focus on policy configuration without showing how policies attach to resources.

To keep things focused, common security policy patterns are omitted, including:

  • Policy attachment to backend services or load balancers
  • Advanced options (JSON parsing, log configuration)
  • Rate limiting and adaptive protection
  • Custom error responses and redirects

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

Let's configure GCP Cloud Armor Region Security Policies

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Policy Types & Use Cases
What's the difference between CLOUD_ARMOR, CLOUD_ARMOR_EDGE, and CLOUD_ARMOR_NETWORK?
CLOUD_ARMOR filters HTTP requests to backend services before they reach origin servers. CLOUD_ARMOR_EDGE filters HTTP requests to backend services and Cloud Storage buckets before serving from Google’s cache. CLOUD_ARMOR_NETWORK filters packets targeting network load balancing resources like backend services, target pools, and instances with external IPs.
Can I use user-defined fields with CLOUD_ARMOR policies?
No, user-defined fields are only available for CLOUD_ARMOR_NETWORK policies. They extract up to 4 bytes from packet headers (IPv4, IPv6, TCP, or UDP) with optional masks for custom matching rules.
Rules & Default Behavior
Why does my security policy require a default rule with priority 2147483647?
Every security policy must have a default rule with priority 2147483647 and match “*” to handle traffic that doesn’t match other rules.
What happens if I don't specify any rules when creating a security policy?
The system automatically adds a default rule with action “allow” and priority 2147483647.
How do I create rules with reCAPTCHA scoring?
Use the match.expr.expression field with reCAPTCHA session tokens, like token.recaptcha_session.score < 0.2 to deny low-scoring requests.
Immutability & Constraints
What properties are immutable after creation?
The name, project, region, and type properties cannot be changed after resource creation. Modifying these requires recreating the resource.
What are the naming requirements for a security policy?
The name must be 1-63 characters long and comply with RFC1035. It must match the pattern [a-z](?:[-a-z0-9]{0,61}[a-z0-9])?, starting with a lowercase letter and ending with a letter or digit.
Advanced Features
How do I enable DDoS protection for my security policy?
Configure ddosProtectionConfig with the ddosProtection field set to a value like ADVANCED_PREVIEW. This is typically used with CLOUD_ARMOR_NETWORK policies.

Using a different cloud?

Explore security guides for other cloud providers: