14 Prompts for Terraform and IaC: From Modules to Multi-Cloud

Introduction

Terraform has become the de facto standard for Infrastructure as Code (IaC), powering cloud provisioning across AWS, Azure, GCP, and beyond. Mastering it requires not just syntax knowledge but also patterns for modular, secure, and multi-cloud setups. This article presents 14 carefully crafted prompts—grouped into Basic, Advanced, and Expert—that you can directly use to generate Terraform code, learn best practices, or automate your workflow. Each prompt includes a specific task, the exact prompt to give (to an AI assistant or yourself), and an example result with code.


🟢 Basic Prompts (1–5)

1. Create a Modular S3 Bucket

  • Task: Build a reusable Terraform module for an S3 bucket with versioning, encryption, and tags.
  • Prompt: "Generate a Terraform module for an S3 bucket that enables versioning, default encryption (AES256), and supports custom tags. Show the module structure and how to call it."
  • Example result:
# modules/s3-bucket/main.tf
resource "aws_s3_bucket" "this" {
  bucket = var.bucket_name
  tags   = var.tags
}

resource "aws_s3_bucket_versioning" "this" {
  bucket = aws_s3_bucket.this.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
  bucket = aws_s3_bucket.this.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

Call it with:

module "my_bucket" {
  source      = "./modules/s3-bucket"
  bucket_name = "app-logs-2026"
  tags        = { Environment = "prod" }
}

2. Configure Remote State with Locking

  • Task: Set up an S3 backend with DynamoDB state locking.
  • Prompt: "Provide a Terraform backend configuration using S3 and DynamoDB for state locking. Include the required IAM policy statements."
  • Example result:
terraform {
  backend "s3" {
    bucket         = "my-tfstate-bucket"
    key            = "prod/network/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

IAM policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": "arn:aws:s3:::my-tfstate-bucket"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
      "Resource": "arn:aws:s3:::my-tfstate-bucket/*"
    },
    {
      "Effect": "Allow",
      "Action": ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem"],
      "Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/terraform-locks"
    }
  ]
}

3. Initialize, Plan, and Apply

  • Task: Demonstrate the standard Terraform workflow.
  • Prompt: "Explain the steps terraform init, plan, apply with an example that creates a single EC2 instance. Show the commands and expected output."
  • Example result:
terraform init      # Downloads providers, initializes backend
terraform plan      # Shows execution plan
terraform apply     # Creates resources (prompts for approval)

Minimal config:

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

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}

4. Use Variables and Outputs

  • Task: Parameterize infrastructure and extract values.
  • Prompt: "Write Terraform code that accepts an environment name as variable and outputs the public IP of an EC2 instance."
  • Example result:
variable "environment" {
  description = "Deployment environment (dev/staging/prod)"
  type        = string
}

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
  tags = {
    Name = "web-${var.environment}"
  }
}

output "public_ip" {
  value = aws_instance.web.public_ip
}

Run with: terraform apply -var="environment=dev"

5. Multi-Provider Configuration (AWS + Azure)

  • Task: Set up providers for two clouds.
  • Prompt: "Write Terraform provider blocks for AWS (us-east-1) and Azure (West Europe) with authentication using environment variables."
  • Example result:
provider "aws" {
  region = "us-east-1"
}

provider "azurerm" {
  features {}
  location = "West Europe"
}

Authentication: AWS relies on AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY; Azure on ARM_SUBSCRIPTION_ID, ARM_CLIENT_ID, etc.


🔵 Advanced Prompts (6–10)

6. Reusable VPC Module

  • Task: Design a VPC module with public/private subnets, NAT Gateway, and route tables.
  • Prompt: "Create a Terraform module for a VPC with configurable CIDR, public and private subnet lists, a NAT Gateway for private subnets, and proper routing. Provide module interface and example usage."
  • Example result (abbreviated):
# modules/vpc/main.tf
resource "aws_vpc" "this" {
  cidr_block = var.cidr
  tags       = { Name = var.name }
}

resource "aws_subnet" "public" {
  count = length(var.public_subnet_cidrs)
  vpc_id = aws_vpc.this.id
  cidr_block = var.public_subnet_cidrs[count.index]
}

resource "aws_nat_gateway" "this" {
  allocation_id = aws_eip.nat.id
  subnet_id     = aws_subnet.public[0].id
}

Usage:

module "vpc" {
  source = "./modules/vpc"
  name   = "prod"
  cidr   = "10.0.0.0/16"
  public_subnet_cidrs  = ["10.0.1.0/24", "10.0.2.0/24"]
  private_subnet_cidrs = ["10.0.10.0/24"]
}

7. Workspace-Based Environment Separation

  • Task: Manage dev, staging, prod with workspaces.
  • Prompt: "Show how to create and use Terraform workspaces. Include conditional resource creation based on workspace name."
  • Example result:
terraform workspace new dev
terraform workspace new staging
terraform workspace select prod

Conditional code:

resource "aws_instance" "app" {
  count = terraform.workspace == "prod" ? 3 : 1
  ami   = "ami-0c55b159cbfafe1f0"
  instance_type = terraform.workspace == "prod" ? "m5.large" : "t3.micro"
}

Workspace names typically map to environment names.

8. Fetch Secrets from AWS Secrets Manager

  • Task: Use a database secret without exposing it in state.
  • Prompt: "Write Terraform to retrieve an RDS password from AWS Secrets Manager and use it as the master_password for an RDS instance. Ensure the password is not stored in plain text in the state file."
  • Example result:
data "aws_secretsmanager_secret_version" "db_pass" {
  secret_id = "prod/rds/password"
}

resource "aws_db_instance" "default" {
  allocated_storage = 20
  engine            = "mysql"
  instance_class    = "db.t3.micro"
  username          = "admin"
  password          = data.aws_secretsmanager_secret_version.db_pass.secret_string
  skip_final_snapshot = true
}

Terraform marks the password as sensitive in output. Use sensitive = true in variable definitions.

9. CI/CD Pipeline with GitHub Actions

  • Task: Automate Terraform validation and deployment.
  • Prompt: "Create a GitHub Actions workflow that runs terraform fmt, init, validate, and plan on pull requests, and applies on merge to main. Use OIDC to authenticate to AWS."
  • Example result:
name: Terraform CI
on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  terraform:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-oidc
          aws-region: us-east-1
      - run: terraform fmt -check
      - run: terraform init
      - run: terraform validate
      - run: terraform plan -out=tfplan
      - if: github.ref == 'refs/heads/main'
        run: terraform apply tfplan

10. for_each vs count

  • Task: Create multiple IAM users using for_each and multiple S3 buckets with count.
  • Prompt: "Generate Terraform code that creates IAM users from a map using for_each, and S3 buckets from a list using count. Explain the difference."
  • Example result:
# Using for_each
variable "users" {
  default = {
    alice = { department = "eng" }
    bob   = { department = "ops" }
  }
}
resource "aws_iam_user" "this" {
  for_each = var.users
  name     = each.key
  tags     = { Department = each.value.department }
}

# Using count
variable "bucket_names" {
  default = ["logs-1", "logs-2"]
}
resource "aws_s3_bucket" "logs" {
  count  = length(var.bucket_names)
  bucket = var.bucket_names[count.index]
}

for_each is preferred when order does not matter and you need stable addresses; count works with lists.


🔴 Expert Prompts (11–14)

11. Multi-Cloud with AWS and Azure

  • Task: Provision an EC2 instance and an Azure VM sharing a common security group via cross-cloud provider aliases.
  • Prompt: "Write Terraform configuration that creates an AWS security group and an EC2 instance in one region, and an Azure virtual network and VM in another region, using provider aliases. Ensure the security group rules allow traffic from the Azure VM's IP."
  • Example result:
provider "aws" {
  alias  = "us_east"
  region = "us-east-1"
}

provider "azurerm" {
  alias      = "west_eu"
  features {}
}

data "http" "azure_vm_ip" {
  url = "https://checkip.amazonaws.com/"
}

resource "aws_security_group" "web" {
  provider = aws.us_east
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["${chomp(data.http.azure_vm_ip.response_body)}/32"]
  }
}

resource "azurerm_public_ip" "vm" {
  provider            = azurerm.west_eu
  name                = "vm-pip"
  location            = "West Europe"
  resource_group_name = azurerm_resource_group.main.name
  allocation_method   = "Static"
}

This demonstrates cross-cloud dependencies using data.http.

12. Terragrunt for DRY IaC

  • Task: Reduce duplication across environments using Terragrunt.
  • Prompt: "Explain how Terragrunt helps manage remote state and module dependencies. Show a terragrunt.hcl example with dependency and remote_state configuration."
  • Example result:
# terragrunt.hcl (root)
remote_state {
  backend = "s3"
  config = {
    bucket         = "my-terragrunt-state"
    key            = "${path_relative_to_include()}/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terragrunt-locks"
    encrypt        = true
  }
}

# dev/terragrunt.hcl
dependency "vpc" {
  config_path = "../vpc"
}
inputs = {
  vpc_id   = dependency.vpc.outputs.vpc_id
  subnet_ids = dependency.vpc.outputs.public_subnet_ids
}

Run terragrunt run-all plan to execute all modules.

13. Custom Provider Skeleton & terraform-bundle

  • Task: Create a minimal custom provider and understand offline workflows.
  • Prompt: "Describe the structure of a custom Terraform provider written in Go, and when to use terraform-bundle to pre-download providers for air-gapped environments. Show a skeleton provider.go."
  • Example result:
package main

import (
    "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
    "github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
)

func main() {
    plugin.Serve(&plugin.ServeOpts{
        ProviderFunc: func() *schema.Provider {
            return &schema.Provider{
                ResourcesMap: map[string]*schema.Resource{
                    "mycloud_instance": resourceInstance(),
                },
            }
        },
    })
}

terraform-bundle is used by running terraform-bundle package <config.hcl> to produce a ZIP with all provider binaries.

14. Advanced State Manipulation (Move, Import, Split)

  • Task: Refactor infrastructure without downtime.
  • Prompt: "Show terraform state mv to rename a resource, import an existing S3 bucket, and split a monolithic state into two using terraform state pull/push."
  • Example result:
# Rename resource in state  erraform state mv 'aws_instance.web' 'aws_instance.app_server'

# Import existing bucket
terraform import 'aws_s3_bucket.existing' 'my-existing-bucket'

# Split state: pull current, filter, push new
terraform state pull > full.tfstate
terraform state list | grep vpc > vpc_resources.txt
terraform state pull | jq '...' > vpc.tfstate
terraform init -reconfigure -backend-config="key=vpc/terraform.tfstate"
terraform state push vpc.tfstate

Always back up state before manipulation.


Conclusion

These 14 prompts cover the full spectrum of Terraform and IaC—from single-resource basics to cross-cloud orchestration. Use them as templates for your own infrastructure code, as interview preparation, or as a checklist for skill growth. Remember that IaC is about version control, collaboration, and reproducibility. Start with the basic prompts, then advance to multi-environment and multi-cloud patterns. The examples are meant to be adapted—modify the resource types, regions, and variable values to match your real workloads. Happy automating!

← All posts

Comments