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.

Talk to an engineer

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

  1. Start with OU design: Your OU structure determines how policies apply. Get it right early.
  2. Management account is sacred: Never deploy workloads in the management account.
  3. SCPs are guardrails, not policies: Use SCPs to set boundaries, not grant permissions.
  4. Automate account vending: Manual account creation doesn't scale.
  5. Centralize logging immediately: Set up Log Archive as part of initial deployment.
  6. Leverage Control Tower: Use it unless you have specific requirements it can't meet.
  7. 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.