- Simple AWS
- Posts
- AWS Organizations Deep Dive
AWS Organizations Deep Dive
Building a secure and sane multi-account strategy, part 1
When you start with AWS you usually create a single account, get familiar with some services, and deploy a few workloads. But as your AWS footprint grows, managing everything in one account becomes unwieldy and risky. What if a developer accidentally deletes a production database? What if someone creates resources in an expensive region? What if your root credentials get compromised? (Yes, that still happens, I saved someone from that last year).
AWS Organizations solves these problems by enabling you to manage multiple AWS accounts centrally. The trick is governing those accounts with consistent policies, tracking costs collectively, implementing proper isolation between different environments and workloads, and doing all of that without it becoming an even bigger pain in the backend.
In this series we're going to talk about AWS Organizations, with a focus on how to implement a secure and sane multi-account strategy (sane as in it doesn't drive you mad). This article is part 1 of the series, where I'll cover the key features of Organizations. In part 2, which will be published next week, I'll cover best practices and suggestions.
Root User Security and Management
Each AWS account comes with a single root user that has unlimited powers. These god-like permissions make the root user your biggest security risk.
There are very few things that only the root user can do, like changing the account name or root email address or closing the account. Some billing activities like enabling IAM access to the billing console and specific security features like enabling MFA delete on S3 buckets also require the root user. But beyond that, you really don't need it.
Here's what you would do if you were using a single account:
Enable multi-factor authentication (MFA) for the root user.
Use a strong, randomly generated password and never reuse it on other systems.
Don't create access keys for the root user.
Only use the root user for the few tasks that absolutely require it. Treat the root user as a "break-glass" credential that stays locked away until absolutely needed.
Use a corporate group email for the root user so multiple trusted admins receive security notices and can recover the account if needed.
I even wrote an article on single account security a couple of years ago.
Well, in a multi-account environment with AWS Organizations you can do all of that and more. AWS Organizations allows the management account to centralize root access by removing root credentials from member accounts. If you use Organizations to create AWS Accounts they don't even come with a root password. Plus, you can add Service Control Policies so even if someone manages to log in with the root user they can't perform any actions.
But I'm getting ahead of myself. Let's talk about what you can do with Organizations.
Organizational Units (OUs): Design and Structure
Organizational Units (OUs) are logical groupings of accounts within AWS Organizations. They're the containers that let you apply management policies (like Service Control Policies) to multiple accounts simultaneously in a hierarchical fashion. The key to effective OUs is structuring them based on common functions or compliance requirements, not mirroring your org chart.

Example Organizational Units structure
A well-planned hierarchy typically starts with a few foundational OUs (which I didn't include in the diagram up there because I forgot):
Security: Contains accounts for centralized security services such as log archiving, security tooling, and audit functions.
Shared: Shared services like networking, identity management, and possibly shared CI/CD systems that are used organization-wide.
Workloads: Contains accounts for business application workloads. These are often subdivided by environment (e.g., Production and Non-Production sub-OUs).
Sandbox: Experimental accounts where developers can try things without worrying about affecting someone else. Even non-production accounts like Dev need to be somewhat stable and have some rules. Sandboxes usually don't have many rules except for security and to prevent excessively expensive experiments.
You can add more OUs as needed, such as a Policy Staging OU for testing new policies before broader deployment, or a Suspended OU to contain decommissioned accounts with an SCP that denies all actions.
OUs can be nested up to five levels deep, enabling hierarchical policy inheritance. This means that policies attached at a parent OU automatically apply to all child OUs and accounts beneath it. For example, you can attach broad security baseline SCPs at a top-level OU, and then apply more specific rules to sub-OUs as required.
When designing your OU structure, remember that OUs are primarily a tool for policy management, not arbitrary grouping. Create them when you need to apply different policies or controls to a set of accounts. And avoid making your hierarchy too deep or overly reflective of organizational changes, which tend to happen more frequently than policy needs change.
Consolidated Billing: Streamlining Financial Management
One of the most immediate benefits of AWS Organizations is consolidated billing. Instead of managing separate bills for each AWS account, you get a single monthly bill that covers all accounts in your organization.
When you set up AWS Organizations, all member accounts automatically have their usage rolled up to the management account's bill. This simplifies finance operations and makes it easier to process payments.
Another major advantage is that your combined usage qualifies for volume pricing discounts. AWS treats all the usage across your organization as one pool for services with tiered pricing, like data transfer or S3 requests. This may lead to lower overall costs than if each account operated independently. I mean, volume pricing discounts are not easy to reach, but putting every dollar into the same bill helps. And remember that you can negotiate private pricing with AWS if your bill is large enough.
Even more significant is the ability to share Reserved Instances (RIs) and Savings Plans across accounts. If one account purchases a heavy RI and isn't fully utilizing it, other accounts can automatically benefit from the discounted rate. This maximizes the value of your commitments and avoids the common problem of "stranded" discounts.
While the management account is responsible for paying the consolidated bill, you can still track costs by individual account. The management account can see a breakdown of charges incurred by each account, which is useful for internal chargeback to departments or projects.
There are some limitations to be aware of. AWS Support fees are typically not pooled under Organizations. Each account's support plan is billed separately and calculated based on that account's usage, though Enterprise Support customers have options for aggregated support.
Also, if an account leaves your organization, it loses access to historical cost data from when it was a member. This can complicate matters if you're reorganizing or spinning off accounts, so plan accordingly.
Cost Explorer and Multi-Account Cost Management
AWS Cost Explorer provides fantastic visualization and analysis capabilities. For multi-account environments, the management account has access to all cost data for all member accounts in the organization, and you can view it all in Cost Explorer.
Within Cost Explorer you can apply the Account filter to view spending for specific accounts or compare spending across accounts. You can also create custom reports that group accounts in meaningful ways, like by environment or business unit.
Member accounts, by default, can only see their own costs. If you want teams managing member accounts to have visibility into their spending, you'll need to grant them billing access through the "Activate IAM Access to Billing" setting and appropriate IAM policies.
For more advanced cost allocation, AWS offers Cost Categories. These let you define custom groupings for cost reporting. For example you can create a cost category called "Environment" that groups certain accounts as "Production" and others as "Development." This is particularly helpful because Cost Explorer doesn't natively understand the OU hierarchy or tags from AWS Organizations.
When implementing cost management across accounts, consider centralizing cost monitoring through the management account, creating read-only billing dashboard roles that team leads or finance can assume. Enable AWS Cost and Usage Report (CUR) and have it delivered to an S3 bucket. The CUR contains granular data for all usage and costs, including each account's consumption, which is invaluable for advanced analysis.
Use consistent resource tagging across accounts for shared resources, and enable them as cost allocation tags in the Billing Console. Set up AWS Budgets on both a per-account basis and a rolled-up basis. Create budgets that track each account's monthly spend with notifications to account owners if they approach limits.
Leverage data from Cost Explorer or CUR to allocate charges to business units. An account-based model simplifies this since each account's bill is clearly defined.
Unfortunately there's no sane way to enforce tags on AWS resources. That I know of. It's a billion-dollar problem, so if you have a reliable solution please let me know.
Accessing Your AWS Accounts in an Organization
Managing access across multiple AWS accounts can get messy quickly if you try to create and maintain separate IAM users in each account. So how do you provide secure, manageable access to dozens or hundreds of accounts? AWS IAM Identity Center is the way.
IAM Identity Center lets you connect your corporate identity provider (like Active Directory, Okta, or other SAML 2.0 providers) or use its built-in directory to manage users. You then assign these identities different levels of access to your AWS accounts.
Setting up IAM Identity Center is pretty straightforward. First enable it from within the AWS Organizations management account. Next choose your identity source, either the built-in Identity Center directory, Active Directory, or an external identity provider. Then create permission sets that define what users can do in your AWS accounts. Finally assign users some permission sets in some accounts.
Permission sets are essentially IAM roles with predefined policies (like AdministratorAccess or ReadOnlyAccess) or custom permission policies you create. Once your permission sets are defined, you assign them to specific users or groups for specific accounts or entire OUs. For example, you might give your DevOps team administrator access to development accounts but only read-only access to production accounts. Or you might give your finance team billing access to all accounts.
When users need to work in AWS, they log into the AWS access portal with their corporate credentials. They're presented with a list of accounts they can access and the roles they can assume in each. Clicking on a role takes them directly to that account with those permissions, without requiring separate passwords for each account.

Example list of AWS accounts
Behind the scenes, IAM Identity Center creates the corresponding IAM roles in each account for each permission set assignment. It also handles the authentication and federation process, making it seamless for users to switch between accounts.
This approach has several major benefits:
You eliminate the need to create and manage IAM users in each account separately.
Users have a single set of credentials to remember and secure.
When someone leaves your organization, you can revoke access in one place instead of hunting through multiple accounts.
Access assignments map naturally to your organizational structure through OUs.
IAM Identity Center should be the primary way that humans access AWS accounts. Period.
You can also get programmatic access for humans, in the form of short-lived credentials that you can paste in your CLI or .env file. Just click on Access keys 🔑 and you'll see this:

Temporary credentials
For occasional access, this is the easiest way. For regular access you can Configure IAM Identity Center authentication with the AWS CLI. For programmatic access not meant for humans, e.g. for a web server, you shouldn't use this, nor should you use long-term credentials. Instead you should set up an IAM Role.
Cross-Account Programmatic Access
IAM Identity Center handles human access to your accounts, but you also need to manage how services and applications in one account access resources in another. The answer is usually IAM roles with trust relationships, or sometimes Resource Access Manager.
The main way is to create an IAM role in Account B that grants access to certain resources and set the role's trust policy to allow Account A to assume it. Then a service or application in Account A can assume that role to get temporary credentials for Account B.
AWS Organizations automatically creates a role called OrganizationAccountAccessRole in each new member account. This role has full administrative privileges and trusts the organization's management account. It allows administrators in the management account to manage the member accounts as needed, and it's what AWS uses to read data like usage and billing.
Beyond this default administrative role, you'll want to create more narrowly scoped roles for specific cross-account access needs. For example a deployment role that your CI/CD pipelines can assume to deploy resources
Note that if you're setting this up for a resource like an AWS Lambda function in account A, the role in account B can't have a trust relationship for the function. The role in account B has a trust relationship with account A itself, not with specific stuff (resources or principals) within it. Account A delegates this capability of assuming the role in account B to its principals, in this case the IAM Role that the Lambda function has assigned to it. So, when the Lambda function in account A accesses a resource in account B, this is what's actually happening:
The Lambda function in account A gets temporary credentials for the role in account A that it has assigned to it. This role has permissions to assume the role in account B.
The Lambda function uses these credentials to assume the role in account B, receiving temporary credentials scoped for account B.
The Lambda function uses those credentials to access the intended resource in account B.
Nobody in account A can assume the role in account B unless they use credentials that have this explicit permission (via an IAM Policy applied to the user or role).
For resource-specific access, you can use resource-based policies instead of roles. For instance, an S3 bucket in Account A can include a bucket policy that grants read/write permissions to a specific role in Account B. Similar cross-account resource policies exist for services like KMS, SNS, and SQS.
AWS Resource Access Manager (RAM) is another service that works with Organizations to let you share resources across accounts. Instead of giving Account A's IAM principal direct access to Account B's subnet, you can share the subnet from Account B to Account A using RAM. This simplifies resource sharing, especially for networking and infrastructure components.
For service-level delegation, AWS Organizations allows certain services to designate a member account as a delegated administrator. This means a specific account (other than the management account) can manage that service's organization-wide settings. For example, you could delegate GuardDuty administration to your security account, allowing that account to enable/disable GuardDuty for all accounts and aggregate findings without using the management account.
Tip 1: Limit the use of the management account to organizational tasks only and don't use it for routine operations in member accounts.
Tip 2: Establish a clear access model, and use that consistently.
Tip 3: Use external IDs in trust policies when delegating access to third parties, adding an additional verification check to the role assumption process.
Tip 4: Automate the provisioning of standard cross-account roles. When new accounts are created, automatically set up the roles they need to participate in your environment.
Service Control Policies (SCPs)
Service Control Policies (SCPs) act as guardrails that define the maximum allowed actions for accounts in your organization. They're written in JSON format similar to IAM policies, but instead of granting permissions, they filter which permissions can be exercised. A user or role in an account affected by an SCP can only perform actions that are allowed by both the IAM policy and all applicable SCPs, and that are not explicitly denied in any IAM policy or any SCP.
Let's say a developer has an IAM policy that grants them permission to launch any EC2 instance type. If you apply an SCP that denies the ability to launch expensive instance types like x1e.32xlarge
, that developer won't be able to launch those instances regardless of their IAM permissions. The effective permissions are the intersection of IAM permissions and SCP allowances. To understand more about how IAM permissions are applied, read my IAM Permissions Deep Dive.
By default, when you enable SCPs (you must have Organizations set to "all features" mode, not just consolidated billing), AWS provides a policy called FullAWSAccess attached to every OU. This default policy allows all actions, ensuring existing operations aren't disrupted until you start implementing custom SCPs.
There are two main strategies for implementing SCPs:
Deny list strategy: Keep the default FullAWSAccess policy and add SCPs that explicitly deny specific actions or services you want to restrict. This is simpler to start with since it won't accidentally block legitimate activities.
Allow list strategy: Replace the default policy with SCPs that explicitly allow only the services and actions you want to permit. This is more secure but requires more maintenance, as new AWS services will be denied by default until added to your allow list.
SCPs can be attached at the organization root level, to specific OUs, or to individual accounts. Policies are inherited down the hierarchy, so SCPs attached to a parent OU apply to all child OUs and accounts beneath it. Multiple SCPs can apply to the same account, and their effects are cumulative. If any policy denies an action, it's denied regardless of what other policies say.
Importantly, SCPs don't affect the management account by default. This makes sense since you wouldn't want to accidentally lock yourself out of your organization's central account. They also don't interfere with service-linked roles that AWS services use to perform operations on your behalf.
A few interesting things you can do with SCPs:
Restricting regions: You can deny all actions in AWS regions that your company doesn't use, effectively geo-fencing your accounts to approved regions for compliance or cost control.
Blocking risky services: If certain AWS services aren't approved in your organization, an SCP can prevent their use entirely.
Enforcing security controls: SCPs can prevent anyone from disabling security services or changing critical security settings, like turning off CloudTrail logging or disabling encryption.
Quarantining compromised accounts: If you detect a security issue, you can quickly attach an SCP with a
Deny *
statement to effectively freeze the account (existing resources will continue to exist and generate charges, but all actions will be blocked).
Tip: Start with a deny list approach, beginning with a small set of risky actions that you know should never be allowed. Use these SCP templates as a starting point.
Tip 2: Always test SCPs in a limited scope before broad deployment. Create a Policy Staging OU or test account, apply the SCP there, and verify that it works as expected without blocking needed functionality.
Tip 3: Use the IAM Access Analyzer or policy simulator to validate your SCPs and understand their impact before applying them. Document each SCP's purpose and which accounts it affects. When developers encounter "Access Denied" errors due to SCPs, they need to understand why.
Tip 4: Regularly review your SCPs as AWS releases new services or your organization's requirements change. SCPs aren't micromanagement tools, they're guardrails that establish boundaries while still allowing teams to innovate within those boundaries. Use them a lot, but be careful not to stifle innovation. That's what we have sandbox accounts for, after all.
Conclusion of Part 1
This article was getting pretty long, so I'm going to stop it here. I'll post Part 2 next week, with best practices and suggestions.
Did you like this issue? |
Reply