Skip to content

Phase 1: Hands-On Job-Essential Lab Solutions

This module contains complete, production-grade Terraform manifests, AWS CLI commands, and architectural validation for the Phase 1 Job-Essential Exercises.


πŸ› οΈ Lab 1: The Multi-Account Organizational Vending Machine

Objective

Deploy an AWS Organization structure with root governance, an Infrastructure Organizational Unit (OU), and a Workloads OU with child member accounts using declarative Terraform.

Production Terraform Manifest (organizations.tf)

# 1. Enable AWS Organizations with all features enabled
resource "aws_organizations_organization" "org" {
  aws_service_access_principals = [
    "cloudtrail.amazonaws.com",
    "config.amazonaws.com",
    "sso.amazonaws.com",
    "ram.amazonaws.com"
  ]
  feature_set = "ALL"
}

# 2. Create Core Organizational Units
resource "aws_organizations_organizational_unit" "infrastructure" {
  name      = "Infrastructure"
  parent_id = aws_organizations_organization.org.roots[0].id
}

resource "aws_organizations_organizational_unit" "workloads" {
  name      = "Workloads"
  parent_id = aws_organizations_organization.org.roots[0].id
}

# 3. Provision Member Accounts directly into their respective OUs
resource "aws_organizations_account" "network_hub" {
  name      = "Network-Hub-Production"
  email     = "aws-network-hub@corp.internal"
  parent_id = aws_organizations_organizational_unit.infrastructure.id
  role_name = "OrganizationAccountAccessRole"
}

resource "aws_organizations_account" "workload_prod" {
  name      = "EKS-Workload-Production"
  email     = "aws-workload-prod@corp.internal"
  parent_id = aws_organizations_organizational_unit.workloads.id
  role_name = "OrganizationAccountAccessRole"
}

πŸ›‘οΈ Lab 2: The Region-Restriction Service Control Policy (SCP)

Objective

Write an SCP and attach it to the Workloads OU that strictly denies any resource creation outside of us-east-1 and us-west-2, while exempting global services (IAM, Route53, CloudFront). Test and prove that launching an EC2 instance in eu-west-1 fails.

1. The SCP Policy Manifest (region_restriction_scp.tf)

resource "aws_organizations_policy" "deny_unapproved_regions" {
  name        = "DenyUnapprovedRegions"
  description = "Prevents any resource creation outside us-east-1 and us-west-2"
  type        = "SERVICE_CONTROL_POLICY"

  content = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "DenyAllOutsideApprovedRegions"
        Effect = "Deny"
        NotAction = [
          "iam:*",
          "organizations:*",
          "route53:*",
          "budgets:*",
          "cloudfront:*",
          "support:*",
          "waf:*"
        ]
        Resource = "*"
        Condition = {
          StringNotEquals = {
            "aws:RequestedRegion" = [
              "us-east-1",
              "us-west-2"
            ]
          }
        }
      }
    ]
  })
}

# Attach SCP directly to Workloads OU
resource "aws_organizations_policy_attachment" "workloads_scp_attach" {
  policy_id = aws_organizations_policy.deny_unapproved_regions.id
  target_id = aws_organizations_organizational_unit.workloads.id
}

2. Negative Verification Test (Proving Policy Enforcement)

Run the following AWS CLI command inside the member account targeting eu-west-1:

aws ec2 run-instances \
    --image-id ami-0c55b159cbfafe1f0 \
    --instance-type t3.nano \
    --region eu-west-1

Expected Result (Access Denied):

An error occurred (Client.UnauthorizedOperation) when calling the RunInstances operation: 
You are not authorized to perform this operation. User: arn:aws:sts::987654321098:assumed-role/Admin 
is not authorized to perform: ec2:RunInstances on resource with an explicit deny in a service control policy.


🌐 Lab 3: Centralized Egress with AWS Transit Gateway & RAM

Objective

Architect a Hub-and-Spoke network where Account A (Network Hub) contains the Transit Gateway and NAT Gateway, and Account B (Workload) routes all 0.0.0.0/0 outbound traffic over the TGW through Account A.

flowchart LR
    subgraph SpokeAcc ["Account B (Workload)"]
        SpokeEC2["Private EC2 / Pod (10.2.1.50)"]
        SpokeRT["Route Table<br/>0.0.0.0/0 -> tgw-xxx"]
        SpokeEC2 --> SpokeRT
    end

    subgraph HubAcc ["Account A (Central Networking)"]
        TGW["AWS Transit Gateway"]
        NAT["Central NAT Gateway"]
        IGW["Internet Gateway"]

        TGW --> NAT --> IGW
    end

    SpokeRT -->|"TGW Attachment (Shared via RAM)"| TGW
    IGW --> Internet(("🌐 Public Internet"))

1. In Network Hub Account: Share TGW via AWS RAM

# Create Transit Gateway
resource "aws_ec2_transit_gateway" "hub_tgw" {
  description = "Central Enterprise Transit Gateway"
  tags        = { Name = "hub-tgw" }
}

# Share TGW with Organization
resource "aws_ram_resource_share" "tgw_share" {
  name                      = "tgw-org-share"
  allow_external_principals = false
}

resource "aws_ram_resource_association" "tgw_assoc" {
  resource_arn       = aws_ec2_transit_gateway.hub_tgw.arn
  resource_share_arn = aws_ram_resource_share.tgw_share.arn
}

resource "aws_ram_principal_association" "org_principal" {
  principal          = aws_organizations_organization.org.arn
  resource_share_arn = aws_ram_resource_share.tgw_share.arn
}

2. In Workload Account: Route 0.0.0.0/0 to TGW Attachment

# Workload VPC Attachment to the Shared TGW
resource "aws_ec2_transit_gateway_vpc_attachment" "workload_attach" {
  transit_gateway_id = data.aws_ec2_transit_gateway.shared_tgw.id
  vpc_id             = aws_vpc.workload_vpc.id
  subnet_ids         = aws_subnet.tgw_subnets[*].id # Dedicated /28 subnets
}

# Private Route Table: Route all external internet traffic to TGW
resource "aws_route" "private_to_tgw" {
  route_table_id         = aws_route_table.workload_private.id
  destination_cidr_block = "0.0.0.0/0"
  transit_gateway_id     = data.aws_ec2_transit_gateway.shared_tgw.id
}