UnauthorizedOperation" - the automation role can't stop instances in the prod account
Diagnoses why an automation role gets UnauthorizedOperation on EC2 stop calls in a locked-down account. Use it when a cost agent cannot stop instances in prod; the key trigger is a missing allow, an SCP explicit deny, or a permission boundary, so confirm the real caller identity first with sts and simulate the policy.
TL;DR
Confirm which identity the code actually assumes - half of these are the wrong role or a silently failed assumption. Then simulate the stop call: a missing allow is fixed in the role policy, but an explicit deny from an SCP or permission boundary must be carved out at that layer. Scope the fix to the prod account's instances with a tag condition.
UnauthorizedOperationSteps
Log the real caller identity from inside the automation:
aws sts get-caller-identity. Expected: the role ARN you expect - if it is not, fix the assumption chain before touching any policy.Simulate the call:
aws iam simulate-principal-policy --policy-source-arn [ROLE_ARN] --action-names ec2:StopInstances --resource-arns [INSTANCE_ARN]. Expected: allowed, or an explicit deny naming the policy.If the identity policy allows it but the call still fails, check the SCPs on the OU and the permission boundary on the role - explicit denies win over allows. Expected: the denying policy is identified.
Fix at the right layer: add the missing allow scoped to the prod account's instance ARNs with a tag condition like cost-optimizer-managed, or ask the org admin for an SCP exception. Expected: the simulation returns allowed with no deny.
Re-run one stop in dry-run mode, then live on a single canary instance, before unleashing the full sweep. Expected: dry run passes, canary stops, full run is safe to schedule.
Use this when
- A cost agent gets UnauthorizedOperation stopping instances in prod.
- The same script works in dev but fails in the prod account.
- You need to prove to security exactly which policy denies the action.
Not for this skill when
- The loop gets InvalidInstanceID.NotFound - that is stale inventory.
- A dry run returns DryRunOperation - that is the success signal, not a denial.
Variant phrasings
- EC2 UnauthorizedOperation stop instances automation role
- IAM role cannot stop EC2 instances cross account
- SCP denies ec2 StopInstances
Why it happens
Prod accounts sit under SCPs and tight permission boundaries that dev-tested policies never encountered. The role also frequently is not the role you think: a failed cross-account assumption silently falls back to whatever credentials the environment had, and every subsequent call fails.
Edge cases
- Condition keys like aws:PrincipalTag or ec2:ResourceTag can deny silently - simulate with the context keys the real call carries.
- Cross-account stopping needs the trust policy on the prod role too, not just the permission on the calling side.
- DryRun=true on the stop call is a free permissions check: DryRunOperation means allowed, UnauthorizedOperation means denied.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_S2pJoILIPAyYcPUNX0Yr7A