Configure GCP Cloud Armor Region Security Policies

The gcp:compute/regionSecurityPolicy:RegionSecurityPolicy resource, part of the Pulumi GCP provider, defines a regional Cloud Armor security policy that filters traffic to backend services, load balancers, or instances. This guide focuses on three capabilities: HTTP request filtering with rules, network-layer DDoS protection, and custom packet field extraction.

Security policies are created independently but must be attached to backend services, target pools, or instances to take effect. The examples are intentionally small. Combine them with your own backend infrastructure and attach policies using the appropriate resource properties.

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 applies. CLOUD_ARMOR policies filter HTTP requests before they reach backend services. Without explicit rules, Cloud Armor creates a default allow-all rule automatically.

Filter requests with reCAPTCHA and IP-based rules

Applications often block suspicious traffic based on reCAPTCHA scores or source IP addresses.

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

Cloud Armor evaluates rules in priority order (lowest number first). The first rule denies requests to /login.html when reCAPTCHA scores fall below 0.2. The match.expr.expression property accepts CEL (Common Expression Language) for flexible filtering. The second rule with priority 2147483647 serves as the default rule, required by Cloud Armor. Here it denies all traffic not matched by higher-priority rules.

Enable advanced DDoS protection for network traffic

Network-layer attacks require specialized protection below the HTTP layer.

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 shifts protection to the network layer, defending load balancers and instances with external IPs. The ddosProtectionConfig enables advanced DDoS mitigation that analyzes packet patterns and traffic volume. This configuration protects against volumetric attacks that HTTP-layer policies cannot detect.

Extract custom packet fields for network filtering

Some protocols require inspection of specific byte ranges in UDP or TCP packets.

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 bytes from packet headers for matching in rules. Each field specifies a base protocol (UDP, TCP, IPv4, IPv6), an offset in bytes from the header start, a size (1-4 bytes), and an optional mask to select specific bits. The name property creates an identifier you reference in rule match conditions. This enables protocol-specific filtering beyond standard IP and port matching.

Beyond these examples

These snippets focus on specific security policy features: HTTP and network-layer policy types, rule-based filtering with priorities, and DDoS protection and custom packet inspection. They’re intentionally minimal rather than full security configurations.

The examples may reference pre-existing infrastructure such as GCP project and region, and backend services or load balancers to attach policies to. They focus on configuring the policy rather than provisioning the protected infrastructure.

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
  • Preconfigured WAF rules and threat intelligence

These omissions are intentional: the goal is to illustrate how each security policy feature is wired, not provide drop-in protection 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.
Rules & Default Behavior
Why does my security policy need a rule with priority 2147483647?
Every security policy must have a default rule with priority 2147483647 and match “*”. This serves as the catch-all rule for traffic that doesn’t match other rules.
What happens if I don't specify any rules when creating a policy?
A default rule with action “allow” is automatically added with priority 2147483647 and match “*”.
Immutability & Constraints
What properties can't I change after creating a security policy?
The name, project, region, and type properties are all immutable and can only be set at resource creation time.
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])?, meaning it starts with a lowercase letter, followed by lowercase letters, digits, or hyphens, and cannot end with a hyphen.
Advanced Features
What are user-defined fields and when can I use them?
User-defined fields extract up to 4 bytes from packet headers (IPv4, IPv6, TCP, or UDP) at a fixed offset, with an optional mask to select specific bits. Rules can then match on these extracted values. They’re only available for CLOUD_ARMOR_NETWORK policies.
How do I enable DDoS protection for my security policy?
Configure ddosProtectionConfig with the desired protection level. Use type: "CLOUD_ARMOR_NETWORK" and set ddosProtectionConfig.ddosProtection to your preferred level (e.g., “ADVANCED_PREVIEW”).
Can I use reCAPTCHA scores in my security policy rules?
Yes, you can use expressions like token.recaptcha_session.score < 0.2 in the match.expr.expression field to filter requests based on reCAPTCHA scores.

Using a different cloud?

Explore security guides for other cloud providers: