Chapter

Chapter 2: Core Concepts & Technology Stack


Overview

Understand the core concepts behind a production-grade, multi-environment AWS Infrastructure as Code (IaC) setup built with Terraform and Terragrunt. This chapter explains how the stack fits together (VPC, EKS, RDS), how DRY configuration enables O(1) environment scaling, and the operational foundations you need for repeatable, secure deployments.


2.1 Infrastructure as Code Fundamentals

The Friction

  • ✕ Duplication Nightmare: Copy-pasting code across envs ensures drift & bugs.
  • ✕ Unlimited Blast Radius: One bad apply destroys Prod because state is shared.
  • ✕ Dev Friction: Developers wait days for 'simple' infra changes.
  • ✕ Security Vulnerabilities: Open buckets & loose IAM roles are the default.

The R8way Solution

  • ✓ DRY Architecture: Write once, deploy everywhere with reusable modules.
  • ✓ Account Isolation: Separate states & accounts limit damage to zero.
  • ✓ Self-Service Config: Devs change values.yaml, not Terraform code.
  • ✓ Secure Defaults: Hardened out of the box.

2.2 Terraform Overview

What is Terraform?

Terraform is an open-source Infrastructure as Code tool created by HashiCorp. It allows you to define infrastructure using a declarative configuration language called HCL (HashiCorp Configuration Language).

Core Terraform Concepts

Resources

Resources are the most important element in Terraform. Each resource block describes one or more infrastructure objects.

Code
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "main-vpc"
  }
}

Breakdown:

  • resource: Keyword declaring a resource
  • "aws_vpc": Resource type (provider + resource)
  • "main": Local name (used to reference this resource)
  • Block content: Configuration arguments

Variables

Variables allow you to parameterize your configurations.

Code
variable "vpc_cidr" {
  description = "CIDR block for VPC"
  type        = string
  default     = "10.0.0.0/16"
}

# Usage
resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr
}

Variable Types:

  • string: Text values
  • number: Numeric values
  • bool: true/false
  • list(type): Ordered collection
  • map(type): Key-value pairs
  • object({...}): Complex structures

Outputs

Outputs expose values from your configuration for use by other configurations or for display.

Code
output "vpc_id" {
  description = "The ID of the VPC"
  value       = aws_vpc.main.id
}

Modules

Modules are containers for multiple resources that are used together. Every Terraform configuration has at least one module (the root module).

Code
module "vpc" {
  source = "./modules/vpc"

  env      = "production"
  vpc_cidr = "10.0.0.0/16"
}

# Access module outputs
resource "aws_instance" "app" {
  subnet_id = module.vpc.private_subnets[0]
}

Terraform Workflow

flowchart TD; classDef plain fill:#f1f5f9,stroke:#94a3b8,color:#334155,stroke-width:1px; classDef navy fill:#1e3a5f,stroke:#2c5282,color:#ffffff,stroke-width:2px; classDef gold fill:#d4af37,stroke:#f4d03f,color:#1e3a5f,stroke-width:2px; classDef amber fill:#f59e0b,stroke:#fbbf24,color:#ffffff,stroke-width:2px; A[Code Changes]:::plain -->|Plan| B(Terraform Plan):::gold; B -->|Review| C{Approval}:::amber; C -->|Yes| D[Apply]:::gold; C -->|No| E[Refine]:::navy; D --> F[State Update]:::navy;

State Management

Terraform State is a critical concept. The state file maps your configuration to real-world resources.

State File Contents:

Code
{
  "version": 4,
  "resources": [
    {
      "type": "aws_vpc",
      "name": "main",
      "instances": [{
        "attributes": {
          "id": "vpc-12345",
          "cidr_block": "10.0.0.0/16"
        }
      }]
    }
  ]
}

Why State Matters:

  • Tracks resource metadata
  • Improves performance (caching)
  • Enables collaboration
  • Manages dependencies

State Storage:

  • Local: terraform.tfstate file (development only)
  • Remote: S3, Terraform Cloud, etc. (production)

2.3 Terragrunt Overview

What is Terragrunt?

  • Keeping your Terraform code DRY (Don't Repeat Yourself)
  • Working with multiple Terraform modules
  • Managing remote state

Why Terragrunt?

Problem with Vanilla Terraform:

Code
project/
├── dev/
│   └── main.tf          # Duplicated code
├── staging/
│   └── main.tf          # Duplicated code
└── prod/
    └── main.tf          # Duplicated code

Solution with Terragrunt:

Code
project/
├── modules/
│   └── vpc/
│       └── main.tf      # Code written ONCE
├── live/
│   ├── dev/
│   │   └── terragrunt.hcl    # Config only
│   ├── staging/
│   │   └── terragrunt.hcl    # Config only
│   └── prod/
│       └── terragrunt.hcl    # Config only

Core Terragrunt Concepts

1. DRY Configurations

Traditional Terraform (Repeated Code):

Code
# dev/main.tf
terraform {
  backend "s3" {
    bucket = "my-state"
    key    = "dev/terraform.tfstate"
    region = "us-east-1"
  }
}

# staging/main.tf  
terraform {
  backend "s3" {
    bucket = "my-state"
    key    = "staging/terraform.tfstate"  # Only this changes!
    region = "us-east-1"
  }
}

Terragrunt (Write Once):

Code
# Root terragrunt.hcl
remote_state {
  backend = "s3"
  config = {
    bucket = "my-state"
    key    = "${path_relative_to_include()}/terraform.tfstate"
    region = "us-east-1"
  }
}

# Child configs just include this
include "root" {
  path = find_in_parent_folders()
}

2. Dependencies

Terragrunt can manage dependencies between modules:

Code
# EKS depends on VPC
dependency "vpc" {
  config_path = "../vpc"
}

inputs = {
  vpc_id     = dependency.vpc.outputs.vpc_id
  subnet_ids = dependency.vpc.outputs.private_subnets
}

3. Mock Outputs

For planning without dependencies:

Code
dependency "vpc" {
  config_path = "../vpc"

  mock_outputs = {
    vpc_id          = "vpc-fake-id"
    private_subnets = ["subnet-fake-1", "subnet-fake-2"]
  }
}

4. Dynamic Configuration

Use functions and locals:

Code
locals {
  env_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))
  env      = local.env_vars.locals.env
}

inputs = {
  env = local.env
  tags = {
    Environment = local.env
  }
}

5. Code Generation

Terragrunt can generate files:

Code
generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite"
  contents  = <<EOF
provider "aws" {
  region = "us-east-1"
}
EOF
}

6. Terragrunt Workflow

flowchart TD; classDef plain fill:#f1f5f9,stroke:#94a3b8,color:#334155,stroke-width:1px; classDef navy fill:#1e3a5f,stroke:#2c5282,color:#ffffff,stroke-width:2px; classDef gold fill:#d4af37,stroke:#f4d03f,color:#1e3a5f,stroke-width:2px; Root[terragrunt.hcl]:::navy -->|Inherits| Child[Env/Region]:::gold; Child -->|Infastructure| Mod[AWS Module]:::plain; Mod -->|Generates| Res[Resources]:::gold;

7. Terragrunt Commands

Command Description
terragrunt plan Run terraform plan
terragrunt apply Run terraform apply
terragrunt run-all plan Plan all modules
terragrunt run-all apply Apply all modules
terragrunt output Show outputs
terragrunt destroy Destroy infrastructure

2.4 AWS Services Overview

VPC (Virtual Private Cloud)

What it is:
A logically isolated section of the AWS cloud where you can launch resources in a virtual network you define.

Key Components:

Code
VPC (10.0.0.0/16)
│
├── Public Subnet (10.0.1.0/24)
│   ├── Internet Gateway (IGW)
│   └── NAT Gateway
│
├── Private Subnet (10.0.2.0/24)
│   └── Resources (EKS nodes, RDS)
│
├── Route Tables
│   ├── Public: 0.0.0.0/0 → IGW
│   └── Private: 0.0.0.0/0 → NAT
│
└── Security Groups
    └── Firewall rules

Why we need it:

  • Network isolation
  • Security boundaries
  • Control over IP addressing
  • Internet/private connectivity

EKS (Elastic Kubernetes Service)

What it is:
Managed Kubernetes service that makes it easy to run Kubernetes on AWS without needing to install and operate your own Kubernetes control plane.

Architecture:

Code
EKS Cluster
│
├── Control Plane (Managed by AWS)
│   ├── API Server
│   ├── etcd
│   └── Controllers
│
└── Data Plane (Your Node Groups)
    ├── Node Group 1 (t3.medium)
    │   ├── Pod 1
    │   ├── Pod 2
    │   └── Pod 3
    └── Node Group 2 (t3.large)
        ├── Pod 4
        └── Pod 5

Key Features:

  • Automatic Kubernetes version upgrades
  • Integrated with AWS services
  • High availability
  • Security & compliance
  • Scalability

Why we need it:

  • Run containerized applications
  • Microservices architecture
  • Auto-scaling
  • Self-healing infrastructure

RDS (Relational Database Service)

What it is:
Managed relational database service supporting multiple database engines (PostgreSQL, MySQL, etc.).

Architecture:

Code
RDS Instance
│
├── Database Engine (PostgreSQL/MySQL/etc.)
├── Automated Backups
├── Point-in-Time Recovery
├── Multi-AZ (Optional)
│   ├── Primary
│   └── Standby
└── Read Replicas (Optional)

Managed Features:

  • Automated backups
  • Software patching
  • Monitoring
  • Replication
  • Scaling

Why we need it:

  • Persistent data storage
  • Reduced operational overhead
  • High availability
  • Backup & recovery
  • Security (encryption)

2.5 Supporting Technologies

Bash Scripting

Purpose: Automation wrappers around Terragrunt commands.

Example from our boilerplate:

Code
#!/bin/bash
set -e  # Exit on error

TARGET_ENV=$1

if [ -z "$TARGET_ENV" ]; then
  echo "Usage: $0 <path-to-environment>"
  exit 1
fi

./scripts/scaffold.sh "$TARGET_ENV"
cd "$TARGET_ENV"
terragrunt run-all plan

Key Concepts:

  • set -e: Exit on error
  • $1, $2: Command-line arguments
  • if [ condition ]: Conditionals
  • for item in list: Loops

YAML Configuration

Purpose: Human-readable configuration format.

Example:

Code
vpc:
  cidr: "10.0.0.0/16"
  azs: ["us-east-1a", "us-east-1b"]

eks:
  primary:
    version: "1.27"
    instance_types: ["t3.medium"]
    desired_size: 2

rds:
  main-db:
    engine: "postgres"
    instance_class: "db.t3.micro"

Syntax Rules:

  • Indentation matters (2 spaces standard)
  • Key: Value pairs
  • Lists use - item or [item1, item2]
  • Nested structures via indentation

Git Workflows

Purpose: Version control for infrastructure code.

Recommended Workflow:

Code
Feature Branch Workflow
────────────────────────────────────────

main
 │
 ├─── feature/add-rds-database
 │    │
 │    ├── Edit values.yaml
 │    ├── Commit changes
 │    ├── Push to remote
 │    └── Create Pull Request
 │         │
 │         ├── Code Review
 │         ├── terraform plan (CI)
 │         └── Merge to main
 │              │
 └──────────────┘
 │
 └─── terraform apply (CD)

Best Practices:

  • Never commit state files
  • Use .gitignore
  • Meaningful commit messages
  • Pull Request reviews
  • CI/CD automation

2.6 How It All Fits Together

The Complete Stack

flowchart BT; classDef app fill:#fef3c7,stroke:#f59e0b,color:#92400e,stroke-width:2px; classDef compute fill:#d4af37,stroke:#f4d03f,color:#1e3a5f,stroke-width:2px; classDef data fill:#1e3a5f,stroke:#2c5282,color:#ffffff,stroke-width:2px; classDef net fill:#f1f5f9,stroke:#94a3b8,color:#334155,stroke-width:1px; subgraph Applications App[Microservices]:::app; end subgraph Compute EKS[EKS Cluster]:::compute; end subgraph Data RDS[RDS Database]:::data --> S3[State Bucket]:::data; end subgraph Network VPC[VPC / Subnets]:::net; end App --> EKS; EKS --> VPC; EKS --> RDS;


2.7 Chapter Summary

Executive Summary

  • ★
    IaC
  • enables treating infrastructure like software
  • provides the engine for infrastructure provisioning
  • eliminates code duplication and manages complexity
  • (VPC, EKS, RDS) provide the infrastructure building blocks
  • ties everything together for smooth operations