---
sidebar_label: WAF
toc_max_heading_level: 3
doc_id: b4f7c218-3e91-4a6d-9c52-1f8e0d3a7b6c
description: >-
  Attach an existing AWS WAF WebACL to the CloudFront distribution of a
  Static files scope. Configuration, IAM requirements, and operational
  behavior.
keywords:
  - static files
  - WAF
  - WebACL
  - CloudFront
  - AWS
  - security
---

# WAF protection

The Static files scope can attach an existing AWS WAF WebACL to its CloudFront distribution, adding an extra security layer in front of your assets. The feature is opt-in and adds no IAM overhead unless enabled.

## When to use it

Attach a WebACL when you need:

- Managed AWS rules (common rule set, IP reputation lists, bot control)
- Rate-based rules per IP
- Custom request filtering or geo restrictions
- WAF logging to S3, CloudWatch, or Firehose

If you only need basic HTTPS and edge caching, leave the WAF off.

## Limits

The integration is intentionally narrow to keep the configuration simple. The following are out of scope today:

| Limit | Detail |
|---|---|
| WAF version | Only AWS WAFv2 is supported. WAF Classic (`aws_waf_*`) is not. |
| Account | Same-account only. The WebACL must live in the same AWS account that the agent authenticates against. |
| Scope | The WebACL must have `scope=CLOUDFRONT`. AWS confines those WebACLs to `us-east-1`, so the WebACL must live in `us-east-1`. |
| Lifecycle | The scope attaches an existing WebACL by name. It does not create, update, or delete WebACL resources. |

If you need any of the above, manage the WebACL outside the scope and reference it by name once it exists.

## Configuration

Add a `security` block to the `attributes` of your scope-configurations provider:

```hcl
attributes = {
  cloud_provider = "aws"
  provider       = { ... }
  network        = { ... }
  distribution   = { aws_distribution = "cloudfront" }

  security = {
    aws_security     = "waf"
    aws_web_acl_name = "my-web-acl"
  }
}
```

| Field | Values | Description |
|---|---|---|
| `security.aws_security` | `"none"` (default) or `"waf"` | Choose whether to attach a WebACL. `"none"` skips the WAF integration entirely. |
| `security.aws_web_acl_name` | string | Name of an existing WAFv2 WebACL with `scope=CLOUDFRONT` in `us-east-1`. Required when `aws_security="waf"`. |

The same fields are also available in the nullplatform UI under the **Security** tab of the scope configuration.

## IAM permissions

When `aws_security="waf"`, the agent needs two additional permissions on top of the ones documented in [AWS setup](/docs/agent-backed-scopes/static-files/setup-aws):

```json
{
  "Sid": "WAFv2WebACLLookup",
  "Effect": "Allow",
  "Action": [
    "wafv2:ListWebACLs",
    "wafv2:GetWebACL"
  ],
  "Resource": "arn:aws:wafv2:us-east-1:YOUR_ACCOUNT_ID:global/webacl/*/*"
}
```

`ListWebACLs` is called by the scope's setup script to validate the WebACL name before running OpenTofu. `GetWebACL` is called by the OpenTofu data source that resolves the name to an ARN.

When `aws_security="none"`, neither action is invoked, so the policy above is only needed in clusters that actually use the WAF feature.

## Operational behavior

Attaching or detaching a WebACL is always an **in-place update** of the CloudFront distribution. The distribution ID stays stable across the change, no DNS or edge propagation reset is required, and there is no downtime.

- **Attach**: setting `aws_security="waf"` with a valid name on an existing scope triggers a distribution update that sets `WebACLId` to the WebACL ARN. The scope is redeployed normally.
- **Detach**: setting `aws_security="none"` (or clearing the name) on the next deployment sets `WebACLId` back to empty. The distribution keeps the same ID.
- **Swap**: changing `aws_web_acl_name` to a different existing WebACL re-points `WebACLId` to the new ARN, also in-place.

:::note
The configuration change in the provider does not by itself update CloudFront. You need to trigger a deployment of the scope (initial, blue-green, or any redeploy) for the agent to apply the new value.
:::

## Validation and errors

Before running OpenTofu, the scope's setup script validates that the WebACL exists by calling `wafv2:ListWebACLs` with `scope=CLOUDFRONT` in `us-east-1`. If the name is misspelled, lives in a different account, or has the wrong scope, the deployment fails fast with a message that points to the most common causes:

- Name misspelled
- WebACL exists but with `scope=REGIONAL` instead of `CLOUDFRONT`
- WebACL lives in a different AWS account
- Agent role lacks `wafv2:ListWebACLs`

Fix the underlying issue and redeploy. No partial state is left behind because the failure happens before the OpenTofu apply.
