Skip to content

Field to Cloud — Architecture & Operations

Everything in this course is infrastructure as code. One terraform apply (locally, via CI, or from the student's own machine) produces the entire lab. Nothing is clicked together in a console.


1. Deployment steps — from clone to lab

flowchart TD
    A["📦 git clone github.com/helhindi/devops-course"] --> B["🔑 AWS + gh CLI authenticated (OIDC — no static keys)"]
    B --> C["☁️ ./scripts/bootstrap-aws.sh<br/>(once, by the owner)"]
    C --> C1["creates S3 state bucket + DynamoDB lock table"]
    C1 --> C2["creates GitHub OIDC provider + IAM role"]
    C2 --> C3["registers repo VARIABLES via gh:<br/>TF_STATE_BUCKET · TF_LOCK_TABLE · AWS_DEPLOY_ROLE_ARN · keys"]
    C3 --> D["🏗️ cd terraform && terraform init<br/>-backend-config=… (shared remote state)"]
    D --> E["terraform plan — shows the whole stack, auto-tagged"]
    E --> F["terraform apply — provisions everything"]
    F --> G["✅ Resultant infrastructure (below)"]
    G --> H1["👩‍🎓 Student: ssh ubuntu@lab.course.elhindi.net"]
    G --> H2["🌐 Course site: https://course.elhindi.net"]
    G --> H3["✅ Progress tracker auto-verifies tasks"]
    G --> H4["⏹ 90-min idle → auto-stop (or pill buttons)"]

2. Resultant infrastructure

flowchart LR
    subgraph EDGE["Edge (CloudFront, eu-west-1 ACM cert)"]
        CF["🌐 CloudFront<br/>course.elhindi.net"]
        FN["⚡ CloudFront Function<br/>/progress/ → index.html"]
    end

    subgraph STATIC["Serverless course (always on, ~$0 idle)"]
        S3["🗄️ S3 bucket<br/>devops-course-elhindi"]
        LAM_PROG["⚡ Lambda · progress tracker<br/>GET/PUT checklist + site activity"]
        DDB_PROG[("🧱 DynamoDB<br/>cloud-course-progress<br/>student + auto items")]
        LAM_CTRL["⚡ Lambda · lab control<br/>GET / · POST /start · POST /stop"]
        DDB_ACT[("🧱 DynamoDB<br/>cloud-course-activity")]
        LAM_STOP["⚡ Lambda · auto-stop<br/>(EventBridge, 10 min)"]
        EB["⏰ EventBridge rule<br/>rate(10 minutes)"]
    end

    subgraph LAB["Training lab (EC2 — costs while running)"]
        VPC["VPC 10.42.0.0/16<br/>SG: key-based SSH, world-open"]
        LABBOX["🖥️ lab-box · t3.small<br/>tools · podman · terraform<br/>verify agent + heartbeat cron"]
        MONBOX["🖥️ monitor-box · t3.small<br/>Grafana :3000 · Prometheus :9090<br/>module 7"]
    end

    subgraph DNS["Dynamic DNS (Route53 · elhindi.net)"]
        R53["lab.course.elhindi.net → lab-box IP<br/>monitor.course.elhindi.net → monitor IP<br/>(refreshed on every start)"]
    end

    subgraph AUTH["Auth & cost (OIDC only)"]
        OIDC["GitHub OIDC provider<br/>role: github-actions-cloud-course"]
        BUDGET["💵 $10/mo budget<br/>alerts 30/50/80/90%"]
        SSM["SSM Session Manager<br/>admin from any IP"]
    end

    USER["👩‍🎓 Student / 👨‍🏫 Instructor"] --> CF
    CF --> FN --> S3
    S3 --> LAM_PROG --> DDB_PROG
    USER --> LAM_CTRL --> DDB_ACT
    LAM_STOP --> EB
    LAM_STOP --> DDB_ACT
    LAM_STOP -->|"stop if idle > 90 min"| LABBOX
    LAM_STOP -->|"stop if idle > 90 min"| MONBOX
    LAM_CTRL -->|"start/stop + refresh DNS"| LABBOX
    LAM_CTRL -->|"start/stop + refresh DNS"| MONBOX
    LAM_CTRL --> R53
    LABBOX -->|"heartbeat (SSH active)"| DDB_ACT
    LABBOX -->|"auto-verify ✓"| DDB_PROG
    USER --> R53
    USER --> LABBOX
    USER --> MONBOX
    AUTH -.->|"assumes role"| CF
    AUTH -.->|"assumes role"| LAM_CTRL

3. CI/CD pipeline (every merge to main)

flowchart LR
    PR["PR: feature/branch"] --> CONV["✅ Commit convention<br/>[feature|bugfix|hotfix]/<branch>: desc"]
    CONV --> PLAN["✅ Terraform Plan (multi-OS)<br/>ubuntu · macos · windows"]
    PLAN --> MERGE["🔀 Merge to main"]
    MERGE --> VER["🔖 Version & release<br/>semver tag + GitHub release"]
    VER --> APPLY["✅ Terraform Apply (OIDC)<br/>S3 state + DynamoDB lock"]
    APPLY --> PUB["✅ Publish Course Site<br/>mkdocs → S3 → CloudFront"]
    PUB --> SITE["🌐 course.elhindi.net live"]

4. Auto-stop & auto-verify (the cost guardrails)

sequenceDiagram
    participant S as Student
    participant LB as lab-box
    participant A as activity table
    participant ST as auto-stop Lambda
    participant P as progress table
    participant TR as tracker page

    S->>LB: SSH session opens
    loop every 5 min
        LB->>A: heartbeat last_seen (only while SSH active)
    end
    S->>TR: opens /progress/ or ticks a task
    TR->>A: site activity record
    loop every 10 min
        ST->>A: read latest activity
        alt idle > 90 min
            ST->>LB: StopInstances
            ST->>MON: StopInstances
        end
    end
    loop every 15 min (lab box)
        LB->>LB: run verify checks (tools, ports, files, tf, podman)
        LB->>P: write id=auto results
        TR->>P: GET progress + auto_keys → ✓ AUTO badges
    end

5. Key commands (single source of truth)

# One-time bootstrap (owner, AWS + gh authed):
./scripts/bootstrap-aws.sh

# Any engineer / student — full stack:
cd terraform
terraform init \
  -backend-config="bucket=$TF_STATE_BUCKET" \
  -backend-config="key=cloud-course/terraform.tfstate" \
  -backend-config="region=eu-west-2" \
  -backend-config="dynamodb_table=$TF_LOCK_TABLE"
terraform plan
terraform apply          # or: destroy
terraform output         # SSH hostnames, token, URLs

# Manual lab control:
./scripts/lab-ctl.sh start|stop|status|ssh|verify

# CI (no secrets needed — OIDC + repo variables):
gh workflow run terraform-apply.yml
gh workflow run publish-course-site.yml

6. Design decisions at a glance

Concern Choice Why
Course content S3 + CloudFront + Lambda serverless, always on, ~$0 idle
Hands-on labs 2 × EC2 (t3.small) only part that costs while running
Cost $10/mo budget + 90-min auto-stop alarms at 30/50/80/90% to both engineers
Access key-based SSH (world-open) + SSM travel-proof, no IP chasing
DNS lab/monitor.course.elhindi.net A records IPs change on stop/start; DNS follows
AWS auth OIDC only, repo variables via gh zero manually inserted secrets
Tags terraform/modules/tags/ + default_tags every resource tagged automatically
Versioning semver tag on every mainline merge resources carry appVersion=<tag>
Progress auto-verify agent on lab-box tasks tick ✓ AUTO when genuinely done
State S3 + DynamoDB lock two engineers, no drift