Menu

DevOps & Cloud Topic

DevOps Infrastructure as Code (IaC)

What is Infrastructure as Code?

Infrastructure as Code (IaC) is the practice of managing and provisioning computing infrastructure through machine-readable definition files, rather than physical hardware configuration or interactive configuration tools.

Core Principles of IaC:

  • Declarative vs Imperative approaches

  • Idempotency - same result regardless of how many times applied

  • Version Control - infrastructure definitions in Git

  • Automation - eliminate manual processes

  • Reproducibility - consistent environments


Terraform Fundamentals

1. Core Concepts & Architecture

How Terraform Works:

text

Terraform Configuration (.tf files)
        ↓
Terraform Core (Planning & Execution)
        ↓
Provider Plugins (AWS, Azure, GCP, etc.)
        ↓
Cloud APIs

Key Components:

  • Configuration Files (.tf, .tfvars)

  • State Files (.tfstate) - current state of infrastructure

  • Providers - plugins that interact with APIs

  • Resources - infrastructure components

  • Data Sources - read-only information about existing infrastructure

2. Basic Terraform Syntax

Main Configuration Blocks:

hcl

# Provider Configuration
terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

# Provider Block
provider "aws" {
  region = "us-east-1"
}

# Resource Block
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  tags = {
    Name = "main-vpc"
  }
}

# Data Source
data "aws_availability_zones" "available" {
  state = "available"
}

# Output Values
output "vpc_id" {
  value = aws_vpc.main.id
}

# Variable Definitions
variable "environment" {
  description = "Deployment environment"
  type        = string
  default     = "dev"
}

3. Terraform Workflow

Standard Commands:

bash

# Initialize Terraform
terraform init

# Validate configuration
terraform validate

# Plan changes
terraform plan -out=tfplan

# Apply changes
terraform apply tfplan

# Destroy infrastructure
terraform destroy

Advanced Workflow:

bash

# Format code
terraform fmt

# Security scanning
terraform tfsec

# Cost estimation
terraform plan -out=tfplan && terraform show -json tfplan | infracost breakdown --path=-

Pulumi Fundamentals

1. Pulumi vs Terraform Approach

Key Differences:

  • Programming Languages: TypeScript, Python, Go, C#, Java vs HCL

  • State Management: Similar concepts but different implementation

  • Abstraction Level: Higher-level abstractions available

2. Basic Pulumi Syntax

TypeScript Example:

typescript

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// Create an AWS resource (S3 Bucket)
const bucket = new aws.s3.Bucket("my-bucket", {
    website: {
        indexDocument: "index.html",
    },
    tags: {
        Environment: "Dev",
    },
});

// Export the bucket name
export const bucketName = bucket.id;
export const bucketEndpoint = pulumi.interpolate`http://${bucket.websiteEndpoint}`;

Python Example:

python

import pulumi
import pulumi_aws as aws

# Create a VPC
vpc = aws.ec2.Vpc("main-vpc",
    cidr_block="10.0.0.0/16",
    enable_dns_hostnames=True,
    tags={
        "Name": "main-vpc"
    }
)

# Export the VPC ID
pulumi.export("vpc_id", vpc.id)

3. Pulumi Architecture

text

Pulumi Program (TypeScript/Python/Go)
        ↓
Pulumi CLI
        ↓
Language Host (Node.js/Python/Go)
        ↓
Resource Providers
        ↓
Cloud APIs

Pulumi State Backends:

  • Pulumi Service (cloud-based)

  • Self-hosted (S3, Azure Blob, Google Cloud Storage)

  • Local (file-based)


Modules & Reusability

1. Terraform Modules

Module Structure:

text

modules/vpc/
├── main.tf
├── variables.tf
├── outputs.tf
└── README.md

Creating a Reusable VPC Module:

hcl

# modules/vpc/variables.tf
variable "vpc_name" {
  description = "Name of the VPC"
  type        = string
}

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

variable "availability_zones" {
  description = "List of availability zones"
  type        = list(string)
}

# modules/vpc/main.tf
resource "aws_vpc" "this" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  tags = {
    Name = var.vpc_name
  }
}

resource "aws_subnet" "private" {
  count             = length(var.availability_zones)
  vpc_id            = aws_vpc.this.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 8, count.index)
  availability_zone = var.availability_zones[count.index]
  
  tags = {
    Name = "${var.vpc_name}-private-${count.index}"
  }
}

# modules/vpc/outputs.tf
output "vpc_id" {
  value = aws_vpc.this.id
}

output "private_subnet_ids" {
  value = aws_subnet.private[*].id
}

Using the Module:

hcl

# root module
module "network" {
  source = "./modules/vpc"
  
  vpc_name          = "production"
  vpc_cidr          = "10.1.0.0/16"
  availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
}

# Reference module outputs
resource "aws_instance" "web" {
  subnet_id     = module.network.private_subnet_ids[0]
  # ... other configuration
}

2. Pulumi Components

Creating Reusable Components:

typescript

// components/WebService.ts
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

export interface WebServiceArgs {
    vpcId: pulumi.Input<string>;
    subnetIds: pulumi.Input<string>[];
    instanceType?: pulumi.Input<string>;
    minSize?: pulumi.Input<number>;
    maxSize?: pulumi.Input<number>;
}

export class WebService extends pulumi.ComponentResource {
    public readonly loadBalancerDns: pulumi.Output<string>;
    
    constructor(name: string, args: WebServiceArgs, opts?: pulumi.ComponentResourceOptions) {
        super("custom:app:WebService", name, {}, opts);
        
        // Create security group
        const webSg = new aws.ec2.SecurityGroup(`${name}-sg`, {
            vpcId: args.vpcId,
            ingress: [{
                protocol: "tcp",
                fromPort: 80,
                toPort: 80,
                cidrBlocks: ["0.0.0.0/0"],
            }],
        }, { parent: this });
        
        // Create launch template
        const launchTemplate = new aws.ec2.LaunchTemplate(`${name}-lt`, {
            imageId: "ami-0c02fb55956c7d316",
            instanceType: args.instanceType || "t3.micro",
            vpcSecurityGroupIds: [webSg.id],
            userData: `#!/bin/bash
                echo "Hello, World!" > index.html
                nohup python -m SimpleHTTPServer 80 &`,
        }, { parent: this });
        
        // Create auto scaling group
        const asg = new aws.autoscaling.Group(`${name}-asg`, {
            launchTemplate: {
                id: launchTemplate.id,
                version: "$Latest",
            },
            vpcZoneIdentifiers: args.subnetIds,
            minSize: args.minSize || 1,
            maxSize: args.maxSize || 3,
            targetGroupArns: [targetGroup.arn],
        }, { parent: this });
        
        this.loadBalancerDns = alb.dnsName;
        
        this.registerOutputs({
            loadBalancerDns: this.loadBalancerDns
        });
    }
}

Using the Component:

typescript

// index.ts
import { WebService } from "./components/WebService";

const webService = new WebService("app", {
    vpcId: vpc.id,
    subnetIds: privateSubnets.map(s => s.id),
    instanceType: "t3.small",
    minSize: 2,
    maxSize: 5
});

export const url = webService.loadBalancerDns;

Remote State Management

1. Terraform Remote State

Backend Configuration:

hcl

# terraform.tf
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "production/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

State Locking with DynamoDB:

hcl

resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  
  attribute {
    name = "LockID"
    type = "S"
  }
  
  tags = {
    Name = "Terraform Lock Table"
  }
}

Workspaces for Environment Isolation:

bash

# Create workspaces
terraform workspace new dev
terraform workspace new staging
terraform workspace new production

# Switch between workspaces
terraform workspace select dev

2. Pulumi State Management

Stack Configuration:

yaml

# Pulumi.dev.yaml
config:
  aws:region: us-east-1
  my-project:environment: dev
  my-project:instanceCount: 2

# Pulumi.prod.yaml  
config:
  aws:region: us-east-1
  my-project:environment: production
  my-project:instanceCount: 5

Using Stacks:

typescript

// Get stack-specific configuration
const config = new pulumi.Config();
const environment = config.require("environment");
const instanceCount = config.getNumber("instanceCount") || 1;

Policy as Code

1. Terraform - Sentinel & OPA

Sentinel Policies:

python

# Enforce tagging policy
import "tfplan"

main = rule {
    all tfplan.resources as _, instances {
        all instances as index, r {
            all r.applied.tags as _ {
                r.applied.tags contains "Environment"
            }
        }
    }
}

# Cost control policy
import "tfplan"
import "decimal"

allowed_instance_types = ["t3.micro", "t3.small", "t3.medium"]

main = rule {
    all tfplan.resources.aws_instance as _, instances {
        all instances as _, r {
            r.applied.instance_type in allowed_instance_types
        }
    }
}

Open Policy Agent (OPA) with Terraform:

rego

# policies/terraform.rego
package terraform

deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_instance"
    instance_type := resource.change.after.instance_type
    not startswith(instance_type, "t3.")
    msg := sprintf("Instance type %v not allowed. Use t3 series.", [instance_type])
}

deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_db_instance"
    resource.change.after.allocated_storage > 100
    msg := "Database storage exceeds 100GB limit"
}

2. Pulumi Policy Pack

TypeScript Policy Pack:

typescript

import * as policy from "@pulumi/policy";
import { PolicyPack } from "@pulumi/policy";

new PolicyPack("aws-security-policies", {
    policies: [{
        name: "s3-bucket-no-public-read",
        description: "Prohibits S3 buckets with PUBLIC_READ access.",
        enforcementLevel: "mandatory",
        validateResource: (args, reportViolation) => {
            if (args.type === "aws:s3/bucket:Bucket") {
                const acl = args.props.acl;
                if (acl === "public-read" || acl === "public-read-write") {
                    reportViolation("S3 buckets should not have public read access.");
                }
            }
        },
    }, {
        name: "ec2-instance-type-check",
        description: "Ensures EC2 instances use approved instance types.",
        enforcementLevel: "mandatory",
        validateResource: (args, reportViolation) => {
            if (args.type === "aws:ec2/instance:Instance") {
                const allowedTypes = ["t3.micro", "t3.small", "t3.medium"];
                if (!allowedTypes.includes(args.props.instanceType)) {
                    reportViolation(`EC2 instance type ${args.props.instanceType} not allowed.`);
                }
            }
        },
    }],
});

Production-Grade Infrastructure Patterns

1. Multi-Region Deployment

Terraform Multi-Region:

hcl

# providers.tf
provider "aws" {
  alias  = "primary"
  region = "us-east-1"
}

provider "aws" {
  alias  = "secondary"
  region = "us-west-2"
}

# multi-region resources
resource "aws_dynamodb_table" "primary" {
  provider = aws.primary
  name     = "my-table"
  # ... configuration
}

resource "aws_dynamodb_table" "secondary" {
  provider = aws.secondary
  name     = "my-table"
  # ... configuration
}

2. Blue-Green Deployment

Pulumi Blue-Green Pattern:

typescript

// Create two identical environments
const blue = new WebService("blue", {
    vpcId: vpc.id,
    subnetIds: privateSubnets.map(s => s.id),
    desiredCapacity: 2
});

const green = new WebService("green", {
    vpcId: vpc.id, 
    subnetIds: privateSubnets.map(s => s.id),
    desiredCapacity: 0 // Initially scaled down
});

// Route traffic based on deployment phase
const listener = new aws.lb.Listener("app", {
    loadBalancerArn: alb.arn,
    port: 80,
    defaultActions: [{
        type: "forward",
        targetGroupArn: blue.targetGroup.arn // Start with blue
    }]
});

3. Disaster Recovery Setup

Terraform DR Strategy:

hcl

# dr.tf - Disaster Recovery configuration
module "primary_region" {
  source = "./modules/multi-region"
  
  providers = {
    aws = aws.primary
  }
  
  region        = "us-east-1"
  vpc_cidr      = "10.1.0.0/16"
  is_primary    = true
}

module "secondary_region" {
  source = "./modules/multi-region"
  
  providers = {
    aws = aws.secondary
  }
  
  region        = "us-west-2"
  vpc_cidr      = "10.2.0.0/16" 
  is_primary    = false
}

# Cross-region replication
resource "aws_s3_bucket" "primary_backup" {
  provider = aws.primary
  bucket   = "primary-backup-bucket"
}

resource "aws_s3_bucket_replication" "cross_region" {
  provider = aws.primary
  bucket   = aws_s3_bucket.primary_backup.id
  
  replication_configuration {
    role = aws_iam_role.replication.arn
    
    rules {
      status = "Enabled"
      
      destination {
        bucket        = aws_s3_bucket.secondary_backup.arn
        storage_class = "STANDARD"
      }
    }
  }
}

Advanced Patterns & Best Practices

1. Terragrunt for DRY Code

Terragrunt Structure:

hcl

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

# Generate provider configuration
generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
provider "aws" {
  region = "us-east-1"
  
  default_tags {
    tags = {
      Environment = "production"
      ManagedBy   = "terraform"
    }
  }
}
EOF
}

2. Pulumi Automation API

Programmatic Infrastructure:

typescript

import * as automation from "@pulumi/pulumi/x/automation";

async function deployStack(environment: string) {
    const stackArgs = {
        stackName: `${environment}`,
        projectName: "my-infrastructure",
    };
    
    // Create or select stack
    const stack = await automation.LocalWorkspace.createOrSelectStack(stackArgs);
    
    // Set configuration
    await stack.setConfig("aws:region", { value: "us-east-1" });
    await stack.setConfig("environment", { value: environment });
    
    // Preview changes
    const preview = await stack.preview();
    console.log(preview.stdout);
    
    // Deploy
    const up = await stack.up();
    console.log(up.stdout);
}

// Deploy multiple environments
deployStack("dev");
deployStack("production");

3. Security Best Practices

Secrets Management:

hcl

# Use AWS Secrets Manager or Parameter Store
data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "production/db/password"
}

resource "aws_db_instance" "database" {
  # ... other configuration
  password = data.aws_secretsmanager_secret_version.db_password.secret_string
}

Infrastructure Security Scanning:

yaml

# GitHub Actions workflow for security scanning
name: Security Scan
on:
  push:
    branches: [main]
  pull_request:

jobs:
  tfsec:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Run tfsec
        uses: aquasecurity/[email protected]
        with:
          soft_fail: true

Learning Path & Certification

Recommended Learning Journey:

Phase 1: Fundamentals (1-2 months)

  1. Terraform Basics: Resources, providers, state management

  2. Pulumi Introduction: Choose your preferred language

  3. Basic Modules: Create reusable components

  4. Hands-on: Deploy simple infrastructure (VPC, EC2, S3)

Phase 2: Intermediate (2-3 months)

  1. Remote State: Backend configuration, state locking

  2. Workspaces/Stacks: Environment management

  3. Policy as Code: Sentinel, OPA, Pulumi Policy Packs

  4. CI/CD Integration: GitHub Actions, GitLab CI

Phase 3: Advanced (3-4 months)

  1. Enterprise Patterns: Multi-account, multi-region

  2. Security: Secrets management, compliance

  3. Performance: Large-scale deployments, state optimization

  4. Testing: Terratest, kitchen-terraform

Certification Preparation:

  • HashiCorp Terraform Associate

  • Cloud-specific IaC patterns (AWS/Azure/GCP)

  • Security and compliance frameworks

This extensive IaC foundation will enable you to manage complex, production-grade infrastructure with confidence, applying best practices for security, reliability, and maintainability.

Comments (0)

No comments yet. Be the first to comment!

AlphaAI Assistant

Hello! I'm AlphaAI

What would you like to learn today?