AWS Cloud

AWS Firewall Manager is a control plane that isn't a firewall

It doesn't inspect a single packet. It's the service that stamps WAF, Shield, security groups, and Network Firewall across every account in your organization — and keeps them there.

Deep dive·cloud & security engineers·

You open the "Firewall Manager" console expecting a firewall. No rules to write, no packets to inspect, no traffic graph — just a list of policies, a compliance dashboard, and a nagging prerequisite: turn on AWS Config in every account and every Region first. That's the whole point, and the whole trap. Firewall Manager (FMS) configures other firewalls org-wide, and both its power and its cost fall out of that one design decision.

TL;DR

  • FMS is a policy manager, not a firewall. It centrally deploys and enforces AWS WAF, Shield Advanced, security groups, network ACLs, Network Firewall, Route 53 DNS Firewall, and third-party firewalls (Palo Alto Cloud NGFW, Fortigate CNF) — it never inspects traffic itself; the managed services do that.
  • It scopes in two layers, and wires resources itself. A policy targets accounts / OUs, then narrows to resources by resource_type + resource_tags; FMS creates the managed resource and associates it automatically — no manual attach, and new matching resources are picked up as they appear.
  • It depends on AWS Organizations and AWS Config. Organizations owns membership, Config owns compliance detection, FMS owns policy intent — miss either prerequisite and FMS silently can't protect an account.
  • The management account can't be the policy admin. The Organizations management account designates a delegated administrator member account; that account is where you author policies.
  • Config-everywhere is the real bill. FMS itself is about $100 per policy, per Region, per month — but it forces AWS Config on in every account and Region and creates two Config rules per policy per account, a cost that usually dwarfs the FMS fee.
  • Auto-remediation is disruptive by design, so it ships off. New policies default to "detect but don't remediate." You review what would change, scope tightly, then enable it.
  • It earns its keep at organization scale. Below a handful of accounts, per-account config or Terraform is cheaper and simpler. FMS wins when you need "every new account is protected automatically" as an invariant.
Part 1What Firewall Manager is

What it actually is — a manager, not a firewall

Start with the misconception everyone brings: Firewall Manager does not sit in your data path — no listeners, no rules engine, no packet counters. It's a control plane: you declare a protection once; FMS applies it across every account and resource in your AWS Organization, and keeps applying it as new ones appear.

The docs are blunt about what it wraps: it manages "AWS WAF, AWS Shield Advanced, Amazon VPC security groups and network ACLs, AWS Network Firewall, and Amazon Route 53 Resolver DNS Firewall." Those services do the filtering; FMS just ensures they exist everywhere, configured the way you said. The pricing page says the quiet part out loud: "Charges incurred by AWS Firewall Manager are for the underlying services."

Why not Control Tower or Security Hub?

They solve different problems. Control Tower builds and governs a landing zone — the accounts and guardrails FMS operates within. Security Hub aggregates and scores findings; it flags a missing WAF but won't create one. FMS is the only one of the three that will deploy and continuously enforce a firewall configuration across accounts. So they stack: Control Tower lays down the org, FMS enforces protections, Security Hub reports (and FMS can feed it findings).

With vs without Firewall Manager

What actually changes when you adopt it: on the left, every account is secured by hand — baselines drift apart, edits go unnoticed, each new account slips through unprotected. On the right, one policy set secures the whole organization and keeps it that way.

Without Firewall Managerevery account configured by handWith Firewall Managerone policy set, enforced org-wideAWS Organization — no central controlAWS Organization — AWS Firewall ManagerEach team,on its ownmanual:tickets ·runbooks ·copied TFFMS adminone policy setauthor once,auto-deploy+ enforceorg-wideAccount A!hand-tunedAccount AenforcedAccount B!no WAFAccount BenforcedAccount C!drifted, unseenAccount Cdrift revertedNew account!nothing yetNew accountauto-protected123456Config not tied to policy intent — drift & gaps go unseenInconsistent · drifting · every new account a gapConfig detects drift → FMS re-applies, no ticketUniform · self-healing · new accounts covered automaticallyVS
  1. Without FMS, every account is configured by hand — baselines drift apart and gaps open silently.
  2. A manual security-group or rule-set edit slips in and goes unnoticed until an incident or audit.
  3. A newly-created account has no protection until someone remembers to wire it up.
  4. With FMS, one aws_fms_policy per protection type is authored once and auto-deployed to every in-scope account.
  5. AWS Config detects any drift and FMS re-applies the intended configuration automatically — no ticket.
  6. New accounts are enrolled and protected the moment they join the OU.
Figure 1 · With vs without Firewall Manager — the same fleet configured by hand (left) drifts and leaves gaps; one FMS policy set (right) deploys a uniform baseline, reverts drift, and auto-covers new accounts.

How FMS manages resources in each account

FMS never signs into your accounts as you: it assumes a service-linked role in each member account and creates, associates, and remediates the managed resources through it. That’s how a policy authored in one delegated account becomes a live firewall in all of them.

FMS admin account (delegated)Member account · in the in-scope OUApp VPC (in scope)1234aws_fms_policyscoped to an OUFMS service-linked roleAWS Configrecords stateManaged WAFweb ACLALBBaselinesecurity groupEC2 ENIsNetwork FirewallendpointVPC subnets
  1. FMS scopes the policy to an OU (or tags) and assumes its service-linked role (AWSServiceRoleForFMS) inside each in-scope member account.
  2. Through that role, FMS creates and configures the managed resource — a WAF web ACL, a baseline security group, a Network Firewall endpoint (same mechanism for DNS Firewall, network ACLs, Shield).
  3. FMS associates each managed resource to the in-scope targets — the web ACL to ALBs/CloudFront, the security group to EC2 ENIs, the firewall endpoint to VPC subnets.
  4. AWS Config records the resource state; FMS compares it to policy intent and re-applies automatically on drift — no per-account action.
Figure 2 · How FMS deploys into an individual account — from the delegated admin’s policy, via a service-linked role, to managed resources, their associations, and continuous drift remediation.

Where a policy applies — accounts, then resources

Scoping happens in two layers, and the second is the one people miss. First FMS selects accounts and OUs (include_map / exclude_map); then inside each, it narrows to resources matching resource_type (only ALBs, only VPCs…) and resource_tags (an include or exclude set, never both). So a policy blankets a whole OU or lands on just the handful of tagged load balancers you point it at.

Two layers of scope — accounts / OUs, then resources by type & tagaws_fms_policy(admin account)① Account scopeinclude_map /exclude_mapin-scope accountsOUs / accounts+ new accounts auto-join② Resource scoperesource_type +resource_tagsmatching resourcesget the protectionuntagged / wrong typeleft untouched
Scope is two layers: first accounts and OUs (include_map / exclude_map), then, inside them, resources by resource_type and resource_tags. Only matching resources get the managed protection; a new account in an in-scope OU is captured automatically.
resource "aws_fms_policy" "waf_edge" {
  name = "org-waf-edge-albs"

  # -- Layer 1: account scope -- which accounts / OUs the policy covers
  include_map { orgunit = ["ou-abcd-1example"] }   # the OU(s) in scope
  exclude_map { account = ["111122223333"] }       # ...minus the FMS admin account

  # -- Layer 2: resource scope -- which resources INSIDE those accounts
  resource_type         = "AWS::ElasticLoadBalancingV2::LoadBalancer"  # only ALBs
  resource_tags         = { protect = "true" }     # ...and only if tagged protect=true
  exclude_resource_tags = false                    # tags are an INCLUDE set (true flips to exclude)

  security_service_policy_data { type = "WAFV2"  /* managed_service_data = ... */ }
}
Both layers are optional filters that narrow the target set. Tags are include OR exclude, never both. Leave resource scope open and the policy applies to every resource of that type in scope.
Two things that trip people up. The resource criteria is resource_type / resource_type_list (required) plus resource_tags (optional) — omit the tags and the policy covers every resource of that type in scope; tags only narrow it. And you never attach anything by hand: FMS creates and associates the managed resource automatically, re-attaching on drift and catching new resources as they appear. That's the difference from AWS RAM, which shares one resource for you to wire up: FMS builds, wires, and keeps a managed one wired in every account. (FMS does use RAM under the hood to distribute reusable rule groups for Network Firewall and DNS Firewall.)
Part 2What it can deploy — every policy type

What it can deploy — every policy type

Everything in FMS is a security policy (the Terraform aws_fms_policy), and each carries a single type field — WAFV2, SHIELD_ADVANCED, SECURITY_GROUPS_COMMON, NETWORK_ACL_COMMON, NETWORK_FIREWALL, DNS_FIREWALL, or THIRD_PARTY_FIREWALL — that decides which managed service deploys and what it lands on. The map below shows all seven types (numbered, colour-coded) stamping protection into member accounts A, B and C, over the AWS Config substrate that detects drift.

FMS admin account (delegated) — every card is an FMS policy (aws_fms_policy); its type field picks the service:Account AAccount BAccount Cone Security-group policy → every accounttype = WAFV21WAFV2FMS policytype = SHIELD_ADVANCED2Shield Adv.FMS policytype = SECURITY_GROUPS_COMMON3Security groupFMS policytype = NETWORK_ACL_COMMON4Network ACLFMS policytype = NETWORK_FIREWALL5Network FWFMS policytype = DNS_FIREWALL6DNS FirewallFMS policytype = THIRD_PARTY_FIREWALL7Third-partyFMS policy1Web ACLCloudFrontALBAPI GatewayAppSyncCognito2ShieldCloudFrontALB / NLBElastic IPGlobal AcceleratorRoute 533Baseline SGEC2 ENIsVPCssecurity groups4Network ACLVPC subnets5NFW endpointIn-scope VPCsper AZ3Baseline SGEC2 ENIsVPCssecurity groups6DNS FirewallIn-scope VPCs7NGFWVPC endpoints3Baseline SGEC2 ENIsVPCssecurity groupsAWS Config · every account + Regionrecords state · detects drift · the real cost driver
Each numbered arrow maps one policy to the resource it deploys:
  1. WAFV2 policy → web ACL on the ALB. FMS creates a managed web ACL and associates it with the account's Application Load Balancers (the policy's target resource type) — and equally with CloudFront distributions or API Gateway stages. The web ACL isn't standalone; it attaches to the load balancer.
  2. Shield Advanced policy → Shield protection. Subscribes the account to Shield Advanced and protects CloudFront, ALB/ELB, and Elastic IPs against DDoS.
  3. Security group policy → Baseline SG. Replicates a common security group onto tagged ENIs in every in-scope account (A, B, and C).
  4. Network ACL policy → Network ACL. Enforces first/last inbound and outbound NACL rules on the account's subnets.
  5. Network Firewall policy → NFW endpoint. Deploys AWS Network Firewall endpoints into the account's VPCs (distributed or centralized).
  6. DNS Firewall policy → DNS Firewall. Associates Route 53 Resolver DNS Firewall rule groups with the account's VPCs.
  7. Third-party policy → NGFW. Deploys Palo Alto Cloud NGFW / Fortigate CNF resources across the account.
Figure 3 · The FMS admin account holds one security policy per policy type (top). Each numbered arrow maps a policy to the managed resource it deploys into a member account (A, B, or C) — the seven associations are explained above. The security-group policy (3) replicates to every account (Baseline SG appears in all three). Underneath, AWS Config records state so FMS can reconcile drift.

Each policy names a single protection, a Region (except global CloudFront/WAF), a scope, and a remediation setting — here is every type, what it deploys, and its deployment model.

Policy typeWhat it deploysWhere it landsModel / note
AWS WAF (v2)A managed web ACL per account, associated to resourcesCloudFront, ALB, API Gateway, AppSync, CognitoFirst/last rule groups; replace or retrofit
Shield AdvancedSubscribes accounts, applies DDoS protectionCloudFront, ALB/ELB, EIP, Route 53, Global AcceleratorPolicy fee waived for Shield Advanced customers
Security groupCommon baseline SG, or a rule/usage auditTagged ENIs, VPCs, security groupsThree modes: common / content-audit / usage-audit
Network FirewallFirewall endpoints (VPC or inspection VPC)In-scope VPCs, or one inspection VPCDistributed vs centralized; RAM-shared rule groups
Route 53 DNS FirewallRule-group associations to VPCsIn-scope VPCsFirst/last priorities; needs RAM
Network ACLFirst/last inbound & outbound NACL rulesIn-scope subnetsSlower to apply (EC2 API limits)
Third-partyVendor firewall resources and rule setsAcross accounts, via MarketplacePalo Alto Cloud NGFW; Fortigate CNF

How it guards your web apps — AWS WAF policies

FMS admin account (delegated) — every card is an FMS policy (aws_fms_policy); its type field picks the service:Account AAccount BAccount Ctype = WAFV21WAFV2FMS policytype = SHIELD_ADVANCEDShield Adv.FMS policytype = SECURITY_GROUPS_COMMONSecurity groupFMS policytype = NETWORK_ACL_COMMONNetwork ACLFMS policytype = NETWORK_FIREWALLNetwork FWFMS policytype = DNS_FIREWALLDNS FirewallFMS policytype = THIRD_PARTY_FIREWALLThird-partyFMS policy1Web ACLCloudFrontALBAPI GatewayAppSyncCognito1Web ACLCloudFrontALBAPI GatewayAppSyncCognito1Web ACLCloudFrontALBAPI GatewayAppSyncCognitoAWS Config · every account + Regionrecords state · detects drift · the real cost driver
The WAFV2 policy shown against all seven — every other card greyed. Scoped across the organization, it deploys a managed WAF web ACL into each in-scope account (A, B and C).

A WAF policy names a set of AWS WAF (v2) rule groups; FMS creates a managed web ACL in each in-scope account (named FMManagedWebACLV2-<policy>-<timestamp>) and associates it with in-scope CloudFront distributions, ALBs, API Gateway stages, AppSync, and Cognito user pools. If a resource already carries its own web ACL, overrideCustomerWebACLAssociation decides whether FMS leaves it in place (the safe default) or replaces it. You set first and last rule groups (PREFMManaged- / POSTFMManaged-), owners adding their own between; two modes, replace any existing web ACL or retrofit (keep existing ones, create new ACLs only where none exists). For CloudFront, set the policy Region to Global.

Sharp edge: an FMS-managed web ACL can't participate in CloudFront flat-rate pricing plans — a flat-rate distribution needs a web ACL enrolled in that plan, and the FMS-managed one can’t be added to it. Point one at such a distribution and both the distribution and the policy sit permanently non-compliant — the docs say this "can't be remediated." Know it before you scope CloudFront in.

Building blocks — and how the FMS policy wraps them

AWS WAF building blocks — and how the FMS policy wraps them1Rule groupsAWS-managed + your custom rulesAWS Firewall Manager policytype = WAFV22Web ACLcomposes rule groups + a default actionFMS wraps it and adds:Org scope — OUs · accounts · tagsAssociates to CloudFront · ALB · API GW · AppSync · CognitoAuto-remediation — detect, then remediatedeploys3Managed web ACLFMManagedWebACLV2-… in every in-scope account
AWS WAF building blocks — and how the FMS policy wraps them.
  • Rule groups — AWS-managed + your custom rules.
  • Web ACL — composes rule groups + a default action.
  • FMS WAF policy — wraps the web ACL; adds org scope, associations, remediation.
  • Managed web ACL — created & associated in every in-scope account.

In Terraform — the same aws_fms_policy wrapper, this section’s type:

resource "aws_fms_policy" "waf" {
  name                = "org-waf-edge-albs"
  remediation_enabled = false                     # ship report-only, then enable

  # Layer 1 — account scope: which accounts / OUs
  include_map { orgunit = ["ou-abcd-1example"] }
  exclude_map { account = ["111122223333"] }      # never the FMS admin account

  # Layer 2 — resource scope: which resources inside them
  # WHY: only internet-facing ALBs need a web ACL — tagging keeps WAF (and its bill) off internal load balancers
  resource_type = "AWS::ElasticLoadBalancingV2::LoadBalancer"
  resource_tags = { edge = "true" }

  security_service_policy_data {
    type = "WAFV2"
    managed_service_data = jsonencode({
      type          = "WAFV2"
      defaultAction = { type = "ALLOW" }
      preProcessRuleGroups = [{
        managedRuleGroupIdentifier = { vendorName = "AWS", managedRuleGroupName = "AWSManagedRulesCommonRuleSet" }
        overrideAction = { type = "NONE" }, ruleGroupType = "ManagedRuleGroup"
      }]
    })
  }
}
Every policy takes both scope layers: account (include_map/exclude_map) then resource (resource_type + resource_tags). The WHY comment is the point of layer 2 — narrow to the resources that need it, so you don’t protect (and pay for) everything.

How it blunts DDoS at scale — Shield Advanced policies

FMS admin account (delegated) — every card is an FMS policy (aws_fms_policy); its type field picks the service:Account AAccount BAccount Ctype = WAFV2WAFV2FMS policytype = SHIELD_ADVANCED2Shield Adv.FMS policytype = SECURITY_GROUPS_COMMONSecurity groupFMS policytype = NETWORK_ACL_COMMONNetwork ACLFMS policytype = NETWORK_FIREWALLNetwork FWFMS policytype = DNS_FIREWALLDNS FirewallFMS policytype = THIRD_PARTY_FIREWALLThird-partyFMS policy2Shield protectionCloudFrontALB / NLBElastic IPGlobal AcceleratorRoute 532Shield protectionCloudFrontALB / NLBElastic IPGlobal AcceleratorRoute 532Shield protectionCloudFrontALB / NLBElastic IPGlobal AcceleratorRoute 53AWS Config · every account + Regionrecords state · detects drift · the real cost driver
The Shield Adv. policy shown against all seven — every other card greyed. Scoped across the organization, it deploys Shield Advanced protection into each in-scope account (A, B and C).

A Shield Advanced policy subscribes in-scope member accounts to AWS Shield Advanced and applies DDoS protection to eligible resources — per AWS's Shield documentation, CloudFront, ALB/ELB, EC2 Elastic IPs, Route 53, and Global Accelerator — auto-enrolling new in-scope accounts as they join. It's the one policy type at no extra FMS charge for Shield Advanced customers, though the Config rules it spins up are still billed separately — and it's FMS's centralized-DDoS-monitoring story: one dashboard for attacks across the org.

Building blocks

Shield Advanced building blocks — and how the FMS policy wraps them1Eligible resourcesCloudFront · ALB/ELB · EIP · Route 53 · GAAWS Firewall Manager policytype = SHIELD_ADVANCED2Shield Advanced protection+ optional protection groupsFMS wraps it and adds:Org scope — OUs · accounts · tagsAuto-subscribes accounts to Shield AdvancedAuto-remediation — detect, then remediatedeploys3Subscription + resource protectionsper in-scope account
Shield Advanced building blocks — and how the FMS policy wraps them.
  • Eligible resources — CloudFront, ALB/ELB, EIP, Route 53, Global Accelerator.
  • Shield Advanced protection — + optional protection groups.
  • FMS Shield policy — org scope + auto-subscribe + remediation.
  • Subscription + protections — in every in-scope account.

Same aws_fms_policy wrapper in Terraform — only the type changes:

resource "aws_fms_policy" "shield" {
  name                = "org-shield-critical"
  remediation_enabled = true

  # Layer 1 — account scope
  include_map { orgunit = ["ou-abcd-1example"] }

  # Layer 2 — resource scope
  # WHY: Shield Advanced bills per protected resource — protect only business-critical, internet-facing endpoints
  resource_type_list = ["AWS::ElasticLoadBalancingV2::LoadBalancer", "AWS::CloudFront::Distribution"]
  resource_tags      = { tier = "critical" }

  security_service_policy_data { type = "SHIELD_ADVANCED" }
}

How it governs security groups — the three audit modes

FMS admin account (delegated) — every card is an FMS policy (aws_fms_policy); its type field picks the service:Account AAccount BAccount Ctype = WAFV2WAFV2FMS policytype = SHIELD_ADVANCEDShield Adv.FMS policytype = SECURITY_GROUPS_COMMON3Security groupFMS policytype = NETWORK_ACL_COMMONNetwork ACLFMS policytype = NETWORK_FIREWALLNetwork FWFMS policytype = DNS_FIREWALLDNS FirewallFMS policytype = THIRD_PARTY_FIREWALLThird-partyFMS policy3Baseline SGEC2 ENIsVPCssecurity groups3Baseline SGEC2 ENIsVPCssecurity groups3Baseline SGEC2 ENIsVPCssecurity groupsAWS Config · every account + Regionrecords state · detects drift · the real cost driver
The Security group policy shown against all seven — every other card greyed. Scoped across the organization, it deploys a baseline security group into each in-scope account (A, B and C).
Commontype = SECURITY_GROUPS_COMMONReplicates one baseline securitygroup to every tagged ENI —the same rules everywhere.Content audittype = SECURITY_GROUPS_CONTENT_AUDITChecks rules against an allow/denytemplate; bans e.g. 0.0.0.0/0 onport 22 org-wide.Usage audittype = SECURITY_GROUPS_USAGE_AUDITFinds unused & redundantsecurity groups so you canclean them up.
The three security-group policy modes — the same aws_fms_policy wrapper, a different type.

Security-group policies come in three flavours, and mixing them up is a classic mistake:

  • Common security group replicates a primary security group onto every tagged ENI in scope, so all get the same baseline rules — the "one SG to rule them all" mode.
  • Content audit checks the rules inside security groups against an allowed/denied template, flagging (and optionally remediating) non-compliant rules. This is how you ban 0.0.0.0/0 on port 22 org-wide.
  • Usage audit finds unused and redundant security groups so you can clean them up.

The docs are explicit about two guardrails: exclude the FMS admin account from scope (the default for usage-audit policies), and start with automatic remediation disabled for content and usage audit policies until you've reviewed what would change. Watch out for one more failure mode: FMS and an outside SG tool each "own" the same security group and fight in an endless remediation loop — keep their scopes mutually exclusive with tags.

Latency you can't tune: changes to a security group on an EC2 ENI aren't immediately visible to FMS — the docs say detection usually happens within several hours but can lag up to six. FMS also doesn't manage SGs on RDS-created ENIs, Fargate ENIs, NLB ENIs, or RAM-shared security groups.

Building blocks

Security-group building blocks — and how the FMS policy wraps them1Primary security groupthe baseline inbound/outbound rulesAWS Firewall Manager policytype = SECURITY_GROUPS_COMMON2Security-group policymode: COMMON / CONTENT_AUDIT / USAGE_AUDITFMS wraps it and adds:Org scope — OUs · accounts · tagsReplicate to tagged ENIs · revert manual editsAuto-remediation — detect, then remediatedeploys3Baseline SG on every tagged ENIacross in-scope accounts
Security-group building blocks — and how the FMS policy wraps them.
  • Primary security group — the baseline rules.
  • Security-group policy — mode: COMMON / CONTENT_AUDIT / USAGE_AUDIT.
  • FMS wrapper — org scope + replicate/audit + remediation.
  • Baseline SG — on every tagged ENI, across accounts.

In Terraform — the same aws_fms_policy wrapper, this section’s type:

# Mode 1 — COMMON: replicate one baseline SG to matching ENIs
resource "aws_fms_policy" "sg_common" {
  name = "org-sg-baseline"

  # Layer 1 — account scope
  include_map { account = ["*"] }                 # every account in the org

  # Layer 2 — resource scope
  # WHY: apply the baseline only to your workload ENIs — leave appliance / third-party ENIs alone
  resource_type = "AWS::EC2::NetworkInterface"
  resource_tags = { managed = "true" }

  security_service_policy_data {
    type = "SECURITY_GROUPS_COMMON"
    managed_service_data = jsonencode({
      type = "SECURITY_GROUPS_COMMON"
      securityGroups            = [{ id = "sg-0123456789abcdef0" }]
      applyToAllEC2InstanceENIs = false           # false = honour the resource_tags filter above
    })
  }
}
# Mode 2 — CONTENT_AUDIT: check rules against an allow/deny template
resource "aws_fms_policy" "sg_content" {
  name = "org-sg-content-audit"

  # Layer 1 — account scope
  include_map { orgunit = ["ou-abcd-1example"] }

  # Layer 2 — resource scope
  # WHY: audit only the SGs for a given app/team, not every SG in the account
  resource_type = "AWS::EC2::SecurityGroup"
  resource_tags = { app = "payments" }

  security_service_policy_data {
    type = "SECURITY_GROUPS_CONTENT_AUDIT"
    managed_service_data = jsonencode({
      type                = "SECURITY_GROUPS_CONTENT_AUDIT"
      securityGroups      = [{ id = "sg-0allowed00000000" }]
      securityGroupAction = { type = "ALLOW" }
    })
  }
}
# Mode 3 — USAGE_AUDIT: find unused / redundant SGs
resource "aws_fms_policy" "sg_usage" {
  name = "org-sg-usage-audit"

  # Layer 1 — account scope
  include_map { orgunit = ["ou-abcd-1example"] }
  exclude_map { account = ["111122223333"] }      # exclude the FMS admin account (default for usage audit)

  # Layer 2 — resource scope
  # WHY: only clean up prod SGs here — keep sandbox / experiment SGs out of scope
  resource_type = "AWS::EC2::SecurityGroup"
  resource_tags = { env = "prod" }

  security_service_policy_data {
    type = "SECURITY_GROUPS_USAGE_AUDIT"
    managed_service_data = jsonencode({
      type                            = "SECURITY_GROUPS_USAGE_AUDIT"
      deleteUnusedSecurityGroups      = true
      coalesceRedundantSecurityGroups = true
    })
  }
}

How it drops in network firewalls — distributed vs centralized

FMS admin account (delegated) — every card is an FMS policy (aws_fms_policy); its type field picks the service:Account AAccount BAccount Ctype = WAFV2WAFV2FMS policytype = SHIELD_ADVANCEDShield Adv.FMS policytype = SECURITY_GROUPS_COMMONSecurity groupFMS policytype = NETWORK_ACL_COMMONNetwork ACLFMS policytype = NETWORK_FIREWALL5Network FWFMS policytype = DNS_FIREWALLDNS FirewallFMS policytype = THIRD_PARTY_FIREWALLThird-partyFMS policy5NFW endpointIn-scope VPCsper AZ5NFW endpointIn-scope VPCsper AZ5NFW endpointIn-scope VPCsper AZAWS Config · every account + Regionrecords state · detects drift · the real cost driver
The Network FW policy shown against all seven — every other card greyed. Scoped across the organization, it deploys a Network Firewall endpoint into each in-scope account (A, B and C).

Network Firewall is where FMS's deployment model forks, and it’s a real architectural decision, not a toggle. Both models share rule groups through AWS RAM and leave most route-table wiring to you.

  • Distributed — FMS deploys firewall endpoints into each in-scope VPC (one or more per AZ), identified by VPC tags; placement is automatic (AZs with public subnets) or a custom AZ list. Good for many small, independent VPCs; more endpoints to pay for.
  • Centralized — FMS creates endpoints in a single inspection VPC per account behind a Transit Gateway, inspecting east-west (VPC-to-VPC) and north-south (internet / on-prem) traffic in one place. Fewer endpoints, one choke point. The constraint, per AWS's networking blog: FMS supports only one inspection VPC per account in this model.

Import existing firewalls is a third deployment model. Instead of having FMS create endpoints, you point the policy at Network Firewalls you have already deployed — distributed across your VPCs, or centralized in an inspection VPC — and FMS adopts them, taking over their firewall policy and compliance and still auto-remediating drift. It's the brownfield path: keep the firewalls you built, gain org-wide, Config-backed enforcement. In every model FMS offers a "monitoring" mode to confirm routing before you commit — get route tables wrong here and traffic bypasses the firewall entirely.

Distributed · an endpoint in every VPCCentralized · one inspection VPCSpoke VPC ASpoke VPC BSpoke VPC CInspection VPC1234InternetNFW endpointNFW endpointNFW endpointSpoke VPC ASpoke VPC BSpoke VPC CTransit GatewayNFW endpointInternet
  1. Distributed: each spoke VPC's own firewall endpoint inspects, then egresses locally.
  2. Centralized: every spoke routes to the Transit Gateway.
  3. The TGW sends traffic to the single inspection VPC's firewall.
  4. Inspected once, then out — one choke point, fewer endpoints to run.
Figure 4 · Distributed puts a firewall endpoint in every spoke VPC; centralized routes every spoke through one inspection VPC behind a Transit Gateway. Same rule groups, RAM-shared; different number of endpoints to run and pay for.

Building blocks

Network Firewall building blocks — and how an FMS policy wraps themIP sets and variablesReusable CIDR lists, port vars1Rule groupsStateless (5-tuple) + stateful (Suricata)2AWS Firewall Manager policy4type = NETWORK_FIREWALLFirewall policy · Network Firewall native3References rule groups — orderedStateless default actionStateful default actionFMS wraps that firewall policy and adds:Org scope — OUs · accounts · tagsDeployment model — distributed | centralizedAuto-remediation — detect, then remediatedeploysFirewall endpointsDeployed per VPC and AZ5An FMS policy's type selects the service it covers — Network Firewall is one type:WAFShieldSec groupsNetwork FirewallDNS FW3rd-partyNetwork Firewall nativeFirewall Manager wrapper
The chain, bottom-up:
  1. IP sets & variables — reusable named CIDR lists and port variables the rules reference.
  2. Rule groups — the reusable matching unit: stateless (5-tuple) + stateful (Suricata).
  3. Firewall policy — the object that holds the rule-group references plus the stateless and stateful default actions (shown nested).
  4. FMS policy — the Firewall Manager wrapper that contains that firewall policy and adds org scope, deployment model, and remediation. Its type field (here NETWORK_FIREWALL) is what makes it cover Network Firewall — sibling types cover WAF, Shield, security groups, DNS Firewall, and third-party.
  5. Firewall endpoints — the data-plane resource the FMS policy deploys, per VPC and AZ.
Network Firewall building blocks — the native chain (IP sets → rule groups → firewall policy), and how the FMS policy wraps it with org scope, a deployment model, and remediation.
  • IP sets & variables — reusable CIDR lists + port vars.
  • Rule groups — stateless (5-tuple) + stateful (Suricata).
  • Firewall policy — composes rule groups + default actions.
  • FMS policy — wraps the firewall policy; adds org scope, deployment model, remediation.
  • Firewall endpoints — deployed per VPC & AZ.

In Terraform — the same aws_fms_policy wrapper, this section’s type:

# Distributed — a firewall endpoint in every in-scope VPC
resource "aws_fms_policy" "nfw_dist" {
  name = "org-nfw-distributed"

  # Layer 1 — account scope
  include_map { orgunit = ["ou-abcd-1example"] }

  # Layer 2 — resource scope
  # WHY: firewall endpoints cost per AZ — tag only the VPCs that actually carry sensitive / egress traffic
  resource_type = "AWS::EC2::VPC"
  resource_tags = { inspect = "true" }

  security_service_policy_data {
    type = "NETWORK_FIREWALL"
    managed_service_data = jsonencode({
      type = "NETWORK_FIREWALL"
      networkFirewallStatelessDefaultActions         = ["aws:forward_to_sfe"]
      networkFirewallStatelessFragmentDefaultActions = ["aws:forward_to_sfe"]
      networkFirewallStatefulRuleGroupReferences     = [{ resourceARN = aws_networkfirewall_rule_group.stateful.arn }]
      networkFirewallOrchestrationConfig = { singleFirewallEndpointPerVPC = false }
    })
  }
}
# Centralized — one inspection VPC per account behind a Transit Gateway
resource "aws_fms_policy" "nfw_central" {
  name = "org-nfw-centralized"

  # Layer 1 — account scope
  include_map { orgunit = ["ou-abcd-1example"] }

  # Layer 2 — resource scope
  # WHY: only route the tagged VPCs through inspection — dev / isolated VPCs skip the choke point
  resource_type = "AWS::EC2::VPC"
  resource_tags = { inspect = "true" }

  security_service_policy_data {
    type = "NETWORK_FIREWALL"
    managed_service_data = jsonencode({
      type = "NETWORK_FIREWALL"
      networkFirewallStatefulRuleGroupReferences = [{ resourceARN = aws_networkfirewall_rule_group.stateful.arn }]
      networkFirewallOrchestrationConfig = {
        singleFirewallEndpointPerVPC = true
        allowedIPV4CidrList          = ["100.64.0.0/16"]
      }
      firewallDeploymentModel = "CENTRALIZED"
    })
  }
}

How it filters outbound DNS — Resolver DNS Firewall policies

FMS admin account (delegated) — every card is an FMS policy (aws_fms_policy); its type field picks the service:Account AAccount BAccount Ctype = WAFV2WAFV2FMS policytype = SHIELD_ADVANCEDShield Adv.FMS policytype = SECURITY_GROUPS_COMMONSecurity groupFMS policytype = NETWORK_ACL_COMMONNetwork ACLFMS policytype = NETWORK_FIREWALLNetwork FWFMS policytype = DNS_FIREWALL6DNS FirewallFMS policytype = THIRD_PARTY_FIREWALLThird-partyFMS policy6DNS FirewallIn-scope VPCs6DNS FirewallIn-scope VPCs6DNS FirewallIn-scope VPCsAWS Config · every account + Regionrecords state · detects drift · the real cost driver
The DNS Firewall policy shown against all seven — every other card greyed. Scoped across the organization, it deploys a DNS Firewall association into each in-scope account (A, B and C).

A DNS Firewall policy manages the associations between Route 53 Resolver DNS Firewall rule groups (which must already exist in the FMS admin account) and in-scope VPCs, and needs RAM sharing on. You set first rule groups (priority 1–99) and last rule groups (priority 9,901–10,000); account owners slot their own into the range between. FMS names its associations FMManaged_<policyId>; if an owner overrides one, the policy goes non-compliant and FMS proposes remediation — the same first/last-plus-owner-middle sandwich as WAF.

Building blocks

DNS Firewall building blocks — and how the FMS policy wraps them1Domain listsallow / block2Resolver DNS Firewall rule groupsreference domain lists + actionsAWS Firewall Manager policytype = DNS_FIREWALL3DNS-Firewall policypre/post-process rule groups, by priorityFMS wraps it and adds:Org scope — OUs · accounts · tagsRAM-shares rule groups · associates to VPCsAuto-remediation — detect, then remediatedeploys4Rule-group associationson in-scope VPCs
DNS Firewall building blocks — and how the FMS policy wraps them.
  • Domain lists — allow / block.
  • Resolver DNS Firewall rule groups — reference domain lists + actions.
  • FMS DNS policy — org scope + RAM-share + associate.
  • Rule-group associations — on in-scope VPCs.

In Terraform, again the aws_fms_policy wrapper — here the type is:

resource "aws_fms_policy" "dns" {
  name                = "org-dns-firewall"
  remediation_enabled = true

  # Layer 1 — account scope
  include_map { orgunit = ["ou-abcd-1example"] }

  # Layer 2 — resource scope
  # WHY: only associate rule groups to VPCs that resolve external names — isolated VPCs don't need it
  resource_type = "AWS::EC2::VPC"
  resource_tags = { dns_filter = "on" }

  security_service_policy_data {
    type = "DNS_FIREWALL"
    managed_service_data = jsonencode({
      type                  = "DNS_FIREWALL"
      preProcessRuleGroups  = [{ ruleGroupId = "rslvr-frg-0123456789abcdef0", priority = 100 }]
      postProcessRuleGroups = []
    })
  }
}

How it fences off subnets — network ACL policies

FMS admin account (delegated) — every card is an FMS policy (aws_fms_policy); its type field picks the service:Account AAccount BAccount Ctype = WAFV2WAFV2FMS policytype = SHIELD_ADVANCEDShield Adv.FMS policytype = SECURITY_GROUPS_COMMONSecurity groupFMS policytype = NETWORK_ACL_COMMON4Network ACLFMS policytype = NETWORK_FIREWALLNetwork FWFMS policytype = DNS_FIREWALLDNS FirewallFMS policytype = THIRD_PARTY_FIREWALLThird-partyFMS policy4Network ACLVPC subnets4Network ACLVPC subnets4Network ACLVPC subnetsAWS Config · every account + Regionrecords state · detects drift · the real cost driver
The Network ACL policy shown against all seven — every other card greyed. Scoped across the organization, it deploys a managed network ACL into each in-scope account (A, B and C).

A newer type: FMS manages VPC network ACLs org-wide. You define first and last inbound and outbound rules; FMS enforces their presence and ordering on in-scope subnets, then tags the managed NACLs FMManaged=true. Account owners keep custom rules in the reserved range in between (5,000–32,000). Two caveats: NACL updates are slower than other types (EC2 NACL API rate limits), and if a subnet lands in several NACL policies at once, the oldest wins.

Building blocks

Network-ACL building blocks — and how the FMS policy wraps them1First + last rule entriesinbound & outbound, orderedAWS Firewall Manager policytype = NETWORK_ACL_COMMON2Network-ACL policyenforce presence + orderingFMS wraps it and adds:Org scope — OUs · accounts · tagsForce-remediate first / last entriesAuto-remediation — detect, then remediatedeploys3Managed NACL on subnetsin-scope VPC subnets
Network-ACL building blocks — and how the FMS policy wraps them.
  • First + last rule entries — inbound & outbound, ordered.
  • Network-ACL policy — enforce presence + ordering.
  • FMS wrapper — org scope + force-remediate.
  • Managed NACL — on in-scope VPC subnets.

In Terraform — the same aws_fms_policy wrapper, this section’s type:

resource "aws_fms_policy" "nacl" {
  name                = "org-nacl-baseline"
  remediation_enabled = true

  # Layer 1 — account scope
  include_map { orgunit = ["ou-abcd-1example"] }

  # Layer 2 — resource scope
  # WHY: pin the deny rules to internet-facing subnets — private / data subnets keep their own NACLs
  resource_type = "AWS::EC2::Subnet"
  resource_tags = { tier = "public" }

  security_service_policy_data {
    type = "NETWORK_ACL_COMMON"
    managed_service_data = jsonencode({
      type = "NETWORK_ACL_COMMON"
      networkAclCommonPolicy = { networkAclEntrySet = {
        forceRemediateForFirstEntries = true
        firstEntries = [{ egress = false, protocol = "6", ruleAction = "deny", cidrBlock = "0.0.0.0/0",
                          portRange = { from = 22, to = 22 } }]
        forceRemediateForLastEntries = false, lastEntries = []
      }}
    })
  }
}

How it deploys someone else's firewall — Palo Alto and Fortigate

FMS admin account (delegated) — every card is an FMS policy (aws_fms_policy); its type field picks the service:Account AAccount BAccount Ctype = WAFV2WAFV2FMS policytype = SHIELD_ADVANCEDShield Adv.FMS policytype = SECURITY_GROUPS_COMMONSecurity groupFMS policytype = NETWORK_ACL_COMMONNetwork ACLFMS policytype = NETWORK_FIREWALLNetwork FWFMS policytype = DNS_FIREWALLDNS FirewallFMS policytype = THIRD_PARTY_FIREWALL7Third-partyFMS policy7NGFW endpointVPC endpoints7NGFW endpointVPC endpoints7NGFW endpointVPC endpointsAWS Config · every account + Regionrecords state · detects drift · the real cost driver
The Third-party policy shown against all seven — every other card greyed. Scoped across the organization, it deploys a third-party NGFW endpoint into each in-scope account (A, B and C).

FMS can orchestrate two third-party firewalls as native policy types once you subscribe in AWS Marketplace and configure the vendor side:

  • Palo Alto Networks Cloud NGFW — subscribe to "Palo Alto Networks Cloud NGFW Pay-As-You-Go," configure in the Cloud NGFW service, then an FMS policy centrally deploys NGFW resources and rulestacks across accounts.
  • Fortigate Cloud Native Firewall (CNF) as a Service — subscribe in Marketplace, define global policy sets in Fortigate CNF, then an FMS policy deploys CNF resources across accounts.

Same pattern as the AWS-native types: FMS handles org-wide placement and lifecycle, the vendor console owns the rule content. Only the default administrator can manage third-party firewalls, and vendor support and pricing shift — check current Marketplace listings before committing.

Building blocks

Third-party firewall building blocks — and how the FMS policy wraps them1Vendor policy / rulestackdefined in Palo Alto Cloud NGFW or Fortigate CNFAWS Firewall Manager policytype = THIRD_PARTY_FIREWALL2Third-party-firewall policyreferences the vendor policy setFMS wraps it and adds:Org scope — OUs · accounts · tagsDeployment model — distributed / centralizedVendor console owns the rule contentdeploys3NGFW endpointsacross in-scope accounts
Third-party firewall building blocks — and how the FMS policy wraps them.
  • Vendor policy / rulestack — Palo Alto Cloud NGFW or Fortigate CNF.
  • Third-party-firewall policy — references the vendor policy.
  • FMS wrapper — org scope + deployment model + lifecycle.
  • NGFW endpoints — across in-scope accounts.

Same wrapper in Terraform; the type is what makes it third-party:

# Palo Alto Cloud NGFW — subscribe in Marketplace + configure Cloud NGFW first
resource "aws_fms_policy" "tp_palo" {
  name = "org-palo-alto-ngfw"

  # Layer 1 — account scope
  include_map { orgunit = ["ou-abcd-1example"] }

  # Layer 2 — resource scope
  # WHY: NGFW endpoints are billed per VPC — only route production VPCs through the vendor firewall
  resource_type = "AWS::EC2::VPC"
  resource_tags = { ngfw = "true" }

  security_service_policy_data {
    type = "THIRD_PARTY_FIREWALL"
    managed_service_data = jsonencode({
      type               = "THIRD_PARTY_FIREWALL"
      thirdPartyFirewall = "PALO_ALTO_NETWORKS_CLOUD_NGFW"
      thirdPartyFirewallConfig = { thirdPartyFirewallPolicyList = ["global-1"]  # a Cloud NGFW firewall-policy NAME in your tenant, not an ARN }
      firewallDeploymentModel = { distributedFirewallDeploymentModel = {
        distributedFirewallOrchestrationConfig = { firewallCreationConfig = { endpointLocation = { availabilityZoneConfigList = [] } } }
      }}
    })
  }
}
# Fortigate CNF — subscribe in Marketplace + define CNF policy sets first
resource "aws_fms_policy" "tp_forti" {
  name = "org-fortigate-cnf"

  # Layer 1 — account scope
  include_map { orgunit = ["ou-abcd-1example"] }

  # Layer 2 — resource scope
  # WHY: same as above — keep the vendor firewall (and its per-VPC cost) on the VPCs that need it
  resource_type = "AWS::EC2::VPC"
  resource_tags = { ngfw = "true" }

  security_service_policy_data {
    type = "THIRD_PARTY_FIREWALL"
    managed_service_data = jsonencode({
      type               = "THIRD_PARTY_FIREWALL"
      thirdPartyFirewall = "FORTIGATE_CLOUD_NATIVE_FIREWALL"
      firewallDeploymentModel = { centralizedFirewallDeploymentModel = {
        centralizedFirewallOrchestrationConfig = {
          inspectionVpcIds    = [{ resourceId = "vpc-0inspection00000", accountId = "111122223333" }]
          allowedIPV4CidrList = ["100.64.0.0/16"]
        }
      }}
    })
  }
}
Part 3Standing it up and running it

What you stand up first — Organizations, a delegated admin, Config everywhere

What to stand up first — in the order the docs prescribe1AWS Organizations — all featuresnot consolidated-billing-only · no Organizations, no FMSthen2Designate the FMS adminthe management account onboards the org + names a delegated member accountthen3AWS Config — every account + Regioncontinuous recording, incl. the admin account · the real cost driverFMS ready to author policiesONLY IF YOU USE THEMAWS RAM sharingfor Network Firewall / DNS Firewall— shares rule groups + firewallsAWS Marketplace subscriptionfor third-party firewalls— Palo Alto Cloud NGFW / Fortigate CNF
The prerequisites, in the order AWS prescribes: Organizations (all features) → a delegated FMS admin → AWS Config everywhere. AWS RAM and a Marketplace subscription are only needed for Network Firewall / DNS Firewall and third-party policies.

FMS has more prerequisites than most AWS services, and skipping one produces silent gaps, not errors. The docs prescribe a strict order — follow it:

  1. AWS Organizations with all features enabled. Not "consolidated billing only" — all features. No Organizations, no FMS; there is no standalone mode.
  2. Designate a Firewall Manager administrator. From the management account, onboard the org and create a default administrator account (a member account). More on this below — it's the part people get wrong.
  3. Enable AWS Config in every member account and every Region that holds resources you want to protect, including the FMS admin account itself. Recording frequency must be Continuous (the default).
  4. Enable AWS RAM resource sharing if you'll use Network Firewall or DNS Firewall policies — those share rule groups and firewalls across accounts through Resource Access Manager.
  5. Subscribe in AWS Marketplace if you'll deploy a third-party firewall (Palo Alto or Fortigate).
The Config caveat that surprises people: the docs note "you incur charges for your AWS Config settings, according to AWS Config pricing." FMS forces Config on across your whole org — every account, every Region you protect — billing per configuration item recorded plus per rule evaluation. For a large org that's routinely the biggest line item in the whole FMS setup, larger than the FMS fees. Budget it before you enable it.

You needn't record all resource types in Config, but you must record the ones each policy needs. A WAF policy needs Config recording CloudFront distributions, ALBs (as ElasticLoadBalancingV2), API Gateway, and the WebACL types; a common security-group policy needs EC2 SecurityGroup, EC2 VPC, plus the instance and ENI types; a Network Firewall policy needs the firewall policy, rule group, VPC, internet gateway, route table, and subnet types. Get the recording set wrong and FMS can't see the resource — and, per the docs, "Firewall Manager doesn't have visibility into these permission misconfigurations." It just quietly under-protects.

Who gets to write the rules — the delegated-admin model

This is the most misunderstood piece of FMS, so be precise. There are two roles:

  • The Organizations management account onboards the org to FMS and designates administrators. It is the only account that can create, update, or revoke FMS admins — but not where you live day to day.
  • The Firewall Manager default administrator is a member account the management account nominates — where you actually author policies. It has full administrative scope and is the only account that can manage third-party firewalls.

Under the covers, when the management account creates an FMS administrator, FMS checks whether that account is already an Organizations delegated administrator for the service and registers it if not. So the "FMS admin account" is an Organizations delegated administrator — the standard multi-account pattern of pushing a security function out of the management account into a dedicated security account, exactly what AWS recommends: don't run FMS from the management account, delegate it.

Since a 2023 update, FMS supports multiple administrators with scoped permissions: the management account grants each admin full or restricted scope along three axes — which accounts and OUs they can touch, which Regions they can act in, and which policy types they can manage. That's how a networking team owns Network Firewall policies while an app-sec team owns WAF, without either seeing the other's blast radius.

AWS Organizations · management accountDelegated FMS admin (security account)Scoped admin (restricted)12Management accountdesignates adminsCannot author or scope policies hereFull-scope adminAll policiesApp-sec adminWAF · 1 Region
  1. The management account designates the delegated admin(s) — the only account that can.
  2. Each admin authors policies in its own account, with scope optionally restricted by account/OU, Region, and policy type.
Figure 5 · The management account designates delegated FMS admin account(s); policies are authored there, optionally scope-restricted by account, Region, and policy type. The management account itself cannot scope policies.

How it finds and fixes drift — scope, detect, remediate

Every policy runs the same loop, and it explains most of FMS's behaviour.

  1. Scope selects targets: accounts and OUs (all, or explicit include/exclude), resource types, and tags — tags being include OR exclude, never both. New accounts joining an in-scope OU are picked up automatically; that automatic capture is the whole reason FMS exists.
  2. Detect: FMS reads AWS Config's recorded state for in-scope resources and compares it to the policy. A missing web ACL, a forbidden SG rule, an owner-overridden DNS association — all surface as non-compliant on the dashboard. Detection isn't instant; minutes, and up to hours for SG/ENI changes.
  3. Remediate: if automatic remediation is on, FMS creates or associates the missing protection (builds and attaches the web ACL, replicates the SG, stands up the firewall endpoint). If off — the default — it only reports, showing what it would do.

Drift is a first-class concept. When a local owner changes something FMS manages — deletes the web ACL, re-associates a different DNS rule group — Config records it, FMS marks the policy non-compliant and (if enabled) re-asserts intent. It reconciles continuously, not once: powerful and occasionally infuriating, since you cannot locally "fix" what FMS owns without FMS undoing you.

FMS admin accountMember account1234PolicyintentFMScomparesProtected resourceAWS Config record
  1. Policy intent lives in the FMS admin account.
  2. AWS Config records each member resource's state.
  3. FMS compares to intent and remediates — or only reports, if remediation is off.
  4. An owner's change re-enters the loop as drift and gets corrected.
Figure 6 · The reconcile loop: policy intent in the FMS admin account, Config records state in each member account, FMS compares and remediates non-compliant resources; an owner's drift re-enters the loop and gets corrected.
That's the model, the policy types, the building blocks, and the reconcile loop — if you just need a working mental model, stop here. FMS is a delegated-admin control plane that reads AWS Config, compares reality to per-policy intent, and deploys the matching managed firewall into every in-scope account. The rest is the money, the build, and the traps.
Part 4Cost, rollout, and where it bites

When it earns its keep — and what it costs

When FMS beats the alternatives

FMS is not a free lunch: a fixed monthly tax plus a real Config bill, in exchange for one guarantee — every current and future account is protected without anyone remembering to do it. Weigh it like this:

SituationBetter toolWhy
A few accounts, stablePer-account config / TerraformNo Config-everywhere tax, no per-policy fee; you own drift yourself.
Building the org from scratchControl Tower / landing zone (LZA)Sets up accounts and guardrails; FMS then enforces network protections on top.
Many accounts, new ones appearing, must-not-driftFirewall ManagerAuto-enrols new accounts, continuously reconciles, one compliance view.
Org-wide DDoS with Shield AdvancedFirewall ManagerSubscribes accounts and centralizes attack monitoring; policy fee waived.

The cost model — the sticker price and the Config tax

Three layers, and only the first is the "FMS bill":

  • FMS policy fee — roughly $100 per policy, per Region, per month (it varies slightly by Region; some are higher). Shield Advanced policies are included for Shield Advanced customers. This multiplies fast: 5 policy types × 4 Regions is 20 policies, about $2,000/month before anything is protected.
  • The Config tax — FMS requires AWS Config on in every account and Region, and creates two AWS Config rules per policy, per account. Config bills per configuration item recorded and per rule evaluation; for a large org this is typically the dominant cost, and it is not covered by a Shield Advanced subscription.
  • The underlying services — the WAF web ACLs and rules, Network Firewall endpoints plus data processing, DNS Firewall queries, third-party Marketplace charges. FMS deploys them; you pay their normal rates.

Practitioners flag the same thing: the $100/policy fee is visible and predictable; the Config bill is the one that surprises you. Model the Config cost first when scoping a big rollout.

Standing one up end to end

Here’s the concrete path from nothing to a first compliant resource — a common AWS WAF web ACL across an OU. Use the console for the one-time org setup, then CLI or Terraform for the policy itself.

Step 1 — Org, admin, and Config (one-time)

From the management account (both calls must originate here), enable AWS Organizations all-features, then designate the delegated FMS admin:

# CLI — from the Organizations MANAGEMENT account
aws organizations enable-all-features

# Designate a member account as the Firewall Manager admin
aws fms associate-admin-account \
  --admin-account 111122223333        # your dedicated security account
# Terraform — run with management-account credentials
resource "aws_fms_admin_account" "this" {
  account_id = "111122223333"   # the delegated security account
}
Then enable AWS Config in every member account and Region you'll protect (the "Enable AWS Config" CloudFormation StackSet is the sane way to do this org-wide), recording at least the resource types the WAF policy needs. This is the step that costs money — do it deliberately.

Step 2 — Create and scope the policy

Now switch to the delegated FMS admin account and create the WAF policy. In Terraform the policy body is a JSON blob (ManagedServiceData) that mirrors the console's rule-group choices:

resource "aws_fms_policy" "waf_baseline" {
  name                  = "org-waf-baseline"
  resource_type         = "AWS::ElasticLoadBalancingV2::LoadBalancer"  # the TARGET the web ACL attaches to — not a policy type
  remediation_enabled   = false          # start OFF — review first
  exclude_resource_tags = false          # tags below are an INCLUDE set

  security_service_policy_data {
    type = "WAFV2"
    managed_service_data = jsonencode({
      type = "WAFV2"
      defaultAction               = { type = "ALLOW" }
      overrideCustomerWebACLAssociation = false
      preProcessRuleGroups = [{
        managedRuleGroupIdentifier = {
          vendorName            = "AWS"
          managedRuleGroupName  = "AWSManagedRulesCommonRuleSet"
        }
        overrideAction = { type = "NONE" }
        ruleGroupType  = "ManagedRuleGroup"
      }]
      postProcessRuleGroups = []
    })
  }

  # Scope: an OU, minus the shared/admin account
  include_map { orgunit = ["ou-abcd-1example"] }
  exclude_map { account = ["111122223333"] }   # exclude the FMS admin account

  resource_tags = { protect = "true" }         # only tagged ALBs
}
# CLI equivalent — the policy definition lives in policy.json
aws fms put-policy --cli-input-json file://policy.json
Two deliberate choices: remediation_enabled = false ships the policy in report-only mode so you can inspect the compliance dashboard before FMS touches anything, and the scope excludes the FMS admin account and narrows to tagged resources — both are the documented best practice for a first rollout.

Step 3 — Verify, then turn on remediation

Open the policy in the FMS console. In-scope accounts start as Pending, then resolve to compliant/non-compliant counts (allow several minutes). The non-compliant list is exactly what remediation would change; when it matches your intent, flip remediation_enabled to true and re-apply. FMS creates the FMManagedWebACLV2-… web ACL in each account and associates it with the tagged ALBs, and the dashboard flips to compliant. That's the whole loop, live.

The things that are always true

  • Three stores of truth, always. Organizations = membership, Config = compliance, FMS = intent. FMS is only ever as good as the other two — a missing Config recorder is an invisible protection gap, not an error.
  • The management account designates; the delegated admin authors. Policy scope lives in the member admin account, never the management account.
  • Policies are per-Region. Except CloudFront/WAF (Global), you create one policy per Region — the fee and the config multiply accordingly.
  • FMS-managed resources are tagged and named for it (FMManaged, FMManagedWebACLV2-, FMManaged_). If it carries the mark, don't hand-edit it — FMS will reconcile you away.
  • First/last is the sandwich pattern. WAF, DNS Firewall, and NACL policies all reserve a first block and a last block for FMS, leaving the middle for local account owners.

Where it bites

PitfallFix
The Config bill you didn't model — Config-everywhere plus two rules per policy per account is usually the largest cost, and it's separate from Shield Advanced.Price the Config cost before enabling; model configuration-item and rule-evaluation volume first.
Auto-remediation as a production incident — enabling it on a broad scope can rip out or replace live web ACLs and security groups.Ship policies report-only, review the non-compliant list, then enable remediation.
Trying to run FMS from the management account — it can't be the scoped policy admin.Delegate to a dedicated security account first.
Forgetting to exclude the admin/shared accounts — protections land where you least want surprises.Exclude the FMS admin and shared-services accounts in every policy scope.
Overlapping or duelling policies — two SG policies (or FMS plus an outside SG tool) claiming the same resource loop forever.Keep scopes mutually exclusive with tags.
Assuming per-Region is per-org — a policy protects one Region only.Create one policy per Region; budget the multiplied fees and Config recording.
CloudFront flat-rate WAF — an FMS-managed web ACL on a flat-rate plan is permanently, unremediably non-compliant.Don't scope flat-rate CloudFront distributions into a WAF policy.

Conclusion

Firewall Manager isn’t a firewall — it’s the control plane that keeps every account’s firewalls consistent without anyone logging in. You author one aws_fms_policy in a delegated admin account, scope it to accounts and then to resources, and FMS creates the managed protection in each account, associates it, and re-applies it when it drifts — new accounts included, automatically.

That leverage has a price: it rides on AWS Organizations and AWS Config, and the Config-everywhere bill usually dwarfs the per-policy fee. So reach for it when you actually have the problem it solves — many accounts, new ones appearing, a baseline that must not drift; below that, per-account Terraform is simpler and cheaper. When you do adopt it, ship every policy in report-only mode, scope tightly with tags, and turn on remediation once you’ve seen the blast radius.

References

  1. AWS — AWS Firewall Manager (chapter overview), AWS WAF Developer Guide. docs.aws.amazon.com/waf/latest/developerguide/fms-chapter.html
  2. AWS — AWS Firewall Manager prerequisites. docs.aws.amazon.com/waf/latest/developerguide/fms-prereq.html
  3. AWS — Using AWS Firewall Manager administrators (default vs. delegated admin, admin scope). docs.aws.amazon.com/waf/latest/developerguide/fms-administrators.html
  4. AWS — Enabling AWS Config for using Firewall Manager (required resource types, Continuous recording, cost note). docs.aws.amazon.com/waf/latest/developerguide/enable-config.html
  5. AWS — Using AWS WAF policies with Firewall Manager and Setting up AWS Firewall Manager AWS WAF policies. docs.aws.amazon.com/waf/latest/developerguide/waf-policies.html
  6. AWS — Using security group policies (common / content audit / usage audit, best practices, caveats). docs.aws.amazon.com/waf/latest/developerguide/security-group-policies.html
  7. AWS — Using network ACL policies with Firewall Manager. docs.aws.amazon.com/waf/latest/developerguide/network-acl-policies.html
  8. AWS — Using Route 53 Resolver DNS Firewall policies (first/last priorities, RAM). docs.aws.amazon.com/waf/latest/developerguide/dns-firewall-policies.html
  9. AWS — Using Palo Alto Networks Cloud NGFW policies and Using Fortigate CNF policies. docs.aws.amazon.com/waf/latest/developerguide/cloud-ngfw-policies.html
  10. AWS — AWS Network Firewall Developer Guide (rule groups: stateless vs. stateful/Suricata, firewall policy and default actions, firewall endpoints, IP set variables). docs.aws.amazon.com/network-firewall/latest/developerguide/firewalls.html
  11. AWS Security Blog — Gaddamanugu & Liu, How to deploy AWS Network Firewall by using AWS Firewall Manager (2022-08-26; distributed vs centralized). aws.amazon.com/blogs/security/how-to-deploy-aws-network-firewall-by-using-aws-firewall-manager/
  12. AWS Networking Blog — Deployment models for AWS Network Firewall. aws.amazon.com/blogs/networking-and-content-delivery/deployment-models-for-aws-network-firewall/
  13. AWS — AWS Firewall Manager pricing. aws.amazon.com/firewall-manager/pricing/
  14. AWS — Firewall Manager and AWS Organizations (service integration). docs.aws.amazon.com/organizations/latest/userguide/services-that-can-integrate-fms.html
  15. AWS CLI — fms put-policy; AWS API — PutAdminAccount. docs.aws.amazon.com/cli/latest/reference/fms/put-policy.html
  16. HashiCorp — Terraform AWS provider, aws_fms_policy and aws_fms_admin_account. registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/fms_policy
  17. AWS re:Post — AWS FW manager policy cost to manage common SG (community cost reality-check). repost.aws/questions/QUYo_whKsASj2OJv0g5-7yeQ/
  18. Shine Solutions — AWS Firewall Manager Operational Insights (2024-10-23; Config-cost and admin gotchas, practitioner). shinesolutions.com/2024/10/23/aws-firewall-manager-operational-insights/