The aws:wafregional/webAcl:WebAcl resource, part of the Pulumi AWS provider, defines a WAF Regional Web ACL that evaluates HTTP requests against rules and takes actions based on matches. This guide focuses on three capabilities: IP-based blocking with regular rules, managed rule group integration, and request logging to Kinesis Firehose.
Web ACLs must be associated with Application Load Balancers. Rules reference IP sets, rule groups, or other WAF conditions that must exist separately. The examples are intentionally small. Combine them with your own ALB associations and rule definitions.
Block traffic from specific IP ranges
Application Load Balancers often need to block traffic from known malicious IP ranges while allowing legitimate requests through.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const ipset = new aws.wafregional.IpSet("ipset", {
name: "tfIPSet",
ipSetDescriptors: [{
type: "IPV4",
value: "192.0.7.0/24",
}],
});
const wafrule = new aws.wafregional.Rule("wafrule", {
name: "tfWAFRule",
metricName: "tfWAFRule",
predicates: [{
dataId: ipset.id,
negated: false,
type: "IPMatch",
}],
});
const wafacl = new aws.wafregional.WebAcl("wafacl", {
name: "tfWebACL",
metricName: "tfWebACL",
defaultAction: {
type: "ALLOW",
},
rules: [{
action: {
type: "BLOCK",
},
priority: 1,
ruleId: wafrule.id,
type: "REGULAR",
}],
});
import pulumi
import pulumi_aws as aws
ipset = aws.wafregional.IpSet("ipset",
name="tfIPSet",
ip_set_descriptors=[{
"type": "IPV4",
"value": "192.0.7.0/24",
}])
wafrule = aws.wafregional.Rule("wafrule",
name="tfWAFRule",
metric_name="tfWAFRule",
predicates=[{
"data_id": ipset.id,
"negated": False,
"type": "IPMatch",
}])
wafacl = aws.wafregional.WebAcl("wafacl",
name="tfWebACL",
metric_name="tfWebACL",
default_action={
"type": "ALLOW",
},
rules=[{
"action": {
"type": "BLOCK",
},
"priority": 1,
"rule_id": wafrule.id,
"type": "REGULAR",
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/wafregional"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
ipset, err := wafregional.NewIpSet(ctx, "ipset", &wafregional.IpSetArgs{
Name: pulumi.String("tfIPSet"),
IpSetDescriptors: wafregional.IpSetIpSetDescriptorArray{
&wafregional.IpSetIpSetDescriptorArgs{
Type: pulumi.String("IPV4"),
Value: pulumi.String("192.0.7.0/24"),
},
},
})
if err != nil {
return err
}
wafrule, err := wafregional.NewRule(ctx, "wafrule", &wafregional.RuleArgs{
Name: pulumi.String("tfWAFRule"),
MetricName: pulumi.String("tfWAFRule"),
Predicates: wafregional.RulePredicateArray{
&wafregional.RulePredicateArgs{
DataId: ipset.ID(),
Negated: pulumi.Bool(false),
Type: pulumi.String("IPMatch"),
},
},
})
if err != nil {
return err
}
_, err = wafregional.NewWebAcl(ctx, "wafacl", &wafregional.WebAclArgs{
Name: pulumi.String("tfWebACL"),
MetricName: pulumi.String("tfWebACL"),
DefaultAction: &wafregional.WebAclDefaultActionArgs{
Type: pulumi.String("ALLOW"),
},
Rules: wafregional.WebAclRuleArray{
&wafregional.WebAclRuleArgs{
Action: &wafregional.WebAclRuleActionArgs{
Type: pulumi.String("BLOCK"),
},
Priority: pulumi.Int(1),
RuleId: wafrule.ID(),
Type: pulumi.String("REGULAR"),
},
},
})
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 ipset = new Aws.WafRegional.IpSet("ipset", new()
{
Name = "tfIPSet",
IpSetDescriptors = new[]
{
new Aws.WafRegional.Inputs.IpSetIpSetDescriptorArgs
{
Type = "IPV4",
Value = "192.0.7.0/24",
},
},
});
var wafrule = new Aws.WafRegional.Rule("wafrule", new()
{
Name = "tfWAFRule",
MetricName = "tfWAFRule",
Predicates = new[]
{
new Aws.WafRegional.Inputs.RulePredicateArgs
{
DataId = ipset.Id,
Negated = false,
Type = "IPMatch",
},
},
});
var wafacl = new Aws.WafRegional.WebAcl("wafacl", new()
{
Name = "tfWebACL",
MetricName = "tfWebACL",
DefaultAction = new Aws.WafRegional.Inputs.WebAclDefaultActionArgs
{
Type = "ALLOW",
},
Rules = new[]
{
new Aws.WafRegional.Inputs.WebAclRuleArgs
{
Action = new Aws.WafRegional.Inputs.WebAclRuleActionArgs
{
Type = "BLOCK",
},
Priority = 1,
RuleId = wafrule.Id,
Type = "REGULAR",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.wafregional.IpSet;
import com.pulumi.aws.wafregional.IpSetArgs;
import com.pulumi.aws.wafregional.inputs.IpSetIpSetDescriptorArgs;
import com.pulumi.aws.wafregional.Rule;
import com.pulumi.aws.wafregional.RuleArgs;
import com.pulumi.aws.wafregional.inputs.RulePredicateArgs;
import com.pulumi.aws.wafregional.WebAcl;
import com.pulumi.aws.wafregional.WebAclArgs;
import com.pulumi.aws.wafregional.inputs.WebAclDefaultActionArgs;
import com.pulumi.aws.wafregional.inputs.WebAclRuleArgs;
import com.pulumi.aws.wafregional.inputs.WebAclRuleActionArgs;
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 ipset = new IpSet("ipset", IpSetArgs.builder()
.name("tfIPSet")
.ipSetDescriptors(IpSetIpSetDescriptorArgs.builder()
.type("IPV4")
.value("192.0.7.0/24")
.build())
.build());
var wafrule = new Rule("wafrule", RuleArgs.builder()
.name("tfWAFRule")
.metricName("tfWAFRule")
.predicates(RulePredicateArgs.builder()
.dataId(ipset.id())
.negated(false)
.type("IPMatch")
.build())
.build());
var wafacl = new WebAcl("wafacl", WebAclArgs.builder()
.name("tfWebACL")
.metricName("tfWebACL")
.defaultAction(WebAclDefaultActionArgs.builder()
.type("ALLOW")
.build())
.rules(WebAclRuleArgs.builder()
.action(WebAclRuleActionArgs.builder()
.type("BLOCK")
.build())
.priority(1)
.ruleId(wafrule.id())
.type("REGULAR")
.build())
.build());
}
}
resources:
ipset:
type: aws:wafregional:IpSet
properties:
name: tfIPSet
ipSetDescriptors:
- type: IPV4
value: 192.0.7.0/24
wafrule:
type: aws:wafregional:Rule
properties:
name: tfWAFRule
metricName: tfWAFRule
predicates:
- dataId: ${ipset.id}
negated: false
type: IPMatch
wafacl:
type: aws:wafregional:WebAcl
properties:
name: tfWebACL
metricName: tfWebACL
defaultAction:
type: ALLOW
rules:
- action:
type: BLOCK
priority: 1
ruleId: ${wafrule.id}
type: REGULAR
The defaultAction sets the behavior for requests that don’t match any rules; here, unmatched requests are allowed. The rules array defines evaluation order via priority. Each rule references a ruleId (the WAF rule resource) and specifies an action (BLOCK or ALLOW). The IpSet defines the IP range to match, and the Rule’s predicates connect the IP set to the evaluation logic.
Apply managed rule groups to your Web ACL
AWS and third-party vendors publish managed rule groups that bundle common protection patterns, eliminating the need to maintain individual rule definitions.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.wafregional.WebAcl("example", {
name: "example",
metricName: "example",
defaultAction: {
type: "ALLOW",
},
rules: [{
priority: 1,
ruleId: exampleAwsWafregionalRuleGroup.id,
type: "GROUP",
overrideAction: {
type: "NONE",
},
}],
});
import pulumi
import pulumi_aws as aws
example = aws.wafregional.WebAcl("example",
name="example",
metric_name="example",
default_action={
"type": "ALLOW",
},
rules=[{
"priority": 1,
"rule_id": example_aws_wafregional_rule_group["id"],
"type": "GROUP",
"override_action": {
"type": "NONE",
},
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/wafregional"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := wafregional.NewWebAcl(ctx, "example", &wafregional.WebAclArgs{
Name: pulumi.String("example"),
MetricName: pulumi.String("example"),
DefaultAction: &wafregional.WebAclDefaultActionArgs{
Type: pulumi.String("ALLOW"),
},
Rules: wafregional.WebAclRuleArray{
&wafregional.WebAclRuleArgs{
Priority: pulumi.Int(1),
RuleId: pulumi.Any(exampleAwsWafregionalRuleGroup.Id),
Type: pulumi.String("GROUP"),
OverrideAction: &wafregional.WebAclRuleOverrideActionArgs{
Type: pulumi.String("NONE"),
},
},
},
})
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.WafRegional.WebAcl("example", new()
{
Name = "example",
MetricName = "example",
DefaultAction = new Aws.WafRegional.Inputs.WebAclDefaultActionArgs
{
Type = "ALLOW",
},
Rules = new[]
{
new Aws.WafRegional.Inputs.WebAclRuleArgs
{
Priority = 1,
RuleId = exampleAwsWafregionalRuleGroup.Id,
Type = "GROUP",
OverrideAction = new Aws.WafRegional.Inputs.WebAclRuleOverrideActionArgs
{
Type = "NONE",
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.wafregional.WebAcl;
import com.pulumi.aws.wafregional.WebAclArgs;
import com.pulumi.aws.wafregional.inputs.WebAclDefaultActionArgs;
import com.pulumi.aws.wafregional.inputs.WebAclRuleArgs;
import com.pulumi.aws.wafregional.inputs.WebAclRuleOverrideActionArgs;
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 WebAcl("example", WebAclArgs.builder()
.name("example")
.metricName("example")
.defaultAction(WebAclDefaultActionArgs.builder()
.type("ALLOW")
.build())
.rules(WebAclRuleArgs.builder()
.priority(1)
.ruleId(exampleAwsWafregionalRuleGroup.id())
.type("GROUP")
.overrideAction(WebAclRuleOverrideActionArgs.builder()
.type("NONE")
.build())
.build())
.build());
}
}
resources:
example:
type: aws:wafregional:WebAcl
properties:
name: example
metricName: example
defaultAction:
type: ALLOW
rules:
- priority: 1
ruleId: ${exampleAwsWafregionalRuleGroup.id}
type: GROUP
overrideAction:
type: NONE
When type is “GROUP”, the rule references a managed rule group instead of a single rule. The overrideAction determines how to handle matches within the group; “NONE” means use the group’s configured actions. Managed rule groups simplify maintenance by centralizing rule updates.
Send request logs to Kinesis Firehose
Security teams need visibility into blocked and allowed requests for compliance auditing and threat analysis.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.wafregional.WebAcl("example", {loggingConfiguration: {
logDestination: exampleAwsKinesisFirehoseDeliveryStream.arn,
redactedFields: {
fieldToMatches: [
{
type: "URI",
},
{
data: "referer",
type: "HEADER",
},
],
},
}});
import pulumi
import pulumi_aws as aws
example = aws.wafregional.WebAcl("example", logging_configuration={
"log_destination": example_aws_kinesis_firehose_delivery_stream["arn"],
"redacted_fields": {
"field_to_matches": [
{
"type": "URI",
},
{
"data": "referer",
"type": "HEADER",
},
],
},
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/wafregional"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := wafregional.NewWebAcl(ctx, "example", &wafregional.WebAclArgs{
LoggingConfiguration: &wafregional.WebAclLoggingConfigurationArgs{
LogDestination: pulumi.Any(exampleAwsKinesisFirehoseDeliveryStream.Arn),
RedactedFields: &wafregional.WebAclLoggingConfigurationRedactedFieldsArgs{
FieldToMatches: wafregional.WebAclLoggingConfigurationRedactedFieldsFieldToMatchArray{
&wafregional.WebAclLoggingConfigurationRedactedFieldsFieldToMatchArgs{
Type: pulumi.String("URI"),
},
&wafregional.WebAclLoggingConfigurationRedactedFieldsFieldToMatchArgs{
Data: pulumi.String("referer"),
Type: pulumi.String("HEADER"),
},
},
},
},
})
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.WafRegional.WebAcl("example", new()
{
LoggingConfiguration = new Aws.WafRegional.Inputs.WebAclLoggingConfigurationArgs
{
LogDestination = exampleAwsKinesisFirehoseDeliveryStream.Arn,
RedactedFields = new Aws.WafRegional.Inputs.WebAclLoggingConfigurationRedactedFieldsArgs
{
FieldToMatches = new[]
{
new Aws.WafRegional.Inputs.WebAclLoggingConfigurationRedactedFieldsFieldToMatchArgs
{
Type = "URI",
},
new Aws.WafRegional.Inputs.WebAclLoggingConfigurationRedactedFieldsFieldToMatchArgs
{
Data = "referer",
Type = "HEADER",
},
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.wafregional.WebAcl;
import com.pulumi.aws.wafregional.WebAclArgs;
import com.pulumi.aws.wafregional.inputs.WebAclLoggingConfigurationArgs;
import com.pulumi.aws.wafregional.inputs.WebAclLoggingConfigurationRedactedFieldsArgs;
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 WebAcl("example", WebAclArgs.builder()
.loggingConfiguration(WebAclLoggingConfigurationArgs.builder()
.logDestination(exampleAwsKinesisFirehoseDeliveryStream.arn())
.redactedFields(WebAclLoggingConfigurationRedactedFieldsArgs.builder()
.fieldToMatches(
WebAclLoggingConfigurationRedactedFieldsFieldToMatchArgs.builder()
.type("URI")
.build(),
WebAclLoggingConfigurationRedactedFieldsFieldToMatchArgs.builder()
.data("referer")
.type("HEADER")
.build())
.build())
.build())
.build());
}
}
resources:
example:
type: aws:wafregional:WebAcl
properties:
loggingConfiguration:
logDestination: ${exampleAwsKinesisFirehoseDeliveryStream.arn}
redactedFields:
fieldToMatches:
- type: URI
- data: referer
type: HEADER
The loggingConfiguration property streams request logs to a Kinesis Firehose Delivery Stream. The logDestination must point to a stream whose name begins with “aws-waf-logs-” per AWS requirements. The redactedFields property removes sensitive data (like URIs or specific headers) from logs before delivery.
Beyond these examples
These snippets focus on specific Web ACL features: IP-based blocking with regular rules, managed rule group integration, and request logging to Kinesis Firehose. They’re intentionally minimal rather than full WAF deployments.
The examples rely on pre-existing infrastructure such as Application Load Balancers, Kinesis Firehose Delivery Streams, and managed rule groups. They focus on Web ACL configuration rather than provisioning the surrounding infrastructure.
To keep things focused, common WAF patterns are omitted, including:
- Web ACL association with Application Load Balancers
- Rate-based rules for DDoS protection
- String and regex matching conditions
- Size constraint and SQL injection rules
These omissions are intentional: the goal is to illustrate how each Web ACL feature is wired, not provide drop-in security modules. See the WAF Regional WebAcl resource reference for all available configuration options.
Let's configure AWS WAF Regional Web ACLs
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration & Setup
name and metricName properties are immutable. Changing either will force recreation of the Web ACL.region property is required but defaults to the region configured in your provider.name, metricName, and defaultAction (either ALLOW or BLOCK). Rules are optional.Rules & Actions
action property to specify ALLOW or BLOCK. GROUP rules use overrideAction instead, typically set to NONE to use the group’s configured actions.rules array can contain both types. Use type: "REGULAR" with action or type: "GROUP" with overrideAction.Logging & Monitoring
aws-waf-logs- for WAF logging to work.loggingConfiguration.redactedFields.fieldToMatches with field types like URI or HEADER (with optional data for header names).