aws
Markdown

setup guide

AWS Control Tower Complete Setup Guide

Prerequisites: Email Creation

Required Gmail Accounts (Must Create Manually)

  1. testawsrahardja@gmail.com (existing - Management Account)
  2. testawsrahardja-audit@gmail.com (create new)
  3. testawsrahardja-logs@gmail.com (create new)

Alias Emails (Automatic)

  1. testawsrahardja+prod@gmail.com
  2. testawsrahardja+test@gmail.com
  3. testawsrahardja+dev@gmail.com
  4. testawsrahardja+shared@gmail.com (optional)

Phase 1: Control Tower Setup

Console Method

  1. Sign in to AWS with testawsrahardja@gmail.com
  2. Navigate to AWS Control Tower service
  3. Click "Set up landing zone"
  4. Configure organizational units (Security, Sandbox)
  5. Provide emails for Audit and Log Archive accounts
  6. Wait 60-90 minutes for setup completion
  7. Verify accounts created in AWS Organizations

Terraform Method

# main.tf
resource "aws_controltower_control_tower" "main" {
  audit_account_email      = "testawsrahardja-audit@gmail.com"
  log_archive_account_email = "testawsrahardja-logs@gmail.com"

  organizational_unit_names = [
    "Security",
    "Sandbox"
  ]
}

resource "aws_organizations_account" "workload_accounts" {
  for_each = {
    prod   = "testawsrahardja+prod@gmail.com"
    test   = "testawsrahardja+test@gmail.com"
    dev    = "testawsrahardja+dev@gmail.com"
    shared = "testawsrahardja+shared@gmail.com"
  }

  name  = "${each.key}-account"
  email = each.value
}

CDK Method

import * as controltower from "@aws-cdk/aws-controltower";

export class ControlTowerStack extends Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    new controltower.CfnControlTower(this, "ControlTower", {
      auditAccountEmail: "testawsrahardja-audit@gmail.com",
      logArchiveAccountEmail: "testawsrahardja-logs@gmail.com",
      organizationalUnitNames: ["Security", "Sandbox"],
    });

    const accounts = ["prod", "test", "dev", "shared"];
    accounts.forEach((env) => {
      new organizations.CfnAccount(this, `${env}Account`, {
        accountName: `${env}-account`,
        email: `testawsrahardja+${env}@gmail.com`,
      });
    });
  }
}

Phase 2: Root User Security (MANUAL ONLY)

⚠️ WARNING Cannot be automated - must do manually for each account

For Each Account (6 total):

  1. Check email for AWS account creation confirmation
  2. Click verification link in email
  3. Set root password (use password manager)
  4. Sign in to AWS Console as root user
  5. Navigate to Account Settings
  6. Enable MFA (use authenticator app)
  7. Test MFA login to verify it works
  8. Store credentials securely (never use again)

Phase 3: Identity Center (SSO) Setup

Console Method

  1. Navigate to IAM Identity Center
  2. Choose identity source (Identity Center directory)
  3. Create users (your actual working users)
  4. Create groups (Admins, Developers, ReadOnly)
  5. Create permission sets:
    • AdministratorAccess
    • PowerUserAccess
    • ReadOnlyAccess
    • DeveloperAccess
  6. Assign users to groups
  7. Assign permission sets to accounts
  8. Test SSO access to each account

Terraform Method

# Identity Center setup
resource "aws_ssoadmin_permission_set" "admin" {
  name             = "AdministratorAccess"
  instance_arn     = data.aws_ssoadmin_instances.main.arns[0]
  session_duration = "PT2H"
}

resource "aws_ssoadmin_managed_policy_attachment" "admin" {
  instance_arn       = data.aws_ssoadmin_instances.main.arns[0]
  managed_policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
  permission_set_arn = aws_ssoadmin_permission_set.admin.arn
}

resource "aws_ssoadmin_account_assignment" "admin_prod" {
  instance_arn       = data.aws_ssoadmin_instances.main.arns[0]
  permission_set_arn = aws_ssoadmin_permission_set.admin.arn
  principal_id       = aws_identitystore_group.admins.group_id
  principal_type     = "GROUP"
  target_id          = aws_organizations_account.workload_accounts["prod"].id
  target_type        = "AWS_ACCOUNT"
}

CDK Method

import * as sso from "@aws-cdk/aws-sso";

// Permission sets
const adminPermissionSet = new sso.CfnPermissionSet(
  this,
  "AdminPermissionSet",
  {
    instanceArn: identityCenterInstanceArn,
    name: "AdministratorAccess",
    sessionDuration: "PT2H",
  },
);

// Account assignments
new sso.CfnAssignment(this, "AdminProdAssignment", {
  instanceArn: identityCenterInstanceArn,
  permissionSetArn: adminPermissionSet.attrPermissionSetArn,
  principalId: adminGroupId,
  principalType: "GROUP",
  targetId: prodAccountId,
  targetType: "AWS_ACCOUNT",
});

Phase 4: Account Baseline Configuration

Console Method (Per Account)

  1. Switch role via SSO to each account
  2. Set up VPC and networking
  3. Configure CloudTrail (additional trails)
  4. Set up CloudWatch alarms
  5. Create billing alerts
  6. Configure AWS Config rules
  7. Set up cross-account IAM roles

Terraform Method

module "account_baseline" {
  source = "./modules/account-baseline"

  for_each = var.accounts

  account_name = each.key
  account_id   = each.value.id
  vpc_cidr     = each.value.vpc_cidr

  # Common baseline resources
  enable_cloudtrail     = true
  enable_config         = true
  enable_guardduty      = true
  enable_security_hub   = true

  # Account-specific settings
  environment = each.key
}

# modules/account-baseline/main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name        = "${var.account_name}-vpc"
    Environment = var.environment
  }
}

resource "aws_cloudwatch_metric_alarm" "billing" {
  alarm_name          = "billing-alarm"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "2"
  metric_name         = "EstimatedCharges"
  namespace           = "AWS/Billing"
  period              = "86400"
  statistic           = "Maximum"
  threshold           = var.billing_threshold
  alarm_description   = "This metric monitors aws billing"
}

CDK Method

export class AccountBaselineStack extends Stack {
  constructor(scope: Construct, id: string, props: AccountBaselineProps) {
    super(scope, id, props);

    // VPC setup
    const vpc = new ec2.Vpc(this, "VPC", {
      cidr: props.vpcCidr,
      maxAzs: 3,
      natGateways: props.environment === "prod" ? 3 : 1,
    });

    // CloudTrail
    new cloudtrail.Trail(this, "CloudTrail", {
      bucket: s3.Bucket.fromBucketName(
        this,
        "LoggingBucket",
        props.loggingBucket,
      ),
      includeGlobalServiceEvents: true,
      isMultiRegionTrail: true,
    });

    // Billing alarm
    new cloudwatch.Alarm(this, "BillingAlarm", {
      metric: new cloudwatch.Metric({
        namespace: "AWS/Billing",
        metricName: "EstimatedCharges",
        statistic: "Maximum",
        period: Duration.days(1),
      }),
      threshold: props.billingThreshold,
      evaluationPeriods: 2,
    });
  }
}

Phase 5: Governance and Compliance

Service Control Policies (SCPs)

Console Method

  1. Navigate to AWS Organizations
  2. Go to Policies → Service control policies
  3. Create custom SCPs for:
    • Prevent root user usage
    • Restrict regions
    • Enforce MFA
    • Prevent account closure
  4. Attach policies to appropriate OUs

Terraform Method

resource "aws_organizations_policy" "restrict_regions" {
  name        = "RestrictRegions"
  description = "Restrict usage to specific regions"
  type        = "SERVICE_CONTROL_POLICY"

  content = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Deny"
        Action = "*"
        Resource = "*"
        Condition = {
          StringNotEquals = {
            "aws:RequestedRegion" = [
              "us-east-1",
              "us-west-2"
            ]
          }
        }
      }
    ]
  })
}

resource "aws_organizations_policy_attachment" "restrict_regions" {
  policy_id = aws_organizations_policy.restrict_regions.id
  target_id = aws_organizations_organizational_unit.workloads.id
}

CDK Method

const restrictRegionsPolicy = new organizations.CfnPolicy(
  this,
  "RestrictRegions",
  {
    name: "RestrictRegions",
    description: "Restrict usage to specific regions",
    type: "SERVICE_CONTROL_POLICY",
    content: JSON.stringify({
      Version: "2012-10-17",
      Statement: [
        {
          Effect: "Deny",
          Action: "*",
          Resource: "*",
          Condition: {
            StringNotEquals: {
              "aws:RequestedRegion": ["us-east-1", "us-west-2"],
            },
          },
        },
      ],
    }),
  },
);

Summary Comparison

Task Console Terraform CDK
Email Creation Manual Manual Manual
Control Tower Setup 60-90 min manual Automated Automated
Root User Security Manual (critical) Manual (critical) Manual (critical)
Identity Center Manual config Mostly automated Mostly automated
Account Baseline Manual per account Fully automated Fully automated
Governance/SCPs Manual policy creation Fully automated Fully automated
Maintenance Manual updates Version controlled Version controlled
Complexity Low learning curve Medium complexity High learning curve
Repeatability Poor Excellent Excellent
Team Collaboration Difficult Excellent Excellent

Final Checklist

  • Create 3 Gmail accounts manually
  • Set up Control Tower (console/terraform/cdk)
  • Enable MFA on all 6 root users (manual only)
  • Configure Identity Center SSO
  • Set up account baselines
  • Implement governance policies
  • Test access to all accounts via SSO
  • Document all credentials securely
  • Never use root users again