Introduction
In AWS WAFv2, IPSets are commonly used for managing allow/deny lists. Over time, teams may want to move these controls into Rule Groups for modularity, reusability, and cleaner WebACLs.
However, sometime a direct Terraform migration (removing the IPSet and adding the Rule Group in the same apply) often fails with:
WAFAssociatedItemException: AWS WAF couldn’t perform the operation because your resource is being used...
This happens because Terraform attempts to delete the IPSet before the WebACL has stopped referencing it.
Scenario
Current state: A WebACL has a rule referencing an IPSet.
Goal: Migrate that WebACL to instead use a Rule Group.
Challenge: A single terraform apply
replacing the IPSet with the Rule Group fails, since AWS blocks deletion of in-use resources.
Desired outcome: A smooth migration where:
The WebACL starts referencing the Rule Group.
The old IPSet is removed cleanly without error.
Recommendation
Approach 1 — Two-Step Migration
This method splits the migration into two Terraform applies:
For example there is a code for IPset attached to web ACL as below
resource "aws_wafv2_ip_set" "test_ipset" { name = "test-ipset" ........ ...... ..... } resource "aws_wafv2_web_acl" "test_acl" { name = "test-acl" ..... ...... ......... statement { ip_set_reference_statement { arn = aws_wafv2_ip_set.test_ipset.arn } } ..... ........
1. First Apply
Remove the IPSet reference from the WebACL rule.
Add a Rule Group reference instead.
Keep the IPSet resource in the Terraform config.
Apply → WebACL now uses Rule Group, IPSet still exists but is unused.
Example code -:
#Keep the IPSet as it is in first apply resource "aws_wafv2_ip_set" "test_ipset" { name = "test-ipset" ........ ...... ..... } resource "aws_wafv2_rule_group" "test_rg" { name = "test-rule-group" ....... ...... ..... } #Remove the IPSet reference from WebACL and Add the rule group insted resource "aws_wafv2_web_acl" "test_acl" { ...... ....... ............ statement { rule_group_reference_statement { arn = aws_wafv2_rule_group.test_rg.arn #reference to rule group insted of IPsets } } ........ ....... ......
2. Second Apply
Remove the IPSet resource from Terraform config.
Apply → IPSet is cleanly deleted (no active associations).
Approach 2 — Lifecycle Guard
For teams running large-scale deployments or CI/CD pipelines, you can use a lifecycle { prevent_destroy = true }
rule to protect IPSets from premature deletion.
- Use a
lifecycle
block on the IPSet during the initial apply to prevent premature deletion.
lifecycle { prevent_destroy = true }
- Update the WebACL to reference the Rule Group. Apply once across all applications.
- Remove the
prevent_destroy
block in the next module version. - Apply again to clean up the IPSet safely.
Additional Information
https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle