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 and may reference IP sets, rule groups, or Firehose streams. 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
When a request arrives, WAF evaluates it against rules in priority order. The rule references an IP set through its predicates property, checking if the source IP matches the defined range. If matched, the action blocks the request; otherwise, the defaultAction allows it through. The metricName enables CloudWatch monitoring of rule matches.
Apply managed rule groups for common threats
AWS and third-party vendors publish managed rule groups that protect against SQL injection, cross-site scripting, and other common vulnerabilities.
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
Rule groups bundle multiple rules into a single unit. When type is set to “GROUP”, you use overrideAction instead of action to control how the group’s rules behave. Setting overrideAction to “NONE” means the group’s individual rule actions take effect; setting it to “COUNT” would log matches without blocking.
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 and rule evaluation, managed rule groups, and request logging and field redaction. They’re intentionally minimal rather than full application protection configurations.
The examples rely on pre-existing infrastructure such as Application Load Balancers (Web ACL association not shown), Kinesis Firehose delivery streams for logging, and WAF rule groups for managed rule examples. 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
- Geographic and size constraint 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
defaultAction specifies what happens when a request doesn’t match any rule criteria in your Web ACL. You can set it to ALLOW or BLOCK.region property is required but defaults to the region configured in your provider, so you typically don’t need to set it explicitly.Rules & Actions
action property to specify BLOCK or ALLOW, while group rules use overrideAction and reference a rule group via ruleId.action for regular rules (type REGULAR) and overrideAction for group rules (type GROUP). They’re mutually exclusive based on rule type.Logging
aws-waf-logs- as specified in the AWS WAF Developer Guide.Resource Management
name and metricName are immutable. Changing either will force replacement of the Web ACL.