Executive summary
GuardDuty now supports AWS Organizations declarative policies, so you can “centrally enable GuardDuty threat detection across every account and Region in your AWS organization.” All commercial Regions and GovCloud (US).
This is the right mechanism for the job, and the reason is worth stating precisely: declarative policies are “enforced in the service's control plane, which is an important distinction from authorization policies such as service control policies (SCPs) and resource control policies (RCPs)… While authorization policies regulate access to APIs, declarative policies are applied directly at the service level to enforce durable intent.” An SCP can stop someone turning GuardDuty off. Only a declarative policy turns it on.
But the syntax has a rule that inverts what most people will assume, and it costs coverage rather than throwing an error. A Region block replaces the default; it does not add to it.
What changed
One policy type, GUARDDUTY_POLICY, attachable to the root, an OU or an
account, governing the foundational detector and every protection plan together. Eight keys:
| Policy key | GuardDuty feature |
|---|---|
foundational | Foundational threat detection |
s3_data_events | S3 Protection |
eks_audit_logs | EKS Protection |
ebs_malware_protection | Malware Protection (EBS volumes) |
rds_login_events | RDS Protection |
lambda_network_logs | Lambda Protection |
ai_protection | AI Protection |
runtime_monitoring | Runtime Monitoring, with EKS, ECS Fargate and EC2 agent management |
And it takes over the old controls rather than sitting beside them. “While a GuardDuty organization policy is active, the Regional GuardDuty auto-enablement configurations no longer apply, and the per-account controls on the GuardDuty Accounts page are managed by the policy and shown as read-only, marked Managed by Organization policy.”
That is the correct design — two systems both claiming to decide enablement is how drift starts — but it means the policy is now the only place the answer lives.
Architecture
The resolution rule, in AWS's words
The policy is a default block plus optional Region blocks. What happens when
both exist is the whole post:
“A Region-specific block fully replaces the default for that Region; it is
not merged with it. List every feature you want in that Region, because any
feature omitted from a Region block is not inherited from default.”
AWS's own Example 2 is built around this. The stated intent is narrow — disable EBS malware
protection in one Region — and the policy is not short:
“Because a Region block fully replaces the default, the us-east-1
block re-lists every feature it wants active in that Region.”
So the override carries foundational and
s3_data_events set to enabled, purely to keep what
the default already gave it, plus the one key it actually came to change. Drop those two lines and
us-east-1 loses S3 Protection and the detector itself — from a policy
whose diff reads as "turn off malware protection".
And the third state is neither on nor off
“A Region that has neither a default block nor a Region-specific
block is unmanaged. The policy neither enables nor disables GuardDuty there, and existing
settings are left unchanged.”
Unmanaged is not a failure — AWS's Example 4 uses it deliberately, for “when you want a policy to govern GuardDuty in a specific set of Regions without asserting any baseline for the rest.” The problem is that unmanaged looks exactly like covered on any dashboard that only lists what the policy says.
And it has a second-order cost, because the forward guarantee is attached to one block specifically:
“New Regions are automatically included when using the default block
in a policy.” A policy that names Regions and omits default covers
no Region AWS opens in future. The "set once and forget" property is not a property of declarative
policies in general; it is a property of having a default.
The detach behaviour is a documented exception, and it reads the other way
The general rule for every declarative policy type: “If a declarative policy is detached, the attribute state will roll back to its previous state before the declarative policy was attached.”
Read that and you would expect detaching a GuardDuty policy to switch GuardDuty off wherever the policy had turned it on. It does not:
“If you detach an Amazon GuardDuty policy, Amazon GuardDuty remains enabled in previously covered accounts. However, future changes to the organizational structure (such as new accounts joining or existing accounts moving into the OU) no longer automatically enable Amazon GuardDuty. Any further enablement must be performed manually or through re-attaching a policy.”
This is the safer of the two behaviours and it is worth knowing which one you have: detaching costs you nothing today and everything going forward. Current coverage is unharmed, so nothing alarms; the enablement guarantee is gone, so the next account created is uncovered. The gap appears at the moment somebody creates an account, which is weeks after the change that caused it.
Both statements are current and neither is wrong — the general page states the declarative-policy rule, and GuardDuty documents its own departure from it. That is a reasonable way to write documentation and a bad way to be read: anyone who learned declarative policies from the Organizations page and then applied that knowledge to GuardDuty would predict the opposite outcome. Read the service's own page for every declarative policy type you attach, specifically for the detach paragraph.
Business value
The thing being removed is a class of audit finding rather than a quantity of work. The old model was Regional auto-enablement plus per-account controls, which meant coverage was a property of how carefully somebody had clicked, Region by Region, and a new Region opted into by a team was a coverage gap by default.
“The baseline configuration for an AWS service is always maintained, even when the service introduces new features or APIs. The baseline configuration is also maintained when new accounts are added to an organization or when new principals and resources are created.”
The clause about new features matters more here than it sounds. GuardDuty has added protection plans repeatedly — RDS, Lambda, Runtime Monitoring, AI Protection. Under the old model each one arrived switched off and needed a fresh enablement campaign. Under a declarative policy the key exists to be enabled once.
Security considerations
It governs service-linked roles, and the authorization policy types do not. AWS's comparison table answers "Governs service-linked roles?" with No for SCPs, No for RCPs and Yes for declarative policies. For a detection service that works through a service-linked role, that is the difference between a control that applies and one that has a hole in it.
It is enforcement without an API surface — declarative policies work
“by enforcing the desired configuration of an AWS service without using API
actions.” So there is no Deny for a member account to work around
and no API call to condition on.
The delegation is explicit and narrow. “GuardDuty policies are managed by the AWS Organizations delegated administrator… The Configuration policies pages in the GuardDuty console are available only to the delegated administrator account.” The management account designates it and attaches the delegation policy. That is the pattern #36 describes for permissions boundaries, applied here: the delegate gets the task, bounded.
Pair it with an SCP rather than choosing between them. The declarative policy enables
GuardDuty and keeps it enabled; an SCP denying guardduty:Delete* and
guardduty:Disassociate* closes the permission path. They answer different
questions and the names make them sound like alternatives.
Cost considerations
The announcement states no charge for the policy mechanism, and that is the uninteresting half. The interesting half is that this is a switch that turns on billed analysis across every account and Region at once, and the protection plans are priced on volume — S3 data events, EKS audit logs, RDS login activity, Lambda network logs, EBS volume scans.
So a default block with every key enabled is a cost decision taken in one
line, in Regions nobody has looked at. The feature gating does not help you here:
“A feature listed in a Region where it is not yet available has no effect in that
Region” — so an unavailable feature is free and silent today, and starts billing the day
the Region gets it, with no change on your side.
That is the same shape as the coverage trap, pointing the other way. Enable the plans you want
deliberately, and treat a broad default as a forward commitment rather than a
current one.
Operational considerations
Read the effective policy, not the one you wrote. “Multiple policies can apply to a single account and are merged into an effective policy… More specific policies (closer to the account) take precedence.” With a root policy and OU policies in play, the document you are looking at is not the answer for any particular account.
The two forms of the document differ, and the difference will catch a diff.
“Child policies set values using the @@assign inheritance operator.
The authored form (for example {"status": {"@@assign": "enabled"}}) is distinct
from the effective form returned by DescribeEffectivePolicy (which resolves to
plain {"status": "enabled"}).” Comparing authored JSON against
DescribeEffectivePolicy output textually reports a difference on every key.
The account status report is the surface worth wiring up. It “allows you to review the current status of all attributes supported by declarative policies for the accounts in scope” — which is the only thing that distinguishes unmanaged from enabled without reading every policy in the tree.
Custom error messages exist and are worth writing. You can “create customizable error messages, which can help administrators redirect end users to internal wiki pages or provide a descriptive message that can help end users understand why an action failed”. A control-plane refusal with no explanation becomes a support ticket.
Tradeoffs
| Choice | You get | You give up |
|---|---|---|
default only |
Every current and future Region covered; the strongest version of the guarantee | Per-Region nuance, and cost control in Regions you do not use |
default plus Region overrides |
Per-Region feature sets with a baseline underneath | Every override must re-list the full feature set or it silently subtracts |
Named Regions, no default |
Precise scope; untouched settings everywhere else | No automatic coverage of new Regions, ever |
| Declarative policy instead of per-account setup | Enablement that survives new accounts and new features | The Accounts page goes read-only; the policy is the only control |
Implementation guidance
Prerequisites, in order:
“Enable trusted access for the GuardDuty service in AWS Organizations… Enable the
GUARDDUTY_POLICY policy type on the organization root. This is done
automatically when the delegated administrator creates the first policy in the GuardDuty
console.”
The shape to start from is AWS's Example 1 — a single default block with
foundational enabled, attached at the root. One key, every Region, every
account, every future account. Then add protection plans deliberately, and keep the number of Region
overrides as close to zero as the requirements allow.
Two syntax rules that will reject or surprise you:
“Within any block, foundational must be enabled if any other feature
in that same block is enabled”, and
“A policy must contain at least one block.”
For runtime_monitoring, the agent management sub-features sit in a nested
object: additional_configuration carrying
eks_addon_management, ec2_agent_management and
ecs_fargate_agent_management, each with its own status. It is the only feature
with sub-features, so it is the only one where the nesting is deeper than you would guess from the others.
Best practices
Treat every Region block as a complete feature list, never as a patch. Generate them rather than hand-editing, so the full set is always emitted; a hand-edited override is one deleted line away from dropping the detector.
Include a default block unless you have a reason not to, and
write the reason down next to the policy. It is the only thing that covers Regions that do not exist yet.
Review the account status report, not the policy, when asked whether you are covered. The policy tells you what was asserted; the report tells you what is true, including the Regions where nothing was asserted at all.
Before detaching, read the detach paragraph for that specific policy type. GuardDuty leaves coverage in place and drops the forward guarantee. Another declarative policy type rolls the attribute back. Those are opposite outcomes from the same action.
Who should act on this
Anyone running GuardDuty across more than a handful of accounts should move enablement to a policy and delete the per-Region auto-enablement habit, because the policy supersedes it anyway.
Anyone who already wrote Region overrides — in a pilot, or in another declarative policy type with the same model — should re-read them against the replacement rule today. This is the one item here that can already be wrong rather than becoming wrong later.
Anyone whose coverage evidence is a policy document should replace it with the account status report. A policy cannot show you an unmanaged Region; it can only fail to mention it.
Key takeaways
A Region block replaces the default rather than extending it, so an override has to re-state everything it
wants to keep. Omitting default leaves every unnamed Region unmanaged and
forfeits automatic coverage of future Regions. Detaching leaves current coverage intact and silently ends
future enablement, which is the opposite of the general declarative-policy rule.
All three are the same failure: a configuration that is absent rather than wrong. That is also exactly
what #36 is about on the permissions side
— an action missing from a permissions boundary behaves differently from one denied in it, and the
document looks the same either way. Today's #70 is
the third instance: a role trust policy with no aws:SourceArn condition is
reachable by every certificate authority in the account.
The two halves fit together. A declarative policy states the configuration that must hold; an SCP or RCP states the permissions that must not be exceeded. Neither substitutes for the other, and in both the thing that bites is what you did not write down.
Official AWS references
- Amazon GuardDuty now supports centralized management using AWS Organizations declarative policies — the announcement and Region availability
- Amazon GuardDuty policy syntax and examples — the replacement rule, the unmanaged state, and the four worked examples
- Amazon GuardDuty policies — feature mapping, inheritance, detachment behaviour, delegated administrator and prerequisites
- Declarative policies in AWS Organizations — control-plane enforcement, the comparison against SCPs and RCPs, and the general detach rule
Comments