AWS Control Tower Complete Setup Guide
Prerequisites: Email Creation
Required Gmail Accounts (Must Create Manually)
testawsrahardja@gmail.com(existing - Management Account)testawsrahardja-audit@gmail.com(create new)testawsrahardja-logs@gmail.com(create new)
Alias Emails (Automatic)
testawsrahardja+prod@gmail.comtestawsrahardja+test@gmail.comtestawsrahardja+dev@gmail.comtestawsrahardja+shared@gmail.com(optional)
Phase 1: Control Tower Setup
Console Method
- Sign in to AWS with
testawsrahardja@gmail.com - Navigate to AWS Control Tower service
- Click "Set up landing zone"
- Configure organizational units (Security, Sandbox)
- Provide emails for Audit and Log Archive accounts
- Wait 60-90 minutes for setup completion
- 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):
- Check email for AWS account creation confirmation
- Click verification link in email
- Set root password (use password manager)
- Sign in to AWS Console as root user
- Navigate to Account Settings
- Enable MFA (use authenticator app)
- Test MFA login to verify it works
- Store credentials securely (never use again)
Phase 3: Identity Center (SSO) Setup
Console Method
- Navigate to IAM Identity Center
- Choose identity source (Identity Center directory)
- Create users (your actual working users)
- Create groups (Admins, Developers, ReadOnly)
- Create permission sets:
AdministratorAccessPowerUserAccessReadOnlyAccessDeveloperAccess
- Assign users to groups
- Assign permission sets to accounts
- 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)
- Switch role via SSO to each account
- Set up VPC and networking
- Configure CloudTrail (additional trails)
- Set up CloudWatch alarms
- Create billing alerts
- Configure AWS Config rules
- 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
- Navigate to AWS Organizations
- Go to Policies → Service control policies
- Create custom SCPs for:
- Prevent root user usage
- Restrict regions
- Enforce MFA
- Prevent account closure
- 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