AWS Builder Center

Migrating from AWS WAF Classic to WAF v2: A Comprehensive Guide

A detailed roadmap for migrating to AWS WAF v2, covering planning, execution, testing, and rollback strategies with practical code examples to enhance your web security posture.

Introduction

AWS Web Application Firewall (WAF) helps protect your web applications from common web exploits that could affect application availability, compromise security, or consume excessive resources. In 2019, AWS introduced WAF v2 with significant improvements over WAF Classic, including simplified rule management, new rule capabilities, and better performance.
This article provides a detailed roadmap for migrating from WAF Classic to WAF v2, covering planning, execution, testing, and rollback strategies. Whether you're managing a single web application or a complex enterprise environment, this guide will help you navigate the migration process with confidence.

Why Migrate to WAF v2?

Before diving into the migration process, let's understand why migrating to WAF v2 is beneficial:
  • Enhanced rule capabilities: WAF v2 offers more sophisticated rule options, including JSON body inspection and custom request/response handling
  • Simplified management: Improved console experience and API structure
  • Better performance: More efficient rule evaluation and processing
  • Advanced features: CAPTCHA, Challenge actions, and label matching
  • Future-proofing: AWS is focusing development efforts on WAF v2, with WAF Classic receiving minimal updates

Migration Planning Phase

Step 1: Inventory Your Current WAF Classic Resources

Begin by documenting all your WAF Classic resources:
# List all WAF Classic Web ACLs (Global)
aws waf list-web-acls

# List all WAF Classic Regional Web ACLs
aws waf-regional list-web-acls --region <your-region>

# For each Web ACL, get detailed configuration
aws waf-regional get-web-acl --web-acl-id <web-acl-id> --region <your-region>

# List resources associated with each Web ACL
aws waf-regional list-resources-for-web-acl --web-acl-id <web-acl-id> --region <your-region>

# List rule groups
aws waf-regional list-rule-groups --region <your-region>

# List IP sets
aws waf-regional list-ip-sets --region <your-region>

# List regex pattern sets
aws waf-regional list-regex-pattern-sets --region <your-region>
Create a detailed inventory spreadsheet that includes:
  • Web ACL names and IDs
  • Default actions (ALLOW or BLOCK)
  • Associated resources (ALBs, API Gateways, etc.)
  • Rules and rule groups with their actions and conditions
  • IP sets and regex pattern sets

Step 2: Understand WAF v2 Concepts and Differences

Before migrating, familiarize yourself with key WAF v2 concepts:
  1. Scope: WAF v2 requires explicit scope definition (REGIONAL or CLOUDFRONT)
  1. Web ACL Capacity Units (WCUs): WAF v2 uses WCUs to measure rule complexity
  1. Rule Structure: WAF v2 uses a nested structure for rules and statements
  1. Visibility Configuration: Required for all entities in WAF v2
  1. Rule Action: WAF v2 supports additional actions like CAPTCHA and Challenge

Step 3: Map WAF Classic Components to WAF v2

Use this mapping table to understand how WAF Classic components translate to WAF v2:
WAF Classic Component
WAF v2 Equivalent
Web ACL
Web ACL (with scope)
Rule
Rule
Rule Group
Rule Group
IP Match Condition
IP Set Reference Statement
Geo Match Condition
Geo Match Condition
String Match Condition
Byte Match Statement or Regex Pattern Set Reference Statement
Regex Match Condition
Regex Pattern Set Reference Statement
Size Constraint Condition
Size Constraint Condition
SQL Injection Match Condition
SQL Injection Match Condition
XSS Match Condition
XSS Match Condition
Rate-based Rule
Rate-based Rule Statement

Step 4: Create a Migration Strategy

Develop a migration strategy that includes:
  1. Timeline: Set realistic deadlines for each migration phase
  1. Resource prioritization: Identify which Web ACLs to migrate first
  1. Testing approach: Define how you'll validate WAF v2 configurations
  1. Rollback plan: Document steps to revert to WAF Classic if needed
  1. Monitoring strategy: Determine how you'll compare WAF Classic vs WAF v2 performance

Execution Phase

Step 1: Create IP Sets and Regex Pattern Sets in WAF v2

Start by migrating your IP sets and regex pattern sets:
# Create an IP set in WAF v2
aws wafv2 create-ip-set \
--name "MigratedIPSet-1" \
--scope "REGIONAL" \
--ip-address-version "IPV4" \
--addresses "192.0.2.0/24" "198.51.100.0/24" \
--region <your-region>

# Create a regex pattern set in WAF v2
aws wafv2 create-regex-pattern-set \
--name "MigratedRegexSet-1" \
--scope "REGIONAL" \
--regular-expression-list '[{"RegexString":"badbot.*"}, {"RegexString":"malicious.*"}]' \
--region <your-region>

Step 2: Create Rule Groups in WAF v2

Next, create rule groups that match your WAF Classic rule groups:
aws wafv2 create-rule-group \
--name "MigratedRuleGroup-1" \
--scope "REGIONAL" \
--capacity 100 \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=MigratedRuleGroup1 \
--rules file://rules.json \
--region <your-region>

Where rules.json contains your rule definitions. Here's an example structure:
[
{
"Name": "SQLiRule",
"Priority": 1,
"Statement": {
"SqliMatchStatement": {
"FieldToMatch": {
"AllQueryArguments": {}
},
"TextTransformations": [
{
"Priority": 0,
"Type": "NONE"
}
]
}
},
"Action": {
"Block": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "SQLiRule"
}
}
]


Step 3: Create Web ACLs in WAF v2

Now create your Web ACLs in WAF v2:
aws wafv2 create-web-acl \
--name "MigratedWebACL-1" \
--scope "REGIONAL" \
--default-action Allow={} \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=MigratedWebACL1 \
--rules file://web-acl-rules.json \
--region <your-region>

Where web-acl-rules.json contains your Web ACL rules, including any rule group references:
[
{
"Name": "RuleGroupReference1",
"Priority": 0,
"Statement": {
"RuleGroupReferenceStatement": {
"ARN": "arn:aws:wafv2:<region>:<account-id>:regional/rulegroup/MigratedRuleGroup-1/<id>"
}
},
"OverrideAction": {
"None": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "RuleGroupReference1"
}
},
{
"Name": "RateLimitRule",
"Priority": 1,
"Statement": {
"RateBasedStatement": {
"Limit": 1000,
"AggregateKeyType": "IP"
}
},
"Action": {
"Block": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "RateLimitRule"
}
}
]


Step 4: Initial Testing with Count Mode

Before fully implementing your WAF v2 rules, set them to Count mode for testing:
# Update a rule to use Count mode
aws wafv2 update-web-acl \
--name "MigratedWebACL-1" \
--scope "REGIONAL" \
--id <web-acl-id> \
--default-action Allow={} \
--rules file://count-mode-rules.json \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=MigratedWebACL1 \
--lock-token <lock-token> \
--region <your-region>

In count-mode-rules.json, set the action to Count instead of Block:
[
{
"Name": "SQLiRule",
"Priority": 1,
"Statement": {
"SqliMatchStatement": {
"FieldToMatch": {
"AllQueryArguments": {}
},
"TextTransformations": [
{
"Priority": 0,
"Type": "NONE"
}
]
}
},
"Action": {
"Count": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "SQLiRule"
}
}
]

Testing Phase

Step 1: Set Up Logging for Both WAF Versions

Enable logging for both WAF Classic and WAF v2 to compare their behavior:
# Enable logging for WAF Classic
aws waf-regional put-logging-configuration \
--log-destination-configs 'arn:aws:s3:::<bucket-name>/waf-classic-logs/' \
--resource-arn <web-acl-arn> \
--region <your-region>

# Enable logging for WAF v2
aws wafv2 put-logging-configuration \
--resource-arn <web-acl-arn> \
--log-destination-configs 'arn:aws:firehose:us-east-1:<account-id>:deliverystream/aws-waf-logs-delivery-stream' \
--redacted-fields '{"SingleHeader": {"Name": "password"}}' \
--region <your-region>

Step 2: Analyze Traffic Patterns

Compare logs between WAF Classic and WAF v2 to ensure:
  • Similar request blocking patterns
  • No unexpected blocks or allows
  • Comparable rate limiting behavior
Use CloudWatch metrics to compare:
  • Allowed requests
  • Blocked requests
  • Counted requests (for rules in Count mode)

Step 3: Gradual Rule Transition

Once you're confident in your WAF v2 configuration, gradually transition rules from Count to Block mode:
  1. Start with lower-risk rules (e.g., rate limiting)
  1. Monitor for any unexpected blocks
  1. Gradually enable higher-risk rules (e.g., SQL injection protection)

Step 4: Associate Resources with WAF v2

When you're ready to fully migrate, associate your resources with the WAF v2 Web ACLs:
# Associate an ALB with a WAF v2 Web ACL
aws wafv2 associate-web-acl \
--web-acl-arn <web-acl-arn> \
--resource-arn <alb-arn> \
--region <your-region>

# Associate an API Gateway stage with a WAF v2 Web ACL
aws wafv2 associate-web-acl \
--web-acl-arn <web-acl-arn> \
--resource-arn <api-gateway-stage-arn> \
--region <your-region>

# Associate a CloudFront distribution with a WAF v2 Web ACL (global scope)
aws cloudfront update-distribution \
--id <distribution-id> \
--web-acl-id <web-acl-id> \
--region us-east-1


Rollback Plan

Despite careful planning, you might need to roll back to WAF Classic. Here's how to prepare:

Step 1: Maintain WAF Classic Configuration

Keep your WAF Classic configuration intact until you're fully confident in your WAF v2 setup:
# Do not delete WAF Classic resources until migration is complete and verified

Step 2: Document Association Commands

Document the commands to re-associate resources with WAF Classic:
# Re-associate an ALB with a WAF Classic Web ACL
aws waf-regional associate-web-acl \
--web-acl-id <web-acl-id> \
--resource-arn <alb-arn> \
--region <your-region>

# Re-associate an API Gateway stage with a WAF Classic Web ACL
aws waf-regional associate-web-acl \
--web-acl-id <web-acl-id> \
--resource-arn <api-gateway-stage-arn> \
--region <your-region>

Step 3: Create a Rollback Script

Prepare a script that can quickly:
  1. Disassociate resources from WAF v2
  1. Re-associate resources with WAF Classic
  1. Verify the associations

Post-Migration Optimization

After successfully migrating to WAF v2, consider these optimization steps:

Step 1: Leverage WAF v2-Specific Features

Enhance your security posture with WAF v2-specific features:
  1. JSON Body Inspection: Add rules to inspect JSON payloads
  1. CAPTCHA and Challenge Actions: Implement bot mitigation
  1. Custom Request and Response Handling: Configure custom error pages
  1. Label Matching: Use labels to create more sophisticated rule chains

Step 2: Optimize WCU Usage

Review your Web ACL Capacity Units (WCUs) usage:
  1. Identify rules consuming the most WCUs
  1. Consider consolidating similar rules
  1. Use scope-down statements to limit expensive inspections

Step 3: Implement Comprehensive Logging and Monitoring

Set up advanced monitoring:
  1. Configure detailed logging to Amazon S3 or Kinesis Data Firehose
  1. Create CloudWatch dashboards for WAF metrics
  1. Set up alerts for unusual blocking patterns

Real-World Migration Example

To illustrate the migration process, let's walk through a real-world example:

Scenario

A company has a WAF Classic Web ACL protecting several ALBs with the following configuration:
  • Default action: Allow
  • SQL Injection protection rule
  • Cross-site scripting (XSS) protection rule
  • Rate-based rule limiting to 2000 requests per 5 minutes
  • IP blacklist rule

Migration Steps

1. Create IP Set in WAF v2:
bash
aws wafv2 create-ip-set \
--name "BlockedIPs" \
--scope "REGIONAL" \
--ip-address-version "IPV4" \
--addresses "192.0.2.0/24" "198.51.100.0/24" \
--region us-east-1

2. Create Web ACL in WAF v2:
aws wafv2 create-web-acl \
--name "MainProtection" \
--scope "REGIONAL" \
--default-action Allow={} \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=MainProtection \
--rules file://rules.json \
--region us-east-1

3. Test with Count Mode:
The company ran both WAF versions in parallel for two weeks, with WAF v2 in Count mode.
4. Analyze Results:
After comparing logs, they found WAF v2 was more effective at catching SQL injection attempts.
5. Gradual Transition:
They gradually switched rules from Count to Block mode over a one-week period.
6. Complete Migration:
After confirming no false positives, they fully migrated all resources to WAF v2.

Conclusion

Migrating from WAF Classic to WAF v2 requires careful planning and execution, but the benefits are substantial. WAF v2 offers enhanced protection capabilities, better performance, and a more intuitive management experience.
By following this comprehensive guide, you can ensure a smooth migration process while minimizing the risk of security gaps or application disruptions. Remember that a successful migration isn't just about moving configurations—it's about leveraging the new capabilities of WAF v2 to enhance your overall security posture.
Have you migrated from WAF Classic to WAF v2? Share your experiences and insights in the comments below!

Additional Resources

Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article