Use Cases (अपडेट: 2/6/2026)

Claude Code के साथ Terraform IaC: AWS module, state और CI की व्यावहारिक गाइड

AWS उदाहरण से Claude Code और Terraform IaC सीखें: module, backend, state, CI, plan review, secrets और common pitfalls.

Claude Code के साथ Terraform IaC: AWS module, state और CI की व्यावहारिक गाइड

Terraform से AWS infrastructure बनाना शुरू में आसान लगता है। VPC, subnet और route table लिखे, terraform plan चला, और काम पूरा लगने लगता है। असली समस्या बाद में आती है: state कहाँ रहेगा, dev और prod अलग कैसे रहेंगे, module कितना छोटा होगा, CI किस बदलाव को रोकेगा, और Claude Code से बना code कहीं resource delete तो नहीं कर देगा।

IaC का मतलब Infrastructure as Code है, यानी infrastructure को review होने वाले code की तरह लिखना। HCL Terraform की configuration language है। Module reusable infrastructure हिस्सा है। State वह record है जिससे Terraform जानता है कि cloud में कौन सा resource किस code से जुड़ा है। Backend वह जगह है जहाँ state रखा जाता है। इन शब्दों को साफ समझना जरूरी है, क्योंकि Terraform की ज्यादातर गलतियाँ code, state, permission और human review के बीच होती हैं।

Masa ने इस लेख के लिए छोटे AWS test repo में यह flow चलाया। Claude Code ने VPC module बनाना, variables व्यवस्थित करना और CI draft करना तेज कर दिया। लेकिन prompt में साफ rules डालने पड़े: tfvars में secrets नहीं, destroy वाला plan approve नहीं, और नए S3 backend के लिए पुराना DynamoDB locking pattern नहीं।

Workflow और उपयोग के मामले

सुरक्षित flow यह है: requirement लिखें, Claude Code से HCL draft बनवाएँ, Terraform checks चलाएँ, plan review करें, फिर ही apply करें।

flowchart LR
  A["Requirement लिखना"] --> B["Claude Code HCL draft करता है"]
  B --> C["terraform fmt / validate"]
  C --> D["terraform plan"]
  D --> E["AI और human review"]
  E --> F["Approved apply"]

काम में ये use cases सबसे उपयोगी हैं।

Use caseClaude Code क्या कर सकता हैइंसान क्या जरूर देखे
नया AWS networkVPC, subnets, routes, tagsCIDR conflict, NAT Gateway cost
Module designmodules/vpc, variables, outputsModule बहुत बड़ा तो नहीं
Environment splitenvs/dev.tfvars, envs/prod.tfvarsBackend key और prod permissions
CI और policyfmt, validate, plan, JSON checksdestroy, replace, IAM expansion

Claude Code की basic जानकारी के लिएofficial docs देखें। Terraform module concept के लिएTerraform Modules docs उपयोगी हैं। AWS permission समझने के लिए internalAWS IAM guide भी पढ़ें।

Claude Code prompt में safety लिखें

सिर्फ “Terraform code लिखो” न कहें। Scope, forbidden actions और verification commands साथ दें।

claude -p "
AWS VPC module के लिए Terraform बनाइए।
मेरी explicit permission के बिना existing files delete न करें।

Requirements:
- Terraform 1.10 या नया मानें
- hashicorp/aws provider use करें
- VPC, 2 public subnets, 2 private subnets, Internet Gateway, NAT Gateway बनाएँ
- Reusable code modules/vpc में रखें
- dev और prod को tfvars से अलग करें
- secrets या AWS access keys tfvars में न लिखें
- terraform fmt -recursive, terraform validate, terraform plan steps दें
- plan में destroy या replacement हो तो apply approved न मानें
"

यह prompt इसलिए जरूरी है क्योंकि Terraform का generated code real bill, real network और real permissions बदलता है। Claude Code speed देता है, लेकिन production safety की जिम्मेदारी team की रहती है।

AWS Terraform starter structure

यह छोटा structure copy करके start किया जा सकता है। Real plan के लिए AWS credentials और state bucket चाहिए। जून 2026 में Terraform S3 backend use_lockfile से locking support करता है; DynamoDB based locking deprecated बताया गया है। इसलिए पुराने tutorial copy करने से पहलेS3 backend official docs देखें।

infra/
  versions.tf
  variables.tf
  main.tf
  envs/dev.tfvars
  modules/vpc/main.tf
  modules/vpc/variables.tf
  modules/vpc/outputs.tf
# versions.tf
terraform {
  required_version = ">= 1.10.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.0, < 7.0"
    }
  }

  backend "s3" {
    bucket       = "replace-with-your-tfstate-bucket"
    key          = "claude-code-terraform-iac/dev/terraform.tfstate"
    region       = "ap-south-1"
    encrypt      = true
    use_lockfile = true
  }
}

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = var.common_tags
  }
}
# variables.tf
variable "aws_region" {
  type        = string
  description = "AWS region to deploy into."
  default     = "ap-south-1"
}

variable "environment" {
  type        = string
  description = "Environment name such as dev, staging, or prod."
}

variable "common_tags" {
  type        = map(string)
  description = "Tags applied to every supported AWS resource."
}

variable "vpc_cidr" {
  type        = string
  description = "CIDR block for the VPC."
}

variable "public_subnets" {
  type = list(object({
    name = string
    cidr = string
    az   = string
  }))
}

variable "private_subnets" {
  type = list(object({
    name = string
    cidr = string
    az   = string
  }))
}
# main.tf
module "network" {
  source = "./modules/vpc"

  project         = "claude-code-iac"
  environment     = var.environment
  vpc_cidr        = var.vpc_cidr
  public_subnets  = var.public_subnets
  private_subnets = var.private_subnets
  common_tags     = var.common_tags
}

Module design और variables

VPC module को सिर्फ network तक सीमित रखें। उसी module में ALB, ECS, RDS और IAM जोड़ने से plan बड़ा और review कठिन हो जाता है। Long-lived resources के लिए count.index से बेहतर stable name के साथ for_each है।

# modules/vpc/variables.tf
variable "project" {
  type        = string
  description = "Project name used in resource names."
}

variable "environment" {
  type        = string
  description = "Environment name used in tags and names."
}

variable "vpc_cidr" {
  type        = string
  description = "CIDR block for the VPC."

  validation {
    condition     = can(cidrhost(var.vpc_cidr, 0))
    error_message = "vpc_cidr must be a valid CIDR block."
  }
}

variable "public_subnets" {
  type = list(object({
    name = string
    cidr = string
    az   = string
  }))

  validation {
    condition     = length(var.public_subnets) >= 2 && alltrue([for s in var.public_subnets : can(cidrhost(s.cidr, 0))])
    error_message = "Define at least two valid public subnet CIDR blocks."
  }
}

variable "private_subnets" {
  type = list(object({
    name = string
    cidr = string
    az   = string
  }))

  validation {
    condition     = length(var.private_subnets) >= 2 && alltrue([for s in var.private_subnets : can(cidrhost(s.cidr, 0))])
    error_message = "Define at least two valid private subnet CIDR blocks."
  }
}

variable "common_tags" {
  type        = map(string)
  description = "Common tags."
}
# modules/vpc/main.tf
locals {
  public_subnets_by_name  = { for subnet in var.public_subnets : subnet.name => subnet }
  private_subnets_by_name = { for subnet in var.private_subnets : subnet.name => subnet }
  name_prefix             = "${var.project}-${var.environment}"
}

resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = merge(var.common_tags, {
    Name = "${local.name_prefix}-vpc"
  })
}

resource "aws_subnet" "public" {
  for_each                = local.public_subnets_by_name
  vpc_id                  = aws_vpc.main.id
  cidr_block              = each.value.cidr
  availability_zone       = each.value.az
  map_public_ip_on_launch = true

  tags = merge(var.common_tags, {
    Name = "${local.name_prefix}-public-${each.key}"
    Tier = "public"
  })
}

resource "aws_subnet" "private" {
  for_each          = local.private_subnets_by_name
  vpc_id            = aws_vpc.main.id
  cidr_block        = each.value.cidr
  availability_zone = each.value.az

  tags = merge(var.common_tags, {
    Name = "${local.name_prefix}-private-${each.key}"
    Tier = "private"
  })
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }
}

resource "aws_route_table_association" "public" {
  for_each       = aws_subnet.public
  subnet_id      = each.value.id
  route_table_id = aws_route_table.public.id
}

resource "aws_eip" "nat" {
  domain = "vpc"
}

resource "aws_nat_gateway" "main" {
  allocation_id = aws_eip.nat.id
  subnet_id     = values(aws_subnet.public)[0].id
  depends_on    = [aws_internet_gateway.main]
}

resource "aws_route_table" "private" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.main.id
  }
}

resource "aws_route_table_association" "private" {
  for_each       = aws_subnet.private
  subnet_id      = each.value.id
  route_table_id = aws_route_table.private.id
}

State, backend और environment separation

State में resource IDs और कुछ sensitive attributes हो सकते हैं। इसलिए S3 bucket में versioning, locking और strict IAM रखें। dev और prod को अलग backend key दें।

# envs/dev.tfvars
aws_region  = "ap-south-1"
environment = "dev"
vpc_cidr    = "10.40.0.0/16"

common_tags = {
  Project     = "claude-code-iac"
  Environment = "dev"
  Owner       = "masa"
  ManagedBy   = "terraform"
}

public_subnets = [
  { name = "a", cidr = "10.40.0.0/24", az = "ap-south-1a" },
  { name = "b", cidr = "10.40.1.0/24", az = "ap-south-1b" }
]

private_subnets = [
  { name = "a", cidr = "10.40.10.0/24", az = "ap-south-1a" },
  { name = "b", cidr = "10.40.11.0/24", az = "ap-south-1b" }
]

Production में CIDR, tags और backend key अलग होने चाहिए। Workspace भी option है, लेकिन beginners के लिए explicit tfvars files ज्यादा readable रहती हैं।

fmt, validate, plan चलाना

Terraform के official pages fmt, validate, और plan देखें। पहले remote backend के बिना syntax और type check करें।

cd infra
terraform init -backend=false
terraform fmt -recursive
terraform validate

Real AWS changes देखने के लिए backend initialize करें और plan save करें।

terraform init -reconfigure
terraform plan -var-file=envs/dev.tfvars -out=tfplan
terraform show -no-color tfplan > plan.txt

Claude Code को risk-focused review दें।

claude -p "
plan.txt review करें।
destroy, replace, force replacement, IAM permission expansion, public exposure और backend key change को priority दें।
अगर delete action है तो plan approve न करें।
Human reviewer के लिए जरूरी सवाल लिखें।
"

CI, policy, permissions और secrets

GitHub Actions में long-lived AWS access keys न रखें। OIDC से limited IAM role assume करें। Official guide यहाँ है:GitHub Actions OIDC in AWS। IAM role कोAWS IAM best practices के अनुसार छोटा रखें।

name: terraform-plan

on:
  pull_request:
    paths:
      - "infra/**"

permissions:
  contents: read
  id-token: write

jobs:
  plan:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: infra

    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "1.10.0"
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-terraform-plan
          aws-region: ap-south-1
      - run: terraform init -backend=false
      - run: terraform fmt -check -recursive
      - run: terraform validate
      - run: terraform init -reconfigure
      - run: terraform plan -var-file=envs/dev.tfvars -out=tfplan
      - run: terraform show -json tfplan > tfplan.json

Simple policy delete actions रोक सकती है।

package terraform.deny

deny[msg] {
  rc := input.resource_changes[_]
  rc.change.actions[_] == "delete"
  msg := sprintf("Destroy action requires manual approval: %s", [rc.address])
}

CI flow के लिएClaude Code CI/CD guide और secrets के लिएSecrets management guide देखें।

Common pitfalls और test result

पहली गलती state को temporary file समझना है। उसे Git में commit न करें और dev/prod की key अलग रखें। दूसरी गलती count.index पर ज्यादा भरोसा करना है; list order बदलने से replace आ सकता है। तीसरी गलती passwords, tokens या AWS keys को tfvars में लिखना है। चौथी गलती Claude Code की अच्छी summary देखकर plan apply कर देना है। NAT Gateway, RDS, EKS और IAM changes पर हमेशा human review चाहिए।

इस article के sample को test repo में रखने के बाद terraform fmt -recursive और terraform validate pass हुए। Real plan से पहले backend bucket बदलना और AWS credentials लगाना जरूरी है। Review में सबसे आसानी से छूटने वाली बातें NAT Gateway cost, shared state key और replace actions थीं। Claude Code को destroy और replace पहले देखने को कहना सबसे उपयोगी रहा।

अगर आपकी team Terraform review prompts, CI checklist और IaC onboarding को standard बनाना चाहती है, तोClaude Code resources याimplementation training देखें। Personal project में भी यह prompt और commands CLAUDE.md में रखने से हर Terraform PR ज्यादा safe और repeatable बनता है।

#Claude Code #Terraform #IaC #AWS #infrastructure
मुफ़्त

मुफ़्त PDF: Claude Code cheatsheet

Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.

हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.

Masa

लेखक के बारे में

Masa

Claude Code workflow और team adoption पर काम करने वाला engineer.