Building Your AWS Landing Zone with AWS Organizations, SCPs & Identity Center


Table of Contents
Jump to a section
Setting up AWS for enterprise use is a mess. Or even for any solo developer that wants to use AWS in some kind of professional way.
You start with one account, add another for staging, then production needs its own account. Suddenly you're juggling IAM users, access keys, and policies across five different accounts. Everyone "shares" the dev environment because setting up proper permissions takes too long.
I was not only once in the position where somebody started with everything in one account and tried to manage permissions with resource name prefixes.
You know, dev-database-server
and prod-database-server
living in the same account.
When we finally decided to migrate to separate accounts, we knew it would be a pain. Production was already running (= customers were using the system). We couldn't just shut everything down and rebuild it. The migration is quickly taking weeks or months. We had to build a parallel infrastructure in new accounts while keeping the old stuff running.
The lesson: it's much harder to fix a broken AWS setup than to start with a proper one.
In this article, I'll show you how to build a proper AWS landing zone that actually works. Not overcomplicated, but still secure, highly customizable and easy to maintain. This setup will take you some time to implement properly, but it'll save you months of migration headaches later.
And you'll learn the key concepts of AWS Organizations, Service Control Policies and Identity Center. And how they work together to build a secure and scalable AWS environment.
No fluff, no BS, just the facts. π€
What You'll Learn
- Why AWS Organizations is the foundation you need
- How Service Control Policies give you a lot of control and safe guards
- Setting up SSO and Identity Center for sane user management
- How these services work together (the part everyone misses)
- A step-by-step approach to get you started

AWS Lambda on One Page (No Fluff)
Skip the 300-page docs. Our Lambda cheat sheet covers everything from cold starts to concurrency limits - the stuff we actually use daily.
HD quality, print-friendly. Stick it next to your desk.
The Problem with Ad-Hoc AWS Setups
Most teams start with a single AWS account. Then they need staging, so either they put everything into the same account or they create another account. Production definitely needs its own account for security. Suddenly you have multiple accounts, each with...
- its own IAM users (and their credentials) π₯
- its own roles, policies and permissions π
- its own billing π°
Sound familiar? π
I've been there, not just once but multiple times. Not just in small startups (or my own indie projects), but also large enterprises that do lack fundamental AWS knowledge (or the will to do things right) to some extend.
Let's jump into how to do it right; directly from the beginning!
Part 1: AWS Organizations - Your Account Management Foundation
The most important part of the AWS Landing Zone is the AWS Organizations. It will help you to easily work with multiple accounts, users, permissions and services.
Let's start with the management account. The management account is the central account that is used to manage the organization. It's also the account that you use to create the organization. It has complete access to all AWS resources and services.
Creating an organization is easy. Just go to the AWS Organizations console and click on "Create Organization". You'll create the organization in the region that you've selected.
But don't worry. When you work with AWS Organizations youβre interacting with a global control planeβnot a region-specific service. Choosing a region really only affects the endpoint you use; the Organizations data (accounts, policies, invitations, etc.) lives globally.
As this account will be the organization's management account, it's important to keep it secure. Most importantly, use a strong password and enable MFA. At best, use a hardware key instead of your phone.
If you want a real-world walkthrough, we wrote up the whole in another detailed article: How to actually secure your AWS account with MFA.
π‘ You don't need to keep the member account's root credentials. The root user credentials for member accounts can be deleted/reset within the AWS Organizations console. This makes it easier to manage the accounts and reduces the risk of credentials being leaked.
Now that our organization's root is created, we can simply add member accounts. Not by registering new accounts, but by simply using the AWS Organizations console.
You can also invite existing accounts into the organization this way. The invitation will be sent to the account's root user's email address. The account will then be added to the organization and you can manage it like any other account!
When we have multiple accounts, we can create organizational units (OUs) to group them. This is useful to manage permissions and services in a more granular way.
For example, we can create an OU for production
and another for staging
.
OUs can also be used to group accounts by department, team, or any other criteria.
We can also have nested OUs, meaning we can organize them in a hierarchy.
Building It With Terraform
Here's how I actually implemented my own AWS Landing Zone using Terraform. Instead of clicking through the AWS console, I built a reusable module that sets everything up automatically.
# Create the organization with the features we need
resource "aws_organizations_organization" "main" {
aws_service_access_principals = [
"sso.amazonaws.com",
"account.amazonaws.com",
]
feature_set = "ALL"
enabled_policy_types = [
"SERVICE_CONTROL_POLICY",
]
}
# Create organizational units to group accounts
resource "aws_organizations_organizational_unit" "production" {
name = "Production"
parent_id = aws_organizations_organization.main.roots[0].id
}
resource "aws_organizations_organizational_unit" "development" {
name = "Development"
parent_id = aws_organizations_organization.main.roots[0].id
}
The key here is enabling "ALL"
features and specifically enabling Service Control Policies.
Without these, you can't use Identity Center or apply SCPs to control what people can do.
Then I create accounts programmatically:
resource "aws_organizations_account" "sandbox" {
close_on_deletion = false
create_govcloud = false
name = "sandbox"
email = "aws-sandbox@johndoe.com"
parent_id = aws_organizations_organizational_unit.development.id
}
resource "aws_organizations_account" "production" {
close_on_deletion = false
create_govcloud = false
name = "production"
email = "aws-production@johndoe.com"
parent_id = aws_organizations_organizational_unit.production.id
}
Pro tip: Use different email addresses for each account.
I use email aliases like aws-sandbox@johndoe.com
so all emails go to my main inbox but AWS treats them as separate accounts.
Set up email forwarding so all these account emails reach your inbox but AWS treats them as separate entities.
Instead of updating things manually in the console when you need changes, you just update your Terraform code and apply it. Need a new account? Add it to your code. Want to reorganize your OUs? Update the Terraform and run it.
The Complete Setup Process
Let me walk you through the entire process from start to finish. This is exactly how I set up organizations for my own projects:
- Create organization in your management account
- Invite or create member accounts
- Form organizational units to group accounts
- Create and test Service Control Policies with sandbox account first
- Apply SCPs to OUs
This process takes about a day if you're doing it manually, or a few hours with Terraform. The key is to go step by step and test everything before moving to the next step.
Part 2: Service Control Policies (SCPs) - The Guardrails You Actually Need
Here's something that will help you sleep better at night: knowing that even if someone gets admin access to your AWS accounts, they can't do the really dangerous stuff.
You've probably seen the horror stories online - developers waking up to $10k bills because someone spun up mining instances, or entire databases getting deleted because someone thought they were in the test environment.
SCPs are like having a security guard at the door of your AWS accounts. Even if someone has a VIP pass (admin permissions), the security guard can still say "nope, you're not doing that." They don't give permissions, they just block the scary stuff.
What SCPs Actually Do
SCPs are basically a filter. Whatever permissions someone has through IAM, SCPs can block specific actions. Even if you're the root user of an account, SCPs can stop you from doing stupid things.
Here's the thing: SCPs are deny-only. They can't give you permissions, only take them away. Your IAM policies grant access, SCPs restrict it.
For example:
- IAM says: "You can launch any EC2 instance"
- SCP says: "No EC2 instances at all"
- Result: You can't launch any EC2 instances, even with admin permissions
Creating Your First SCP
Let's create an SCP that blocks all EC2 instance launches. This makes sense if you're running serverless services and don't need EC2 at all.
- Go to AWS Organizations console
- Click "Policies" in the left menu
- Select "Service control policies"
- If SCPs aren't enabled yet, click "Enable service control policies"
Now create a new policy called "DenyAllEC2":
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAllEC2Instances",
"Effect": "Deny",
"Action": ["ec2:RunInstances"],
"Resource": "*"
}
]
}
This policy blocks anyone from launching any EC2 instances. Perfect if your entire setup runs on Lambda, containers, or other managed services.
Enforce MFA for Identity Center Users
Here's another useful SCP that enforces MFA for all Identity Center users. This means people can't access AWS without multi-factor authentication enabled.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAllExceptMultiFactorAuthenticated",
"Effect": "Deny",
"NotAction": [
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:GetUser",
"iam:ListMFADevices",
"iam:ListVirtualMFADevices",
"iam:ResyncMFADevice",
"sts:GetSessionToken"
],
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}
This forces everyone to use MFA before they can do anything meaningful in AWS. No more "I'll set up MFA later" excuses.
Applying SCPs to Your Organization
Once you've created an SCP, you need to attach it somewhere. You can attach SCPs to:
- The root of your organization (affects all accounts)
- Organizational units (affects all accounts in that OU)
- Individual accounts
I'd recommend starting with a single test account first. Create a throwaway account, attach your SCP, and see if it actually blocks what you expect. Once you're confident it works, apply it to your development OU.
Applying SCPs with Terraform
Here's how I manage SCPs in my Terraform setup. Instead of clicking through the console, you can define policies as code:
# Create the SCP policy
resource "aws_organizations_policy" "deny_ec2" {
name = "DenyAllEC2"
description = "Blocks EC2 instance launches"
type = "SERVICE_CONTROL_POLICY"
content = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "DenyAllEC2Instances"
Effect = "Deny"
Action = ["ec2:RunInstances"]
Resource = "*"
}]
})
}
# Attach it to your development OU
resource "aws_organizations_policy_attachment" "deny_ec2_dev" {
policy_id = aws_organizations_policy.deny_ec2.id
target_id = aws_organizations_organizational_unit.development.id
}
Pro tip: Use the jsonencode()
function instead of writing JSON as strings.
It's cleaner and Terraform validates the syntax for you.
For the MFA enforcement policy:
resource "aws_organizations_policy" "require_mfa" {
name = "RequireMFA"
description = "Forces MFA for all Identity Center users"
type = "SERVICE_CONTROL_POLICY"
content = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "DenyAllExceptMultiFactorAuthenticated"
Effect = "Deny"
NotAction = [
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:GetUser",
"iam:ListMFADevices",
"iam:ListVirtualMFADevices",
"iam:ResyncMFADevice",
"sts:GetSessionToken"
]
Resource = "*"
Condition = {
BoolIfExists = {
"aws:MultiFactorAuthPresent" = "false"
}
}
}]
})
}
# Apply to all accounts by attaching to root
resource "aws_organizations_policy_attachment" "require_mfa_root" {
policy_id = aws_organizations_policy.require_mfa.id
target_id = aws_organizations_organization.main.roots[0].id
}
When you manage SCPs with Terraform, you get version control for your policies.
You can see exactly when policies changed and who changed them.
Rollbacks become terraform apply
with the previous version.
Testing Your SCPs
Here's how to test if your SCP actually works:
- Log into a member account
- Try to do the thing your SCP should block
- You should get an access denied error
For our EC2 blocking policy, try launching any EC2 instance. You should see something like: "You are not authorized to perform this operation."
If you don't get an error, your SCP isn't working. Check that it's attached to the right OU or account.
Testing SCPs is painful because you can't debug them locally. You attach them, try something, get a cryptic error, modify the policy, wait for propagation, and try again. Rinse and repeat N times.
Now I always:
- Test SCPs in a throwaway account first
- Start with the most permissive policy and restrict gradually
- Test every single action I care about, both through the console and API
- Keep a collection of working SCP snippets for future reference!
SCPs are powerful, but expect to spend way more time debugging them than you think.
How SCPs Are Applied
Understanding where SCPs can be attached is crucial for getting them right:
You can attach SCPs to:
- The root of your organization (affects all accounts)
- Individual organizational units (affects all accounts in that OU)
- Individual accounts (affects only that account)
The key insight: SCPs are inherited down the tree. If you attach an SCP to the root, it applies to every account in your organization. If you attach it to an OU, it applies to all accounts in that OU.
Understanding Root User Permissions
Here's something critical that trips up a lot of people:
The root user in each AWS account has complete, unrestricted access to everything in that account. SCPs can't restrict the management account's root user's permissions. This is why securing your root user credentials is so important!
The Gotchas with SCPs
SCPs have some weird behavior that'll bite you:
π‘οΈ Management Account Immunity: SCPs don't affect the management account. That account can do whatever it wants, always.
π Default Deny: By default, everything is denied until you attach the "FullAWSAccess" policy. Don't accidentally remove this from the root unless you want to lock everyone out.
π Policy Size Limits: SCPs have a 5,120 character limit. If your policy gets too complex, you'll need to split it up.
π§ͺ No Local Testing: You can't test SCPs locally. You have to apply them and see what breaks. Always test in a sandbox account first.
When SCPs Actually Help
SCPs shine in a few specific scenarios:
- Compliance Requirements: "No one can turn off CloudTrail logging."
- Cost Control: "No instances bigger than m5.large in development."
- Regional Restrictions: "All resources must be in us-west-2."
- Security Baselines: "Root users can't do anything."
They're not magic. They won't solve bad IAM design or replace proper training. But they'll catch the obvious mistakes before they become expensive problems.
Part 3: AWS SSO & Identity Center - User Management That Doesn't Suck
Managing users across multiple AWS accounts is a nightmare.
Even as a solo developer, you end up with different IAM users for dev, staging, and production.
Each with their own access keys sitting in your ~/.aws/credentials
file.
Half of them are probably expired.
Or maybe you're working with a small team where everyone just uses the same shared credentials. "Just put the keys in the team Slack" becomes the unofficial onboarding process. It's a mess.
AWS Identity Center (formerly SSO) fixes this. One login for all your accounts, whether you're flying solo or managing a team of 50. No more credential juggling.
What Identity Center Actually Does
Identity Center acts as a central authentication hub. Users log in once and get access to multiple AWS accounts. They assume different roles in different accounts without managing separate credentials.
Here's what it replaces:
- Individual IAM users in every account
- Access key rotation nightmares
- Password management across accounts
- Manual user provisioning for new accounts
Setting Up Identity Center
Identity Center only works if you have AWS Organizations set up first. You can't use it with standalone accounts.
- Go to the AWS Identity Center console
- Click "Enable"
- Choose your region (pick where your users are located)
Important: Once you enable Identity Center in a region, you can't move it. Choose carefully.
Creating Your First User
You can create users directly in Identity Center or connect to external identity providers like Active Directory or Google Workspace. I'll show you the simple approach first.
- Go to "Users" in Identity Center
- Click "Add user"
- Fill in their email and basic info
- They'll get an email to set their password
That user now exists in Identity Center but can't access any AWS accounts yet. You need to create permission sets.
Permission Sets - The Key to Sane Access Control
Permission sets define what someone can do in an AWS account. They're basically IAM roles with a friendlier interface.
Let's create a permission set for developers:
- Go to "Permission sets" in Identity Center
- Click "Create permission set"
- Choose "Predefined permission set" and select "PowerUserAccess"
- Name it "Developer"
PowerUserAccess gives broad access but blocks IAM changes. It's perfect for developers who need to build things but shouldn't mess with permissions.
Assigning Users to Accounts
Now comes the magic part. You assign users to accounts with specific permission sets.
- Go to "AWS accounts" in Identity Center
- Select your staging account
- Click "Assign users or groups"
- Select your user and the "Developer" permission set
Repeat this for your production account, but maybe with a more restrictive permission set.
Automating Identity Center with Terraform
While the initial Identity Center setup must be done manually (enabling it in the console), you can manage users, permission sets, and assignments with Terraform:
# Create a permission set for developers
resource "aws_ssoadmin_permission_set" "developer" {
name = "DeveloperAccess"
description = "Developer access with PowerUserAccess"
instance_arn = local.sso_instance_arn
session_duration = "PT8H" # 8 hours
}
# Attach PowerUserAccess policy to the permission set
resource "aws_ssoadmin_managed_policy_attachment" "developer_poweruser" {
instance_arn = local.sso_instance_arn
permission_set_arn = aws_ssoadmin_permission_set.developer.arn
managed_policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess"
}
# Create a more restrictive ReadOnly permission set
resource "aws_ssoadmin_permission_set" "readonly" {
name = "ReadOnlyAccess"
description = "Read-only access for viewing resources"
instance_arn = local.sso_instance_arn
session_duration = "PT4H" # 4 hours
}
resource "aws_ssoadmin_managed_policy_attachment" "readonly_policy" {
instance_arn = local.sso_instance_arn
permission_set_arn = aws_ssoadmin_permission_set.readonly.arn
managed_policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
To assign users to accounts with specific permission sets:
# Assign developer permission set to sandbox account
resource "aws_ssoadmin_account_assignment" "developer_sandbox" {
instance_arn = local.sso_instance_arn
permission_set_arn = aws_ssoadmin_permission_set.developer.arn
principal_id = "user-id-here" # Get this from Identity Center
principal_type = "USER"
target_id = aws_organizations_account.sandbox.id
target_type = "AWS_ACCOUNT"
}
# Assign readonly access to production for the same user
resource "aws_ssoadmin_account_assignment" "readonly_production" {
instance_arn = local.sso_instance_arn
permission_set_arn = aws_ssoadmin_permission_set.readonly.arn
principal_id = "user-id-here"
principal_type = "USER"
target_id = aws_organizations_account.production.id
target_type = "AWS_ACCOUNT"
}
Note: You can also get the SSO instance ARN programmatically with a data source:
# Get the SSO instance ARN automatically
data "aws_ssoadmin_instances" "current" {}
locals {
sso_instance_arn = tolist(data.aws_ssoadmin_instances.current.arns)[0]
}
With this setup, almost anything can be automated. But permission sets and assignments can be fully automated.
The User Experience
Once everything is set up, here's what your users see:
- They go to their Identity Center portal URL
- They log in with their Identity Center credentials
- They see tiles for each account they have access to
- They click an account tile and get temporary credentials
- They're automatically logged into that account with the right permissions
No more juggling multiple sets of credentials. No more "which account am I in again?"
Connecting to External Identity Providers
If you already have users in another system, you can connect Identity Center to external identity providers like Azure AD or Auth0.
The setup can be tricky, but it means users don't need yet another login.
What About CLI Access?
Developers need CLI access, not just console access. Identity Center handles this, but there are a few ways to do it.
Option 1: AWS CLI v2 (built-in)
- Install the AWS CLI v2 (v1 doesn't support Identity Center)
- Run
aws configure sso
- Enter your Identity Center URL
- Choose your account and role
- The CLI opens a browser for authentication
After setup, developers run aws sso login
when their session expires.
Option 2: Leapp.cloud (recommended) If you want something that actually works well, check out Leapp.cloud. It's the best credential management tool for AWS, Azure, and GCP. Handles SSO sessions, role switching, and credential management way better than the native CLI tools. Plus it has a nice GUI so you don't have to remember commands.
The Limitations You'll Hit
Identity Center isn't perfect. Here are the gotchas you'll run into:
-
Limited Customization: Permission sets are a little bit more limited than raw IAM policies. For complex permissions, you might need custom policies.
-
External Integration Complexity: Connecting to external identity providers can be tricky. You'll need SAML expertise or patience to debug configuration issues.
Nevertheless, Identity Center is always the way to go!
The Real Benefits
After setting up Identity Center properly, you'll notice:
-
Security Improvements: No more access keys sitting in config files. Temporary credentials that rotate automatically.
-
Operational Simplicity: New account? Just assign permission sets. Someone leaves? Disable them in one place.
-
Better Compliance: Clear audit trails of who accessed what account when. No shared credentials to track down.
-
Developer Happiness: No more credential juggling. Single sign-on that actually works.
The setup takes a few hours, but it saves weeks of credential management later.
Part 4: How It All Works Together
Now you've got three pieces: Organizations, SCPs, and Identity Center. Separately, they're useful. Together, they create a system that actually scales.
Most people struggle to understand how accounts, groups, and permission sets work together. Let me explain it with an analogy that actually makes sense.
Think of It Like a Company with Multiple Offices
Imagine your AWS setup is like a company with different offices around the world:
π’ Accounts (Offices)
Your company has 6 offices:
β’ Management (the headquarters)
β’ Finance
β’ Development
β’ Staging
β’ Production
β’ Research
π₯ Users (People)
You have a team: Alice, Bob, Carol, Dave, Eve, and Frank.
π·οΈ Groups (Job Roles)
Everyone is part of a group based on their job:
β’ admins
- Full access everywhere (IT team)
β’ developers
- Access to Development and Research
β’ devops
- Access to Development, Staging, and Production
β’ finance-team
- Access only to Finance
π Permission Sets (The Keys)
Each group gets a "key" (permission set) that defines:
β’ Which offices they can enter
β’ What they can do inside (admin rights, read-only, etc.)
β’ How long they can stay
How It Works in Practice
Employee β joins Group β gets Permission Set β accesses specific Offices
Example: Carol is in the developers
group β gets the "DeveloperAccess" permission set β can access only the Development and Research offices with the permissions defined in that permission set.
The admin Frank is in the admins
group β gets full access to all offices.
With Identity Center: Everyone logs in once and can switch between the offices they're allowed to enter. No need for separate keys for every door.
With SCPs: Even if Frank has admin access, the office security system (SCP) can still block certain dangerous actions, like "no one can use the paper shredder in any office."
My Mental Model Breakdown (And How the Office Analogy Saved Me)
When I first set up Identity Center, I spent three weeks completely confused about how users, groups, and permission sets actually worked.
Coming from traditional IAM, I kept thinking in terms of "users get policies attached directly." So when I created my first user in Identity Center, I immediately went looking for where to attach IAM policies. There was no such option. Instead, I found something called "permission sets".
Okay, so permission sets are like IAM roles, right? I created a permission set called "DeveloperAccess" with PowerUserAccess attached. Then I tried to assign it to my user. The interface wanted me to pick both a user AND an account.
Why do I need to pick an account? In regular IAM, you just attach policies to users and they work everywhere in that account.
I assigned my user the DeveloperAccess permission set for my sandbox account. Then I logged into Identity Center and tried to access my production account. Nothing there. The user could only see the sandbox account.
So I went back and assigned the same permission set to the same user for production. Now they could access both accounts, but with identical permissions. What if I wanted them to have read-only access in production but full developer access in sandbox?
I created another permission set called "ReadOnlyAccess". Then I had to go back and change the assignment for production. But now I had two permission sets doing almost the same thing.
The breaking point came when I needed to add a new developer. I had to:
- Create the user in Identity Center
- Assign them DeveloperAccess for sandbox
- Assign them ReadOnlyAccess for production
- Assign them DeveloperAccess for staging (which I forgot the first time)
Multiply this by five developers, and I had 15 different assignments to manage. Every time I added a new account, I had to remember which permission set each person should get for that specific account.
Then I read about groups. Finally! Groups would solve everything. I'd create a "developers" group, put all developers in it, and assign permission sets to the group instead of individual users.
Except... that's not how it works either. You don't assign permission sets to groups. You assign groups to accounts with specific permission sets.
Let me show you what I mean:
WRONG mental model:
User β Group β Permission Set β Works everywhere
CORRECT mental model:
(User or Group) + Account + Permission Set = Access
The Correct Mental Model
It took me about a month to understand this relationship. Every combination has to be explicit. If you want Alice to access three accounts, you need three separate assignments, even if she's in a group.
The office analogy is what finally made it click:
Traditional IAM thinking: "Alice is a developer, so she gets developer keys that work in the development office."
Identity Center reality: "Alice is a developer, so she gets developer keys for the development office, read-only keys for the production office, and admin keys for the research office. Each key only works for one specific office."
Here's what this looks like in practice:
Alice (User) + Sandbox Account + DeveloperAccess = Can build and deploy in sandbox
Alice (User) + Production Account + ReadOnlyAccess = Can view production resources
Alice (User) + Shared Account + AdminAccess = Can manage shared DNS and monitoring
Same user, three different levels of access, based on which account she's accessing.
The mental shift took time because it's fundamentally different from how IAM works. With IAM, you attach policies to users and they work within that account. With Identity Center, you're defining relationships between users, accounts, and permission sets.
Once I understood this, the office analogy made perfect sense:
- ποΈ Accounts are buildings (each with their own security systems)
- π§βπ€βπ§ Users/Groups are people (they can visit multiple buildings)
- πͺͺ Permission Sets are keycards (different cards for different buildings)
- π‘οΈ Assignments are the security desk saying "Alice gets developer keycard for building A, and visitor keycard for building B"
Now when I set up new environments, I think in terms of:
- Which accounts does this person need to access?
- What should they be able to do in each account?
- Which permission set matches that level of access?
The confusion happens because you have to be explicit about every relationship. There's no "default access" or "inherit from parent" like you might expect. Everything is intentional and specific.
Why This Matters in Practice
The Real User Journey
Here's what happens when a developer needs to deploy to staging:
- Developer goes to Identity Center portal They see tiles for all accounts they have access to
- They click the "Staging" account tile Identity Center authenticates them and generates temporary credentials
- They assume the "Developer" permission set role This gives them PowerUserAccess within that specific account
- They try to launch an EC2 instance Their IAM role says "yes, you can launch instances" But the SCP says "only t2, t3, and t3a instances"
- The action succeeds or fails based on SCP rules If they try to launch a c5.large, they get blocked If they launch a t3.micro, it works
This all happens transparently. The developer doesn't need to understand SCPs or know which account they're in. They just can't do the dangerous stuff.
Consolidated Everything
One massive benefit people miss: consolidated billing and monitoring.
Consolidated Billing - The Financial Superpower
Consolidated brings all your costs together.
-
Single Bill: All costs from member accounts roll up to your management account. Instead of juggling 5 different AWS bills, you get one comprehensive bill. Your accounting team will love you for this.
-
Cost Allocation: Tag resources by team, project, or environment. See exactly how much the mobile team's backend costs versus the web team's infrastructure. Perfect for chargebacks or budget planning.
-
Financial Benefits: You get volume discounts across all accounts, Reserved Instance sharing, and Shared Savings Plans that work organization-wide. These can add up to significant savings as you scale.
Practical Example: Before Organizations: You had 3 separate AWS accounts with bills of $200, $150, and $300. After Organizations: One $650 bill with better visibility into where every dollar goes, plus automatic volume discounts.
Consolidated Monitoring
With consolidated monitoring, you can see everything in one place.
-
CloudTrail: Set up one trail in your management account that logs API calls from all member accounts. Single place to investigate what went wrong instead of hunting through multiple logs.
-
Cost Explorer: See spending across all accounts in one dashboard. Filter by account, service, or tag. Set up budget alerts at the organization level and get Slack notifications when any account exceeds its budget.
-
Centralized Dashboards: Build CloudWatch dashboards that span multiple accounts. Monitor production metrics alongside staging performance and compare resource usage across environments.
The Organizational Structure That Works
Whether you're a solo developer or managing a small team, here's a structure that scales:
Minimal setup (perfect for solo developers or small teams):
Root Organization
βββ Management Account (billing, organization control)
βββ Sandbox Account (your playground, break things here)
βββ Production Account (the real deal, be careful)
Recommended setup (adds staging for safer deployments):
Root Organization
βββ Management Account
βββ Sandbox Account (development and testing)
βββ Staging Account (optional but recommended - production-like testing)
βββ Production Account
For teams of 5+ people:
Root Organization
βββ Core OU
β βββ Management Account
β βββ Shared Account (DNS, monitoring tools)
βββ Development OU
β βββ Sandbox Accounts (one per developer if needed)
βββ Staging Account (optional - test production-like deployments)
βββ Production Account
Sandbox Account gets loose SCPs so people can experiment. Production gets strict SCPs so no one can accidentally break things. Staging (if you use it) gets production-like SCPs to catch issues early.
The Three Permission Sets You Actually Need
Forget about creating 15 different permission sets. Start with these three and you'll cover 90% of your use cases:
π ReadOnly
For the "I just need to see what's happening" people.
New team members learning the ropes, support folks investigating issues, or anyone who needs to look but definitely shouldn't touch anything important.
Think of it as the "safe to give to your manager" permission level.
π¨ Developer
Your bread-and-butter permission set.
Builds on PowerUserAccess, which means they can create resources and deploy stuff, but they can't mess with IAM or delete the entire organization.
Perfect for day-to-day development work.
Most of your team will live here.
β‘ Admin
The "everything is on fire" permission set.
Full access everywhere, but treat it like the nuclear codes.
Use sparingly, require MFA, and maybe consider a buddy system for production changes.
The Stuff You Still Have to Do Manually
I wish I could tell you this setup runs itself, but that would be lying. Here's what you'll still be doing the old-fashioned way:
π₯ Adding New People
Someone new joins your team?
You'll create them in Identity Center and assign permission sets.
Takes a few minutes (even with Terraform), but it's still manual.
Unless you connect to an external identity provider - then it's automatic.
π’ Creating New Accounts
New project needs its own account?
You'll create it and move it to the right OU.
The Terraform code I showed you earlier makes this pretty painless, but someone still has to run it.
βοΈ Permission Updates
Someone needs access to a new service?
You update their permission set and it applies everywhere.
The nice thing is you're updating the template, not tracking down individual user permissions across accounts.
π SCP Changes
Want to block a new service or restrict a region?
Update your SCPs, test them in development first, then roll out to production.
No shortcuts here - always test first. π§ͺ
What You Can Automate (Eventually)
Once your foundation is solid, this is where the fun begins:
π Account Factories
Use AWS Control Tower or build custom Lambda functions.
Someone creates a ticket for a new project?
Boom - new account appears with the right SCPs and permissions already applied.
No manual setup required.
π¨ Compliance Monitoring
AWS Config rules that check if anyone creates something they shouldn't.
Someone launches an expensive GPU instance in the wrong region?
Slack alert hits your security channel immediately.
π° Smart Budget Alerts
Budgets that actually understand your usage patterns.
Dev account spending $50 more than usual?
Probably fine.
Costs jumped 300%?
Time to investigate. π
The Monitoring You Actually Need
Set up alerts for organization-level events:
- New accounts created or invited
- SCPs attached or detached
- Root user activity in any account
- Large spending increases
Here's a simple EventBridge rule that sends Slack notifications for any organization changes:
{
"source": ["aws.organizations"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["organizations.amazonaws.com"],
"eventName": ["CreateAccount", "AttachPolicy", "DetachPolicy"]
}
}
Connect this to SNS, Lambda, and Slack. Now you know immediately when someone changes your organization structure.
What This Actually Prevents
-
The Accidental Bill: You accidentally launch a bunch of expensive instances while testing something. SCP blocks them before your credit card gets charged.
-
The Credential Leak: Your laptop gets stolen and has AWS credentials saved in plaintext. With Identity Center, those credentials expire in hours, not months.
-
The "Who Did This?" Mystery: Something breaks in production and you need to find out who changed what. CloudTrail shows you exactly who accessed what account and when.
-
The Former Employee Problem: Someone leaves your team but still has access to production through old IAM users. Disable their Identity Center account and they're locked out everywhere instantly.
The Real Benefits After 6 Months
After running this setup for a while, you'll notice:
-
Faster Onboarding: New developer? Create them in Identity Center, assign permission sets. They have access to everything they need in minutes.
-
Easier Offboarding: Someone leaves? Disable their Identity Center user. No need to check IAM users across all accounts.
-
Better Security: No long-lived access keys sitting in config files. All access is temporary!
-
Simpler Billing: One bill for everything. Easy to see what each project or account is costing what.
-
Fewer Emergencies: SCPs catch the obvious mistakes before they become disasters. Compliance audits become routine instead of panic.
The setup takes a few days. The benefits last for years.
Common Mistakes and How to Avoid Them
There's a lot of stuff you can automate, but there's also a lot of stuff you can't. And a lot of stuff you can make mistakes with. Let's go through some of the most common mistakes and how to avoid them.
The SCP Lockout
This is the classic mistake. You create an SCP that's too restrictive and apply it to the root of your organization. Suddenly no one can do anything, including you.
What happens: You create an SCP that denies all actions. You attach it to the root. Now nobody can do anything, even in development.
How to avoid it:
- Always test SCPs in a sandbox account first
- Never attach SCPs to the root until you're sure they work
- Remember: SCPs can't be overridden by IAM policies
- Keep the management account separateβSCPs don't affect it
The Permission Set Explosion
You start with simple permission sets: Developer, Admin, ReadOnly. Then someone needs "Developer but with S3 admin access." So you create "DeveloperWithS3." Then "DeveloperWithS3AndRDS." Soon you have 20 permission sets and no idea which is which.
How to avoid it:
- Start with broad permission sets
- Use custom policies for edge cases
- Document what each permission set is for
- Regular cleanup of unused permission sets
Starting Too Complex
You read about AWS Control Tower, AWS Config, GuardDuty, and Security Hub. You try to implement everything at once. Nothing works and you give up.
How to avoid it:
- Start with Organizations only
- Add basic SCPs
- Set up Identity Center
- Add monitoring later
- Automate when things are stable
When Things Go Wrong (And They Will)
Here are the most common issues I've seen and how to fix them:
"I can't access my account after setting up SCPs"
π¨ Symptom: You applied an SCP and now you're locked out. π§ Fix: Log into the management account and detach the problematic SCP. Test it on a throwaway account next time.
"Identity Center login works but I can't access anything"
π¨ Symptom: You can log into the portal but clicking account tiles gives errors. π§ Fix: Check that you've assigned the user to accounts with permission sets. An empty assignment means no access.
"Complains about frequent re-authentication"
π¨ Symptom: People have to log in every few hours. π§ Fix: Increase session duration in your permission sets. The default 1 hour is definitely too short for most workflows. Pain point: it's not possible to have anything longer than 12 hours.
"My SCPs aren't working"
π¨ Symptom: You applied an SCP but people can still do the blocked actions. π§ Fix:
- Make sure the SCP is attached to the right OU or account
- Check that you don't have conflicting policies
- Remember: management account ignores SCPs
What I Wish I'd Known Before Starting
I've now set up AWS Landing Zones three times - twice for clients, once for my own projects. Here's what I wish someone had told me before I started:
It's going to take longer than you think. Even with Terraform, budget a full week, not a day. Between waiting for account creation, debugging SCP conditions, and fighting with Identity Center assignments, things pile up.
Start smaller than you want. I tried to build the perfect setup the first time. Three OUs, five permission sets, complex SCPs. I spent more time managing the setup than actually building software. Start with one sandbox account and one production account. Add complexity when you actually need it.
Nobody cares about your beautiful organization structure. Developers want to deploy code and see logs. They don't care about your perfectly nested OUs or elegant permission set naming conventions. Build for them, not for the AWS architecture diagrams.
The real value shows up in weird places. You think you're building this for security and compliance. But the biggest win ends up being consolidated billing. Or not having to rotate access keys every quarter. Or being able to answer "who can access production?" in five seconds instead of five hours.
Some fights aren't worth having. Yes, you can lock down IAM so developers can't create roles. But then you spend all day creating roles for them. Sometimes it's better to give them PowerUserAccess and sleep well knowing SCPs will catch the dangerous stuff.
When Landing Zones Aren't Worth It
Be honest with yourself about whether you actually need this.
Skip it if:
- You're a solo developer with one or two simple projects
- Your AWS bill is under $100/month and staying there
- You're still figuring out basic AWS services
- You're planning to migrate off AWS in the next year
Do it if:
- You have multiple environments (dev/staging/prod)
- More than two people need AWS access
- Your AWS spending is growing
- You've had a security incident or compliance requirement
- You're tired of managing credentials
The setup overhead is real. Don't create complexity you don't need.
The Stuff That Still Sucks
Even with a perfect Landing Zone setup, some things still suck:
-
Cross-account networking is still painful: VPC peering, Transit Gateway, resource sharing - none of this gets easier. You still need to think hard about how services in different accounts talk to each other.
-
Terraform state management across accounts is tricky: Do you store state in each account? Central state bucket in the management account? There's no perfect answer.
-
Identity Center session durations are too short: Max 12 hours means your developers are re-authenticating constantly. This is AWS being overly cautious, and there's no workaround.
-
SCP debugging will always be terrible: You can't test them locally. Error messages are cryptic. Propagation takes time. Expect to spend way more time on this than seems reasonable.
My Current Setup
After three attempts, here's what I actually run:
π₯ Accounts:
- Management (billing only, nobody works here)
- Sandbox (developers break things here)
- Production (locked down with strict SCPs)
- Shared (DNS, monitoring, CI/CD)
π Permission Sets:
- ReadOnly (for everyone basically)
- Developer (PowerUserAccess, covers most things)
- Admin (full access to everything)
π SCPs:
- Block expensive instance types in all accounts
- Require MFA before allowing anything else
- Deny access to specific regions we don't use (also disable completely, but why don't have two layers of security?)
That's it. Four accounts, three permission sets, three SCPs. It handles a team of a few developers working on a dozen different projects.
Start Here
If you're convinced you need this, start with the absolute minimum:
- Create an organization in your existing account
- Create one new sandbox account for experimentation
- Enable Identity Center and create yourself as a user
- Create one basic SCP that blocks something expensive (like GPU instances)
- Test everything by trying to break it
Don't create multiple OUs. Don't build complex permission sets. Don't try to automate everything with Terraform yet.
Get the basics working first. Then add one thing at a time, only when you actually need it.
The Real Question
The question isn't "should I build an AWS Landing Zone?" It's "what problems am I actually trying to solve?"
If you're trying to solve credential management, start with Identity Center. If you're trying to prevent expensive mistakes, start with simple SCPs. If you're trying to organize projects, start with separate accounts.
Don't build the whole thing because some blog post (including this one) told you to. Build the parts that solve real problems you're actually having.
The best Landing Zone is the one that gets out of your way and lets you build software. Everything else is just complexity for the sake of complexity.
Now go build something. π

AWS Lambda on One Page (No Fluff)
Skip the 300-page docs. Our Lambda cheat sheet covers everything from cold starts to concurrency limits - the stuff we actually use daily.
HD quality, print-friendly. Stick it next to your desk.