Configure AWS Network Firewall Rule Groups

The aws:networkfirewall/ruleGroup:RuleGroup resource, part of the Pulumi AWS provider, defines Network Firewall rule groups that inspect and filter network traffic based on stateful or stateless rules. This guide focuses on three capabilities: domain blocking and IP-based filtering, Suricata rule import from files and S3, and stateless inspection with custom actions.

Rule groups are standalone resources that define inspection logic. They’re attached to firewall policies separately, which then associate with Network Firewall instances. The examples are intentionally small. Combine them with your own firewall policies and network configuration.

Block access to specific domains

Network security teams often need to prevent traffic to known malicious or restricted domains by evaluating HTTP Host headers.

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

const example = new aws.networkfirewall.RuleGroup("example", {
    capacity: 100,
    name: "example",
    type: "STATEFUL",
    ruleGroup: {
        rulesSource: {
            rulesSourceList: {
                generatedRulesType: "DENYLIST",
                targetTypes: ["HTTP_HOST"],
                targets: ["test.example.com"],
            },
        },
    },
    tags: {
        Tag1: "Value1",
        Tag2: "Value2",
    },
});
import pulumi
import pulumi_aws as aws

example = aws.networkfirewall.RuleGroup("example",
    capacity=100,
    name="example",
    type="STATEFUL",
    rule_group={
        "rules_source": {
            "rules_source_list": {
                "generated_rules_type": "DENYLIST",
                "target_types": ["HTTP_HOST"],
                "targets": ["test.example.com"],
            },
        },
    },
    tags={
        "Tag1": "Value1",
        "Tag2": "Value2",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/networkfirewall"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := networkfirewall.NewRuleGroup(ctx, "example", &networkfirewall.RuleGroupArgs{
			Capacity: pulumi.Int(100),
			Name:     pulumi.String("example"),
			Type:     pulumi.String("STATEFUL"),
			RuleGroup: &networkfirewall.RuleGroupRuleGroupArgs{
				RulesSource: &networkfirewall.RuleGroupRuleGroupRulesSourceArgs{
					RulesSourceList: &networkfirewall.RuleGroupRuleGroupRulesSourceRulesSourceListArgs{
						GeneratedRulesType: pulumi.String("DENYLIST"),
						TargetTypes: pulumi.StringArray{
							pulumi.String("HTTP_HOST"),
						},
						Targets: pulumi.StringArray{
							pulumi.String("test.example.com"),
						},
					},
				},
			},
			Tags: pulumi.StringMap{
				"Tag1": pulumi.String("Value1"),
				"Tag2": pulumi.String("Value2"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.NetworkFirewall.RuleGroup("example", new()
    {
        Capacity = 100,
        Name = "example",
        Type = "STATEFUL",
        RuleGroupConfiguration = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupArgs
        {
            RulesSource = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceArgs
            {
                RulesSourceList = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceRulesSourceListArgs
                {
                    GeneratedRulesType = "DENYLIST",
                    TargetTypes = new[]
                    {
                        "HTTP_HOST",
                    },
                    Targets = new[]
                    {
                        "test.example.com",
                    },
                },
            },
        },
        Tags = 
        {
            { "Tag1", "Value1" },
            { "Tag2", "Value2" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.networkfirewall.RuleGroup;
import com.pulumi.aws.networkfirewall.RuleGroupArgs;
import com.pulumi.aws.networkfirewall.inputs.RuleGroupRuleGroupArgs;
import com.pulumi.aws.networkfirewall.inputs.RuleGroupRuleGroupRulesSourceArgs;
import com.pulumi.aws.networkfirewall.inputs.RuleGroupRuleGroupRulesSourceRulesSourceListArgs;
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 example = new RuleGroup("example", RuleGroupArgs.builder()
            .capacity(100)
            .name("example")
            .type("STATEFUL")
            .ruleGroup(RuleGroupRuleGroupArgs.builder()
                .rulesSource(RuleGroupRuleGroupRulesSourceArgs.builder()
                    .rulesSourceList(RuleGroupRuleGroupRulesSourceRulesSourceListArgs.builder()
                        .generatedRulesType("DENYLIST")
                        .targetTypes("HTTP_HOST")
                        .targets("test.example.com")
                        .build())
                    .build())
                .build())
            .tags(Map.ofEntries(
                Map.entry("Tag1", "Value1"),
                Map.entry("Tag2", "Value2")
            ))
            .build());

    }
}
resources:
  example:
    type: aws:networkfirewall:RuleGroup
    properties:
      capacity: 100
      name: example
      type: STATEFUL
      ruleGroup:
        rulesSource:
          rulesSourceList:
            generatedRulesType: DENYLIST
            targetTypes:
              - HTTP_HOST
            targets:
              - test.example.com
      tags:
        Tag1: Value1
        Tag2: Value2

When traffic matches a domain in the targets list, Network Firewall blocks the connection. The generatedRulesType property controls whether this is a DENYLIST or ALLOWLIST. The targetTypes property specifies what to inspect (HTTP_HOST, TLS_SNI, or both). The capacity property sets the maximum operating resources; for stateful rules, this must be at least the number of rules.

Allow HTTP traffic from specific source IPs

Applications receiving traffic from known partners need to permit connections from specific source addresses.

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

const ips = [
    "1.1.1.1/32",
    "1.0.0.1/32",
];
const example = new aws.networkfirewall.RuleGroup("example", {
    capacity: 50,
    description: "Permits http traffic from source",
    name: "example",
    type: "STATEFUL",
    ruleGroup: {
        rulesSource: {
            statefulRules: ips.map((v, k) => ({key: k, value: v})).map(entry => ({
                action: "PASS",
                header: {
                    destination: "ANY",
                    destinationPort: "ANY",
                    protocol: "HTTP",
                    direction: "ANY",
                    sourcePort: "ANY",
                    source: entry.value,
                },
                ruleOptions: [{
                    keyword: "sid",
                    settings: ["1"],
                }],
            })),
        },
    },
    tags: {
        Name: "permit HTTP from source",
    },
});
import pulumi
import pulumi_aws as aws

ips = [
    "1.1.1.1/32",
    "1.0.0.1/32",
]
example = aws.networkfirewall.RuleGroup("example",
    capacity=50,
    description="Permits http traffic from source",
    name="example",
    type="STATEFUL",
    rule_group={
        "rules_source": {
            "stateful_rules": [{
                "action": "PASS",
                "header": {
                    "destination": "ANY",
                    "destination_port": "ANY",
                    "protocol": "HTTP",
                    "direction": "ANY",
                    "source_port": "ANY",
                    "source": entry["value"],
                },
                "rule_options": [{
                    "keyword": "sid",
                    "settings": ["1"],
                }],
            } for entry in [{"key": k, "value": v} for k, v in ips.items()]],
        },
    },
    tags={
        "Name": "permit HTTP from source",
    })
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var ips = new[]
    {
        "1.1.1.1/32",
        "1.0.0.1/32",
    };

    var example = new Aws.NetworkFirewall.RuleGroup("example", new()
    {
        Capacity = 50,
        Description = "Permits http traffic from source",
        Name = "example",
        Type = "STATEFUL",
        RuleGroupConfiguration = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupArgs
        {
            RulesSource = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceArgs
            {
                StatefulRules = ips.Select((v, k) => new { Key = k, Value = v }).Select(entry => 
                {
                    return new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatefulRuleArgs
                    {
                        Action = "PASS",
                        Header = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatefulRuleHeaderArgs
                        {
                            Destination = "ANY",
                            DestinationPort = "ANY",
                            Protocol = "HTTP",
                            Direction = "ANY",
                            SourcePort = "ANY",
                            Source = entry.Value,
                        },
                        RuleOptions = new[]
                        {
                            new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatefulRuleRuleOptionArgs
                            {
                                Keyword = "sid",
                                Settings = new[]
                                {
                                    "1",
                                },
                            },
                        },
                    };
                }).ToList(),
            },
        },
        Tags = 
        {
            { "Name", "permit HTTP from source" },
        },
    });

});

Each statefulRule defines a 5-tuple match (source, destination, ports, protocol, direction) and an action (PASS, DROP, or ALERT). The header block specifies what traffic to match. The ruleOptions array includes a sid (signature ID) that uniquely identifies each rule. Here, the code iterates over an IP list to create one rule per source address.

Import existing Suricata rules from a file

Teams migrating from other network security tools can preserve their existing Suricata-format rules.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as std from "@pulumi/std";

const example = new aws.networkfirewall.RuleGroup("example", {
    capacity: 100,
    name: "example",
    type: "STATEFUL",
    rules: std.file({
        input: "example.rules",
    }).then(invoke => invoke.result),
    tags: {
        Tag1: "Value1",
        Tag2: "Value2",
    },
});
import pulumi
import pulumi_aws as aws
import pulumi_std as std

example = aws.networkfirewall.RuleGroup("example",
    capacity=100,
    name="example",
    type="STATEFUL",
    rules=std.file(input="example.rules").result,
    tags={
        "Tag1": "Value1",
        "Tag2": "Value2",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/networkfirewall"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		invokeFile, err := std.File(ctx, &std.FileArgs{
			Input: "example.rules",
		}, nil)
		if err != nil {
			return err
		}
		_, err = networkfirewall.NewRuleGroup(ctx, "example", &networkfirewall.RuleGroupArgs{
			Capacity: pulumi.Int(100),
			Name:     pulumi.String("example"),
			Type:     pulumi.String("STATEFUL"),
			Rules:    pulumi.String(invokeFile.Result),
			Tags: pulumi.StringMap{
				"Tag1": pulumi.String("Value1"),
				"Tag2": pulumi.String("Value2"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.NetworkFirewall.RuleGroup("example", new()
    {
        Capacity = 100,
        Name = "example",
        Type = "STATEFUL",
        Rules = Std.File.Invoke(new()
        {
            Input = "example.rules",
        }).Apply(invoke => invoke.Result),
        Tags = 
        {
            { "Tag1", "Value1" },
            { "Tag2", "Value2" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.networkfirewall.RuleGroup;
import com.pulumi.aws.networkfirewall.RuleGroupArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.FileArgs;
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 example = new RuleGroup("example", RuleGroupArgs.builder()
            .capacity(100)
            .name("example")
            .type("STATEFUL")
            .rules(StdFunctions.file(FileArgs.builder()
                .input("example.rules")
                .build()).result())
            .tags(Map.ofEntries(
                Map.entry("Tag1", "Value1"),
                Map.entry("Tag2", "Value2")
            ))
            .build());

    }
}
resources:
  example:
    type: aws:networkfirewall:RuleGroup
    properties:
      capacity: 100
      name: example
      type: STATEFUL
      rules:
        fn::invoke:
          function: std:file
          arguments:
            input: example.rules
          return: result
      tags:
        Tag1: Value1
        Tag2: Value2

The rules property accepts Suricata rule strings directly, with one rule per line. This is an alternative to defining rules inline via ruleGroup. Use this when you have existing Suricata rules you want to import without rewriting them into the structured format.

Define reusable variables for Suricata rules

Complex rule sets benefit from variables that define IP ranges and port sets once, then reference them throughout the rules.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as std from "@pulumi/std";

const example = new aws.networkfirewall.RuleGroup("example", {
    capacity: 100,
    name: "example",
    type: "STATEFUL",
    ruleGroup: {
        ruleVariables: {
            ipSets: [
                {
                    key: "WEBSERVERS_HOSTS",
                    ipSet: {
                        definitions: [
                            "10.0.0.0/16",
                            "10.0.1.0/24",
                            "192.168.0.0/16",
                        ],
                    },
                },
                {
                    key: "EXTERNAL_HOST",
                    ipSet: {
                        definitions: ["1.2.3.4/32"],
                    },
                },
            ],
            portSets: [{
                key: "HTTP_PORTS",
                portSet: {
                    definitions: [
                        "443",
                        "80",
                    ],
                },
            }],
        },
        rulesSource: {
            rulesString: std.file({
                input: "suricata_rules_file",
            }).then(invoke => invoke.result),
        },
    },
    tags: {
        Tag1: "Value1",
        Tag2: "Value2",
    },
});
import pulumi
import pulumi_aws as aws
import pulumi_std as std

example = aws.networkfirewall.RuleGroup("example",
    capacity=100,
    name="example",
    type="STATEFUL",
    rule_group={
        "rule_variables": {
            "ip_sets": [
                {
                    "key": "WEBSERVERS_HOSTS",
                    "ip_set": {
                        "definitions": [
                            "10.0.0.0/16",
                            "10.0.1.0/24",
                            "192.168.0.0/16",
                        ],
                    },
                },
                {
                    "key": "EXTERNAL_HOST",
                    "ip_set": {
                        "definitions": ["1.2.3.4/32"],
                    },
                },
            ],
            "port_sets": [{
                "key": "HTTP_PORTS",
                "port_set": {
                    "definitions": [
                        "443",
                        "80",
                    ],
                },
            }],
        },
        "rules_source": {
            "rules_string": std.file(input="suricata_rules_file").result,
        },
    },
    tags={
        "Tag1": "Value1",
        "Tag2": "Value2",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/networkfirewall"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		invokeFile, err := std.File(ctx, &std.FileArgs{
			Input: "suricata_rules_file",
		}, nil)
		if err != nil {
			return err
		}
		_, err = networkfirewall.NewRuleGroup(ctx, "example", &networkfirewall.RuleGroupArgs{
			Capacity: pulumi.Int(100),
			Name:     pulumi.String("example"),
			Type:     pulumi.String("STATEFUL"),
			RuleGroup: &networkfirewall.RuleGroupRuleGroupArgs{
				RuleVariables: &networkfirewall.RuleGroupRuleGroupRuleVariablesArgs{
					IpSets: networkfirewall.RuleGroupRuleGroupRuleVariablesIpSetArray{
						&networkfirewall.RuleGroupRuleGroupRuleVariablesIpSetArgs{
							Key: pulumi.String("WEBSERVERS_HOSTS"),
							IpSet: &networkfirewall.RuleGroupRuleGroupRuleVariablesIpSetIpSetArgs{
								Definitions: pulumi.StringArray{
									pulumi.String("10.0.0.0/16"),
									pulumi.String("10.0.1.0/24"),
									pulumi.String("192.168.0.0/16"),
								},
							},
						},
						&networkfirewall.RuleGroupRuleGroupRuleVariablesIpSetArgs{
							Key: pulumi.String("EXTERNAL_HOST"),
							IpSet: &networkfirewall.RuleGroupRuleGroupRuleVariablesIpSetIpSetArgs{
								Definitions: pulumi.StringArray{
									pulumi.String("1.2.3.4/32"),
								},
							},
						},
					},
					PortSets: networkfirewall.RuleGroupRuleGroupRuleVariablesPortSetArray{
						&networkfirewall.RuleGroupRuleGroupRuleVariablesPortSetArgs{
							Key: pulumi.String("HTTP_PORTS"),
							PortSet: &networkfirewall.RuleGroupRuleGroupRuleVariablesPortSetPortSetArgs{
								Definitions: pulumi.StringArray{
									pulumi.String("443"),
									pulumi.String("80"),
								},
							},
						},
					},
				},
				RulesSource: &networkfirewall.RuleGroupRuleGroupRulesSourceArgs{
					RulesString: pulumi.String(invokeFile.Result),
				},
			},
			Tags: pulumi.StringMap{
				"Tag1": pulumi.String("Value1"),
				"Tag2": pulumi.String("Value2"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.NetworkFirewall.RuleGroup("example", new()
    {
        Capacity = 100,
        Name = "example",
        Type = "STATEFUL",
        RuleGroupConfiguration = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupArgs
        {
            RuleVariables = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRuleVariablesArgs
            {
                IpSets = new[]
                {
                    new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRuleVariablesIpSetArgs
                    {
                        Key = "WEBSERVERS_HOSTS",
                        IpSet = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRuleVariablesIpSetIpSetArgs
                        {
                            Definitions = new[]
                            {
                                "10.0.0.0/16",
                                "10.0.1.0/24",
                                "192.168.0.0/16",
                            },
                        },
                    },
                    new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRuleVariablesIpSetArgs
                    {
                        Key = "EXTERNAL_HOST",
                        IpSet = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRuleVariablesIpSetIpSetArgs
                        {
                            Definitions = new[]
                            {
                                "1.2.3.4/32",
                            },
                        },
                    },
                },
                PortSets = new[]
                {
                    new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRuleVariablesPortSetArgs
                    {
                        Key = "HTTP_PORTS",
                        PortSet = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRuleVariablesPortSetPortSetArgs
                        {
                            Definitions = new[]
                            {
                                "443",
                                "80",
                            },
                        },
                    },
                },
            },
            RulesSource = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceArgs
            {
                RulesString = Std.File.Invoke(new()
                {
                    Input = "suricata_rules_file",
                }).Apply(invoke => invoke.Result),
            },
        },
        Tags = 
        {
            { "Tag1", "Value1" },
            { "Tag2", "Value2" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.networkfirewall.RuleGroup;
import com.pulumi.aws.networkfirewall.RuleGroupArgs;
import com.pulumi.aws.networkfirewall.inputs.RuleGroupRuleGroupArgs;
import com.pulumi.aws.networkfirewall.inputs.RuleGroupRuleGroupRuleVariablesArgs;
import com.pulumi.aws.networkfirewall.inputs.RuleGroupRuleGroupRulesSourceArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.FileArgs;
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 example = new RuleGroup("example", RuleGroupArgs.builder()
            .capacity(100)
            .name("example")
            .type("STATEFUL")
            .ruleGroup(RuleGroupRuleGroupArgs.builder()
                .ruleVariables(RuleGroupRuleGroupRuleVariablesArgs.builder()
                    .ipSets(                    
                        RuleGroupRuleGroupRuleVariablesIpSetArgs.builder()
                            .key("WEBSERVERS_HOSTS")
                            .ipSet(RuleGroupRuleGroupRuleVariablesIpSetIpSetArgs.builder()
                                .definitions(                                
                                    "10.0.0.0/16",
                                    "10.0.1.0/24",
                                    "192.168.0.0/16")
                                .build())
                            .build(),
                        RuleGroupRuleGroupRuleVariablesIpSetArgs.builder()
                            .key("EXTERNAL_HOST")
                            .ipSet(RuleGroupRuleGroupRuleVariablesIpSetIpSetArgs.builder()
                                .definitions("1.2.3.4/32")
                                .build())
                            .build())
                    .portSets(RuleGroupRuleGroupRuleVariablesPortSetArgs.builder()
                        .key("HTTP_PORTS")
                        .portSet(RuleGroupRuleGroupRuleVariablesPortSetPortSetArgs.builder()
                            .definitions(                            
                                "443",
                                "80")
                            .build())
                        .build())
                    .build())
                .rulesSource(RuleGroupRuleGroupRulesSourceArgs.builder()
                    .rulesString(StdFunctions.file(FileArgs.builder()
                        .input("suricata_rules_file")
                        .build()).result())
                    .build())
                .build())
            .tags(Map.ofEntries(
                Map.entry("Tag1", "Value1"),
                Map.entry("Tag2", "Value2")
            ))
            .build());

    }
}
resources:
  example:
    type: aws:networkfirewall:RuleGroup
    properties:
      capacity: 100
      name: example
      type: STATEFUL
      ruleGroup:
        ruleVariables:
          ipSets:
            - key: WEBSERVERS_HOSTS
              ipSet:
                definitions:
                  - 10.0.0.0/16
                  - 10.0.1.0/24
                  - 192.168.0.0/16
            - key: EXTERNAL_HOST
              ipSet:
                definitions:
                  - 1.2.3.4/32
          portSets:
            - key: HTTP_PORTS
              portSet:
                definitions:
                  - '443'
                  - '80'
        rulesSource:
          rulesString:
            fn::invoke:
              function: std:file
              arguments:
                input: suricata_rules_file
              return: result
      tags:
        Tag1: Value1
        Tag2: Value2

The ruleVariables block defines ipSets and portSets that your Suricata rules can reference by key (e.g., $HOME_NET, $HTTP_PORTS). The rulesString property contains the actual Suricata rules that use these variables. This extends the file-based approach by adding variable definitions, reducing duplication when the same IPs or ports appear in multiple rules.

Publish metrics from stateless rules

Stateless inspection evaluates packets individually without tracking connection state, enabling fast filtering and custom metric publishing.

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

const example = new aws.networkfirewall.RuleGroup("example", {
    description: "Stateless Rate Limiting Rule",
    capacity: 100,
    name: "example",
    type: "STATELESS",
    ruleGroup: {
        rulesSource: {
            statelessRulesAndCustomActions: {
                customActions: [{
                    actionDefinition: {
                        publishMetricAction: {
                            dimensions: [{
                                value: "2",
                            }],
                        },
                    },
                    actionName: "ExampleMetricsAction",
                }],
                statelessRules: [{
                    priority: 1,
                    ruleDefinition: {
                        actions: [
                            "aws:pass",
                            "ExampleMetricsAction",
                        ],
                        matchAttributes: {
                            sources: [{
                                addressDefinition: "1.2.3.4/32",
                            }],
                            sourcePorts: [{
                                fromPort: 443,
                                toPort: 443,
                            }],
                            destinations: [{
                                addressDefinition: "124.1.1.5/32",
                            }],
                            destinationPorts: [{
                                fromPort: 443,
                                toPort: 443,
                            }],
                            protocols: [6],
                            tcpFlags: [{
                                flags: ["SYN"],
                                masks: [
                                    "SYN",
                                    "ACK",
                                ],
                            }],
                        },
                    },
                }],
            },
        },
    },
    tags: {
        Tag1: "Value1",
        Tag2: "Value2",
    },
});
import pulumi
import pulumi_aws as aws

example = aws.networkfirewall.RuleGroup("example",
    description="Stateless Rate Limiting Rule",
    capacity=100,
    name="example",
    type="STATELESS",
    rule_group={
        "rules_source": {
            "stateless_rules_and_custom_actions": {
                "custom_actions": [{
                    "action_definition": {
                        "publish_metric_action": {
                            "dimensions": [{
                                "value": "2",
                            }],
                        },
                    },
                    "action_name": "ExampleMetricsAction",
                }],
                "stateless_rules": [{
                    "priority": 1,
                    "rule_definition": {
                        "actions": [
                            "aws:pass",
                            "ExampleMetricsAction",
                        ],
                        "match_attributes": {
                            "sources": [{
                                "address_definition": "1.2.3.4/32",
                            }],
                            "source_ports": [{
                                "from_port": 443,
                                "to_port": 443,
                            }],
                            "destinations": [{
                                "address_definition": "124.1.1.5/32",
                            }],
                            "destination_ports": [{
                                "from_port": 443,
                                "to_port": 443,
                            }],
                            "protocols": [6],
                            "tcp_flags": [{
                                "flags": ["SYN"],
                                "masks": [
                                    "SYN",
                                    "ACK",
                                ],
                            }],
                        },
                    },
                }],
            },
        },
    },
    tags={
        "Tag1": "Value1",
        "Tag2": "Value2",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/networkfirewall"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := networkfirewall.NewRuleGroup(ctx, "example", &networkfirewall.RuleGroupArgs{
			Description: pulumi.String("Stateless Rate Limiting Rule"),
			Capacity:    pulumi.Int(100),
			Name:        pulumi.String("example"),
			Type:        pulumi.String("STATELESS"),
			RuleGroup: &networkfirewall.RuleGroupRuleGroupArgs{
				RulesSource: &networkfirewall.RuleGroupRuleGroupRulesSourceArgs{
					StatelessRulesAndCustomActions: &networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsArgs{
						CustomActions: networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionArray{
							&networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionArgs{
								ActionDefinition: &networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionActionDefinitionArgs{
									PublishMetricAction: &networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionActionDefinitionPublishMetricActionArgs{
										Dimensions: networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionActionDefinitionPublishMetricActionDimensionArray{
											&networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionActionDefinitionPublishMetricActionDimensionArgs{
												Value: pulumi.String("2"),
											},
										},
									},
								},
								ActionName: pulumi.String("ExampleMetricsAction"),
							},
						},
						StatelessRules: networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleArray{
							&networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleArgs{
								Priority: pulumi.Int(1),
								RuleDefinition: &networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionArgs{
									Actions: pulumi.StringArray{
										pulumi.String("aws:pass"),
										pulumi.String("ExampleMetricsAction"),
									},
									MatchAttributes: &networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesArgs{
										Sources: networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesSourceArray{
											&networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesSourceArgs{
												AddressDefinition: pulumi.String("1.2.3.4/32"),
											},
										},
										SourcePorts: networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesSourcePortArray{
											&networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesSourcePortArgs{
												FromPort: pulumi.Int(443),
												ToPort:   pulumi.Int(443),
											},
										},
										Destinations: networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesDestinationArray{
											&networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesDestinationArgs{
												AddressDefinition: pulumi.String("124.1.1.5/32"),
											},
										},
										DestinationPorts: networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesDestinationPortArray{
											&networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesDestinationPortArgs{
												FromPort: pulumi.Int(443),
												ToPort:   pulumi.Int(443),
											},
										},
										Protocols: pulumi.IntArray{
											pulumi.Int(6),
										},
										TcpFlags: networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesTcpFlagArray{
											&networkfirewall.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesTcpFlagArgs{
												Flags: pulumi.StringArray{
													pulumi.String("SYN"),
												},
												Masks: pulumi.StringArray{
													pulumi.String("SYN"),
													pulumi.String("ACK"),
												},
											},
										},
									},
								},
							},
						},
					},
				},
			},
			Tags: pulumi.StringMap{
				"Tag1": pulumi.String("Value1"),
				"Tag2": pulumi.String("Value2"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.NetworkFirewall.RuleGroup("example", new()
    {
        Description = "Stateless Rate Limiting Rule",
        Capacity = 100,
        Name = "example",
        Type = "STATELESS",
        RuleGroupConfiguration = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupArgs
        {
            RulesSource = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceArgs
            {
                StatelessRulesAndCustomActions = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsArgs
                {
                    CustomActions = new[]
                    {
                        new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionArgs
                        {
                            ActionDefinition = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionActionDefinitionArgs
                            {
                                PublishMetricAction = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionActionDefinitionPublishMetricActionArgs
                                {
                                    Dimensions = new[]
                                    {
                                        new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionActionDefinitionPublishMetricActionDimensionArgs
                                        {
                                            Value = "2",
                                        },
                                    },
                                },
                            },
                            ActionName = "ExampleMetricsAction",
                        },
                    },
                    StatelessRules = new[]
                    {
                        new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleArgs
                        {
                            Priority = 1,
                            RuleDefinition = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionArgs
                            {
                                Actions = new[]
                                {
                                    "aws:pass",
                                    "ExampleMetricsAction",
                                },
                                MatchAttributes = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesArgs
                                {
                                    Sources = new[]
                                    {
                                        new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesSourceArgs
                                        {
                                            AddressDefinition = "1.2.3.4/32",
                                        },
                                    },
                                    SourcePorts = new[]
                                    {
                                        new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesSourcePortArgs
                                        {
                                            FromPort = 443,
                                            ToPort = 443,
                                        },
                                    },
                                    Destinations = new[]
                                    {
                                        new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesDestinationArgs
                                        {
                                            AddressDefinition = "124.1.1.5/32",
                                        },
                                    },
                                    DestinationPorts = new[]
                                    {
                                        new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesDestinationPortArgs
                                        {
                                            FromPort = 443,
                                            ToPort = 443,
                                        },
                                    },
                                    Protocols = new[]
                                    {
                                        6,
                                    },
                                    TcpFlags = new[]
                                    {
                                        new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesTcpFlagArgs
                                        {
                                            Flags = new[]
                                            {
                                                "SYN",
                                            },
                                            Masks = new[]
                                            {
                                                "SYN",
                                                "ACK",
                                            },
                                        },
                                    },
                                },
                            },
                        },
                    },
                },
            },
        },
        Tags = 
        {
            { "Tag1", "Value1" },
            { "Tag2", "Value2" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.networkfirewall.RuleGroup;
import com.pulumi.aws.networkfirewall.RuleGroupArgs;
import com.pulumi.aws.networkfirewall.inputs.RuleGroupRuleGroupArgs;
import com.pulumi.aws.networkfirewall.inputs.RuleGroupRuleGroupRulesSourceArgs;
import com.pulumi.aws.networkfirewall.inputs.RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsArgs;
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 example = new RuleGroup("example", RuleGroupArgs.builder()
            .description("Stateless Rate Limiting Rule")
            .capacity(100)
            .name("example")
            .type("STATELESS")
            .ruleGroup(RuleGroupRuleGroupArgs.builder()
                .rulesSource(RuleGroupRuleGroupRulesSourceArgs.builder()
                    .statelessRulesAndCustomActions(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsArgs.builder()
                        .customActions(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionArgs.builder()
                            .actionDefinition(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionActionDefinitionArgs.builder()
                                .publishMetricAction(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionActionDefinitionPublishMetricActionArgs.builder()
                                    .dimensions(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsCustomActionActionDefinitionPublishMetricActionDimensionArgs.builder()
                                        .value("2")
                                        .build())
                                    .build())
                                .build())
                            .actionName("ExampleMetricsAction")
                            .build())
                        .statelessRules(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleArgs.builder()
                            .priority(1)
                            .ruleDefinition(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionArgs.builder()
                                .actions(                                
                                    "aws:pass",
                                    "ExampleMetricsAction")
                                .matchAttributes(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesArgs.builder()
                                    .sources(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesSourceArgs.builder()
                                        .addressDefinition("1.2.3.4/32")
                                        .build())
                                    .sourcePorts(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesSourcePortArgs.builder()
                                        .fromPort(443)
                                        .toPort(443)
                                        .build())
                                    .destinations(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesDestinationArgs.builder()
                                        .addressDefinition("124.1.1.5/32")
                                        .build())
                                    .destinationPorts(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesDestinationPortArgs.builder()
                                        .fromPort(443)
                                        .toPort(443)
                                        .build())
                                    .protocols(6)
                                    .tcpFlags(RuleGroupRuleGroupRulesSourceStatelessRulesAndCustomActionsStatelessRuleRuleDefinitionMatchAttributesTcpFlagArgs.builder()
                                        .flags("SYN")
                                        .masks(                                        
                                            "SYN",
                                            "ACK")
                                        .build())
                                    .build())
                                .build())
                            .build())
                        .build())
                    .build())
                .build())
            .tags(Map.ofEntries(
                Map.entry("Tag1", "Value1"),
                Map.entry("Tag2", "Value2")
            ))
            .build());

    }
}
resources:
  example:
    type: aws:networkfirewall:RuleGroup
    properties:
      description: Stateless Rate Limiting Rule
      capacity: 100
      name: example
      type: STATELESS
      ruleGroup:
        rulesSource:
          statelessRulesAndCustomActions:
            customActions:
              - actionDefinition:
                  publishMetricAction:
                    dimensions:
                      - value: '2'
                actionName: ExampleMetricsAction
            statelessRules:
              - priority: 1
                ruleDefinition:
                  actions:
                    - aws:pass
                    - ExampleMetricsAction
                  matchAttributes:
                    sources:
                      - addressDefinition: 1.2.3.4/32
                    sourcePorts:
                      - fromPort: 443
                        toPort: 443
                    destinations:
                      - addressDefinition: 124.1.1.5/32
                    destinationPorts:
                      - fromPort: 443
                        toPort: 443
                    protocols:
                      - 6
                    tcpFlags:
                      - flags:
                          - SYN
                        masks:
                          - SYN
                          - ACK
      tags:
        Tag1: Value1
        Tag2: Value2

Stateless rules match on packet attributes (source/destination IPs, ports, protocols, TCP flags) and execute actions immediately. The customActions block defines a publishMetricAction that sends dimensions to CloudWatch when the rule matches. The statelessRules array assigns priorities (lower numbers evaluated first) and combines standard actions (aws:pass, aws:drop, aws:forward_to_sfe) with custom actions. The matchAttributes block specifies the 5-tuple and TCP flags to inspect.

Load Suricata rules from S3

Centralized rule management often stores Suricata rules in S3, allowing multiple rule groups to reference the same source.

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

const suricataRules = aws.s3.getObject({
    bucket: suricataRulesAwsS3Bucket.id,
    key: "rules/custom.rules",
});
const s3RulesExample = new aws.networkfirewall.RuleGroup("s3_rules_example", {
    capacity: 1000,
    name: "my-terraform-s3-rules",
    type: "STATEFUL",
    ruleGroup: {
        ruleVariables: {
            ipSets: [{
                key: "HOME_NET",
                ipSet: {
                    definitions: [
                        "10.0.0.0/16",
                        "192.168.0.0/16",
                        "172.16.0.0/12",
                    ],
                },
            }],
            portSets: [{
                key: "HTTP_PORTS",
                portSet: {
                    definitions: [
                        "443",
                        "80",
                    ],
                },
            }],
        },
        rulesSource: {
            rulesString: suricataRules.then(suricataRules => suricataRules.body),
        },
    },
    tags: {
        ManagedBy: "terraform",
    },
});
import pulumi
import pulumi_aws as aws

suricata_rules = aws.s3.get_object(bucket=suricata_rules_aws_s3_bucket["id"],
    key="rules/custom.rules")
s3_rules_example = aws.networkfirewall.RuleGroup("s3_rules_example",
    capacity=1000,
    name="my-terraform-s3-rules",
    type="STATEFUL",
    rule_group={
        "rule_variables": {
            "ip_sets": [{
                "key": "HOME_NET",
                "ip_set": {
                    "definitions": [
                        "10.0.0.0/16",
                        "192.168.0.0/16",
                        "172.16.0.0/12",
                    ],
                },
            }],
            "port_sets": [{
                "key": "HTTP_PORTS",
                "port_set": {
                    "definitions": [
                        "443",
                        "80",
                    ],
                },
            }],
        },
        "rules_source": {
            "rules_string": suricata_rules.body,
        },
    },
    tags={
        "ManagedBy": "terraform",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/networkfirewall"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		suricataRules, err := s3.GetObject(ctx, &s3.GetObjectArgs{
			Bucket: suricataRulesAwsS3Bucket.Id,
			Key:    "rules/custom.rules",
		}, nil)
		if err != nil {
			return err
		}
		_, err = networkfirewall.NewRuleGroup(ctx, "s3_rules_example", &networkfirewall.RuleGroupArgs{
			Capacity: pulumi.Int(1000),
			Name:     pulumi.String("my-terraform-s3-rules"),
			Type:     pulumi.String("STATEFUL"),
			RuleGroup: &networkfirewall.RuleGroupRuleGroupArgs{
				RuleVariables: &networkfirewall.RuleGroupRuleGroupRuleVariablesArgs{
					IpSets: networkfirewall.RuleGroupRuleGroupRuleVariablesIpSetArray{
						&networkfirewall.RuleGroupRuleGroupRuleVariablesIpSetArgs{
							Key: pulumi.String("HOME_NET"),
							IpSet: &networkfirewall.RuleGroupRuleGroupRuleVariablesIpSetIpSetArgs{
								Definitions: pulumi.StringArray{
									pulumi.String("10.0.0.0/16"),
									pulumi.String("192.168.0.0/16"),
									pulumi.String("172.16.0.0/12"),
								},
							},
						},
					},
					PortSets: networkfirewall.RuleGroupRuleGroupRuleVariablesPortSetArray{
						&networkfirewall.RuleGroupRuleGroupRuleVariablesPortSetArgs{
							Key: pulumi.String("HTTP_PORTS"),
							PortSet: &networkfirewall.RuleGroupRuleGroupRuleVariablesPortSetPortSetArgs{
								Definitions: pulumi.StringArray{
									pulumi.String("443"),
									pulumi.String("80"),
								},
							},
						},
					},
				},
				RulesSource: &networkfirewall.RuleGroupRuleGroupRulesSourceArgs{
					RulesString: pulumi.String(suricataRules.Body),
				},
			},
			Tags: pulumi.StringMap{
				"ManagedBy": pulumi.String("terraform"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var suricataRules = Aws.S3.GetObject.Invoke(new()
    {
        Bucket = suricataRulesAwsS3Bucket.Id,
        Key = "rules/custom.rules",
    });

    var s3RulesExample = new Aws.NetworkFirewall.RuleGroup("s3_rules_example", new()
    {
        Capacity = 1000,
        Name = "my-terraform-s3-rules",
        Type = "STATEFUL",
        RuleGroupConfiguration = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupArgs
        {
            RuleVariables = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRuleVariablesArgs
            {
                IpSets = new[]
                {
                    new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRuleVariablesIpSetArgs
                    {
                        Key = "HOME_NET",
                        IpSet = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRuleVariablesIpSetIpSetArgs
                        {
                            Definitions = new[]
                            {
                                "10.0.0.0/16",
                                "192.168.0.0/16",
                                "172.16.0.0/12",
                            },
                        },
                    },
                },
                PortSets = new[]
                {
                    new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRuleVariablesPortSetArgs
                    {
                        Key = "HTTP_PORTS",
                        PortSet = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRuleVariablesPortSetPortSetArgs
                        {
                            Definitions = new[]
                            {
                                "443",
                                "80",
                            },
                        },
                    },
                },
            },
            RulesSource = new Aws.NetworkFirewall.Inputs.RuleGroupRuleGroupRulesSourceArgs
            {
                RulesString = suricataRules.Apply(getObjectResult => getObjectResult.Body),
            },
        },
        Tags = 
        {
            { "ManagedBy", "terraform" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.S3Functions;
import com.pulumi.aws.s3.inputs.GetObjectArgs;
import com.pulumi.aws.networkfirewall.RuleGroup;
import com.pulumi.aws.networkfirewall.RuleGroupArgs;
import com.pulumi.aws.networkfirewall.inputs.RuleGroupRuleGroupArgs;
import com.pulumi.aws.networkfirewall.inputs.RuleGroupRuleGroupRuleVariablesArgs;
import com.pulumi.aws.networkfirewall.inputs.RuleGroupRuleGroupRulesSourceArgs;
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 suricataRules = S3Functions.getObject(GetObjectArgs.builder()
            .bucket(suricataRulesAwsS3Bucket.id())
            .key("rules/custom.rules")
            .build());

        var s3RulesExample = new RuleGroup("s3RulesExample", RuleGroupArgs.builder()
            .capacity(1000)
            .name("my-terraform-s3-rules")
            .type("STATEFUL")
            .ruleGroup(RuleGroupRuleGroupArgs.builder()
                .ruleVariables(RuleGroupRuleGroupRuleVariablesArgs.builder()
                    .ipSets(RuleGroupRuleGroupRuleVariablesIpSetArgs.builder()
                        .key("HOME_NET")
                        .ipSet(RuleGroupRuleGroupRuleVariablesIpSetIpSetArgs.builder()
                            .definitions(                            
                                "10.0.0.0/16",
                                "192.168.0.0/16",
                                "172.16.0.0/12")
                            .build())
                        .build())
                    .portSets(RuleGroupRuleGroupRuleVariablesPortSetArgs.builder()
                        .key("HTTP_PORTS")
                        .portSet(RuleGroupRuleGroupRuleVariablesPortSetPortSetArgs.builder()
                            .definitions(                            
                                "443",
                                "80")
                            .build())
                        .build())
                    .build())
                .rulesSource(RuleGroupRuleGroupRulesSourceArgs.builder()
                    .rulesString(suricataRules.body())
                    .build())
                .build())
            .tags(Map.of("ManagedBy", "terraform"))
            .build());

    }
}
resources:
  s3RulesExample:
    type: aws:networkfirewall:RuleGroup
    name: s3_rules_example
    properties:
      capacity: 1000
      name: my-terraform-s3-rules
      type: STATEFUL
      ruleGroup:
        ruleVariables:
          ipSets:
            - key: HOME_NET
              ipSet:
                definitions:
                  - 10.0.0.0/16
                  - 192.168.0.0/16
                  - 172.16.0.0/12
          portSets:
            - key: HTTP_PORTS
              portSet:
                definitions:
                  - '443'
                  - '80'
        rulesSource:
          rulesString: ${suricataRules.body}
      tags:
        ManagedBy: terraform
variables:
  suricataRules:
    fn::invoke:
      function: aws:s3:getObject
      arguments:
        bucket: ${suricataRulesAwsS3Bucket.id}
        key: rules/custom.rules

The getObject function retrieves the rule file from S3, and the body property provides the content as a string. This is an alternative to the file-based approach, useful when rules are managed centrally or updated frequently. The ruleVariables block works the same way as in the file-based example, defining reusable IP and port sets.

Beyond these examples

These snippets focus on specific rule group features: stateful domain blocking and IP-based filtering, Suricata rule import (file and S3), and stateless inspection with custom metrics. They’re intentionally minimal rather than full network security policies.

The examples may reference pre-existing infrastructure such as S3 buckets for centralized rule storage and IAM permissions for S3 access. They focus on defining rule groups rather than provisioning the surrounding firewall infrastructure.

To keep things focused, common rule group patterns are omitted, including:

  • Encryption configuration (encryptionConfiguration)
  • IP set references for dynamic IP lists (referenceSets)
  • Stateful rule options (flow control, protocol detection)
  • TCP flag matching and protocol-specific inspection

These omissions are intentional: the goal is to illustrate how each rule group feature is wired, not provide drop-in security policies. See the Network Firewall RuleGroup resource reference for all available configuration options.

Let's configure AWS Network Firewall Rule Groups

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Rule Group Basics
What's the difference between STATEFUL and STATELESS rule groups?
STATEFUL rule groups contain stateful rules that track connection state, while STATELESS rule groups contain stateless rules that evaluate each packet independently. Specify the type using the type property with values STATEFUL or STATELESS.
What properties can't I change after creation?
Both capacity and name are immutable. Changing either property after creation will force replacement of the entire rule group.
How do I calculate the capacity for my rule group?
For stateless rule groups, capacity is the sum of individual rule capacity requirements. For stateful rule groups, the minimum capacity is the number of individual rules.
Rule Definition Methods
Can I use both ruleGroup and rules properties?
No, these properties are mutually exclusive. Use ruleGroup for structured rule definitions, or rules for Suricata flat format (one rule per line). You must specify one, but not both.
How do I load Suricata rules from a file?

You have two options:

  1. Simple format - Use the rules property with a file reference
  2. With variables - Use ruleGroup.rulesSource.rulesString with file content, allowing you to define ruleVariables
Can I load rules from an S3 bucket?
Yes, use aws.s3.getObject to fetch the rules file from S3, then pass the body to ruleGroup.rulesSource.rulesString.
What are rule variables and when should I use them?
Rule variables (ipSets and portSets) define reusable IP addresses and ports that can be referenced in Suricata rules. Use them when you have common IP ranges or ports referenced across multiple rules.
Common Use Cases
How do I block access to specific domains?
Create a STATEFUL rule group with rulesSource.rulesSourceList, setting generatedRulesType to DENYLIST, targetTypes to ["HTTP_HOST"], and targets to your domain list.
How do I permit traffic from specific source IPs?
Use rulesSource.statefulRules with action set to PASS, specifying the source IPs in the header.source field. Set other header fields like destination, protocol, and ports as needed.
How do I block traffic to specific destinations?
Use rulesSource.statefulRules with action set to DROP, specifying the destination IP in header.destination and other matching criteria like ports and protocol.
How do I create custom actions for stateless rules?
Define customActions in statelessRulesAndCustomActions with an actionDefinition (such as publishMetricAction), then reference the actionName in your rule’s actions array alongside standard actions like aws:pass.
Can I reference IP sets from other resources?
Yes, use ruleGroup.referenceSets.ipSetReferences to reference IP sets by their ARN, allowing you to share IP set definitions across rule groups.

Using a different cloud?

Explore security guides for other cloud providers: