I run personal AWS projects for under $5 a month. My coding agent regularly reaches for a NAT Gateway ($32/month), an ALB ($16/month), and a customer-managed KMS key ($1/month). Three reasonable enterprise choices would cost ten times my entire budget before serving a request. The agent is doing what it was designed to do. So I encoded my personal cost constraints into the AWS permission boundary.
Coding Agents Make Expensive Defaults More Likely #
Risk is a function of likelihood and impact. AWS cost controls traditionally addressed an attacker spinning up crypto miners with stolen credentials, or an engineer forgetting to stop a development environment over the weekend. Budget alerts surface the first problem. A scheduled off-switch helps with the second; I described one in An Off-Switch for NAT Gateway and ALB.
Coding agents add a different path to the same bill. The credentials are valid, the infrastructure works, and each choice may look reasonable in isolation. It is simply designed for a company rather than a personal account. Because agents propose and deploy infrastructure throughout a development session, expensive defaults get many more opportunities to reach AWS.
Personal AWS Guardrails addresses both sides of the risk equation:
- Prevention rejects resource patterns that do not fit the cost model.
- Containment blocks further spend paths after a budget threshold.
- Remediation stops supported resources that are already running.
flowchart TB
Agent[Human or coding agent] --> API[AWS API request]
subgraph Prevention[1 · Prevention]
direction TB
Root[Org-root SCPs
universal boundaries] --> OU[OU SCP
opinionated boundaries]
end
API --> Root
OU -->|allowed| Resource[Workload resource]
OU -->|explicit deny| Never[Resource never exists]
Resource -. spend observed .-> Budget
subgraph Containment[2 · Containment]
direction TB
Budget[AWS Budget threshold] --> Quarantine[Quarantine SCP
blocks new spend paths]
end
Quarantine --> Event
subgraph Remediation[3 · Remediation]
direction TB
Event[EventBridge] --> SF[Regional Step Functions]
SF --> ECS[ECS services → 0]
SF --> ASG[ASGs → 0]
SF --> EC2[Standalone EC2 → stopped]
SF --> EB[EventBridge rules → disabled]
SF --> LM[Lambda concurrency → 0]
end
The first layer should handle routine agent proposals. The remaining layers exist because a permitted resource can still consume far more than expected.
Put the $5 Budget Into the Permission Boundary #
Administrator access in a workload account does not need to include every architecture AWS supports. The useful model here is broad permissions inside narrow boundaries: let the agent create and update complete applications, but remove options that have no place in this particular environment.
Deny Whole Categories You Never Intend to Use #
The organization-root policies start with a deny-unless-permitted service list. If a service namespace is not listed, it cannot be used in a member account. RDS, EKS, ElastiCache, OpenSearch, Kinesis, managed Kafka, and dozens of other services stay unavailable because my projects do not need them.


The same outer boundary limits workloads to five selected regions, blocks reserved capacity and Dedicated Hosts, and prevents long-lived workload credentials. Each region can be narrowed further to a single purpose: one region kept for CloudFront’s global certificates, another reserved for Bedrock, and a departing region locked to cleanup only. These controls remove entire categories of cost and security exposure before the policy needs to consider individual resources.
This layer is deliberately separate from my personal architecture choices. The root policies contain the reusable outer boundary. A second policy at the OU level contains choices another engineer may reasonably reject. That separation lets you keep the credential and commitment controls while replacing my service list and cost opinions.
Remove Expensive Options From Services You Do Use #
Allowed services still contain options that do not fit a $5 budget. I need VPCs, but I do not need NAT Gateways, Transit Gateways, Client VPN endpoints, or Elastic IP addresses. For suitable projects, I would rather experiment with IPv6-only subnets and an Egress-Only Internet Gateway than pay a fixed IPv4 egress charge.
I need EC2 occasionally, but nano, micro, and small instances cover my use cases. I need encryption, but AWS-managed keys provide the service integration I need without another monthly line item. Bedrock stays available for on-demand inference while provisioned throughput and model customization jobs are blocked. Lambda functions stay available while provisioned concurrency is not.
Some controls target costs that are easy to overlook. A CloudWatch dashboard costs $3/month, so the policy requires CreatedBy=manual on the request. That leaves an intentional escape hatch for me while rejecting an agent’s routine monitoring add-on. Provisioned IOPS volumes are blocked in favor of gp3. S3 Transfer Acceleration is blocked because I would choose CloudFront for these workloads.
These are not universal AWS recommendations. They encode how I build personal serverless, event-driven, and AI projects. The OU policy keeps the disagreement visible and easy to customize.
Turn Denied API Calls Into Design Feedback #
The value appears when a deployment meets the boundary. Suppose an agent adds a customer-managed key for a secret:
$ aws kms create-key
AccessDeniedException: explicit deny in a service control policy
Nothing was created, nothing needs cleanup, and the error gives the agent a concrete constraint. It can use the service’s AWS-managed key, redesign the resource, or ask for an exception. The same feedback applies when it proposes a NAT Gateway or an oversized EC2 instance.
This is more useful than hoping a cost instruction remains in context throughout a long session. The budget is part of the execution environment. A deployment cannot quietly ignore it.
Quarantine What the Permission Boundary Cannot Predict #
Static policies can reject known configurations, but they cannot price runtime behavior. Lambda is a good example. A small function running for 100 milliseconds at 256MB looks harmless. Lambda also allows 10GB of memory, which provides up to 6 vCPUs, and a typical regional concurrency quota is 1,000. Now let the function publish an event that matches an overly broad EventBridge rule and invokes the same function again. That accidental feedback loop can saturate concurrency and accrue charges in dollars per minute, even though every API request and resource configuration is valid.
The project therefore separates the spending target, the normal budget, and the emergency threshold. I aim to stay below $5 in an ordinary month. The normal budget is $20, which leaves room for irregular charges such as Route 53 domain renewals. At $50, spend is far enough outside normal that I prefer downtime while I investigate. The defaults quarantine the Test account at $50 forecasted spend and Prod at $50 actual spend; change those values to match your own tolerance.
When the threshold is crossed, a Budget action attaches a quarantine SCP that freezes the workload account. The policy denies everything except a short list of investigation and remediation actions: read CloudWatch, Cost Explorer, CloudFormation, and resource inventory; contact Support; stop or scale down supported compute. There is no data-plane access, no resource creation, and no new invocations. Because lambda:InvokeFunction is not exempted, the quarantine closes the Lambda invocation path for principals governed by the SCP.
Regional Step Functions state machines then assume a protected cross-account role and actively reduce supported compute: ECS services go to a desired count of zero, Auto Scaling groups go to a desired capacity of zero, standalone EC2 instances stop, EventBridge rules and schedules are disabled, and Lambda function concurrency is set to zero. The workflow uses EventBridge, Step Functions AWS SDK integrations, and JSONata, with no Lambda functions in the remediation path.
Test the Emergency Shutdown Before You Need It #
An emergency control is only useful if you have exercised it. The included integration test creates a temporary Fargate service, a nano-sized Auto Scaling group, and a standalone nano EC2 instance. It invokes the state machine and verifies the result:
ECS service desired count: 0 ✓
ASG desired capacity: 0 ✓
Standalone EC2 state: stopping ✓
State machine: SUCCEEDEDThe test does not need to wait for delayed billing data or trigger a real quarantine. A separate full-path test can execute the Budget action, attach the SCP, and start remediation in all five regions.
Understand What the Guardrails Cannot Stop #
AWS Budgets works with delayed billing data, so $50 is a trigger rather than a spending cap. The Lambda loop can run until billing data reaches the threshold and the quarantine attaches. Even then, EventBridge invokes Lambda through a resource-based policy on the function rather than through an IAM role in the account, so the quarantine SCP alone cannot block that path. The remediation workflow addresses this by disabling EventBridge rules and schedules, then setting reserved concurrency to zero on all functions. Existing storage and snapshots also keep billing until you clean them up.
SCPs also do not apply to the Organizations management account. That account remains the recovery path, so its identities need tighter protection than the workload accounts. This project reduces the likelihood and impact of a cost event. It does not make an AWS bill mathematically bounded.
Start With Your Own Cost Model #
Begin with four decisions: your normal monthly budget, the AWS services and regions you actually use, the recurring costs you reject, and the amount at which downtime becomes preferable to continued spend. Those decisions are the inputs to the guardrails.
Then point your preferred coding agent at the repository and your current AWS inventory. Ask it to propose changes to the root allowlist, the opinionated OU policy, and the quarantine thresholds, with a reason for every addition and removal. Have it produce a reviewable change set rather than deploying immediately.
Create the policies detached and inspect the rendered documents. Attach them to the Test OU, deploy a representative workload, and run the remediation test. Move the boundaries to Prod only after your normal create, update, and delete paths still work. The agent can do much of the inventory and customization; you decide which architectures the account will accept.
View the full source code on GitHubReferences #
- AWS Organizations Service Control Policies: how SCPs work, evaluation logic, and limits
- AWS Budgets actions: automatic responses when a budget threshold is crossed
- Amazon VPC pricing: NAT Gateway hourly and data-processing rates
- AWS KMS pricing: customer-managed key monthly charges
- Lambda quotas: memory, CPU, and regional concurrency limits
- Step Functions AWS SDK integrations: calling AWS APIs directly from state machines