The AWS Foundation Most Teams Skip and How to Get It Right the First Time

Cloud

Almost every AWS environment I’m brought in to fix started the same way: one account, one root login, a handful of IAM users with access keys that never expire, and dev, staging, and production all sharing the same blast radius. It works until it doesn’t. Then it’s a leaked key or a surprise bill or an auditor asking a question nobody can answer, and the foundation everyone planned to clean up later turns out to be holding the building up.

I’ve spent 25+ years building and untangling infrastructure. The pattern is consistent. Doing the foundation right costs a few days up front. Skipping it costs nothing at all, right until it costs everything at once.

AWS tells you how to avoid this. It’s called the Well-Architected Framework: six pillars covering operational excellence, security, reliability, performance efficiency, cost, and sustainability. The advice isn’t wrong. The problem is that it’s another checklist you nod along to and never operationalize. So this post is about the concrete version. The specific setup I stand up for a new client, and why each piece earns its place.

I’ll use a recent engagement as the example: a small team, a straightforward web app with front and backend components, no real AWS footprint yet. Exactly the situation where people assume they’re too small for any of this. They’re not. Small is when it’s cheap.

Start with the boundaries, not the servers

The decision that matters most in an AWS environment is how many accounts you use and where the walls between them go. Most teams start with one account because it’s the default. It’s also the most expensive default in AWS.

I set up a multi-account organization instead, using AWS Control Tower to provision it. Control Tower gives you a governed landing zone, an AWS Organization with logging, guardrails, and identity wired up as a baseline, in about an hour. Inside it, each environment and each shared responsibility gets its own account.

Two of those accounts you don’t create. Control Tower hands them to you, and they’re the backbone of the whole thing.

Log Archive is the write-once record of everything that happened: organization-wide audit trails and configuration history, landing in an account nobody logs into day to day. Retention is a storage-lifecycle question rather than an architecture question. Logs age from hot storage to deep archive on a schedule you set, replicate to a second region, and expire when your obligation ends. One destination, tiered by policy.

Audit is where security tooling lives and sees across every account at once: threat detection, vulnerability scanning, findings aggregation, and read-only access for anyone who needs to inspect the organization without the power to change it.

The rest you create yourself, one per concern.

Production, staging, and QA each live in separate accounts with different VPCs. A mistake in QA cannot touch production data. There’s no “oops, wrong environment,” because the wall is an account and network boundary rather than a naming convention.

Networking owns the VPCs, subnets, routing, and DNS in one place, sharing them out to the workload accounts through Resource Access Manager.

Tooling centralizes operational monitoring, dashboards, and alerting, and it hosts the deployment pipeline that pushes to production. Note what isn’t there: security detection lives in Audit. Keeping those apart matters, because the people investigating security findings shouldn’t also be the people who can turn off the alarms.

A sandbox gives engineers somewhere to experiment without anyone holding their breath.

Backups get their own decision. Most of the time they belong right alongside the thing they protect: same account, short restore path, copied to a second region for disaster recovery. But when backups need to survive a compromise of the workload account itself, whether that’s ransomware, a malicious insider, or a regulator asking for provably immutable retention, they move into a dedicated account with a vault lock nobody can override. Not the account admin, not the organization’s owner, not even root. That guarantee works precisely because the vault sits behind a different account boundary. Most small teams don’t need it on day one. The ones who do usually find out the hard way, which is why I’d rather decide it on purpose than by default.

None of this is complexity for its own sake. It’s how you get the security and reliability pillars close to free: isolation by default, and a clear home for every concern. Within each account the application and database tiers sit in private subnets, and the only thing facing the internet is a load balancer behind a web application firewall.

One note that matters for reusing this as a template. I describe the app and database tiers generically on purpose, because the framework specifies boundaries rather than services. The application might run on EC2 behind an auto-scaling group, or on ECS, Fargate, or Kubernetes. The database might be RDS or Aurora, or a self-managed engine on an instance because of a licensing constraint or a version AWS doesn’t offer. The account and network boundaries don’t change. Only what runs inside them does.

That choice isn’t cost-free, though, and I’d rather clients make it with open eyes. Managed services move the failover and patching burden onto AWS instead of your on-call rotation, which is the reliability and operational excellence pillars quietly paying you back. Run your own database on an instance and you own that work. Sometimes that’s the right call. Just plan for it deliberately rather than discovering it at 3am during your first failover.

The account you should almost never log into

Control Tower’s organization has a management account at its root. It owns billing for everything and the ability to set organization-wide policy. It’s the most powerful account you have, which is exactly why you should run nothing in it: no applications, no databases, no day-to-day users. Its blast radius is the entire organization, so a compromise there is a compromise of everything.

Treating your most powerful account as off-limits for normal work is the security pillar in one sentence. It also sets up the next decision, which I see done wrong more than any other.

Stop creating IAM users for people

If you take one thing from this post: don’t create IAM users for humans.

IAM users mean long-lived access keys. Keys that don’t expire, get copied into config files, committed to git, and pasted into scripts. Every one is a standing liability, and managing them across seven or eight accounts is a job nobody wants and everybody does badly.

The right answer is AWS IAM Identity Center, the service that used to be branded AWS SSO. One person, one sign-in, and they see exactly the accounts and roles they’re entitled to through a single portal, with multi-factor authentication and short-lived credentials that expire on their own. Nothing permanent to leak.

Access is granted as groups mapped to accounts mapped to permission sets. You manage group membership rather than a sprawl of per-account permissions. My starting set is deliberately small and opinionated:

  • Read-only for everyone by default. Easier to grant more than to claw back.
  • Power-user access for developers, but only in QA, staging, and sandbox.
  • Read-only in production, with administrative access reserved as a monitored break-glass path. Production changes flow through a pipeline, not through someone’s console session at 11pm.
  • Billing access for finance, in the management account only.
  • Security-audit access for reviewers, scoped to look but not touch.

That’s least privilege and separation of duties made real. Not principles on a slide, but five groups you can explain to a new hire in five minutes. When the team outgrows the built-in directory, the same structure federates cleanly to Okta, Entra ID, or Google without redesigning access.

Lock the front door: root, billing, and guardrails

Three more things separate a foundation that holds up from one that merely looks tidy.

The root user. Every account has one, the original email login that no permission policy can constrain. So you constrain it physically: hardware multi-factor authentication, no access keys at all, credentials in a manager the organization controls, and an alert that fires the instant anyone logs in as root. In a healthy environment that alert should almost never go off, which is what makes it worth having.

Billing, before the surprise. Consolidated billing rolls everything up to the management account, and that’s where the financial guardrails go: budgets with alerts at sensible thresholds, plus anomaly detection to catch the runaway resource a fixed budget would miss. The sandbox instance someone forgot. The misconfigured autoscaler. This is the cost optimization pillar, and it’s the one clients thank me for most, usually right after the first month a forgotten resource didn’t quietly cost them four figures.

Guardrails. Control Tower lets you apply organization-wide rules that even account administrators can’t override: you can’t disable audit logging, you can only deploy in these regions, instances must use the secure metadata endpoint. Some prevent bad actions outright, others detect and report drift after the fact. Together they mean your security posture doesn’t depend on everyone remembering to do the right thing.

Turn on the detection layer, and be honest about what it costs

Guardrails stop the things you anticipated. Detection catches the things you didn’t. From the Audit account I enable threat detection, vulnerability scanning for instances and container images, external-access analysis, and a findings aggregator that pulls it all into one console, with auto-enrollment so an account created six months from now is covered without anyone remembering to opt it in.

Two things I’ll say here that vendors usually don’t.

First, this is not free. Some of it genuinely is, and external access analysis costs nothing and should be on everywhere. But most of it is usage-priced: a few dollars a month at small scale, which is a bargain, though still a real line item rather than a rounding error. Configuration recording in particular will cost more than you expect if you record every resource type in every account, which is the default nobody revisits. I’d rather put a number in the budget on day one than explain a surprise later.

Second, turning everything on is not the goal. Some services in this space are built for teams with someone triaging alerts every morning, or for environments holding regulated data. If that’s not the client, those services generate findings nobody reads, which is worse than not having them, because it manufactures the appearance of coverage. I leave them off, write down why, and note what would change the answer. A decision you deferred on purpose and can point to is part of your security posture. One you never examined is a gap with a dashboard in front of it.

Why small is the best time to do this

People get this part backwards. They assume the setup is for big companies and that a small team should keep things simple and revisit them once they scale.

But retrofitting account boundaries onto a running production system is genuinely hard and genuinely risky. Doing it on day one, when the accounts are empty and there’s nothing to migrate, takes a couple of days and costs almost nothing. The framework above is the simple option in the ways that matter: fewer 2am incidents, fewer credential-rotation fire drills, and a much shorter answer when someone asks who can touch production.

The Well-Architected Framework isn’t a certification to chase or a checklist to file. It’s a set of decisions best made once, early, on purpose. Make them then and the cleanup project never has to exist.


This is the approach I bring to new AWS engagements: a governed foundation right-sized for where a team is today and built to hold as it grows. If you’re standing up a new environment, or you’ve inherited one that’s showing its seams, I’m happy to talk through what a solid foundation looks like for your situation.