Hardening policies: the basics
What does it mean for a policy to be scoped on purpose?
Policies are where AWS decides who can do what. This capability is about three places that policy goes wrong by default or by habit: an IAM policy that allows every action on every resource, a CloudFormation stack that deploys with the deployer's broad credentials instead of a scoped service role, and an EventBridge custom bus with no resource-based policy governing who may publish to it. Different services, different policy types, one theme: permissions granted broadly because it was faster, or left implicit because nobody decided, when they should be deliberate and minimal.
AWS Security Hub turns each of these into its own control. IAM.1 fails any customer-managed policy whose default version allows Action * on Resource *. CloudFormation.4 fails a stack with no RoleARN, meaning it runs with whatever identity triggered the deploy. EventBridge.3 fails a custom event bus that has no resource-based policy attached, leaving access governed only by callers' IAM. They read as separate findings, but they are one capability: scope the grant to exactly what is needed, and write down who is allowed in.
These almost never reflect what the principal or resource actually needs. A full-admin policy is usually AdministratorAccess copied into a custom policy to unblock a deploy; a roleless stack inherits a CI role with iam:* because nobody attached a scoped one; an unpolicied bus is a default-allow-within-the-account that nobody decided. The job is to find every over-broad or implicit grant, derive what is genuinely required, replace the grant with a scoped one, and put a guardrail so the broad path requires a deliberate, reviewed exception.
In this lesson you will learn how AWS expresses over-broad and implicit grants across IAM, CloudFormation and EventBridge, how to find every one in an account, and how to scope them down without breaking the deploys, principals or integrations that depend on them. The Controls this lesson covers section lists every Security Hub control in this capability, each linking to a deep page with the exact check and a copy-and-paste fix.
The weekend an admin key cost five figures
A startup committed an IAM access key to a public repository on a Friday afternoon. The key belonged to a service account whose custom policy granted Action * on Resource *, written that way months earlier to just make the CI pipeline work. Automated scanners that crawl repositories for AWS keys found it within minutes, and by Monday the attacker had launched hundreds of GPU instances across every Region to mine cryptocurrency, running up a five-figure bill before the billing alarm fired. A scoped policy granting only the dozen actions the pipeline actually used would have made the same leaked key nearly worthless: the attacker could have pushed to one repository and nothing else.
Finding over-broad and implicit grants across an estate
Devon runs cloud security at a Series B company. Security Hub shows policy-hardening failures across IAM, CloudFormation and EventBridge, and the most dangerous is an IAM.1 finding on a customer-managed policy attached to the role the CI pipeline assumes on every merge.
Rather than work the findings one by one, he starts with the over-broad grant most likely to leak, the CI deploy policy, by reading its default version to confirm the allow-all statement before he derives a scoped replacement.
Read the default version of the suspect customer-managed policy. An allow-all-actions-on-all-resources statement is exactly the IAM.1 failure.
A wildcard action on a wildcard resource is the worst case. Derive the real footprint before replacing it so the deploy keeps working.
How AWS evaluates these policiesdeep dive
Each control checks a different policy surface. IAM.1, backed by the Config rule iam-policy-no-statements-with-admin-access, inspects only the default version of customer-managed policies and fails on a statement with Effect Allow, Action *, and Resource *. It deliberately ignores inline policies, AWS-managed policies and permission boundaries, so a full-admin grant can hide inline and still pass; audit those separately. CloudFormation.4 fails a stack whose RoleARN is null, because without a service role the stack inherits the deployer's identity for every API call it makes. EventBridge.3 fails a custom event bus with no resource-based policy, which is a default-allow within the account, not a locked door.
Scoping correctly means deriving the real need, not guessing. IAM Access Analyzer can generate a least-privilege policy from a role's CloudTrail history, and the service-last-accessed details show which services a principal has actually used; together they turn replace the wildcard from a risky guess into a data-backed exercise. A CloudFormation service role's trust policy must name cloudformation.amazonaws.com and only that, so no human or other service can assume it; its permissions policy then becomes the stack's exact blast radius. An event-bus policy should scope to a specific principal, the bus ARN, and conditions like aws:PrincipalOrgID rather than a bare wildcard.
The durable version of this capability is preventive. Replace the over-broad grant as a new policy version, verify, then delete the old one, but back it with guardrails: a permission boundary that caps what roles can grant, a Service Control Policy that denies creating full-admin policies or denies roleless CreateStack in production, an IaC default that attaches a bus policy at creation. Detective controls catch drift; the guardrails stop the broad or implicit grant shipping in the first place.
What is the impact of leaving these policies broad?
The direct impact is blast radius. A credential carrying Action * on Resource * can read or delete every resource, create new admin users for persistence and disable the logging that would reveal it; once it leaks, the worst case and the actual case are the same. A stack deploying with the deployer's broad credentials means a poisoned template or a hijacked CI runner can do anything the deployer could, including creating back-door admin access. An unpolicied event bus accepts events from any same-account principal, so an unexpected publisher can set off real workflows on input you never intended to accept.
The second-order impact is auditability. With a scoped service role, every API call CloudFormation makes is attributed to a named role in CloudTrail; without one, a pipeline deploy is indistinguishable from the human whose role it borrowed, and forensics gets significantly harder. The same logic runs through the capability: a policy with a named principal implies a named owner who made a deliberate decision, while a wildcard or a null policy implies nobody owns the access question.
On the compliance side, least privilege is an explicit requirement in CIS, PCI DSS and NIST 800-53, and these controls are direct evidence of whether it holds. A standing failure shows up in audit evidence and customer security questionnaires and can stall a SOC 2 report, a PCI attestation or an enterprise deal whose close is gated on the prospect's security review. The fix is cheap; the deal delay or audit remediation from an open finding is not.
How do you harden these policies safely?
Work the capability as one loop rather than chasing individual findings. The order matters: derive the real need before you replace a grant, and verify before you delete the old one, so you do not break a deploy or a live integration.
1. Inventory the over-broad and implicit grants
Enumerate customer-managed policies whose default version is Action * on Resource *, CloudFormation stacks with a null RoleARN, and custom event buses with no resource-based policy. Rank by leak risk and blast radius: full-admin policies on CI, third-party or source-control-touching roles first, then IAM-managing and production roleless stacks, then unpolicied buses. Remember IAM.1 ignores inline grants, so audit those separately.
2. Derive what each grant actually needs
Do not guess the scope. Use IAM Access Analyzer policy generation against a principal's CloudTrail history and the service-last-accessed details to find the real footprint, usually a small set of actions on named resources. For a stack, read the template and map each resource type to its minimum permissions. For an event bus, answer who is supposed to publish, a specific account, an Organization, same-account services only, before writing anything; a bus nobody can answer for is an ownership problem, not a policy one.
3. Replace the grant, verify, then remove the old one
Create the scoped IAM policy as a new default version while keeping the old one as rollback, attach a scoped service role to the stack with update-stack --use-previous-template so there is no resource churn, and attach a least-privilege bus policy with put-permission. Exercise the principal, deploy or integration end-to-end, then delete the old wildcard version once it is confirmed working. Deleting prematurely risks an outage; leaving the old version risks someone reverting to it.
4. Prevent it shipping again
Closing existing grants is one-off; stopping new ones is the durable fix. Use a permission boundary to cap what roles can grant, a Service Control Policy to deny full-admin policy creation and roleless CreateStack or UpdateStack in production, and IaC defaults that attach a service role per stack and a resource policy per bus at creation. Keep the relevant AWS Config rules enabled. The goal is that a broad or implicit grant requires a deliberate, reviewed exception, never an accidental copy-paste.
# Replace a full-admin policy with a scoped version (keep the old one as rollback, then delete).
aws iam create-policy-version \
--policy-arn arn:aws:iam::111122223333:policy/ci-deploy-policy \
--policy-document file://ci-deploy-scoped.json --set-as-default
# ... verify a staging and a prod run, then ...
aws iam delete-policy-version \
--policy-arn arn:aws:iam::111122223333:policy/ci-deploy-policy --version-id v3
# Attach a scoped service role to a CloudFormation stack with no resource churn.
aws cloudformation update-stack --stack-name payments-iam-prod \
--use-previous-template \
--role-arn arn:aws:iam::111122223333:role/cfn-payments-iam-deployer \
--capabilities CAPABILITY_NAMED_IAM
# Attach a least-privilege resource policy to a custom event bus (one named account).
aws events put-permission --event-bus-name orders-bus \
--statement-id AllowPartner444455556666 --action events:PutEvents --principal 444455556666 Quick quiz
Question 1 of 5Security Hub shows policy failures across IAM.1, CloudFormation.4 and EventBridge.3. What is the most efficient way to think about them?
You scored
0 / 5
Keep learning
Go deeper on least privilege and the policy surfaces this capability hardens.
- IAM Access Analyzer policy generation Generate a least-privilege policy from a principal's actual CloudTrail activity, the safe way to scope a wildcard policy down.
- CloudFormation IAM service roles The service-role pattern, trust policies, and how RoleARN behaves across stack operations.
- Using resource-based policies for Amazon EventBridge How to scope who may publish to a custom event bus, including the put-permission API and condition keys.
You can now treat policy hardening as one capability rather than a scatter of findings: inventory the over-broad and implicit grants across IAM, CloudFormation and EventBridge, derive what each one actually needs, replace and verify before deleting the old grant, and enforce least privilege at creation with permission boundaries, Service Control Policies and IaC defaults. The Controls this lesson covers section below links every control in this group to its deep page and fix.
Back to the library