Multi-Account AWS Strategy with Organizations: Complete Enterprise Implementation Guide 2024
Master AWS multi-account architecture with this comprehensive 3500+ word guide covering AWS Organizations setup, Service Control Policies (SCPs), Control Tower implementation, cross-account IAM roles, centralized logging, Config Aggregator, StackSets deployment, tag policies, backup policies, and enterprise governance patterns. Includes 8 production-ready code examples.
As organizations scale their AWS footprint from a handful of workloads to hundreds of applications, a single AWS account quickly becomes a liability rather than an asset. Managing security, compliance, cost allocation, and blast radius containment in a single account becomes increasingly difficult—and risky. A multi-account AWS strategy using AWS Organizations has become the industry standard for enterprises serious about cloud governance, security, and operational excellence.
This comprehensive guide walks you through the complete process of designing, implementing, and managing a multi-account AWS architecture. You'll learn from real-world patterns, get production-ready code examples in AWS CLI, Terraform, CloudFormation, and Python boto3, and understand how to leverage AWS Control Tower for automated governance. Whether you're starting fresh or refactoring an existing single-account deployment, this guide provides the blueprint for enterprise-grade AWS multi-account architecture.
Why Multi-Account Architecture Matters: The Business Case for Account Separation
Before diving into implementation, it's essential to understand why leading organizations invest significant effort in multi-account architectures. The benefits span security, financial management, operational efficiency, and compliance—making it one of the most impactful architectural decisions you can make for your AWS environment.
Security Isolation and Blast Radius Reduction
The most compelling argument for multi-account architecture is security isolation. In a single-account environment, a compromised credential or vulnerable application can potentially access all resources. With multi-account architecture, accounts act as hard security boundaries that cannot be crossed even by compromised administrative credentials.
Blast Radius Containment: If an application in a development account is compromised, the attacker cannot pivot to production resources in a separate account—even with stolen credentials. IAM roles and policies are account-scoped, creating natural containment that significantly limits the potential damage from security incidents.
Workload Isolation by Sensitivity: Financial data, PII, healthcare records, and other sensitive workloads can reside in dedicated accounts with enhanced security controls, separate compliance monitoring, and restricted access patterns. This isolation makes it easier to apply defense-in-depth strategies and meet regulatory requirements.
Separation of Duties: Different teams can have administrative access to their accounts without risking organization-wide impact. A database administrator in the analytics team doesn't need (and shouldn't have) access to production customer databases. This principle reduces both accidental and malicious damage potential.
Independent Security Postures: Each account can implement security controls appropriate to its risk level. A sandbox account for experimentation has different security requirements than a PCI-DSS compliant payment processing account. Multi-account architecture allows you to right-size security investments based on actual risk.
Billing, Cost Allocation, and FinOps Excellence
Multi-account architecture transforms AWS cost management from a manual tagging exercise into automated, accurate financial reporting that finance teams can trust:
Automatic Cost Attribution: Each account's costs are automatically tracked and attributable to specific business units or projects. No more relying on cost allocation tags that developers might forget to apply or misconfigure.
Department-Level Chargebacks: Finance can allocate cloud costs to business units based on their dedicated accounts, enabling accurate P&L statements and budget accountability. This visibility drives cost-conscious behavior across the organization.
Consolidated Billing Benefits: AWS Organizations provides consolidated billing across all accounts while maintaining detailed per-account visibility. You get volume discounts, Reserved Instance sharing, and Savings Plans benefits across the organization automatically.
Budget Controls: Set AWS Budgets at the account level to prevent any single team or project from causing unexpected spending spikes. Budget alerts can trigger automated responses before costs spiral out of control.
Service Quotas and Resource Independence
AWS service quotas (formerly called limits) are applied per account. This isolation prevents resource contention and provides predictable capacity:
Quota Independence: A runaway script creating thousands of EC2 instances in a development account won't exhaust your production account's instance quota. Each account operates with its own allocation of AWS resources.
Predictable Capacity: Production workloads have guaranteed access to their account's full quota allocation without competition from development or testing activities. This separation ensures production reliability.
Simplified Quota Management: Request quota increases only for accounts that need them, rather than managing a complex shared quota across diverse workloads with competing priorities.
Compliance and Regulatory Requirements
Many compliance frameworks expect or require environment separation, and multi-account architecture makes demonstrating compliance significantly easier:
PCI-DSS Requirement: Payment Card Industry Data Security Standards require segmentation of cardholder data environments from other systems. Separate accounts provide the strongest possible boundary.
HIPAA Considerations: Healthcare workloads benefit from dedicated accounts with enhanced audit logging, access controls, and the ability to demonstrate complete data isolation to auditors.
SOC 2 Controls: Demonstrating logical separation of environments is easier with distinct AWS accounts than with complex IAM policies. Auditors appreciate the clear boundaries that account separation provides.
GDPR Data Residency: Separate accounts can enforce regional restrictions through Service Control Policies, ensuring data stays in required geographical regions.
Setting Up AWS Organizations: Step-by-Step Implementation with Terraform and CloudFormation
AWS Organizations is the foundation for multi-account management. Let's walk through the complete setup process using infrastructure as code with both Terraform and AWS CLI approaches.
Creating Your Organization with AWS CLI
First, create an organization from your management account (this becomes the root of your organization):
#!/bin/bash
# AWS Organizations Setup Script
# Run this from your designated management account
# Create the organization with all features enabled
aws organizations create-organization --feature-set ALL
# Verify the organization was created
aws organizations describe-organization
# List the root organizational unit (auto-created)
ROOT_ID=$(aws organizations list-roots --query 'Roots[0].Id' --output text)
echo "Organization root ID: $ROOT_ID"
# Enable all AWS service integrations you'll need for governance
aws organizations enable-aws-service-access --service-principal cloudtrail.amazonaws.com
aws organizations enable-aws-service-access --service-principal config.amazonaws.com
aws organizations enable-aws-service-access --service-principal config-multiaccountsetup.amazonaws.com
aws organizations enable-aws-service-access --service-principal guardduty.amazonaws.com
aws organizations enable-aws-service-access --service-principal securityhub.amazonaws.com
aws organizations enable-aws-service-access --service-principal ram.amazonaws.com
aws organizations enable-aws-service-access --service-principal sso.amazonaws.com
aws organizations enable-aws-service-access --service-principal backup.amazonaws.com
aws organizations enable-aws-service-access --service-principal tagpolicies.tag.amazonaws.com
aws organizations enable-aws-service-access --service-principal member.org.stacksets.cloudformation.amazonaws.com
echo "Organization created and service integrations enabled"
Creating Organizational Units with Terraform
Here's a comprehensive Terraform configuration for creating your OU structure:
# organizations/main.tf
# Terraform configuration for AWS Organizations OU structure
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Get the organization root
data "aws_organizations_organization" "org" {}
locals {
root_id = data.aws_organizations_organization.org.roots[0].id
}
# Top-level Organizational Units
resource "aws_organizations_organizational_unit" "security" {
name = "Security"
parent_id = local.root_id
tags = {
Description = "Security tooling and audit accounts"
ManagedBy = "Terraform"
}
}
resource "aws_organizations_organizational_unit" "infrastructure" {
name = "Infrastructure"
parent_id = local.root_id
tags = {
Description = "Shared services and networking"
ManagedBy = "Terraform"
}
}
resource "aws_organizations_organizational_unit" "workloads" {
name = "Workloads"
parent_id = local.root_id
tags = {
Description = "Application workload accounts"
ManagedBy = "Terraform"
}
}
resource "aws_organizations_organizational_unit" "sandbox" {
name = "Sandbox"
parent_id = local.root_id
tags = {
Description = "Developer experimentation accounts"
ManagedBy = "Terraform"
}
}
resource "aws_organizations_organizational_unit" "suspended" {
name = "Suspended"
parent_id = local.root_id
tags = {
Description = "Accounts pending deletion"
ManagedBy = "Terraform"
}
}
# Nested OUs under Workloads
resource "aws_organizations_organizational_unit" "production" {
name = "Production"
parent_id = aws_organizations_organizational_unit.workloads.id
tags = {
Description = "Production workload accounts"
Environment = "production"
ManagedBy = "Terraform"
}
}
resource "aws_organizations_organizational_unit" "non_production" {
name = "Non-Production"
parent_id = aws_organizations_organizational_unit.workloads.id
tags = {
Description = "Development, staging, and QA accounts"
Environment = "non-production"
ManagedBy = "Terraform"
}
}
# Outputs for use in other configurations
output "security_ou_id" {
value = aws_organizations_organizational_unit.security.id
description = "Security OU ID"
}
output "infrastructure_ou_id" {
value = aws_organizations_organizational_unit.infrastructure.id
description = "Infrastructure OU ID"
}
output "production_ou_id" {
value = aws_organizations_organizational_unit.production.id
description = "Production OU ID"
}
output "non_production_ou_id" {
value = aws_organizations_organizational_unit.non_production.id
description = "Non-Production OU ID"
}
output "sandbox_ou_id" {
value = aws_organizations_organizational_unit.sandbox.id
description = "Sandbox OU ID"
}
Organizational Unit Design Patterns: Architecture for Scale
The structure of your OUs determines how you apply policies, organize teams, and scale governance. Here are proven patterns for enterprise AWS organizations that have been validated across hundreds of implementations.
The Security-First OU Hierarchy
This pattern prioritizes security controls and places security accounts at the top level for maximum visibility and protection:
Root
├── Security (OU)
│ ├── Log Archive Account
│ ├── Security Tooling Account
│ └── Audit Account
├── Infrastructure (OU)
│ ├── Network Hub Account
│ ├── Shared Services Account
│ └── Identity Account (IAM Identity Center)
├── Workloads (OU)
│ ├── Production (Nested OU)
│ │ ├── App1-Prod Account
│ │ ├── App2-Prod Account
│ │ └── Data-Prod Account
│ └── Non-Production (Nested OU)
│ ├── Dev Account
│ ├── Staging Account
│ └── QA Account
├── Sandbox (OU)
│ └── Developer Sandbox Accounts
└── Suspended (OU)
└── Accounts pending deletion
Key Account Roles and Responsibilities
Management Account: This is your organization's root and should contain ONLY organization management resources. Never deploy workloads here—it's the most privileged account and should have minimal surface area. Access should be restricted to a small group of cloud platform administrators.
Log Archive Account: Centralized storage for CloudTrail logs, Config snapshots, VPC Flow Logs, and application logs from all accounts. Use S3 Object Lock to ensure log immutability for compliance. This account should have extremely restricted access—typically only security and compliance teams.
Security Tooling Account: Houses GuardDuty administrator, Security Hub aggregator, Amazon Detective, Inspector, and other security services that need organization-wide visibility. This is where your security operations team operates.
Network Hub Account: Contains Transit Gateway, shared VPCs, Direct Connect gateways, and centralized network infrastructure shared via Resource Access Manager. The network team manages this account.
Shared Services Account: Hosts shared resources like container registries (ECR), artifact repositories, shared databases, and central CI/CD infrastructure that multiple workload accounts consume.
Service Control Policies (SCPs): Guardrails for Governance
Service Control Policies are the most powerful governance tool in AWS Organizations. They define the maximum permissions available to any principal in member accounts—even administrators cannot exceed SCP boundaries. Understanding and implementing SCPs correctly is critical for enterprise security.
Understanding SCP Evaluation Logic
SCPs work as permission boundaries. Even if an IAM policy grants *:*, an SCP can restrict what's actually allowed. The effective permissions are the intersection of IAM policies and SCPs—a principal can only perform an action if BOTH the IAM policy allows it AND the SCP doesn't deny it.
Critical Point: SCPs affect all principals in an account EXCEPT the account's root user performing account management tasks. However, best practice is to never use root credentials for any operations.
Comprehensive Security SCP for Production Accounts
Here's a production-ready SCP that implements multiple security guardrails. Deploy this to your Production OU:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyLeavingOrganization",
"Effect": "Deny",
"Action": [
"organizations:LeaveOrganization"
],
"Resource": "*"
},
{
"Sid": "RequireIMDSv2",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotEquals": {
"ec2:MetadataHttpTokens": "required"
}
}
},
{
"Sid": "DenyPublicS3AccessChanges",
"Effect": "Deny",
"Action": [
"s3:PutBucketPublicAccessBlock",
"s3:DeletePublicAccessBlock",
"s3:PutAccountPublicAccessBlock",
"s3:DeleteAccountPublicAccessBlock"
],
"Resource": "*",
"Condition": {
"ArnNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/CloudPlatformAdmin"
}
}
},
{
"Sid": "RequireS3Encryption",
"Effect": "Deny",
"Action": "s3:PutObject",
"Resource": "*",
"Condition": {
"Null": {
"s3:x-amz-server-side-encryption": "true"
}
}
},
{
"Sid": "DenyUnencryptedEBSVolumes",
"Effect": "Deny",
"Action": "ec2:CreateVolume",
"Resource": "*",
"Condition": {
"Bool": {
"ec2:Encrypted": "false"
}
}
},
{
"Sid": "RequireRDSEncryption",
"Effect": "Deny",
"Action": [
"rds:CreateDBInstance",
"rds:CreateDBCluster"
],
"Resource": "*",
"Condition": {
"Bool": {
"rds:StorageEncrypted": "false"
}
}
},
{
"Sid": "ProtectSecurityServices",
"Effect": "Deny",
"Action": [
"guardduty:DeleteDetector",
"guardduty:DeleteMembers",
"guardduty:DisassociateFromMasterAccount",
"guardduty:StopMonitoringMembers",
"securityhub:DisableSecurityHub",
"securityhub:DeleteMembers",
"config:DeleteConfigurationRecorder",
"config:StopConfigurationRecorder",
"access-analyzer:DeleteAnalyzer"
],
"Resource": "*"
},
{
"Sid": "ProtectOrganizationCloudTrail",
"Effect": "Deny",
"Action": [
"cloudtrail:DeleteTrail",
"cloudtrail:StopLogging",
"cloudtrail:UpdateTrail",
"cloudtrail:PutEventSelectors"
],
"Resource": "arn:aws:cloudtrail:*:*:trail/organization-*"
},
{
"Sid": "RestrictToApprovedRegions",
"Effect": "Deny",
"NotAction": [
"iam:*",
"organizations:*",
"support:*",
"sts:*",
"cloudfront:*",
"route53:*",
"waf:*",
"wafv2:*",
"waf-regional:*",
"budgets:*",
"s3:GetBucketLocation",
"s3:ListAllMyBuckets",
"health:*",
"trustedadvisor:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"us-east-1",
"us-west-2",
"eu-west-1",
"eu-central-1"
]
}
}
}
]
}
This SCP enforces: IMDSv2 requirement for SSRF protection, S3 and EBS/RDS encryption requirements, protection of security services from tampering, CloudTrail integrity, and regional restrictions to approved regions only.
AWS Control Tower: Automated Landing Zone Governance
AWS Control Tower provides a pre-packaged landing zone with automated account provisioning and pre-configured guardrails. It's the fastest path to a well-architected multi-account environment and is recommended for most organizations.
Control Tower Core Components
Landing Zone: The foundational multi-account environment including the management account, log archive account, and audit account with pre-configured security settings that follow AWS best practices.
Guardrails: Pre-built preventive (SCPs) and detective (Config Rules) controls that enforce governance policies. Over 400 guardrails are available covering security, operations, and compliance.
Account Factory: Self-service portal for provisioning new accounts that automatically inherit organizational policies, VPC configurations, and baseline security controls.
Dashboard: Centralized view of organizational compliance status, account health, and guardrail violations across all enrolled accounts.
Enabling Control Tower Guardrails with Python boto3
import boto3
import time
from typing import List, Dict
def setup_control_tower_guardrails(target_ou_arn: str) -> Dict[str, str]:
"""
Enable additional Control Tower guardrails programmatically.
Control Tower must already be set up via the console.
Args:
target_ou_arn: The ARN of the OU to enable guardrails on
Returns:
Dictionary of guardrail names and their enable status
"""
controltower = boto3.client('controltower')
# Define strongly recommended guardrails to enable
guardrails_to_enable = [
'AWS-GR_ENCRYPTED_VOLUMES',
'AWS-GR_EBS_OPTIMIZED_INSTANCE',
'AWS-GR_EC2_VOLUME_INUSE_CHECK',
'AWS-GR_RDS_INSTANCE_PUBLIC_ACCESS_CHECK',
'AWS-GR_RDS_SNAPSHOTS_PUBLIC_PROHIBITED',
'AWS-GR_RESTRICTED_INCOMING_TRAFFIC',
'AWS-GR_S3_BUCKET_PUBLIC_READ_PROHIBITED',
'AWS-GR_S3_BUCKET_PUBLIC_WRITE_PROHIBITED',
'AWS-GR_LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED',
'AWS-GR_ROOT_ACCOUNT_MFA_ENABLED',
'AWS-GR_IAM_USER_MFA_ENABLED'
]
results = {}
# Enable each guardrail on the target OU
for guardrail in guardrails_to_enable:
try:
# Get the control identifier ARN
control_arn = f"arn:aws:controltower:us-east-1::control/{guardrail}"
controltower.enable_control(
controlIdentifier=control_arn,
targetIdentifier=target_ou_arn
)
results[guardrail] = "ENABLED"
print(f"Successfully enabled guardrail: {guardrail}")
time.sleep(2) # Avoid API throttling
except controltower.exceptions.ConflictException:
results[guardrail] = "ALREADY_ENABLED"
print(f"Guardrail already enabled: {guardrail}")
except Exception as e:
results[guardrail] = f"FAILED: {str(e)}"
print(f"Failed to enable {guardrail}: {e}")
return results
def list_enabled_controls(target_ou_arn: str) -> List[str]:
"""
List all currently enabled controls for an OU.
"""
controltower = boto3.client('controltower')
enabled_controls = []
paginator = controltower.get_paginator('list_enabled_controls')
for page in paginator.paginate(targetIdentifier=target_ou_arn):
for control in page['enabledControls']:
enabled_controls.append(control['controlIdentifier'])
return enabled_controls
if __name__ == "__main__":
# Example: Enable guardrails on Production OU
production_ou_arn = "arn:aws:organizations::123456789012:ou/o-example/ou-xxxx-yyyy"
results = setup_control_tower_guardrails(production_ou_arn)
print(f"\nGuardrail enablement results: {results}")
Cross-Account IAM Roles: Secure Access Patterns with Terraform
Cross-account access is fundamental to multi-account architecture. Applications in one account need to access resources in another, and administrators need to switch between accounts seamlessly. Here's a production-ready Terraform module:
# modules/cross-account-role/main.tf
# Terraform module for secure cross-account IAM role
variable "trusted_account_ids" {
description = "List of AWS account IDs that can assume this role"
type = list(string)
}
variable "role_name" {
description = "Name of the cross-account role"
type = string
}
variable "external_id" {
description = "External ID for additional security (recommended for third-party access)"
type = string
default = ""
}
variable "require_mfa" {
description = "Require MFA for role assumption"
type = bool
default = true
}
variable "max_session_duration" {
description = "Maximum session duration in seconds (1-12 hours)"
type = number
default = 3600
}
variable "policy_arns" {
description = "List of IAM policy ARNs to attach to the role"
type = list(string)
default = ["arn:aws:iam::aws:policy/SecurityAudit"]
}
# Define the trust policy with conditions
data "aws_iam_policy_document" "trust_policy" {
statement {
sid = "AllowCrossAccountAssumption"
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = [for account in var.trusted_account_ids : "arn:aws:iam::${account}:root"]
}
dynamic "condition" {
for_each = var.require_mfa ? [1] : []
content {
test = "Bool"
variable = "aws:MultiFactorAuthPresent"
values = ["true"]
}
}
dynamic "condition" {
for_each = var.external_id != "" ? [1] : []
content {
test = "StringEquals"
variable = "sts:ExternalId"
values = [var.external_id]
}
}
}
}
# Create the role
resource "aws_iam_role" "cross_account_role" {
name = var.role_name
assume_role_policy = data.aws_iam_policy_document.trust_policy.json
max_session_duration = var.max_session_duration
tags = {
Purpose = "Cross-account access"
ManagedBy = "Terraform"
}
}
# Attach managed policies
resource "aws_iam_role_policy_attachment" "policies" {
count = length(var.policy_arns)
role = aws_iam_role.cross_account_role.name
policy_arn = var.policy_arns[count.index]
}
output "role_arn" {
value = aws_iam_role.cross_account_role.arn
}
output "role_name" {
value = aws_iam_role.cross_account_role.name
}
AWS Config Aggregator: Centralized Compliance Visibility
AWS Config Aggregator provides a unified view of compliance across all accounts in your organization. Deploy an aggregator in your Security Tooling or Audit account:
AWSTemplateFormatVersion: '2010-09-09'
Description: AWS Config Aggregator for Organization-wide Compliance
Parameters:
AggregatorName:
Type: String
Default: OrganizationConfigAggregator
Description: Name for the Config Aggregator
AggregatorAccountId:
Type: String
Description: Account ID where the aggregator is deployed
Resources:
ConfigAggregatorRole:
Type: AWS::IAM::Role
Properties:
RoleName: AWSConfigRoleForOrganizations
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: config.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSConfigRoleForOrganizations
OrganizationConfigAggregator:
Type: AWS::Config::ConfigurationAggregator
Properties:
ConfigurationAggregatorName: !Ref AggregatorName
OrganizationAggregationSource:
RoleArn: !GetAtt ConfigAggregatorRole.Arn
AllAwsRegions: true
Tags:
- Key: Purpose
Value: Organization-wide Config aggregation
- Key: ManagedBy
Value: CloudFormation
ComplianceDashboard:
Type: AWS::CloudWatch::Dashboard
Properties:
DashboardName: OrgConfigCompliance
DashboardBody: !Sub |
{
"widgets": [
{
"type": "metric",
"properties": {
"title": "Compliant Resources",
"metrics": [
["AWS/Config", "ComplianceByConfigRule", "AggregatorName", "${AggregatorName}"]
],
"period": 3600,
"region": "${AWS::Region}"
}
}
]
}
Outputs:
AggregatorArn:
Description: Config Aggregator ARN
Value: !Ref OrganizationConfigAggregator
Export:
Name: !Sub '${AWS::StackName}-AggregatorArn'
CloudFormation StackSets: Multi-Account Deployments at Scale
CloudFormation StackSets enable you to deploy resources across multiple accounts and regions from a single template. This is essential for deploying baseline configurations, security controls, and compliance requirements:
#!/bin/bash
# Deploy security baseline to all accounts in Production OU
# Create the StackSet (run from Management Account)
aws cloudformation create-stack-set \
--stack-set-name SecurityBaseline \
--template-body file://security-baseline.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--permission-model SERVICE_MANAGED \
--auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false \
--managed-execution Active=true \
--description "Security baseline configuration for all member accounts"
# Deploy to Production OU
PROD_OU_ID="ou-xxxx-production"
aws cloudformation create-stack-instances \
--stack-set-name SecurityBaseline \
--deployment-targets OrganizationalUnitIds=$PROD_OU_ID \
--regions us-east-1 us-west-2 eu-west-1 \
--operation-preferences \
FailureTolerancePercentage=10,MaxConcurrentPercentage=25,RegionConcurrencyType=PARALLEL
echo "StackSet deployment initiated to Production OU"
# Check deployment status
aws cloudformation describe-stack-set-operation \
--stack-set-name SecurityBaseline \
--operation-id $(aws cloudformation list-stack-set-operations \
--stack-set-name SecurityBaseline \
--query 'Summaries[0].OperationId' --output text)
Tag Policies: Enforcing Consistent Resource Tagging
Tag policies ensure consistent resource tagging across your organization, enabling accurate cost allocation, automation, and governance:
{
"tags": {
"Environment": {
"tag_key": {
"@@assign": "Environment"
},
"tag_value": {
"@@assign": [
"production",
"staging",
"development",
"sandbox"
]
},
"enforced_for": {
"@@assign": [
"ec2:instance",
"ec2:volume",
"rds:db",
"s3:bucket",
"lambda:function"
]
}
},
"CostCenter": {
"tag_key": {
"@@assign": "CostCenter"
},
"tag_value": {
"@@assign": [
"CC-100-Engineering",
"CC-200-Marketing",
"CC-300-Operations",
"CC-400-Security"
]
}
},
"Owner": {
"tag_key": {
"@@assign": "Owner"
}
},
"Application": {
"tag_key": {
"@@assign": "Application"
}
}
}
}
Backup Policies: Organization-Wide Data Protection
AWS Backup policies ensure consistent backup schedules across all accounts. Deploy backup policies at the organization level:
{
"plans": {
"ProductionBackupPlan": {
"regions": {
"@@assign": ["us-east-1", "us-west-2", "eu-west-1"]
},
"rules": {
"DailyBackups": {
"schedule_expression": {
"@@assign": "cron(0 5 ? * * *)"
},
"start_backup_window_minutes": {
"@@assign": "60"
},
"complete_backup_window_minutes": {
"@@assign": "180"
},
"lifecycle": {
"delete_after_days": {
"@@assign": "35"
},
"move_to_cold_storage_after_days": {
"@@assign": "7"
}
},
"target_backup_vault_name": {
"@@assign": "OrganizationBackupVault"
},
"copy_actions": {
"arn:aws:backup:us-west-2:$account:backup-vault:CrossRegionVault": {
"lifecycle": {
"delete_after_days": {
"@@assign": "35"
}
}
}
}
},
"MonthlyBackups": {
"schedule_expression": {
"@@assign": "cron(0 6 1 * ? *)"
},
"lifecycle": {
"delete_after_days": {
"@@assign": "365"
},
"move_to_cold_storage_after_days": {
"@@assign": "30"
}
},
"target_backup_vault_name": {
"@@assign": "OrganizationBackupVault"
}
}
},
"selections": {
"tags": {
"BackupRequired": {
"iam_role_arn": {
"@@assign": "arn:aws:iam::$account:role/AWSBackupDefaultServiceRole"
},
"tag_key": {
"@@assign": "BackupRequired"
},
"tag_value": {
"@@assign": ["true", "yes", "1"]
}
}
}
}
}
}
}
Centralized Logging Architecture with Resource Access Manager
Implement centralized logging with RAM to share log destinations across accounts:
# centralized-logging/main.tf
resource "aws_s3_bucket" "central_logs" {
bucket = "org-central-logs-${var.log_archive_account_id}"
tags = {
Purpose = "Centralized logging for organization"
}
}
resource "aws_s3_bucket_versioning" "central_logs" {
bucket = aws_s3_bucket.central_logs.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_lifecycle_configuration" "central_logs" {
bucket = aws_s3_bucket.central_logs.id
rule {
id = "log-lifecycle"
status = "Enabled"
transition {
days = 30
storage_class = "STANDARD_IA"
}
transition {
days = 90
storage_class = "GLACIER"
}
transition {
days = 365
storage_class = "DEEP_ARCHIVE"
}
expiration {
days = 2555
}
}
}
# Enable RAM sharing for the log group
resource "aws_ram_resource_share" "logs" {
name = "CentralizedLogs"
allow_external_principals = false
tags = {
Purpose = "Share log destinations across organization"
}
}
resource "aws_ram_principal_association" "org" {
principal = var.organization_arn
resource_share_arn = aws_ram_resource_share.logs.arn
}
Working with Warqline
We are a cloud engineering consultancy and an official AWS and Google Cloud partner. If you are running this in production and want a second pair of eyes, we scope work in a free 45-minute technical call: you describe what you are running and what worries you, and we tell you what we would look at first.
Conclusion: Building Enterprise-Grade Multi-Account Architecture
A well-designed multi-account strategy with AWS Organizations transforms how enterprises manage cloud infrastructure. The benefits compound as your organization scales:
Security at Scale: Account boundaries provide blast radius containment that no amount of IAM policy engineering can match.
Financial Clarity: Automatic cost attribution eliminates the pain of cost allocation tagging.
Governance Automation: SCPs, Control Tower guardrails, tag policies, and backup policies ensure consistent controls.
Operational Efficiency: Centralized logging, shared networking, Config Aggregator, and StackSets reduce overhead.
Compliance Readiness: Environment separation and immutable logs satisfy auditor requirements.
Key Implementation Takeaways
- Start with OU design: Your OU structure determines how policies apply. Get it right early.
- Management account is sacred: Never deploy workloads in the management account.
- SCPs are guardrails, not policies: Use SCPs to set boundaries, not grant permissions.
- Automate account vending: Manual account creation doesn't scale.
- Centralize logging immediately: Set up Log Archive as part of initial deployment.
- Leverage Control Tower: Use it unless you have specific requirements it can't meet.
- Monitor continuously: Tools like Warqline provide the unified view you need.
Running this across a lot of accounts and want a second opinion on it? Talk to an engineer — we scope the work in a free 45-minute call.