The Three Zeros: Why AWS Keeps Deleting the Excuse for Permanent Access
On November 19, 2025, AWS published two "What's New" posts on the same day. One was aws login, a CLI command that turns a browser-based Console sign-in into short-lived credentials. The other was Regional NAT Gateway, a mode that lets a NAT Gateway exist without a public subnet to host it. I didn't notice the coincidence until after I'd already written a deep-dive on each one separately, plus a third piece on getting IAM users down to zero. Lined up next to each other, the three stopped looking like unrelated features and started looking like the same idea, applied three times.
The pattern: a permanent resource that only existed to anchor something temporary
Look at what each "before" state actually was:
- An access key is permanent. It exists because a developer's laptop, or a root user, needed some credential to call the API with, and a login session doesn't normally travel outside a browser.
- A public subnet is permanent, as a route-table configuration, not just a CIDR block. It exists because a NAT Gateway is an ENI, and an ENI has to sit inside some subnet, and that subnet's route table needs a path to an Internet Gateway.
- An IAM user is permanent. It exists because a human without a corporate directory, a CI/CD job, or a server outside AWS isn't natively an IAM principal — something has to hold long-lived credentials on their behalf.
In every case, the permanent resource was never really the thing anyone wanted. It's a stand-in, holding a place for something genuinely temporary: a login session, a NAT Gateway's uplink, an external identity. AWS's answer, worked out three separate times by three separate teams, is the same move — give the temporary thing its own native attachment point, and the permanent stand-in stops being necessary.
That's the throughline for the rest of this post. I've already written a full hands-on deep-dive on each of the three; this is the synthesis, not a rehash, so each section below covers only what's new about the pattern and links out for the verification detail.
Zero #1 — Access keys, replaced by aws login
The stand-in was an access key sitting in ~/.aws/credentials, created once and never expiring on its own. aws login (AWS CLI ≥ 2.32.0) replaces it by letting your existing Console sign-in — root, IAM user, or federation — mint short-lived CLI/SDK credentials through a browser OAuth 2.0 + PKCE flow, auto-refreshed every 15 minutes, hard-capped at 12 hours.
What I didn't expect going in: it's not a universal replacement. It's explicitly not for IAM Identity Center users (aws sso login stays the answer there), and it's fundamentally interactive — every flow, including --remote, ends with a human in a browser, so it changes nothing for CI/CD. The zero is real, but it's scoped to one specific anchor: the local-dev and root access key.
→ Full can/can't breakdown: aws login: What AWS CLI's New Console-Credential Command Can (and Can't) Do
Zero #2 — Public subnets, replaced by Regional NAT Gateway
The stand-in was a subnet whose route table pointed 0.0.0.0/0 at an Internet Gateway — the only reason it existed was to give a NAT Gateway's ENI somewhere to live. Regional NAT Gateway removes the subnet from that job entirely: you attach an Internet Gateway to the VPC and nothing else, create the NAT Gateway with --availability-mode regional and no --subnet-id, and AWS auto-generates a route table edge-associated to the NAT Gateway itself, carrying the 0.0.0.0/0 → igw-... route that used to live on a public subnet.
I verified this wasn't just a console illusion by querying the API directly: after building a VPC with two private-only subnets (MapPublicIpOnLaunch=false, no IGW route anywhere near them), a script that counts route tables which are both subnet-associated and carry an IGW-bound default route returned:
public_subnet_count=0
I also watched the auto-expansion behavior live: adding an EC2 instance in a second AZ triggered the NAT Gateway to provision EIPs in every AZ with a workload ENI within about 22 minutes (docs say up to 60), and roughly an hour later it auto-contracted the EIP in the AZ that never got a workload. The anchor moved from "a subnet" to "the VPC," and the routing followed automatically.
The exceptions here are concrete, not vague: Private NAT (VPC-to-VPC) isn't supported in regional mode, and migrating an existing zonal NAT setup involves a connection reset, so it's not a drop-in swap for a live workload.
→ Full verification log with commands and timestamps: Can You Really Run a VPC with Zero Public Subnets Using Regional NAT Gateway?
Zero #3 — IAM users, replaced by three different anchors for three different callers
This one's the odd case out — not shipped in November 2025, but the pattern is identical, and it's honestly the clearest illustration of it, because it needs three separate replacements instead of one:
| Who needed a stand-in identity | Old anchor | New anchor |
|---|---|---|
| A human logging into the Console/CLI | IAM user + access key | IAM Identity Center federation, or aws login
|
| A CI/CD job (GitHub Actions, Vercel) | A dedicated "deploy" IAM user | OIDC Federation — the CI provider's OIDC token lets the job assume a role directly |
| A server outside AWS (on-prem, another cloud) | An IAM user's access key baked into config | IAM Roles Anywhere, using an X.509 certificate to assume a role |
I've run all three in production, not just on paper: nico-comment-app and marp-ai-app both reach Lambda and S3 from Vercel with nothing but AWS_ROLE_ARN and OIDC Federation — no stored key at all.
I also hit the exception in production: wallet-agent combines Vercel, AWS, and Privy (a third-party signing service), and Privy's signer integration doesn't support OIDC. The Vercel connection for that one project still runs on an IAM user with a real access key. Once a third party outside your own architecture doesn't speak OIDC or certificates, the permanent anchor comes back — not because the design failed, but because the constraint isn't yours to remove.
→ Full four-stage walkthrough: Is a "Zero IAM Users" AWS Setup Actually Possible?
None of the three is actually zero — and that's the point
| Zero | What's genuinely gone | What's still there |
|---|---|---|
| Access keys | Local-dev and root access keys | IAM Identity Center users, CI/CD, anything unattended |
| Public subnets | Subnets whose only job was hosting a NAT Gateway | Private NAT (VPC-to-VPC), live migrations |
| IAM users | Users backing human logins, CI/CD, on-prem servers | Any third-party integration that can't speak OIDC or certificates |
Put together, this is less "AWS eliminated three things" and more "AWS identified, three times, exactly which permanent resource was standing in for a temporary need, and gave that need its own native, expiring attachment point instead." The remaining exception in each case isn't a flaw in the feature — it's the boundary of what AWS's own architecture can reach. Past that boundary sits a third party's integration, a workload pattern the feature doesn't cover yet, or an org that hasn't adopted the replacement.
The useful discipline isn't chasing a literal zero. It's knowing exactly where your own remaining anchor is, why it's there, and whether it's still actually necessary — which, in practice, is a shorter and more honest list than "zero" implies.
References
- AWS enables developers to use console credentials for AWS CLI and SDK authentication (Nov 19, 2025)
- AWS NAT Gateway now supports regional availability (Nov 19, 2025)
- Introducing Amazon VPC Regional NAT Gateway — AWS Networking & Content Delivery Blog
- Simplified developer access to AWS with 'aws login' — AWS Security Blog
This post is a synthesis of three hands-on deep-dives I wrote separately on aws login, Regional NAT Gateway, and eliminating IAM users. Each linked section points to the full verification behind it.



Top comments (0)