The gcp:compute/securityPolicyRule:SecurityPolicyRule resource, part of the Pulumi GCP provider, defines individual rules within a Cloud Armor security policy: their match conditions, actions, and priority ordering. This guide focuses on three capabilities: IP-based traffic filtering, priority-based rule evaluation, and allow/deny actions.
Security policy rules belong to a Cloud Armor security policy that must exist separately. The examples are intentionally small. Combine them with your own security policies and backend services.
Allow traffic from specific IP ranges
Cloud Armor protects applications by filtering traffic based on IP addresses, geographic location, or request patterns. Most deployments start with simple IP-based rules.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const _default = new gcp.compute.SecurityPolicy("default", {
name: "policyruletest",
description: "basic global security policy",
type: "CLOUD_ARMOR",
});
const policyRule = new gcp.compute.SecurityPolicyRule("policy_rule", {
securityPolicy: _default.name,
description: "new rule",
priority: 100,
match: {
versionedExpr: "SRC_IPS_V1",
config: {
srcIpRanges: ["10.10.0.0/16"],
},
},
action: "allow",
preview: true,
});
import pulumi
import pulumi_gcp as gcp
default = gcp.compute.SecurityPolicy("default",
name="policyruletest",
description="basic global security policy",
type="CLOUD_ARMOR")
policy_rule = gcp.compute.SecurityPolicyRule("policy_rule",
security_policy=default.name,
description="new rule",
priority=100,
match={
"versioned_expr": "SRC_IPS_V1",
"config": {
"src_ip_ranges": ["10.10.0.0/16"],
},
},
action="allow",
preview=True)
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 {
_default, err := compute.NewSecurityPolicy(ctx, "default", &compute.SecurityPolicyArgs{
Name: pulumi.String("policyruletest"),
Description: pulumi.String("basic global security policy"),
Type: pulumi.String("CLOUD_ARMOR"),
})
if err != nil {
return err
}
_, err = compute.NewSecurityPolicyRule(ctx, "policy_rule", &compute.SecurityPolicyRuleArgs{
SecurityPolicy: _default.Name,
Description: pulumi.String("new rule"),
Priority: pulumi.Int(100),
Match: &compute.SecurityPolicyRuleMatchArgs{
VersionedExpr: pulumi.String("SRC_IPS_V1"),
Config: &compute.SecurityPolicyRuleMatchConfigArgs{
SrcIpRanges: pulumi.StringArray{
pulumi.String("10.10.0.0/16"),
},
},
},
Action: pulumi.String("allow"),
Preview: 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 @default = new Gcp.Compute.SecurityPolicy("default", new()
{
Name = "policyruletest",
Description = "basic global security policy",
Type = "CLOUD_ARMOR",
});
var policyRule = new Gcp.Compute.SecurityPolicyRule("policy_rule", new()
{
SecurityPolicy = @default.Name,
Description = "new rule",
Priority = 100,
Match = new Gcp.Compute.Inputs.SecurityPolicyRuleMatchArgs
{
VersionedExpr = "SRC_IPS_V1",
Config = new Gcp.Compute.Inputs.SecurityPolicyRuleMatchConfigArgs
{
SrcIpRanges = new[]
{
"10.10.0.0/16",
},
},
},
Action = "allow",
Preview = true,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.SecurityPolicy;
import com.pulumi.gcp.compute.SecurityPolicyArgs;
import com.pulumi.gcp.compute.SecurityPolicyRule;
import com.pulumi.gcp.compute.SecurityPolicyRuleArgs;
import com.pulumi.gcp.compute.inputs.SecurityPolicyRuleMatchArgs;
import com.pulumi.gcp.compute.inputs.SecurityPolicyRuleMatchConfigArgs;
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 SecurityPolicy("default", SecurityPolicyArgs.builder()
.name("policyruletest")
.description("basic global security policy")
.type("CLOUD_ARMOR")
.build());
var policyRule = new SecurityPolicyRule("policyRule", SecurityPolicyRuleArgs.builder()
.securityPolicy(default_.name())
.description("new rule")
.priority(100)
.match(SecurityPolicyRuleMatchArgs.builder()
.versionedExpr("SRC_IPS_V1")
.config(SecurityPolicyRuleMatchConfigArgs.builder()
.srcIpRanges("10.10.0.0/16")
.build())
.build())
.action("allow")
.preview(true)
.build());
}
}
resources:
default:
type: gcp:compute:SecurityPolicy
properties:
name: policyruletest
description: basic global security policy
type: CLOUD_ARMOR
policyRule:
type: gcp:compute:SecurityPolicyRule
name: policy_rule
properties:
securityPolicy: ${default.name}
description: new rule
priority: 100
match:
versionedExpr: SRC_IPS_V1
config:
srcIpRanges:
- 10.10.0.0/16
action: allow
preview: true
When incoming traffic matches the srcIpRanges in the match configuration, Cloud Armor applies the specified action. The priority determines evaluation order: lower numbers are evaluated first. The preview property lets you test rules without enforcing them. The versionedExpr field specifies the match expression format; SRC_IPS_V1 indicates IP-based matching.
Configure a default deny rule with exceptions
Security policies often follow a deny-by-default approach where all traffic is blocked unless explicitly allowed.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const _default = new gcp.compute.SecurityPolicy("default", {
name: "policyruletest",
description: "basic global security policy",
type: "CLOUD_ARMOR",
});
const defaultRule = new gcp.compute.SecurityPolicyRule("default_rule", {
securityPolicy: _default.name,
description: "default rule",
action: "deny",
priority: 2147483647,
match: {
versionedExpr: "SRC_IPS_V1",
config: {
srcIpRanges: ["*"],
},
},
});
const policyRule = new gcp.compute.SecurityPolicyRule("policy_rule", {
securityPolicy: _default.name,
description: "new rule",
priority: 100,
match: {
versionedExpr: "SRC_IPS_V1",
config: {
srcIpRanges: ["10.10.0.0/16"],
},
},
action: "allow",
preview: true,
});
import pulumi
import pulumi_gcp as gcp
default = gcp.compute.SecurityPolicy("default",
name="policyruletest",
description="basic global security policy",
type="CLOUD_ARMOR")
default_rule = gcp.compute.SecurityPolicyRule("default_rule",
security_policy=default.name,
description="default rule",
action="deny",
priority=2147483647,
match={
"versioned_expr": "SRC_IPS_V1",
"config": {
"src_ip_ranges": ["*"],
},
})
policy_rule = gcp.compute.SecurityPolicyRule("policy_rule",
security_policy=default.name,
description="new rule",
priority=100,
match={
"versioned_expr": "SRC_IPS_V1",
"config": {
"src_ip_ranges": ["10.10.0.0/16"],
},
},
action="allow",
preview=True)
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 {
_default, err := compute.NewSecurityPolicy(ctx, "default", &compute.SecurityPolicyArgs{
Name: pulumi.String("policyruletest"),
Description: pulumi.String("basic global security policy"),
Type: pulumi.String("CLOUD_ARMOR"),
})
if err != nil {
return err
}
_, err = compute.NewSecurityPolicyRule(ctx, "default_rule", &compute.SecurityPolicyRuleArgs{
SecurityPolicy: _default.Name,
Description: pulumi.String("default rule"),
Action: pulumi.String("deny"),
Priority: pulumi.Int(2147483647),
Match: &compute.SecurityPolicyRuleMatchArgs{
VersionedExpr: pulumi.String("SRC_IPS_V1"),
Config: &compute.SecurityPolicyRuleMatchConfigArgs{
SrcIpRanges: pulumi.StringArray{
pulumi.String("*"),
},
},
},
})
if err != nil {
return err
}
_, err = compute.NewSecurityPolicyRule(ctx, "policy_rule", &compute.SecurityPolicyRuleArgs{
SecurityPolicy: _default.Name,
Description: pulumi.String("new rule"),
Priority: pulumi.Int(100),
Match: &compute.SecurityPolicyRuleMatchArgs{
VersionedExpr: pulumi.String("SRC_IPS_V1"),
Config: &compute.SecurityPolicyRuleMatchConfigArgs{
SrcIpRanges: pulumi.StringArray{
pulumi.String("10.10.0.0/16"),
},
},
},
Action: pulumi.String("allow"),
Preview: 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 @default = new Gcp.Compute.SecurityPolicy("default", new()
{
Name = "policyruletest",
Description = "basic global security policy",
Type = "CLOUD_ARMOR",
});
var defaultRule = new Gcp.Compute.SecurityPolicyRule("default_rule", new()
{
SecurityPolicy = @default.Name,
Description = "default rule",
Action = "deny",
Priority = 2147483647,
Match = new Gcp.Compute.Inputs.SecurityPolicyRuleMatchArgs
{
VersionedExpr = "SRC_IPS_V1",
Config = new Gcp.Compute.Inputs.SecurityPolicyRuleMatchConfigArgs
{
SrcIpRanges = new[]
{
"*",
},
},
},
});
var policyRule = new Gcp.Compute.SecurityPolicyRule("policy_rule", new()
{
SecurityPolicy = @default.Name,
Description = "new rule",
Priority = 100,
Match = new Gcp.Compute.Inputs.SecurityPolicyRuleMatchArgs
{
VersionedExpr = "SRC_IPS_V1",
Config = new Gcp.Compute.Inputs.SecurityPolicyRuleMatchConfigArgs
{
SrcIpRanges = new[]
{
"10.10.0.0/16",
},
},
},
Action = "allow",
Preview = true,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.SecurityPolicy;
import com.pulumi.gcp.compute.SecurityPolicyArgs;
import com.pulumi.gcp.compute.SecurityPolicyRule;
import com.pulumi.gcp.compute.SecurityPolicyRuleArgs;
import com.pulumi.gcp.compute.inputs.SecurityPolicyRuleMatchArgs;
import com.pulumi.gcp.compute.inputs.SecurityPolicyRuleMatchConfigArgs;
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 SecurityPolicy("default", SecurityPolicyArgs.builder()
.name("policyruletest")
.description("basic global security policy")
.type("CLOUD_ARMOR")
.build());
var defaultRule = new SecurityPolicyRule("defaultRule", SecurityPolicyRuleArgs.builder()
.securityPolicy(default_.name())
.description("default rule")
.action("deny")
.priority(2147483647)
.match(SecurityPolicyRuleMatchArgs.builder()
.versionedExpr("SRC_IPS_V1")
.config(SecurityPolicyRuleMatchConfigArgs.builder()
.srcIpRanges("*")
.build())
.build())
.build());
var policyRule = new SecurityPolicyRule("policyRule", SecurityPolicyRuleArgs.builder()
.securityPolicy(default_.name())
.description("new rule")
.priority(100)
.match(SecurityPolicyRuleMatchArgs.builder()
.versionedExpr("SRC_IPS_V1")
.config(SecurityPolicyRuleMatchConfigArgs.builder()
.srcIpRanges("10.10.0.0/16")
.build())
.build())
.action("allow")
.preview(true)
.build());
}
}
resources:
default:
type: gcp:compute:SecurityPolicy
properties:
name: policyruletest
description: basic global security policy
type: CLOUD_ARMOR
defaultRule:
type: gcp:compute:SecurityPolicyRule
name: default_rule
properties:
securityPolicy: ${default.name}
description: default rule
action: deny
priority: '2147483647'
match:
versionedExpr: SRC_IPS_V1
config:
srcIpRanges:
- '*'
policyRule:
type: gcp:compute:SecurityPolicyRule
name: policy_rule
properties:
securityPolicy: ${default.name}
description: new rule
priority: 100
match:
versionedExpr: SRC_IPS_V1
config:
srcIpRanges:
- 10.10.0.0/16
action: allow
preview: true
The default rule uses priority 2147483647 (the lowest possible value) to catch any traffic not matched by higher-priority rules. The srcIpRanges value “*” matches all traffic. This configuration creates a layered policy: the priority 100 rule allows traffic from 10.10.0.0/16, while the default rule denies everything else.
Layer multiple rules with priority ordering
Complex security policies require multiple rules evaluated in priority order.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const _default = new gcp.compute.SecurityPolicy("default", {
name: "policywithmultiplerules",
description: "basic global security policy",
type: "CLOUD_ARMOR",
});
const policyRuleOne = new gcp.compute.SecurityPolicyRule("policy_rule_one", {
securityPolicy: _default.name,
description: "new rule one",
priority: 100,
match: {
versionedExpr: "SRC_IPS_V1",
config: {
srcIpRanges: ["10.10.0.0/16"],
},
},
action: "allow",
preview: true,
});
const policyRuleTwo = new gcp.compute.SecurityPolicyRule("policy_rule_two", {
securityPolicy: _default.name,
description: "new rule two",
priority: 101,
match: {
versionedExpr: "SRC_IPS_V1",
config: {
srcIpRanges: [
"192.168.0.0/16",
"10.0.0.0/8",
],
},
},
action: "allow",
preview: true,
});
import pulumi
import pulumi_gcp as gcp
default = gcp.compute.SecurityPolicy("default",
name="policywithmultiplerules",
description="basic global security policy",
type="CLOUD_ARMOR")
policy_rule_one = gcp.compute.SecurityPolicyRule("policy_rule_one",
security_policy=default.name,
description="new rule one",
priority=100,
match={
"versioned_expr": "SRC_IPS_V1",
"config": {
"src_ip_ranges": ["10.10.0.0/16"],
},
},
action="allow",
preview=True)
policy_rule_two = gcp.compute.SecurityPolicyRule("policy_rule_two",
security_policy=default.name,
description="new rule two",
priority=101,
match={
"versioned_expr": "SRC_IPS_V1",
"config": {
"src_ip_ranges": [
"192.168.0.0/16",
"10.0.0.0/8",
],
},
},
action="allow",
preview=True)
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 {
_default, err := compute.NewSecurityPolicy(ctx, "default", &compute.SecurityPolicyArgs{
Name: pulumi.String("policywithmultiplerules"),
Description: pulumi.String("basic global security policy"),
Type: pulumi.String("CLOUD_ARMOR"),
})
if err != nil {
return err
}
_, err = compute.NewSecurityPolicyRule(ctx, "policy_rule_one", &compute.SecurityPolicyRuleArgs{
SecurityPolicy: _default.Name,
Description: pulumi.String("new rule one"),
Priority: pulumi.Int(100),
Match: &compute.SecurityPolicyRuleMatchArgs{
VersionedExpr: pulumi.String("SRC_IPS_V1"),
Config: &compute.SecurityPolicyRuleMatchConfigArgs{
SrcIpRanges: pulumi.StringArray{
pulumi.String("10.10.0.0/16"),
},
},
},
Action: pulumi.String("allow"),
Preview: pulumi.Bool(true),
})
if err != nil {
return err
}
_, err = compute.NewSecurityPolicyRule(ctx, "policy_rule_two", &compute.SecurityPolicyRuleArgs{
SecurityPolicy: _default.Name,
Description: pulumi.String("new rule two"),
Priority: pulumi.Int(101),
Match: &compute.SecurityPolicyRuleMatchArgs{
VersionedExpr: pulumi.String("SRC_IPS_V1"),
Config: &compute.SecurityPolicyRuleMatchConfigArgs{
SrcIpRanges: pulumi.StringArray{
pulumi.String("192.168.0.0/16"),
pulumi.String("10.0.0.0/8"),
},
},
},
Action: pulumi.String("allow"),
Preview: 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 @default = new Gcp.Compute.SecurityPolicy("default", new()
{
Name = "policywithmultiplerules",
Description = "basic global security policy",
Type = "CLOUD_ARMOR",
});
var policyRuleOne = new Gcp.Compute.SecurityPolicyRule("policy_rule_one", new()
{
SecurityPolicy = @default.Name,
Description = "new rule one",
Priority = 100,
Match = new Gcp.Compute.Inputs.SecurityPolicyRuleMatchArgs
{
VersionedExpr = "SRC_IPS_V1",
Config = new Gcp.Compute.Inputs.SecurityPolicyRuleMatchConfigArgs
{
SrcIpRanges = new[]
{
"10.10.0.0/16",
},
},
},
Action = "allow",
Preview = true,
});
var policyRuleTwo = new Gcp.Compute.SecurityPolicyRule("policy_rule_two", new()
{
SecurityPolicy = @default.Name,
Description = "new rule two",
Priority = 101,
Match = new Gcp.Compute.Inputs.SecurityPolicyRuleMatchArgs
{
VersionedExpr = "SRC_IPS_V1",
Config = new Gcp.Compute.Inputs.SecurityPolicyRuleMatchConfigArgs
{
SrcIpRanges = new[]
{
"192.168.0.0/16",
"10.0.0.0/8",
},
},
},
Action = "allow",
Preview = true,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.SecurityPolicy;
import com.pulumi.gcp.compute.SecurityPolicyArgs;
import com.pulumi.gcp.compute.SecurityPolicyRule;
import com.pulumi.gcp.compute.SecurityPolicyRuleArgs;
import com.pulumi.gcp.compute.inputs.SecurityPolicyRuleMatchArgs;
import com.pulumi.gcp.compute.inputs.SecurityPolicyRuleMatchConfigArgs;
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 SecurityPolicy("default", SecurityPolicyArgs.builder()
.name("policywithmultiplerules")
.description("basic global security policy")
.type("CLOUD_ARMOR")
.build());
var policyRuleOne = new SecurityPolicyRule("policyRuleOne", SecurityPolicyRuleArgs.builder()
.securityPolicy(default_.name())
.description("new rule one")
.priority(100)
.match(SecurityPolicyRuleMatchArgs.builder()
.versionedExpr("SRC_IPS_V1")
.config(SecurityPolicyRuleMatchConfigArgs.builder()
.srcIpRanges("10.10.0.0/16")
.build())
.build())
.action("allow")
.preview(true)
.build());
var policyRuleTwo = new SecurityPolicyRule("policyRuleTwo", SecurityPolicyRuleArgs.builder()
.securityPolicy(default_.name())
.description("new rule two")
.priority(101)
.match(SecurityPolicyRuleMatchArgs.builder()
.versionedExpr("SRC_IPS_V1")
.config(SecurityPolicyRuleMatchConfigArgs.builder()
.srcIpRanges(
"192.168.0.0/16",
"10.0.0.0/8")
.build())
.build())
.action("allow")
.preview(true)
.build());
}
}
resources:
default:
type: gcp:compute:SecurityPolicy
properties:
name: policywithmultiplerules
description: basic global security policy
type: CLOUD_ARMOR
policyRuleOne:
type: gcp:compute:SecurityPolicyRule
name: policy_rule_one
properties:
securityPolicy: ${default.name}
description: new rule one
priority: 100
match:
versionedExpr: SRC_IPS_V1
config:
srcIpRanges:
- 10.10.0.0/16
action: allow
preview: true
policyRuleTwo:
type: gcp:compute:SecurityPolicyRule
name: policy_rule_two
properties:
securityPolicy: ${default.name}
description: new rule two
priority: 101
match:
versionedExpr: SRC_IPS_V1
config:
srcIpRanges:
- 192.168.0.0/16
- 10.0.0.0/8
action: allow
preview: true
Rules are evaluated from lowest to highest priority number. In this configuration, priority 100 is evaluated before priority 101. Each rule can specify multiple srcIpRanges in its match configuration. This allows you to build granular access control by combining multiple allow rules with different IP ranges.
Beyond these examples
These snippets focus on specific security policy rule features: IP-based traffic filtering, priority-based rule evaluation, and allow and deny actions. They’re intentionally minimal rather than full security configurations.
The examples reference pre-existing infrastructure such as Cloud Armor security policies (gcp.compute.SecurityPolicy). They focus on configuring individual rules rather than provisioning the entire security policy.
To keep things focused, common rule patterns are omitted, including:
- Rate limiting and ban actions (rateLimitOptions)
- reCAPTCHA and redirect actions (redirectOptions)
- Header manipulation (headerAction)
- Preconfigured WAF rules (preconfiguredWafConfig)
These omissions are intentional: the goal is to illustrate how each rule feature is wired, not provide drop-in security modules. See the SecurityPolicyRule resource reference for all available configuration options.
Let's configure GCP Cloud Armor Security Policy Rules
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Rule Configuration & Priority
priority, securityPolicy, and project properties are immutable. To change these values, you must delete and recreate the rule.SecurityPolicyRule resources with different priorities that reference the same securityPolicy name.priority to 2147483647 (lowest priority), action to "deny", and configure match.config.srcIpRanges to ["*"] to match all traffic.Actions & Enforcement
allow, deny(STATUS) with HTTP codes 403/404/502, rate_based_ban, redirect, or throttle. The rate_based_ban and throttle actions require rateLimitOptions, while redirect requires redirectOptions.rateLimitOptions to be configured. Make sure you’ve specified rateLimitOptions when using rate_based_ban or throttle as your action.preview is set to true, the rule is evaluated but the specified action is not enforced. This lets you test rules without affecting traffic.Policy Type Restrictions
headerAction and redirectOptions features only work with Global Security Policies of type CLOUD_ARMOR. Standard security policies don’t support these features.redirect action is only supported in Global Security Policies of type CLOUD_ARMOR. It can perform internal reCAPTCHA redirects or external URL-based redirects via 302 responses.