Configure AWS Network Firewall Rule Groups

The aws:networkfirewall/ruleGroup:RuleGroup resource, part of the Pulumi AWS provider, defines a Network Firewall rule group containing stateful or stateless traffic filtering rules. This guide focuses on three capabilities: domain and IP-based filtering, Suricata rule import and variables, and stateless inspection with custom actions.

Rule groups attach to Network Firewall policies, which are deployed to VPC endpoints. The examples are intentionally small. Combine them with your own firewall policies and VPC configuration.

Block access to specific domains

Network security teams often need to prevent traffic to known malicious or restricted domains. Network Firewall can inspect HTTP traffic and block requests based on domain names.

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

The rulesSourceList property defines a simple domain-based filter. Set generatedRulesType to “DENYLIST” to block traffic, targetTypes to [“HTTP_HOST”] to inspect HTTP Host headers, and targets to the list of domains to block. The type property must be “STATEFUL” for domain inspection.

Allow traffic from specific source IPs

Applications that receive traffic from known partners or internal services need to permit connections from specific IP addresses while blocking others.

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]],
        },
    },
    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. The header property specifies what traffic to match; the action property determines whether to PASS or DROP. The ruleOptions property includes a unique sid (signature ID) required by the Suricata rule format.

Import existing Suricata rules from a file

Teams migrating from other firewall solutions or using community rule sets often have existing Suricata-format rules they want to reuse.

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-format rules as a string, with one rule per line. This approach bypasses the ruleGroup property entirely, letting you import existing rule files without converting them to Pulumi’s structured format. Use this when you already have Suricata rules and want to preserve them as-is.

Define reusable variables for Suricata rules

Complex rule sets benefit from variables that define IP ranges and port lists once, then reference them across multiple 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 property defines ipSets and portSets that your Suricata rules can reference by name. This centralizes network definitions, making rules easier to maintain. The rulesString property contains the actual Suricata rules, which can reference variables like $WEBSERVERS_HOSTS and $HTTP_PORTS.

Publish metrics from stateless rules

Stateless inspection evaluates packets individually without tracking connection state, enabling high-performance filtering with custom CloudWatch metrics.

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 headers (IPs, ports, protocols, TCP flags) without tracking connections. The customActions property defines a publishMetricAction that emits CloudWatch metrics when the rule matches. The matchAttributes property specifies the 5-tuple to match, and the actions array can include both standard actions (aws:pass, aws:drop) and custom metric actions.

Beyond these examples

These snippets focus on specific rule group features: stateful domain and IP-based filtering, Suricata rule import and variables, and stateless inspection with custom metrics. They’re intentionally minimal rather than full firewall configurations.

The examples may reference pre-existing infrastructure such as Network Firewall policies (rule groups attach to policies), VPC and subnets where firewall endpoints are deployed, and S3 buckets for rule file storage. They focus on configuring the rule group rather than provisioning the surrounding firewall infrastructure.

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

  • Rule group attachment to firewall policies
  • Stateful rule ordering and priority
  • Encryption configuration (encryptionConfiguration)
  • IP set references for dynamic allow/deny lists

These omissions are intentional: the goal is to illustrate how each rule group feature is wired, not provide drop-in firewall modules. 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 & Configuration
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.
Why can't I change the capacity or name after creating a rule group?
Both capacity and name are immutable properties that force resource replacement if changed. Plan your capacity carefully upfront based on your rule requirements.
How do I calculate the capacity I need?
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.
Should I use ruleGroup or rules to define my rules?
You must specify either ruleGroup or rules, not both. Use ruleGroup for structured rule definitions with rule variables and multiple rule types. Use rules for importing existing Suricata-compatible rules in flat format.
Rule Formats & Sources
Can I use Suricata format rules?

Yes, you have two options:

  1. Flat format - Use the rules property with one rule per line
  2. With variables - Use ruleGroup.rulesSource.rulesString to combine Suricata rules with rule variables
Can I load rules from an S3 bucket?
Yes, use aws.s3.getObject to fetch the rules file from S3, then pass the body content to ruleGroup.rulesSource.rulesString.
How do I define reusable rule variables?
Configure ruleGroup.ruleVariables with ipSets and portSets blocks to define variables like HOME_NET or HTTP_PORTS that can be referenced in your Suricata rules.
Stateful Rules
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 or block traffic from specific IP addresses?
Use rulesSource.statefulRules with action set to PASS or DROP, and configure the header block with source/destination IPs, ports, protocol, and direction.
Stateless Rules
What are custom actions in stateless rules?
Custom actions like publishMetricAction let you define additional actions beyond standard ones (e.g., aws:pass). You can use them to publish CloudWatch metrics or perform other custom processing alongside packet handling.

Using a different cloud?

Explore security guides for other cloud providers: