Configure AWS WAFv2 Web ACL Rule Group Associations

The aws:wafv2/webAclRuleGroupAssociation:WebAclRuleGroupAssociation resource, part of the Pulumi AWS provider, associates WAFv2 rule groups (custom or AWS-managed) with Web ACLs by creating a rule that references the entire rule group. This guide focuses on three capabilities: custom rule group association, AWS-managed rule group integration, and rule action overrides for testing and customization.

Associations require existing Web ACLs and, for custom rule groups, existing rule group resources. Web ACLs must use lifecycle.ignore_changes to prevent configuration drift when this resource modifies rules. The examples are intentionally small. Combine them with your own Web ACLs and rule groups.

Associate a custom rule group with a Web ACL

Most WAF deployments attach custom rule groups to Web ACLs, allowing you to reuse rule definitions across multiple Web ACLs without duplicating configuration.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// Web ACL must use lifecycle.ignore_changes to prevent drift from this resource
const example = new aws.wafv2.WebAcl("example", {
    name: "example-web-acl",
    scope: "REGIONAL",
    defaultAction: {
        allow: {},
    },
    visibilityConfig: {
        cloudwatchMetricsEnabled: true,
        metricName: "example-web-acl",
        sampledRequestsEnabled: true,
    },
});
// Associate a custom rule group
const exampleWebAclRuleGroupAssociation = new aws.wafv2.WebAclRuleGroupAssociation("example", {
    ruleName: "example-rule-group-rule",
    priority: 100,
    webAclArn: example.arn,
    ruleGroupReference: {
        arn: exampleAwsWafv2RuleGroup.arn,
    },
});
import pulumi
import pulumi_aws as aws

# Web ACL must use lifecycle.ignore_changes to prevent drift from this resource
example = aws.wafv2.WebAcl("example",
    name="example-web-acl",
    scope="REGIONAL",
    default_action={
        "allow": {},
    },
    visibility_config={
        "cloudwatch_metrics_enabled": True,
        "metric_name": "example-web-acl",
        "sampled_requests_enabled": True,
    })
# Associate a custom rule group
example_web_acl_rule_group_association = aws.wafv2.WebAclRuleGroupAssociation("example",
    rule_name="example-rule-group-rule",
    priority=100,
    web_acl_arn=example.arn,
    rule_group_reference={
        "arn": example_aws_wafv2_rule_group["arn"],
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/wafv2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Web ACL must use lifecycle.ignore_changes to prevent drift from this resource
		example, err := wafv2.NewWebAcl(ctx, "example", &wafv2.WebAclArgs{
			Name:  pulumi.String("example-web-acl"),
			Scope: pulumi.String("REGIONAL"),
			DefaultAction: &wafv2.WebAclDefaultActionArgs{
				Allow: &wafv2.WebAclDefaultActionAllowArgs{},
			},
			VisibilityConfig: &wafv2.WebAclVisibilityConfigArgs{
				CloudwatchMetricsEnabled: pulumi.Bool(true),
				MetricName:               pulumi.String("example-web-acl"),
				SampledRequestsEnabled:   pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		// Associate a custom rule group
		_, err = wafv2.NewWebAclRuleGroupAssociation(ctx, "example", &wafv2.WebAclRuleGroupAssociationArgs{
			RuleName:  pulumi.String("example-rule-group-rule"),
			Priority:  pulumi.Int(100),
			WebAclArn: example.Arn,
			RuleGroupReference: &wafv2.WebAclRuleGroupAssociationRuleGroupReferenceArgs{
				Arn: pulumi.Any(exampleAwsWafv2RuleGroup.Arn),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    // Web ACL must use lifecycle.ignore_changes to prevent drift from this resource
    var example = new Aws.WafV2.WebAcl("example", new()
    {
        Name = "example-web-acl",
        Scope = "REGIONAL",
        DefaultAction = new Aws.WafV2.Inputs.WebAclDefaultActionArgs
        {
            Allow = null,
        },
        VisibilityConfig = new Aws.WafV2.Inputs.WebAclVisibilityConfigArgs
        {
            CloudwatchMetricsEnabled = true,
            MetricName = "example-web-acl",
            SampledRequestsEnabled = true,
        },
    });

    // Associate a custom rule group
    var exampleWebAclRuleGroupAssociation = new Aws.WafV2.WebAclRuleGroupAssociation("example", new()
    {
        RuleName = "example-rule-group-rule",
        Priority = 100,
        WebAclArn = example.Arn,
        RuleGroupReference = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceArgs
        {
            Arn = exampleAwsWafv2RuleGroup.Arn,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.wafv2.WebAcl;
import com.pulumi.aws.wafv2.WebAclArgs;
import com.pulumi.aws.wafv2.inputs.WebAclDefaultActionArgs;
import com.pulumi.aws.wafv2.inputs.WebAclDefaultActionAllowArgs;
import com.pulumi.aws.wafv2.inputs.WebAclVisibilityConfigArgs;
import com.pulumi.aws.wafv2.WebAclRuleGroupAssociation;
import com.pulumi.aws.wafv2.WebAclRuleGroupAssociationArgs;
import com.pulumi.aws.wafv2.inputs.WebAclRuleGroupAssociationRuleGroupReferenceArgs;
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) {
        // Web ACL must use lifecycle.ignore_changes to prevent drift from this resource
        var example = new WebAcl("example", WebAclArgs.builder()
            .name("example-web-acl")
            .scope("REGIONAL")
            .defaultAction(WebAclDefaultActionArgs.builder()
                .allow(WebAclDefaultActionAllowArgs.builder()
                    .build())
                .build())
            .visibilityConfig(WebAclVisibilityConfigArgs.builder()
                .cloudwatchMetricsEnabled(true)
                .metricName("example-web-acl")
                .sampledRequestsEnabled(true)
                .build())
            .build());

        // Associate a custom rule group
        var exampleWebAclRuleGroupAssociation = new WebAclRuleGroupAssociation("exampleWebAclRuleGroupAssociation", WebAclRuleGroupAssociationArgs.builder()
            .ruleName("example-rule-group-rule")
            .priority(100)
            .webAclArn(example.arn())
            .ruleGroupReference(WebAclRuleGroupAssociationRuleGroupReferenceArgs.builder()
                .arn(exampleAwsWafv2RuleGroup.arn())
                .build())
            .build());

    }
}
resources:
  # Web ACL must use lifecycle.ignore_changes to prevent drift from this resource
  example:
    type: aws:wafv2:WebAcl
    properties:
      name: example-web-acl
      scope: REGIONAL
      defaultAction:
        allow: {}
      visibilityConfig:
        cloudwatchMetricsEnabled: true
        metricName: example-web-acl
        sampledRequestsEnabled: true
  # Associate a custom rule group
  exampleWebAclRuleGroupAssociation:
    type: aws:wafv2:WebAclRuleGroupAssociation
    name: example
    properties:
      ruleName: example-rule-group-rule
      priority: 100
      webAclArn: ${example.arn}
      ruleGroupReference:
        arn: ${exampleAwsWafv2RuleGroup.arn}

The ruleGroupReference property points to your custom rule group by ARN. The priority determines evaluation order (lower numbers first). The Web ACL must include lifecycle { ignore_changes = [rule] } to prevent drift, since this resource modifies the Web ACL’s rules outside its direct management.

Apply AWS-managed rule sets to a Web ACL

AWS provides pre-configured managed rule groups that protect against common threats like SQL injection and cross-site scripting.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.wafv2.WebAclRuleGroupAssociation("example", {
    ruleName: "aws-common-rule-set",
    priority: 50,
    webAclArn: exampleAwsWafv2WebAcl.arn,
    managedRuleGroup: {
        name: "AWSManagedRulesCommonRuleSet",
        vendorName: "AWS",
    },
});
import pulumi
import pulumi_aws as aws

example = aws.wafv2.WebAclRuleGroupAssociation("example",
    rule_name="aws-common-rule-set",
    priority=50,
    web_acl_arn=example_aws_wafv2_web_acl["arn"],
    managed_rule_group={
        "name": "AWSManagedRulesCommonRuleSet",
        "vendor_name": "AWS",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/wafv2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := wafv2.NewWebAclRuleGroupAssociation(ctx, "example", &wafv2.WebAclRuleGroupAssociationArgs{
			RuleName:  pulumi.String("aws-common-rule-set"),
			Priority:  pulumi.Int(50),
			WebAclArn: pulumi.Any(exampleAwsWafv2WebAcl.Arn),
			ManagedRuleGroup: &wafv2.WebAclRuleGroupAssociationManagedRuleGroupArgs{
				Name:       pulumi.String("AWSManagedRulesCommonRuleSet"),
				VendorName: pulumi.String("AWS"),
			},
		})
		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.WafV2.WebAclRuleGroupAssociation("example", new()
    {
        RuleName = "aws-common-rule-set",
        Priority = 50,
        WebAclArn = exampleAwsWafv2WebAcl.Arn,
        ManagedRuleGroup = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationManagedRuleGroupArgs
        {
            Name = "AWSManagedRulesCommonRuleSet",
            VendorName = "AWS",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.wafv2.WebAclRuleGroupAssociation;
import com.pulumi.aws.wafv2.WebAclRuleGroupAssociationArgs;
import com.pulumi.aws.wafv2.inputs.WebAclRuleGroupAssociationManagedRuleGroupArgs;
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 WebAclRuleGroupAssociation("example", WebAclRuleGroupAssociationArgs.builder()
            .ruleName("aws-common-rule-set")
            .priority(50)
            .webAclArn(exampleAwsWafv2WebAcl.arn())
            .managedRuleGroup(WebAclRuleGroupAssociationManagedRuleGroupArgs.builder()
                .name("AWSManagedRulesCommonRuleSet")
                .vendorName("AWS")
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:wafv2:WebAclRuleGroupAssociation
    properties:
      ruleName: aws-common-rule-set
      priority: 50
      webAclArn: ${exampleAwsWafv2WebAcl.arn}
      managedRuleGroup:
        name: AWSManagedRulesCommonRuleSet
        vendorName: AWS

The managedRuleGroup property specifies the rule group by name and vendorName instead of ARN. AWS-managed rule groups are maintained by AWS and automatically updated with new threat signatures.

Override managed rule actions for testing or monitoring

When deploying managed rule groups, teams often test rules in count mode before enforcing blocks, or customize actions for specific rules.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.wafv2.WebAclRuleGroupAssociation("example", {
    ruleName: "aws-common-rule-set-with-overrides",
    priority: 70,
    webAclArn: exampleAwsWafv2WebAcl.arn,
    managedRuleGroup: {
        name: "AWSManagedRulesCommonRuleSet",
        vendorName: "AWS",
        ruleActionOverrides: [
            {
                name: "GenericRFI_BODY",
                actionToUse: {
                    count: {
                        customRequestHandling: {
                            insertHeaders: [{
                                name: "X-RFI-Override",
                                value: "counted",
                            }],
                        },
                    },
                },
            },
            {
                name: "SizeRestrictions_BODY",
                actionToUse: {
                    captcha: {},
                },
            },
        ],
    },
});
import pulumi
import pulumi_aws as aws

example = aws.wafv2.WebAclRuleGroupAssociation("example",
    rule_name="aws-common-rule-set-with-overrides",
    priority=70,
    web_acl_arn=example_aws_wafv2_web_acl["arn"],
    managed_rule_group={
        "name": "AWSManagedRulesCommonRuleSet",
        "vendor_name": "AWS",
        "rule_action_overrides": [
            {
                "name": "GenericRFI_BODY",
                "action_to_use": {
                    "count": {
                        "custom_request_handling": {
                            "insert_headers": [{
                                "name": "X-RFI-Override",
                                "value": "counted",
                            }],
                        },
                    },
                },
            },
            {
                "name": "SizeRestrictions_BODY",
                "action_to_use": {
                    "captcha": {},
                },
            },
        ],
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/wafv2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := wafv2.NewWebAclRuleGroupAssociation(ctx, "example", &wafv2.WebAclRuleGroupAssociationArgs{
			RuleName:  pulumi.String("aws-common-rule-set-with-overrides"),
			Priority:  pulumi.Int(70),
			WebAclArn: pulumi.Any(exampleAwsWafv2WebAcl.Arn),
			ManagedRuleGroup: &wafv2.WebAclRuleGroupAssociationManagedRuleGroupArgs{
				Name:       pulumi.String("AWSManagedRulesCommonRuleSet"),
				VendorName: pulumi.String("AWS"),
				RuleActionOverrides: wafv2.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideArray{
					&wafv2.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideArgs{
						Name: pulumi.String("GenericRFI_BODY"),
						ActionToUse: &wafv2.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseArgs{
							Count: &wafv2.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseCountArgs{
								CustomRequestHandling: &wafv2.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseCountCustomRequestHandlingArgs{
									InsertHeaders: wafv2.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseCountCustomRequestHandlingInsertHeaderArray{
										&wafv2.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseCountCustomRequestHandlingInsertHeaderArgs{
											Name:  pulumi.String("X-RFI-Override"),
											Value: pulumi.String("counted"),
										},
									},
								},
							},
						},
					},
					&wafv2.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideArgs{
						Name: pulumi.String("SizeRestrictions_BODY"),
						ActionToUse: &wafv2.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseArgs{
							Captcha: &wafv2.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseCaptchaArgs{},
						},
					},
				},
			},
		})
		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.WafV2.WebAclRuleGroupAssociation("example", new()
    {
        RuleName = "aws-common-rule-set-with-overrides",
        Priority = 70,
        WebAclArn = exampleAwsWafv2WebAcl.Arn,
        ManagedRuleGroup = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationManagedRuleGroupArgs
        {
            Name = "AWSManagedRulesCommonRuleSet",
            VendorName = "AWS",
            RuleActionOverrides = new[]
            {
                new Aws.WafV2.Inputs.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideArgs
                {
                    Name = "GenericRFI_BODY",
                    ActionToUse = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseArgs
                    {
                        Count = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseCountArgs
                        {
                            CustomRequestHandling = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseCountCustomRequestHandlingArgs
                            {
                                InsertHeaders = new[]
                                {
                                    new Aws.WafV2.Inputs.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseCountCustomRequestHandlingInsertHeaderArgs
                                    {
                                        Name = "X-RFI-Override",
                                        Value = "counted",
                                    },
                                },
                            },
                        },
                    },
                },
                new Aws.WafV2.Inputs.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideArgs
                {
                    Name = "SizeRestrictions_BODY",
                    ActionToUse = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseArgs
                    {
                        Captcha = null,
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.wafv2.WebAclRuleGroupAssociation;
import com.pulumi.aws.wafv2.WebAclRuleGroupAssociationArgs;
import com.pulumi.aws.wafv2.inputs.WebAclRuleGroupAssociationManagedRuleGroupArgs;
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 WebAclRuleGroupAssociation("example", WebAclRuleGroupAssociationArgs.builder()
            .ruleName("aws-common-rule-set-with-overrides")
            .priority(70)
            .webAclArn(exampleAwsWafv2WebAcl.arn())
            .managedRuleGroup(WebAclRuleGroupAssociationManagedRuleGroupArgs.builder()
                .name("AWSManagedRulesCommonRuleSet")
                .vendorName("AWS")
                .ruleActionOverrides(                
                    WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideArgs.builder()
                        .name("GenericRFI_BODY")
                        .actionToUse(WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseArgs.builder()
                            .count(WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseCountArgs.builder()
                                .customRequestHandling(WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseCountCustomRequestHandlingArgs.builder()
                                    .insertHeaders(WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseCountCustomRequestHandlingInsertHeaderArgs.builder()
                                        .name("X-RFI-Override")
                                        .value("counted")
                                        .build())
                                    .build())
                                .build())
                            .build())
                        .build(),
                    WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideArgs.builder()
                        .name("SizeRestrictions_BODY")
                        .actionToUse(WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseArgs.builder()
                            .captcha(WebAclRuleGroupAssociationManagedRuleGroupRuleActionOverrideActionToUseCaptchaArgs.builder()
                                .build())
                            .build())
                        .build())
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:wafv2:WebAclRuleGroupAssociation
    properties:
      ruleName: aws-common-rule-set-with-overrides
      priority: 70
      webAclArn: ${exampleAwsWafv2WebAcl.arn}
      managedRuleGroup:
        name: AWSManagedRulesCommonRuleSet
        vendorName: AWS
        ruleActionOverrides:
          - name: GenericRFI_BODY
            actionToUse:
              count:
                customRequestHandling:
                  insertHeaders:
                    - name: X-RFI-Override
                      value: counted
          - name: SizeRestrictions_BODY
            actionToUse:
              captcha: {}

The ruleActionOverrides array lets you change individual rule actions. Each override specifies the exact rule name and the actionToUse (count, captcha, allow, or block). Rule names must exactly match the case-sensitive names in the managed rule group; WAF silently ignores invalid names for managed rule groups.

Set count mode for all custom rule group rules

During testing or troubleshooting, you can override all rules in a custom rule group to count matches instead of blocking.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.wafv2.WebAclRuleGroupAssociation("example", {
    ruleName: "example-rule-group-rule",
    priority: 100,
    webAclArn: exampleAwsWafv2WebAcl.arn,
    overrideAction: "count",
    ruleGroupReference: {
        arn: exampleAwsWafv2RuleGroup.arn,
    },
});
import pulumi
import pulumi_aws as aws

example = aws.wafv2.WebAclRuleGroupAssociation("example",
    rule_name="example-rule-group-rule",
    priority=100,
    web_acl_arn=example_aws_wafv2_web_acl["arn"],
    override_action="count",
    rule_group_reference={
        "arn": example_aws_wafv2_rule_group["arn"],
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/wafv2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := wafv2.NewWebAclRuleGroupAssociation(ctx, "example", &wafv2.WebAclRuleGroupAssociationArgs{
			RuleName:       pulumi.String("example-rule-group-rule"),
			Priority:       pulumi.Int(100),
			WebAclArn:      pulumi.Any(exampleAwsWafv2WebAcl.Arn),
			OverrideAction: pulumi.String("count"),
			RuleGroupReference: &wafv2.WebAclRuleGroupAssociationRuleGroupReferenceArgs{
				Arn: pulumi.Any(exampleAwsWafv2RuleGroup.Arn),
			},
		})
		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.WafV2.WebAclRuleGroupAssociation("example", new()
    {
        RuleName = "example-rule-group-rule",
        Priority = 100,
        WebAclArn = exampleAwsWafv2WebAcl.Arn,
        OverrideAction = "count",
        RuleGroupReference = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceArgs
        {
            Arn = exampleAwsWafv2RuleGroup.Arn,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.wafv2.WebAclRuleGroupAssociation;
import com.pulumi.aws.wafv2.WebAclRuleGroupAssociationArgs;
import com.pulumi.aws.wafv2.inputs.WebAclRuleGroupAssociationRuleGroupReferenceArgs;
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 WebAclRuleGroupAssociation("example", WebAclRuleGroupAssociationArgs.builder()
            .ruleName("example-rule-group-rule")
            .priority(100)
            .webAclArn(exampleAwsWafv2WebAcl.arn())
            .overrideAction("count")
            .ruleGroupReference(WebAclRuleGroupAssociationRuleGroupReferenceArgs.builder()
                .arn(exampleAwsWafv2RuleGroup.arn())
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:wafv2:WebAclRuleGroupAssociation
    properties:
      ruleName: example-rule-group-rule
      priority: 100
      webAclArn: ${exampleAwsWafv2WebAcl.arn}
      overrideAction: count
      ruleGroupReference:
        arn: ${exampleAwsWafv2RuleGroup.arn}

The overrideAction property applies to all rules in the rule group. Setting it to “count” overrides every rule’s action to count mode, useful for testing without blocking traffic. This differs from ruleActionOverrides, which targets specific rules.

Override individual custom rule actions with headers

For granular control, you can override specific rules within a custom rule group and add custom headers to track which overrides were applied.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.wafv2.WebAclRuleGroupAssociation("example", {
    ruleName: "example-rule-group-rule",
    priority: 100,
    webAclArn: exampleAwsWafv2WebAcl.arn,
    ruleGroupReference: {
        arn: exampleAwsWafv2RuleGroup.arn,
        ruleActionOverrides: [
            {
                name: "geo-block-rule",
                actionToUse: {
                    count: {
                        customRequestHandling: {
                            insertHeaders: [{
                                name: "X-Geo-Block-Override",
                                value: "counted",
                            }],
                        },
                    },
                },
            },
            {
                name: "rate-limit-rule",
                actionToUse: {
                    captcha: {
                        customRequestHandling: {
                            insertHeaders: [{
                                name: "X-Rate-Limit-Override",
                                value: "captcha-required",
                            }],
                        },
                    },
                },
            },
        ],
    },
});
import pulumi
import pulumi_aws as aws

example = aws.wafv2.WebAclRuleGroupAssociation("example",
    rule_name="example-rule-group-rule",
    priority=100,
    web_acl_arn=example_aws_wafv2_web_acl["arn"],
    rule_group_reference={
        "arn": example_aws_wafv2_rule_group["arn"],
        "rule_action_overrides": [
            {
                "name": "geo-block-rule",
                "action_to_use": {
                    "count": {
                        "custom_request_handling": {
                            "insert_headers": [{
                                "name": "X-Geo-Block-Override",
                                "value": "counted",
                            }],
                        },
                    },
                },
            },
            {
                "name": "rate-limit-rule",
                "action_to_use": {
                    "captcha": {
                        "custom_request_handling": {
                            "insert_headers": [{
                                "name": "X-Rate-Limit-Override",
                                "value": "captcha-required",
                            }],
                        },
                    },
                },
            },
        ],
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/wafv2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := wafv2.NewWebAclRuleGroupAssociation(ctx, "example", &wafv2.WebAclRuleGroupAssociationArgs{
			RuleName:  pulumi.String("example-rule-group-rule"),
			Priority:  pulumi.Int(100),
			WebAclArn: pulumi.Any(exampleAwsWafv2WebAcl.Arn),
			RuleGroupReference: &wafv2.WebAclRuleGroupAssociationRuleGroupReferenceArgs{
				Arn: pulumi.Any(exampleAwsWafv2RuleGroup.Arn),
				RuleActionOverrides: wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideArray{
					&wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideArgs{
						Name: pulumi.String("geo-block-rule"),
						ActionToUse: &wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseArgs{
							Count: &wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCountArgs{
								CustomRequestHandling: &wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCountCustomRequestHandlingArgs{
									InsertHeaders: wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCountCustomRequestHandlingInsertHeaderArray{
										&wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCountCustomRequestHandlingInsertHeaderArgs{
											Name:  pulumi.String("X-Geo-Block-Override"),
											Value: pulumi.String("counted"),
										},
									},
								},
							},
						},
					},
					&wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideArgs{
						Name: pulumi.String("rate-limit-rule"),
						ActionToUse: &wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseArgs{
							Captcha: &wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCaptchaArgs{
								CustomRequestHandling: &wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCaptchaCustomRequestHandlingArgs{
									InsertHeaders: wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCaptchaCustomRequestHandlingInsertHeaderArray{
										&wafv2.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCaptchaCustomRequestHandlingInsertHeaderArgs{
											Name:  pulumi.String("X-Rate-Limit-Override"),
											Value: pulumi.String("captcha-required"),
										},
									},
								},
							},
						},
					},
				},
			},
		})
		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.WafV2.WebAclRuleGroupAssociation("example", new()
    {
        RuleName = "example-rule-group-rule",
        Priority = 100,
        WebAclArn = exampleAwsWafv2WebAcl.Arn,
        RuleGroupReference = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceArgs
        {
            Arn = exampleAwsWafv2RuleGroup.Arn,
            RuleActionOverrides = new[]
            {
                new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideArgs
                {
                    Name = "geo-block-rule",
                    ActionToUse = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseArgs
                    {
                        Count = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCountArgs
                        {
                            CustomRequestHandling = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCountCustomRequestHandlingArgs
                            {
                                InsertHeaders = new[]
                                {
                                    new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCountCustomRequestHandlingInsertHeaderArgs
                                    {
                                        Name = "X-Geo-Block-Override",
                                        Value = "counted",
                                    },
                                },
                            },
                        },
                    },
                },
                new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideArgs
                {
                    Name = "rate-limit-rule",
                    ActionToUse = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseArgs
                    {
                        Captcha = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCaptchaArgs
                        {
                            CustomRequestHandling = new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCaptchaCustomRequestHandlingArgs
                            {
                                InsertHeaders = new[]
                                {
                                    new Aws.WafV2.Inputs.WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCaptchaCustomRequestHandlingInsertHeaderArgs
                                    {
                                        Name = "X-Rate-Limit-Override",
                                        Value = "captcha-required",
                                    },
                                },
                            },
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.wafv2.WebAclRuleGroupAssociation;
import com.pulumi.aws.wafv2.WebAclRuleGroupAssociationArgs;
import com.pulumi.aws.wafv2.inputs.WebAclRuleGroupAssociationRuleGroupReferenceArgs;
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 WebAclRuleGroupAssociation("example", WebAclRuleGroupAssociationArgs.builder()
            .ruleName("example-rule-group-rule")
            .priority(100)
            .webAclArn(exampleAwsWafv2WebAcl.arn())
            .ruleGroupReference(WebAclRuleGroupAssociationRuleGroupReferenceArgs.builder()
                .arn(exampleAwsWafv2RuleGroup.arn())
                .ruleActionOverrides(                
                    WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideArgs.builder()
                        .name("geo-block-rule")
                        .actionToUse(WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseArgs.builder()
                            .count(WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCountArgs.builder()
                                .customRequestHandling(WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCountCustomRequestHandlingArgs.builder()
                                    .insertHeaders(WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCountCustomRequestHandlingInsertHeaderArgs.builder()
                                        .name("X-Geo-Block-Override")
                                        .value("counted")
                                        .build())
                                    .build())
                                .build())
                            .build())
                        .build(),
                    WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideArgs.builder()
                        .name("rate-limit-rule")
                        .actionToUse(WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseArgs.builder()
                            .captcha(WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCaptchaArgs.builder()
                                .customRequestHandling(WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCaptchaCustomRequestHandlingArgs.builder()
                                    .insertHeaders(WebAclRuleGroupAssociationRuleGroupReferenceRuleActionOverrideActionToUseCaptchaCustomRequestHandlingInsertHeaderArgs.builder()
                                        .name("X-Rate-Limit-Override")
                                        .value("captcha-required")
                                        .build())
                                    .build())
                                .build())
                            .build())
                        .build())
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:wafv2:WebAclRuleGroupAssociation
    properties:
      ruleName: example-rule-group-rule
      priority: 100
      webAclArn: ${exampleAwsWafv2WebAcl.arn}
      ruleGroupReference:
        arn: ${exampleAwsWafv2RuleGroup.arn}
        ruleActionOverrides:
          - name: geo-block-rule
            actionToUse:
              count:
                customRequestHandling:
                  insertHeaders:
                    - name: X-Geo-Block-Override
                      value: counted
          - name: rate-limit-rule
            actionToUse:
              captcha:
                customRequestHandling:
                  insertHeaders:
                    - name: X-Rate-Limit-Override
                      value: captcha-required

The ruleActionOverrides array works with custom rule groups too. The customRequestHandling block adds headers to requests that match the rule, useful for tracking which overrides triggered. Unlike managed rule groups, invalid rule names in custom rule groups cause Web ACL updates to fail.

Beyond these examples

These snippets focus on specific rule group association features: custom and managed rule group association, rule action overrides and count mode, and priority-based rule ordering. They’re intentionally minimal rather than full WAF configurations.

The examples reference pre-existing infrastructure such as Web ACLs with lifecycle.ignore_changes for rules, and custom rule groups for custom rule group examples. They focus on associating rule groups rather than creating the underlying Web ACLs or rule groups.

To keep things focused, common association patterns are omitted, including:

  • Visibility configuration (visibilityConfig for CloudWatch metrics)
  • Managed rule group versioning (version property)
  • Managed rule group configs (ACFP, ATP, Bot Control settings)
  • CloudFront-specific Web ACL associations

These omissions are intentional: the goal is to illustrate how each rule group association feature is wired, not provide drop-in WAF modules. See the WAFv2 WebAclRuleGroupAssociation resource reference for all available configuration options.

Let's configure AWS WAFv2 Web ACL Rule Group Associations

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Common Errors & Pitfalls
Why is my Web ACL showing configuration drift after adding this association?
This resource modifies the Web ACL’s rules outside of the Web ACL resource’s direct management. Add lifecycle { ignore_changes = [rule] } to your Web ACL resource configuration to prevent drift.
What happens if I use an invalid rule name in my rule action overrides?
With managed rule groups, WAF silently ignores invalid rule names. With custom rule groups, invalid rule names cause Web ACL updates to fail. Rule names must be case-sensitive exact matches to existing rules in the rule group.
Rule Groups & Configuration
What's the difference between managed rule groups and custom rule groups?
Managed rule groups are pre-configured by AWS or third-party vendors (use managedRuleGroup with name and vendorName). Custom rule groups are user-created within your AWS account (use ruleGroupReference with the rule group’s ARN). These properties are mutually exclusive.
How do I associate a managed rule group with my Web ACL?
Use managedRuleGroup with name and vendorName. For example, name: "AWSManagedRulesCommonRuleSet" and vendorName: "AWS". You can optionally specify a version.
How do I associate a custom rule group with my Web ACL?
Use ruleGroupReference with the arn of your custom rule group.
What's the difference between overrideAction and ruleActionOverrides?
overrideAction applies to the entire rule group (set to count to override all rules to count mode). ruleActionOverrides targets individual rules within the group, allowing you to override specific rule actions like changing a block to count or captcha.
Can I override actions for individual rules in a rule group?
Yes, use ruleActionOverrides within either managedRuleGroup or ruleGroupReference. Specify the rule name and the actionToUse (such as count or captcha).
Priority & Evaluation
How does rule priority affect evaluation order?
Rules are evaluated in order of priority, with lower numbers evaluated first. Set the priority parameter to control when this rule group is evaluated relative to other rules in the Web ACL.
Can I specify a version for managed rule groups?
Yes, use the version property within managedRuleGroup to pin to a specific version (e.g., version: "Version_1.0"). If omitted, the default version is used.

Using a different cloud?

Explore security guides for other cloud providers: