This module creates an AWS WAFv2 Web ACL with optional AWS managed rule groups, custom managed rule groups (by ARN), and flexible custom rules. It can optionally send WAF logs to Amazon CloudWatch Logs with a resource policy for log delivery.
You attach the Web ACL to an Application Load Balancer, API Gateway stage, or CloudFront distribution in your own configuration (for example aws_wafv2_web_acl_association or the distribution’s web_acl_id). This module only manages the ACL and its logging.
- Terraform (recent 1.x recommended)
- AWS provider configured for the target account and region
Point source at this repository (or a path if you vendor the module). Replace placeholders with your values.
module "waf" {
source = "git::https://example.com/your-org/tf-module-wafv2.git?ref=main"
pid = "your-project-id"
project = "myapp"
env = "prod"
aws_region = "eu-west-1"
waf_attachment_type = "alb" # used in resource naming: e.g. aws-waf-logs-<project>-<env>-alb-security
waf_enabled = true
web_acl_scope = "REGIONAL" # use CLOUDFRONT for CloudFront distributions
waf_default_action = "allow"
aws_managed_waf_rule_groups = [
{
name = "AWSManagedRulesCommonRuleSet"
priority = 1
action = "none" # "none" = use vendor defaults; "count" = count all rules in the group
},
]
custom_rules = []
custom_managed_waf_rule_groups = []
}| Output | Description |
|---|---|
webacl_arn |
ARN of the Web ACL, or null if waf_enabled is false |
webacl_id |
ID of the Web ACL, or null if disabled |
ip_prefix_ipv4_set_arns |
Map of ip_prefix_sets keys → IPv4 IP set ARNs |
ip_prefix_ipv6_set_arns |
Map of ip_prefix_sets keys → IPv6 IP set ARNs |
Wire webacl_arn into your association resource or CloudFront configuration.
web_acl_scope |
Typical use | Managed rule / custom group ARNs |
|---|---|---|
REGIONAL |
ALB, API Gateway, App Runner, etc. | ARNs containing :regional/ |
CLOUDFRONT |
CloudFront distributions | ARNs containing :global/ |
waf_attachment_type is a label used in the Web ACL name and CloudWatch log group name (alb, api-gateway, cloudfront, etc.). It does not create the attachment by itself.
Each element is an object with at least:
name— AWS managed group name (for exampleAWSManagedRulesCommonRuleSet)priority— unique integer; lower numbers run firstaction—"none"(apply AWS defaults) or"count"(count every rule in the group)
Optional:
inspection_level— forAWSManagedRulesBotControlRuleSetonly (COMMONorTARGETED; defaultCOMMONin the module)rules_override_to_count— list of rule names inside the group to override to count only
Use custom_managed_waf_rule_groups to attach rule groups you already created in WAF. Each object needs name, priority, action (none or count), rule_group_arn, and rules_override_to_count (can be []). The module keeps only ARNs that match the current web_acl_scope (global vs regional).
custom_rules is a list of rules. Each rule has:
name— unique rule name in the ACLpriority— unique integer across all rules (managed groups + custom groups + custom rules)action—allow,block,count,captcha, orchallengestatement— a map matching the Terraformaws_wafv2_web_aclrulestatementschema (snake_case keys:byte_match_statement,geo_match_statement,rate_based_statement,and_statement,or_statement,not_statement, etc.)
Rate limiting is expressed with statement.rate_based_statement (there is no separate variable for rate rules).
Use ip_prefix_sets to define one or more named collections of CIDRs (IPv4 and/or IPv6 per collection). Use ip_prefix_rules to attach Web ACL rules that reference a set by key. This supports multiple allow/block (or count/captcha/challenge) rules, different priorities, and per-rule choice of immediate source IP vs forwarded header (forwarded_ip_config; omit for source IP).
- Priorities must stay unique across managed groups, custom groups,
ip_prefix_rules, andcustom_rules. allowterminates evaluation for matching requests (later rules do not run).
Breaking change: Earlier single-list inputs (ip_whitelist_prefixes, etc.) are removed. Define a set plus one or more rules instead (see README variable table).
Nested logical statements are supported with a practical depth limit (about two levels) as implemented in the module.
Wrapper modules: declare custom_rules with type any (not list(any)), because different rules use different statement shapes.
| File | What it shows |
|---|---|
examples/custom-rules.tfvars |
HCL snippets for custom_rules: IP sets, byte match, SQLi/XSS, size limits, regex, labels, AND/OR, rate-based with scope-down, regex pattern set, captcha/challenge. Copy the custom_rules = [...] block into your .tfvars or module input. |
ip_prefix_sets = {
trusted_office = {
ipv4_prefixes = ["203.0.113.0/24"]
description = "Office egress"
}
known_bad = {
ipv4_prefixes = ["198.51.100.10/32"]
}
}
ip_prefix_rules = [
{
name = "allow-trusted-via-xff"
priority = 0
action = "allow"
ip_set_key = "trusted_office"
forwarded_ip_config = {
header_name = "X-Forwarded-For"
fallback_behavior = "NO_MATCH"
position = "FIRST"
}
},
{
name = "block-bad-source-ip"
priority = 1
action = "block"
ip_set_key = "known_bad"
# no forwarded_ip_config → match immediate source IP
},
]custom_rules = [
{
name = "block-country"
priority = 0
action = "block"
statement = {
geo_match_statement = {
country_codes = ["XX"]
}
}
},
]When aws_waf_logging_enabled is true (default), the module creates a CloudWatch log group named:
aws-waf-logs-<project>-<env>-<waf_attachment_type>-security
and attaches WAF logging plus a log resource policy. Tune retention with waf_log_retention_days (default 365).
| Name | Description |
|---|---|
pid |
Project identifier (required; used by your conventions) |
project |
Project name |
env |
Environment name |
aws_region |
AWS region string |
waf_attachment_type |
Label for naming: alb, api-gateway, cloudfront, … |
waf_enabled |
Whether to create the Web ACL |
web_acl_scope |
REGIONAL or CLOUDFRONT |
web_acl_cloudwatch_enabled |
Metrics on the ACL |
sampled_requests_enabled |
Store sampled requests for matching rules |
aws_waf_logging_enabled |
CloudWatch logging for WAF |
waf_log_retention_days |
Log group retention |
waf_default_action |
allow or block when no rule matches |
aws_managed_waf_rule_groups |
List of AWS managed groups (see above) |
custom_managed_waf_rule_groups |
List of custom rule group ARNs |
custom_rules |
List of custom rules (see above) |
ip_prefix_sets |
Map of named IPv4/IPv6 CIDR collections (WAF IP sets) |
ip_prefix_rules |
Rules referencing ip_prefix_sets (action, priority, optional forwarded IP header) |
See variables.tf for full definitions.