FMS Web ACL Should Have Rule Group Association
More Info:
Checks if the rule groups associate with the web ACL at the correct priority. The correct priority is decided by the rank of the rule groups in the ruleGroups parameter. When AWS Firewall Manager creates this rule, it assigns the highest priority 0 followed by 1, 2, and so on. The FMS policy owner specifies the ruleGroups rank in the FMS policy and can optionally enable remediation.
Risk Level
Medium
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS Critical Security Controls v8
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- Cloudanix Best Practice
- DPDPA
- Digital Operational Resilience Act (EU)
- Essential 8
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Using Console
Below are concise, step‑by‑step console instructions to fix the finding “FMS Web ACL should have rule group association” in AWS (for a Web ACL managed by AWS Firewall Manager / AWS WAF).
1. Identify the FMS Web ACL with the Finding
- Open AWS Management Console.
- Go to Security Hub (or the service that raised the finding, if different).
- Open the specific finding:
- Look for a finding mentioning something like
FMS Web ACL Should Have Rule Group Associationor similar. - In the finding details, note:
- The Web ACL name and ID.
- The Region.
- The resource type (e.g., CloudFront distribution, Application Load Balancer, API Gateway).
- Look for a finding mentioning something like
2. Go to the Web ACL in AWS WAF
- Switch to the Region from the finding (for CloudFront Web ACLs, use us-east-1).
- In the console, go to AWS WAF & Shield.
- In the left menu, choose Web ACLs.
- Find and select the Web ACL identified in the finding (match by name/ID).
3. Create or Choose a Rule Group
If you already have a rule group you want to associate, skip to Step 4.
To create a new rule group:
- In AWS WAF, in the left menu, select Rule groups.
- Click Create rule group.
- Configure:
- Name: e.g.,
fms-required-rule-group. - CloudWatch metric name: any valid name.
- Region: same as Web ACL (or us-east-1 for CloudFront).
- Capacity: set an appropriate WCU number based on your planned rules.
- Name: e.g.,
- Under Rules, click Add rules and define at least one rule (e.g., IP match, rate-based, etc.).
- Click Create rule group to save.
4. Associate the Rule Group with the Web ACL
- Return to Web ACLs.
- Click the target Web ACL.
- Choose the Rules tab (or Rules section within the Web ACL editor).
- Click Add rules → Add my own rules and rule groups.
- In the Rule groups section:
- Select the relevant Rule group (managed by you or AWS managed rule groups, if appropriate).
- Set an Action override if needed (e.g., Count/Block/Allow, if allowed by the UI).
- Arrange rule order (priority) if there are multiple rules/rule groups.
- Click Save or Save changes at the bottom.
5. (If Using AWS Firewall Manager Policy) Ensure Policy Includes a Rule Group
If the Web ACL is managed by Firewall Manager, you typically should not manually edit the Web ACL; instead:
- Open AWS Firewall Manager in the console.
- Go to Security policies.
- Find and select the policy that manages this Web ACL.
- Click Edit policy.
- In the Policy rules or Add AWS WAF rules section:
- Add the desired Rule groups to the policy (e.g., your custom rule group or AWS managed rule groups).
- Save the policy.
Firewall Manager will push the updated rule groups to all member Web ACLs governed by that policy.
6. Verify Remediation
- In AWS WAF, open the Web ACL and confirm at least one Rule group is listed in the Rules.
- Wait for Security Hub (or the originating service) to re‑evaluate:
- Usually within a few minutes to a few hours, the finding should move to Resolved / PASSED state if everything is correct.
This addresses the requirement that the FMS-managed Web ACL must have at least one rule group associated.
Using CLI
In AWS there is no WAF scope called “CloudWatch,” so I’ll assume you mean an AWS WAFv2 Web ACL managed by Firewall Manager (FMS) that currently has no rule groups associated. The fix is to update that Web ACL (or its FMS policy) to include at least one rule group.
Below is a minimal, CLI‑only approach using WAFv2 (scope REGIONAL or CLOUDFRONT); adapt names/ARNs as needed.
1. Identify the Web ACL missing rule groups
aws wafv2 list-web-acls \
--scope REGIONAL \
--region us-east-1
Note the Id and Name of the target Web ACL.
Then get full details:
aws wafv2 get-web-acl \
--name "<WEB_ACL_NAME>" \
--scope REGIONAL \
--id "<WEB_ACL_ID>" \
--region us-east-1
Check the Rules array; it is likely empty ([]).
You’ll also need the Web ACL’s LockToken from this output for the update.
2. Choose or create a rule group
Option A – Use an AWS Managed Rule Group
For example, the AWS managed “CommonRuleSet”:
- Vendor:
AWS - Name:
AWSManagedRulesCommonRuleSet
You don’t create this; you only reference it.
Option B – Use your own existing rule group
List your rule groups if you have custom ones:
aws wafv2 list-rule-groups \
--scope REGIONAL \
--region us-east-1
Pick the rule group ARN from the output.
3. Build the new rule set JSON
Create a file rules.json with at least one rule referencing a rule group.
Example using an AWS managed rule group:
[
{
"Name": "AWSCommonRules",
"Priority": 1,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet"
}
},
"OverrideAction": {
"None": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSCommonRulesMetric"
}
}
]
If using a custom rule group instead:
[
{
"Name": "CustomRGAssociation",
"Priority": 1,
"Statement": {
"RuleGroupReferenceStatement": {
"ARN": "arn:aws:wafv2:us-east-1:123456789012:regional/rulegroup/MyRuleGroup/abcd1234-5678-90ab-cdef-EXAMPLE"
}
},
"OverrideAction": {
"None": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "CustomRGMetric"
}
}
]
4. Update the Web ACL to associate the rule group
From step 1 you have:
NameIdLockToken- Existing
DefaultActionandVisibilityConfig(must be preserved)
First, capture the existing Web ACL config (excluding Rules):
aws wafv2 get-web-acl \
--name "<WEB_ACL_NAME>" \
--scope REGIONAL \
--id "<WEB_ACL_ID>" \
--region us-east-1 > web-acl-current.json
Open web-acl-current.json and note:
"DefaultAction"block"VisibilityConfig"block"Description"(optional)"LockToken"
Now run update-web-acl, providing the new rules:
aws wafv2 update-web-acl \
--name "<WEB_ACL_NAME>" \
--scope REGIONAL \
--id "<WEB_ACL_ID>" \
--lock-token "<LOCK_TOKEN_FROM_GET_WEB_ACL>" \
--region us-east-1 \
--default-action '{
"Allow": {}
}' \
--visibility-config '{
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "MyWebAclMetrics"
}' \
--rules file://rules.json
Replace DefaultAction and VisibilityConfig with the values from your current Web ACL instead of the example above.
5. (If using Firewall Manager) Update via FMS Policy instead
If the Web ACL is managed by Firewall Manager, best practice is to modify the FMS policy, not the Web ACL directly:
- Get the policy:
aws fms list-policies
aws fms get-policy --policy-id "<POLICY_ID>" > fms-policy.json
-
In
fms-policy.json, underSecurityServicePolicyData.ManagedServiceData, you’ll find a JSON string describing the WAF configuration. Edit that JSON to add the rule groups (similar to step 3). -
Put the updated policy:
aws fms put-policy \
--policy file://fms-policy.json \
--region us-east-1
This will regenerate/update the underlying Web ACLs with rule group associations.
After these steps, the FMS-managed Web ACL will have at least one rule group associated, satisfying the “FMS Web ACL Should Have Rule Group Association” requirement.
Using Python
Below are concrete, step‑by‑step remediation instructions and a Python (boto3) example to ensure a Firewall Manager (FMS)–managed WAFv2 Web ACL has at least one rule group associated.
1. Understand the Requirement
“FMS Web ACL Should Have Rule Group Association” means:
- The Web ACLs that are managed by AWS Firewall Manager must include at least one Rule Group (AWS managed or customer managed).
- For WAFv2, this means the Web ACL’s
Ruleslist must contain at least oneRuleGroupReferenceStatement.
You can remediate in two main ways:
- Update the WAFv2 Web ACL directly via
wafv2.update_web_aclto add a rule group. - Update the Firewall Manager policy that configures those Web ACLs so that the rule groups are automatically applied everywhere.
If these Web ACLs are created/owned by FMS, the proper long‑term fix is to update the FMS policy.
2. Prerequisites
- Python 3.x installed.
boto3installed:pip install boto3- AWS credentials configured (
aws configureor environment variables). - The ARN of at least one Rule Group you want to associate:
- Example (regional):
arn:aws:wafv2:us-east-1:111122223333:regional/rulegroup/my-rule-group/12345678-aaaa-bbbb-cccc-1234567890ab
- Or AWS Managed Rule Group:
- Use
RuleGroupReferenceStatementwithVendorNameandNamefor managed rule groups (e.g.,AWS,AWSManagedRulesCommonRuleSet).
- Use
- Example (regional):
3. Option A – Update Web ACLs Directly via WAFv2
Use this if you already know the Web ACL ARN or have them from your scanner.
Step 3.1: Python script to add a Rule Group to a Web ACL
import boto3
# CONFIGURE THESE
REGION = "us-east-1"
WEB_ACL_NAME = "your-fms-managed-web-acl-name"
WEB_ACL_SCOPE = "REGIONAL" # or "CLOUDFRONT"
RULE_GROUP_ARN = "arn:aws:wafv2:us-east-1:111122223333:regional/rulegroup/my-rule-group/12345678-aaaa-bbbb-cccc-1234567890ab"
NEW_RULE_NAME = "AttachRuleGroup"
waf = boto3.client("wafv2", region_name=REGION)
def ensure_rule_group_associated():
# 1. Get current Web ACL
response = waf.list_web_acls(Scope=WEB_ACL_SCOPE, Limit=100)
web_acl_summaries = response.get("WebACLs", [])
web_acl_id = None
web_acl_arn = None
for w in web_acl_summaries:
if w["Name"] == WEB_ACL_NAME:
web_acl_id = w["Id"]
web_acl_arn = w["ARN"]
break
if not web_acl_id:
raise Exception(f"Web ACL {WEB_ACL_NAME} not found in {WEB_ACL_SCOPE}")
web_acl = waf.get_web_acl(
Name=WEB_ACL_NAME,
Scope=WEB_ACL_SCOPE,
Id=web_acl_id
)["WebACL"]
rules = web_acl.get("Rules", [])
# 2. Check if any rule group is already associated
has_rule_group = any(
"RuleGroupReferenceStatement" in r.get("Statement", {})
or "ManagedRuleGroupStatement" in r.get("Statement", {})
for r in rules
)
if has_rule_group:
print(f"Web ACL {WEB_ACL_NAME} already has a rule group associated.")
return
# 3. Add a new rule referencing the desired Rule Group
new_rule = {
"Name": NEW_RULE_NAME,
"Priority": (max([r["Priority"] for r in rules]) + 1) if rules else 0,
"Statement": {
"RuleGroupReferenceStatement": {
"ARN": RULE_GROUP_ARN
}
},
"OverrideAction": {
"None": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": True,
"CloudWatchMetricsEnabled": True,
"MetricName": NEW_RULE_NAME
}
}
rules.append(new_rule)
# 4. Update the Web ACL with the new rule
waf.update_web_acl(
Name=WEB_ACL_NAME,
Scope=WEB_ACL_SCOPE,
Id=web_acl_id,
DefaultAction=web_acl["DefaultAction"],
Description=web_acl.get("Description", ""),
Rules=rules,
VisibilityConfig=web_acl["VisibilityConfig"],
LockToken=web_acl["LockToken"]
)
print(f"Associated rule group {RULE_GROUP_ARN} with Web ACL {WEB_ACL_NAME} ({web_acl_arn}).")
if __name__ == "__main__":
ensure_rule_group_associated()
What it does:
- Finds the Web ACL by name and scope.
- Checks if any existing rule references a Rule Group or Managed Rule Group.
- If not, appends a new
RuleGroupReferenceStatementrule. - Calls
update_web_aclwith the new rules list and the currentLockToken.
4. Option B – Fix via Firewall Manager Policy (Recommended for FMS)
If the Web ACL is managed by Firewall Manager, you should adjust the FMS policy so all future and current Web ACLs have rule groups.
Step 4.1: Inspect FMS policies in Python
import boto3
import json
fms = boto3.client("fms", region_name="us-east-1") # FMS is global, region is mostly for endpoint
def list_waf_policies():
policies = []
next_token = None
while True:
args = {"MaxResults": 100}
if next_token:
args["NextToken"] = next_token
resp = fms.list_policies(**args)
policies.extend(resp.get("PolicyList", []))
next_token = resp.get("NextToken")
if not next_token:
break
return [p for p in policies if p["SecurityServiceType"] == "WAFV2"]
def show_policy_managed_data(policy_id):
resp = fms.get_policy(PolicyId=policy_id)
policy = resp["Policy"]
managed_data = json.loads(policy["SecurityServicePolicyData"]["ManagedServiceData"])
print(json.dumps(managed_data, indent=2))
if __name__ == "__main__":
waf_policies = list_waf_policies()
for p in waf_policies:
print(f"Found WAFv2 policy: {p['PolicyName']} ({p['PolicyId']})")
show_policy_managed_data(p["PolicyId"])
This shows you the JSON ManagedServiceData definition that FMS uses to create Web ACLs, including any rule groups.
Step 4.2: Update a policy to add rule groups
You will see something similar in ManagedServiceData:
{
"type": "WAFV2",
"preProcessRuleGroups": [],
"postProcessRuleGroups": [],
"defaultAction": { "type": "ALLOW" }
}
To ensure rule groups are always associated, add entries to preProcessRuleGroups or postProcessRuleGroups:
{
"type": "WAFV2",
"preProcessRuleGroups": [
{
"ruleGroupArn": "arn:aws:wafv2:us-east-1:111122223333:regional/rulegroup/my-rule-group/12345678-aaaa-bbbb-cccc-1234567890ab",
"overrideAction": { "type": "NONE" },
"name": "MyPreRuleGroup",
"priority": 1
}
],
"postProcessRuleGroups": [],
"defaultAction": { "type": "ALLOW" }
}
Python example to update a specific FMS policy:
import boto3
import json
fms = boto3.client("fms", region_name="us-east-1")
POLICY_ID = "your-fms-waf-policy-id"
RULE_GROUP_ARN = "arn:aws:wafv2:us-east-1:111122223333:regional/rulegroup/my-rule-group/12345678-aaaa-bbbb-cccc-1234567890ab"
def add_rule_group_to_fms_policy():
resp = fms.get_policy(PolicyId=POLICY_ID)
policy = resp["Policy"]
managed_data = json.loads(policy["SecurityServicePolicyData"]["ManagedServiceData"])
pre_groups = managed_data.get("preProcessRuleGroups", [])
post_groups = managed_data.get("postProcessRuleGroups", [])
# Check if rule group already present
all_group_arns = [g.get("ruleGroupArn") for g in pre_groups + post_groups]
if RULE_GROUP_ARN in all_group_arns:
print("Rule group already present in policy.")
return
# Add to preProcessRuleGroups
new_group = {
"ruleGroupArn": RULE_GROUP_ARN,
"overrideAction": { "type": "NONE" },
"name": "MyPreRuleGroup",
"priority": (max([g["priority"] for g in pre_groups], default=0) + 1)
}
pre_groups.append(new_group)
managed_data["preProcessRuleGroups"] = pre_groups
managed_data["postProcessRuleGroups"] = post_groups
policy["SecurityServicePolicyData"]["ManagedServiceData"] = json.dumps(managed_data)
# Update policy
fms.put_policy(
Policy=policy,
PolicyOption=resp.get("PolicyOption", {})
)
print(f"Updated FMS policy {POLICY_ID} to include rule group {RULE_GROUP_ARN}.")
if __name__ == "__main__":
add_rule_group_to_fms_policy()
5. About “CloudWatch” in Your Question
CloudWatch is only indirectly involved (metrics, logs).
The actual remediation is done through:
wafv2API (for Web ACL rules).fmsAPI (for Firewall Manager policies).
CloudWatch configuration (metrics/logs) is controlled via VisibilityConfig in WAF rules and Web ACLs, not where you attach rule groups.
If you tell me:
- Whether the Web ACL is regional or for CloudFront, and
- Whether you want to use AWS Managed Rule Groups or your own rule group ARNs,
I can adjust the Python snippet to exactly match your setup.
Using Terraform
resource "aws_fms_policy" "FMS_WEB_ACL_POLICY" {
name = "FMS Web ACL Policy" # replace with your policy name
exclude_resource_tags = false
remediation_enabled = true # ENABLE AUTOMATIC REMEDIATION (RemediationEnabled = true)
resource_type = "AWS::ElasticLoadBalancingV2::LoadBalancer" # replace with your target type
security_service_policy_data {
type = "WAFV2"
# Replace MANAGED_SERVICE_DATA_JSON with your current policy JSON
# from aws fms get-policy's Policy.SecurityServicePolicyData.ManagedServiceData
managed_service_data = jsonencode({
type = "WAFV2"
preProcessRuleGroups = [
{
ruleGroupType = "RuleGroup"
# priority is derived from order: index 0 => priority 0, index 1 => priority 1, etc.
ruleGroups = [
{
vendorName = "AWS"
name = "AWSManagedRulesCommonRuleSet"
},
{
vendorName = "AWS"
name = "AWSManagedRulesKnownBadInputsRuleSet"
}
]
}
]
defaultAction = {
type = "BLOCK"
}
})
}
# Add include_map / exclude_map / resource_tags as in your existing policy
}
Changing remediation_enabled from false (or omitted) to true does not force replacement; the policy is updated in place but, as with put-policy, AWS treats this as a full-policy update so review all other arguments carefully.
Verification: terraform plan should show remediation_enabled: false => true (or null => true) on aws_fms_policy.FMS_WEB_ACL_POLICY with no -/+ replacement of the resource.