Terraform + AI: Write Infrastructure as Code 10x Faster with GitHub Copilot (2025 Complete Guide)
Stop writing Terraform from scratch. Use GitHub Copilot, tfsec, Checkov, and Infracost to generate, validate, secure, and cost-check your IaC — all before you hit terraform apply.
slug: terraform-ai-github-copilot-iac-faster-2025 tags: [Terraform, GitHubCopilot, IaC, DevOps, AI, AWS, Azure, GCP, InfrastructureAsCode, CloudDevOps, Checkov, tfsec, Infracost, CI/CD] cover: https://cdn.hashnode.com/res/hashnode/image/upload/v1/terraform-ai-copilot-cover.png canonical: https://blog.clouddevopshub.com/terraform-ai-github-copilot-iac-faster-2025 seo_title: "Terraform + GitHub Copilot Tutorial 2025: Write IaC 10x Faster with AI" seo_description: "Learn how to use GitHub Copilot to generate Terraform modules, tfsec and Checkov to validate security, and Infracost to estimate costs — all in one AI-powered IaC workflow. Real examples for AWS, Azure, GCP."
TL;DR: Writing Terraform from scratch is slow. Hunting down security misconfigs is tedious. Discovering your infrastructure costs $3,000/month after deploying it is painful. This guide shows you how GitHub Copilot generates production-ready Terraform in seconds, how tfsec and Checkov catch security issues before they hit production, and how Infracost shows you the bill before you run
terraform apply. Real code. Real tools. Real workflow.
Why Every DevOps Engineer Needs AI for Terraform in 2025
Let's be honest about what writing Terraform actually looks like most of the time:
Open a blank
main.tfStare at it for 30 seconds
Open the Terraform Registry docs
Copy a resource block, adjust 12 variables
Forget the
depends_on, break the planFix it. Deploy. Get a Slack message at 3 AM — S3 bucket is publicly exposed
Fix the security issue. Deploy again. Get a $4,200 AWS bill at month-end
Repeat
This is the Infrastructure-as-Code experience for most teams. Not because Terraform is bad — it's excellent — but because writing correct, secure, cost-aware IaC from scratch requires holding an enormous amount of context in your head simultaneously.
AI changes this. GitHub Copilot doesn't just autocomplete code — it understands Terraform's resource structure, cloud provider APIs, and best practices well enough to generate production-quality configurations from a plain English comment. Pair that with AI-integrated security scanners and cost estimators, and you have a completely different IaC workflow.
Here's what the same workflow looks like with AI:
Type a comment describing what you need
Copilot generates the Terraform module in seconds
tfsec + Checkov scan it for security issues instantly
Infracost shows estimated monthly cost in your PR
terraform plan→terraform applywith confidence
That's the workflow you're going to build in this guide.
What You'll Learn
Set up GitHub Copilot for Terraform in VS Code
Use Copilot to generate real modules — AWS VPC, EC2, S3, RDS, EKS
Write the perfect Copilot prompts for Terraform (with templates)
Validate and secure IaC with tfsec and Checkov
Estimate cloud costs before deploying with Infracost
Build a complete AI-powered Terraform CI/CD pipeline with GitHub Actions
Common Copilot mistakes to avoid
Prerequisites: VS Code installed, GitHub account (free or paid), basic Terraform knowledge, an AWS/Azure/GCP account for examples.
Part 1 — Set Up GitHub Copilot for Terraform
Install GitHub Copilot in VS Code
GitHub Copilot has a free tier — 2,000 code completions and 50 chat messages per month. That's enough to try everything in this guide.
Step 1 — Enable Copilot:
Go to github.com/settings/copilot and enable it on your account. Free tier is available.
Step 2 — Install the VS Code extension:
Open VS Code → Extensions (Ctrl+Shift+X) → Search "GitHub Copilot" → Install both:
GitHub Copilot(inline completions)GitHub Copilot Chat(conversational AI)
Step 3 — Sign in:
Click the Copilot icon in the VS Code status bar → Sign in with GitHub. You'll see a checkmark when active.
Step 4 — Install the HashiCorp Terraform extension:
While you're in Extensions, also install:
HashiCorp Terraform— syntax highlighting, validation, resource documentation
Now open any .tf file and Copilot is active. It reads the context of your file and suggests completions as you type.
Configure VS Code for the Best Copilot + Terraform Experience
Add these to your VS Code settings.json (Ctrl+Shift+P → "Open User Settings JSON"):
{
"github.copilot.enable": {
"*": true,
"terraform": true,
"hcl": true
},
"editor.inlineSuggest.enabled": true,
"editor.suggestOnTriggerCharacters": true,
"github.copilot.editor.enableAutoCompletions": true,
"[terraform]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "hashicorp.terraform"
}
}
Key shortcuts to memorize:
| Action | Shortcut |
|---|---|
| Accept Copilot suggestion | Tab |
| Reject suggestion | Esc |
| See next suggestion | Alt + ] |
| Open Copilot Chat | Ctrl + Shift + I |
| Trigger inline suggestion | Alt + \ |
Part 2 — Generate Terraform with GitHub Copilot
The Golden Rule: Comments Drive Quality
Copilot's output quality is directly proportional to the specificity of your comment. Vague comments produce vague code. Detailed comments produce production-ready code.
Poor prompt:
# create an S3 bucket
Great prompt:
# Create a private S3 bucket for application logs
# - Enable versioning
# - Enable server-side encryption with AES256
# - Block all public access
# - Add lifecycle rule: move to Glacier after 90 days, delete after 365 days
# - Tag with environment, project, and owner
The second comment will generate a complete, secure, production-grade S3 resource. The first will generate 3 lines of bare minimum HCL.
Example 1: Generate a Production AWS VPC Module
Create a new file modules/vpc/main.tf and type this comment:
# Terraform module for a production-grade AWS VPC
# - CIDR: 10.0.0.0/16
# - 3 public subnets across 3 AZs (10.0.1.0/24, 10.0.2.0/24, 10.0.3.0/24)
# - 3 private subnets across 3 AZs (10.0.4.0/24, 10.0.5.0/24, 10.0.6.0/24)
# - Internet Gateway for public subnets
# - NAT Gateway in each AZ for private subnet egress
# - Route tables for public and private subnets
# - Tags: Environment, Project, ManagedBy=Terraform
Press Enter. Copilot generates something like this:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
variable "environment" {
description = "Environment name (dev, staging, prod)"
type = string
}
variable "project" {
description = "Project name for tagging"
type = string
}
locals {
azs = ["\({var.aws_region}a", "\){var.aws_region}b", "${var.aws_region}c"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
private_subnets = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
common_tags = {
Environment = var.environment
Project = var.project
ManagedBy = "Terraform"
}
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = merge(local.common_tags, { Name = "\({var.project}-\){var.environment}-vpc" })
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = merge(local.common_tags, { Name = "\({var.project}-\){var.environment}-igw" })
}
resource "aws_subnet" "public" {
count = 3
vpc_id = aws_vpc.main.id
cidr_block = local.public_subnets[count.index]
availability_zone = local.azs[count.index]
map_public_ip_on_launch = true
tags = merge(local.common_tags, {
Name = "\({var.project}-\){var.environment}-public-${count.index + 1}"
Tier = "public"
})
}
resource "aws_subnet" "private" {
count = 3
vpc_id = aws_vpc.main.id
cidr_block = local.private_subnets[count.index]
availability_zone = local.azs[count.index]
tags = merge(local.common_tags, {
Name = "\({var.project}-\){var.environment}-private-${count.index + 1}"
Tier = "private"
})
}
resource "aws_eip" "nat" {
count = 3
domain = "vpc"
tags = merge(local.common_tags, { Name = "\({var.project}-\){var.environment}-nat-eip-${count.index + 1}" })
}
resource "aws_nat_gateway" "main" {
count = 3
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = merge(local.common_tags, { Name = "\({var.project}-\){var.environment}-nat-${count.index + 1}" })
depends_on = [aws_internet_gateway.main]
}
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
}
tags = merge(local.common_tags, { Name = "\({var.project}-\){var.environment}-public-rt" })
}
resource "aws_route_table" "private" {
count = 3
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = merge(local.common_tags, { Name = "\({var.project}-\){var.environment}-private-rt-${count.index + 1}" })
}
resource "aws_route_table_association" "public" {
count = 3
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
count = 3
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private[count.index].id
}
output "vpc_id" { value = aws_vpc.main.id }
output "public_subnets" { value = aws_subnet.public[*].id }
output "private_subnets" { value = aws_subnet.private[*].id }
That's a complete, production-ready VPC module — generated from a comment. Manually, this would take 30–45 minutes and at least two rounds of fixing mistakes.
Example 2: Generate a Secure S3 Bucket
# AWS S3 bucket for storing application artifacts
# Security requirements:
# - Block ALL public access (all 4 block settings = true)
# - Enable AES-256 server-side encryption by default
# - Enable versioning
# - Enable access logging to a separate log bucket
# - Force destroy = false (protect against accidental deletion)
# - Lifecycle: transition to STANDARD_IA after 30 days, GLACIER after 90 days
Copilot output:
resource "aws_s3_bucket" "artifacts" {
bucket = "\({var.project}-\){var.environment}-artifacts"
force_destroy = false
tags = local.common_tags
}
resource "aws_s3_bucket_public_access_block" "artifacts" {
bucket = aws_s3_bucket.artifacts.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_server_side_encryption_configuration" "artifacts" {
bucket = aws_s3_bucket.artifacts.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
bucket_key_enabled = true
}
}
resource "aws_s3_bucket_versioning" "artifacts" {
bucket = aws_s3_bucket.artifacts.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_lifecycle_configuration" "artifacts" {
bucket = aws_s3_bucket.artifacts.id
rule {
id = "transition-and-expire"
status = "Enabled"
transition {
days = 30
storage_class = "STANDARD_IA"
}
transition {
days = 90
storage_class = "GLACIER"
}
}
}
Notice: Copilot split the S3 configuration into separate resources —
aws_s3_bucket_public_access_block,aws_s3_bucket_versioning, etc. This is the correct AWS provider v4+ approach. Copilot knows this. A developer copy-pasting from old Stack Overflow answers would miss it.
Example 3: Use Copilot Chat for Complex Infrastructure
For more complex tasks, use Copilot Chat (Ctrl+Shift+I) with a detailed conversational prompt:
@workspace Create a Terraform module for an AWS EKS cluster with:
- Kubernetes version 1.30
- 2 managed node groups: one for system workloads (t3.medium, 2-5 nodes)
and one for application workloads (t3.large, 2-10 nodes, with auto-scaling)
- OIDC provider configured for IAM Roles for Service Accounts (IRSA)
- CloudWatch logging enabled for API server and audit logs
- Private endpoint access only (no public API server)
- Security group allowing only VPC internal communication
- Tags for environment and project
- All required IAM roles and policies
Copilot Chat will generate the full module — cluster, node groups, IAM roles, security groups, OIDC provider — and explain each section.
The 7 Best Copilot Prompt Templates for Terraform
Save these as comments in a copilot-prompts.md file in your repo:
1. Database (RDS)
# AWS RDS PostgreSQL instance - production grade
# - Instance: db.t3.medium, Multi-AZ enabled
# - Storage: 100GB gp3, encrypted with KMS
# - Automated backups: 7 day retention
# - Deletion protection: true
# - Parameter group: PostgreSQL 15
# - Subnet group using private subnets
# - Security group: allow port 5432 from app security group only
2. Load Balancer
# AWS Application Load Balancer for web application
# - Internet-facing, in public subnets
# - HTTPS listener on port 443 with SSL certificate from ACM
# - HTTP listener on port 80 that redirects to HTTPS
# - Target group: port 8080, health check path /health
# - Access logs enabled to S3
# - Deletion protection: true in production
3. Lambda Function
# AWS Lambda function for image processing
# - Runtime: Python 3.12, memory 512MB, timeout 30s
# - IAM role with S3 read/write and CloudWatch Logs permissions
# - Environment variables from SSM Parameter Store (not hardcoded)
# - Dead letter queue (SQS) for failed executions
# - CloudWatch log group with 30 day retention
# - VPC config: private subnets, security group allowing outbound only
4. Kubernetes Namespace + RBAC
# Kubernetes namespace with RBAC for the payments team
# - Namespace: payments-prod
# - ResourceQuota: 4 CPU, 8Gi memory, 10 pods max
# - LimitRange: default 100m CPU / 256Mi memory per container
# - ServiceAccount with IAM annotations for IRSA
# - Role: read pods/logs, write deployments/services
# - RoleBinding: bind role to payments-team group
5. CI/CD Variables
# Terraform variables for a complete 3-tier application
# - Include: environment, region, project, vpc_cidr
# - Database: instance type, storage size, engine version
# - Application: instance type, min/max ASG, AMI ID
# - Networking: allowed CIDR blocks for SSH, HTTPS
# - All variables with descriptions, types, and sensible defaults
# - Sensitive variables marked as sensitive = true
6. Azure Resource Group + VNet
# Azure Resource Group and Virtual Network - production
# - Resource Group: region East US, standard tags
# - VNet: 10.0.0.0/16 with DNS servers
# - 4 subnets: web (10.0.1.0/24), app (10.0.2.0/24),
# data (10.0.3.0/24), management (10.0.4.0/24)
# - NSG per subnet with deny-all default, allow specific rules
# - DDoS Standard protection plan
# - Network Watcher enabled
7. GCP Cloud Run + Cloud SQL
# GCP Cloud Run service with Cloud SQL PostgreSQL backend
# - Cloud Run: container image from Artifact Registry,
# min 1 max 10 instances, 1 vCPU, 512Mi memory
# - Cloud SQL: PostgreSQL 15, db-g1-small, private IP only
# - VPC connector for Cloud Run to reach Cloud SQL
# - Service Account with Cloud SQL Client role
# - Secret Manager for database password
# - Cloud Armor for WAF on the frontend
Part 3 — Validate and Secure Terraform with AI-Powered Scanners
Generating Terraform with Copilot is fast. But AI can also hallucinate security misconfigs — an S3 bucket with public ACLs, a security group open to 0.0.0.0/0, or an unencrypted RDS instance. You need automated security validation in your workflow.
Three tools form your security layer:
| Tool | What it does | Speed |
|---|---|---|
terraform validate |
Syntax + logical errors | ~1 sec |
| tfsec | Security misconfiguration scan | ~5 sec |
| Checkov | Security + compliance (CIS, SOC2, HIPAA) | ~10 sec |
Install the Security Tools
# tfsec (macOS)
brew install tfsec
# tfsec (Linux)
curl -s https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash
# Checkov (all platforms via pip)
pip3 install checkov
# Verify installations
tfsec --version
checkov --version
Run tfsec on Your Terraform
# Scan current directory
tfsec .
# Scan with minimum severity (only show HIGH and CRITICAL)
tfsec --minimum-severity HIGH .
# Output as JSON for CI/CD
tfsec --format json --out tfsec-report.json .
Example tfsec output catching a real vulnerability:
Result #1 HIGH S3 Bucket does not have logging enabled
────────────────────────────────────────────────
main.tf:12-28
12 ┌ resource "aws_s3_bucket" "data" {
13 │ bucket = "my-data-bucket"
14 └ }
Impact: No audit trail for bucket access
Resolution: Enable access logging
More info: https://aquasecurity.github.io/tfsec/v1.28.4/checks/aws/s3/enable-logging/
──────────────────────────────────────────────────────────────
Result #2 HIGH S3 Bucket does not have encryption enabled
────────────────────────────────────────────────
main.tf:12-28
Impact: Data at rest is not encrypted
Resolution: Add server-side encryption configuration
tfsec caught two issues that Copilot didn't include because the original comment didn't mention them. This is why you always scan — not because Copilot is wrong, but because your prompts may have gaps.
Run Checkov for Compliance Checks
# Scan Terraform directory
checkov -d .
# Scan with specific framework
checkov -d . --framework terraform
# Only check specific compliance standard (CIS AWS)
checkov -d . --check CKV_AWS_*
# Skip a specific check (with justification comment in code)
checkov -d . --skip-check CKV_AWS_144 # Skip cross-region replication for dev env
# Output SARIF format for GitHub Security tab
checkov -d . --output-file-path . --output sarif
Example Checkov output:
Check: CKV_AWS_18: "Ensure the S3 bucket has access logging enabled"
FAILED for resource: aws_s3_bucket.artifacts
File: /terraform/main.tf:5-15
Check: CKV_AWS_52: "Ensure S3 bucket has MFA delete enabled"
FAILED for resource: aws_s3_bucket.artifacts
File: /terraform/main.tf:5-15
Check: CKV_AWS_21: "Ensure all data stored in the S3 Bucket have versioning enabled"
PASSED for resource: aws_s3_bucket.artifacts
Passed checks: 18, Failed checks: 4, Skipped checks: 0
Use Copilot Chat to Fix Security Findings
Once you have tfsec or Checkov output, paste it into Copilot Chat:
I ran tfsec on my Terraform and got these security findings:
[paste tfsec output here]
Please fix all of these issues in my main.tf and explain what each fix does.
Copilot will read the findings and generate the fixes. This is the AI-powered security fix loop — scan, paste findings to Copilot, get fixes, rescan.
Set Up Pre-commit Hooks (Scan Before Every Commit)
Never let broken or insecure Terraform reach your repo:
pip3 install pre-commit
Create .pre-commit-config.yaml in your repo root:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.92.0
hooks:
# Format all .tf files
- id: terraform_fmt
# Validate Terraform syntax
- id: terraform_validate
# Security scan with tfsec (block on HIGH+)
- id: terraform_tfsec
args:
- --args=--minimum-severity=HIGH
# Compliance scan with Checkov
- id: terraform_checkov
args:
- --args=--skip-check CKV_AWS_144,CKV_AWS_273
# Auto-generate module documentation
- id: terraform_docs
args:
- --hook-config=--path-to-file=README.md
- --hook-config=--add-to-existing-file=true
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: detect-private-key
- id: check-merge-conflict
- id: trailing-whitespace
Install the hooks:
pre-commit install
Now every git commit automatically runs format, validate, tfsec, and Checkov. Bad Terraform never leaves your machine.
Part 4 — Estimate Costs Before You Deploy with Infracost
You've generated Terraform with Copilot. You've secured it with tfsec and Checkov. Now answer the most important question: how much will this cost?
Infracost is an open-source tool that reads your Terraform files and shows you the estimated monthly AWS/Azure/GCP cost — before you deploy anything.
Install Infracost
# macOS
brew install infracost
# Linux
curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh
# Authenticate (free API key, no credit card)
infracost auth login
Get Your First Cost Estimate
# Navigate to your Terraform directory
cd terraform/
# Get a full cost breakdown
infracost breakdown --path .
Example output:
Project: clouddevopshub/terraform-infra
Name Monthly Qty Unit Monthly Cost
aws_eks_cluster.main
└─ EKS cluster 730 hours $73.00
aws_eks_node_group.system
├─ Instance usage (Linux/UNIX, on-demand, t3.medium)
│ └─ 2 × 730 hours 1460 hours $60.63
└─ Root volume: 20 GB gp3 40 GB-months $3.20
aws_eks_node_group.application
├─ Instance usage (Linux/UNIX, on-demand, t3.large)
│ └─ 3 × 730 hours 2190 hours $136.41
└─ Root volume: 20 GB gp3 60 GB-months $4.80
aws_nat_gateway.main (×3)
├─ NAT gateway 2190 hours $97.74
└─ Data processed Cost depends on usage
aws_rds_cluster.main
└─ Database instance (db.t3.medium, multi-az) 730 hours $106.58
MONTHLY ESTIMATE $482.36
──────────────────────────────────────────────────────────────────────────────────────
1 resource type wasn't estimated (aws_nat_gateway data transfer - usage-based)
That's your estimated cost before running a single terraform apply. You can now have an informed conversation with your team or manager about the infrastructure budget.
Show Cost Diff in Pull Requests
This is the killer feature. Every time a PR changes Terraform files, Infracost automatically comments on it with the cost impact:
💰 Infracost estimate: +$127.50/month
┌─────────────────────────────────────────────────────────┐
│ Changed resources │
├─────────────────────────────────────────────────────────┤
│ ~ aws_eks_node_group.application │
│ instance_type: t3.large → t3.xlarge +$127.50/month │
└─────────────────────────────────────────────────────────┘
Your team can see, review, and approve cost increases as part of normal code review — just like reviewing security or functionality changes.
Part 5 — The Complete AI-Powered Terraform CI/CD Pipeline
Put it all together in a GitHub Actions workflow that runs on every PR touching Terraform files:
# .github/workflows/terraform-ai-pipeline.yml
name: Terraform AI Pipeline
on:
pull_request:
paths:
- 'terraform/**'
- '**.tf'
push:
branches: [main]
paths:
- 'terraform/**'
permissions:
contents: read
pull-requests: write # needed for Infracost PR comments
env:
TF_VERSION: "1.9.0"
TF_WORKING_DIR: "./terraform"
AWS_REGION: "ap-south-1"
jobs:
# ─────────────────────────────────────────────
# STEP 1: Format + Validate
# ─────────────────────────────────────────────
validate:
name: Format & Validate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Terraform Format Check
run: terraform fmt -check -recursive
working-directory: ${{ env.TF_WORKING_DIR }}
- name: Terraform Init
run: terraform init -backend=false
working-directory: ${{ env.TF_WORKING_DIR }}
- name: Terraform Validate
run: terraform validate
working-directory: ${{ env.TF_WORKING_DIR }}
# ─────────────────────────────────────────────
# STEP 2: Security Scan with tfsec
# ─────────────────────────────────────────────
security-tfsec:
name: Security Scan (tfsec)
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@v4
- name: Run tfsec
uses: aquasecurity/tfsec-action@v1.0.0
with:
working_directory: ${{ env.TF_WORKING_DIR }}
minimum_severity: HIGH
format: sarif
sarif_file: tfsec.sarif
- name: Upload SARIF results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: tfsec.sarif
# ─────────────────────────────────────────────
# STEP 3: Compliance Scan with Checkov
# ─────────────────────────────────────────────
security-checkov:
name: Compliance Scan (Checkov)
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@v4
- name: Run Checkov
uses: bridgecrewio/checkov-action@v12
with:
directory: ${{ env.TF_WORKING_DIR }}
framework: terraform
output_format: sarif
output_file_path: checkov.sarif
skip_check: CKV_AWS_144,CKV_AWS_273 # document exceptions
- name: Upload Checkov results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: checkov.sarif
# ─────────────────────────────────────────────
# STEP 4: Cost Estimation with Infracost
# ─────────────────────────────────────────────
cost-estimation:
name: Cost Estimate (Infracost)
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@v4
- name: Setup Infracost
uses: infracost/actions/setup@v3
with:
api-key: ${{ secrets.INFRACOST_API_KEY }}
- name: Checkout base branch for comparison
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.sha }}
path: base-branch
- name: Generate base cost estimate
run: |
infracost breakdown \
--path=base-branch/${{ env.TF_WORKING_DIR }} \
--format=json \
--out-file=/tmp/infracost-base.json
- name: Generate PR cost estimate
run: |
infracost breakdown \
--path=${{ env.TF_WORKING_DIR }} \
--format=json \
--out-file=/tmp/infracost-pr.json
- name: Post cost diff comment on PR
run: |
infracost comment github \
--path=/tmp/infracost-pr.json \
--repo=$GITHUB_REPOSITORY \
--pull-request=${{ github.event.pull_request.number }} \
--github-token=${{ github.token }} \
--behavior=update
if: github.event_name == 'pull_request'
# ─────────────────────────────────────────────
# STEP 5: Terraform Plan (on PRs)
# ─────────────────────────────────────────────
plan:
name: Terraform Plan
runs-on: ubuntu-latest
needs: [security-tfsec, security-checkov, cost-estimation]
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Terraform Init
run: terraform init
working-directory: ${{ env.TF_WORKING_DIR }}
- name: Terraform Plan
run: terraform plan -out=tfplan -no-color 2>&1 | tee plan.txt
working-directory: ${{ env.TF_WORKING_DIR }}
- name: Post Plan to PR
uses: actions/github-script@v7
if: github.event_name == 'pull_request'
with:
script: |
const fs = require('fs');
const plan = fs.readFileSync('${{ env.TF_WORKING_DIR }}/plan.txt', 'utf8');
const truncated = plan.length > 65000
? plan.substring(0, 65000) + '\n... (truncated)'
: plan;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## Terraform Plan\n\`\`\`hcl\n${truncated}\n\`\`\``
});
# ─────────────────────────────────────────────
# STEP 6: Terraform Apply (on merge to main)
# ─────────────────────────────────────────────
apply:
name: Terraform Apply
runs-on: ubuntu-latest
needs: [security-tfsec, security-checkov]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
environment: production # requires manual approval in GitHub Environments
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Terraform Init
run: terraform init
working-directory: ${{ env.TF_WORKING_DIR }}
- name: Terraform Apply
run: terraform apply -auto-approve
working-directory: ${{ env.TF_WORKING_DIR }}
What this pipeline does on every PR:
PR opened / updated
│
▼
┌─────────────┐
│ Validate │ terraform fmt + validate (30 sec)
└──────┬──────┘
│
┌─────┴──────┬───────────────┐
▼ ▼ ▼
tfsec Checkov Infracost
(security) (compliance) (cost diff)
│ │ │
└─────┬──────┘ │
│ │
▼ ▼
terraform plan PR comment
posted to PR with $$ diff
│
▼
Human review + approve
│
▼
Merge to main
│
▼
terraform apply
(with manual approval gate)
No insecure or expensive infrastructure ever reaches your cloud account without human sign-off.
Part 6 — Real-World Copilot Mistakes (and How to Avoid Them)
AI is powerful but not perfect. Here are the most common mistakes engineers make when using Copilot for Terraform:
Mistake 1: Accepting Outdated Provider Syntax
Copilot was trained on code that includes older AWS provider patterns. The most common one:
# WRONG — old AWS provider v3 pattern
resource "aws_s3_bucket" "example" {
bucket = "my-bucket"
acl = "private" # ← Deprecated in provider v4+
server_side_encryption_configuration { # ← Separate resource now
rule { ... }
}
}
# CORRECT — AWS provider v5 pattern
resource "aws_s3_bucket" "example" {
bucket = "my-bucket"
}
resource "aws_s3_bucket_acl" "example" {
bucket = aws_s3_bucket.example.id
acl = "private"
}
resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
bucket = aws_s3_bucket.example.id
rule { ... }
}
Fix: Always include the provider version in your comment prompt:
# Using AWS provider v5.x — use separate resources for bucket properties
Mistake 2: Hardcoded Secrets in Variables
Copilot sometimes generates this:
# NEVER DO THIS — Copilot might suggest it
variable "db_password" {
default = "MyPassword123!" # ← Hardcoded secret in version control
}
Fix: Always use sensitive = true and pull from AWS Secrets Manager or SSM:
variable "db_password" {
description = "RDS master password — pull from AWS Secrets Manager"
type = string
sensitive = true
# No default — must be passed at runtime
}
# Or reference SSM directly
data "aws_ssm_parameter" "db_password" {
name = "/${var.environment}/rds/master-password"
with_decryption = true
}
Mistake 3: Trusting Copilot on CIDR Ranges
Copilot sometimes generates security groups with 0.0.0.0/0 ingress rules:
# WRONG — never allow unrestricted SSH in production
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # ← tfsec will catch this
}
Run tfsec after every Copilot generation. It catches this immediately.
Mistake 4: Missing prevent_destroy on Critical Resources
Copilot won't add lifecycle blocks unless you ask. Always add them to databases and stateful resources:
resource "aws_rds_cluster" "main" {
# ... configuration ...
lifecycle {
prevent_destroy = true # Add this manually — Copilot often skips it
}
}
Before vs. After: AI-Powered Terraform Workflow
| Task | Manual Approach | With AI Tools |
|---|---|---|
| Write VPC module | 45–60 min | 2–3 min (Copilot) |
| Security review | Manual code review (often missed) | Automated (tfsec + Checkov, ~10 sec) |
| Cost check | Deploy → surprise bill | Before deploy (Infracost in PR) |
| Fix security issues | Find + fix manually | Paste findings to Copilot Chat, get fixes |
| Documentation | Manually write README | Auto-generated (terraform-docs) |
| PR review | No cost/security context | Full plan + cost diff + security report |
Summary: Your AI-Powered IaC Toolkit
Here's your complete stack:
| Tool | Role | Cost |
|---|---|---|
| GitHub Copilot | Generate Terraform from comments | Free tier / $10/mo |
| Copilot Chat | Explain, fix, refactor Terraform | Included |
| tfsec | Security misconfiguration scanner | Free (open source) |
| Checkov | CIS/SOC2/HIPAA compliance scanner | Free (open source) |
| Infracost | Cloud cost estimation in PRs | Free tier |
| terraform-docs | Auto-generate module README | Free (open source) |
| pre-commit-terraform | Run all checks before every commit | Free (open source) |
The workflow:
Comment in .tf file
↓
GitHub Copilot generates module
↓
pre-commit: fmt → validate → tfsec → Checkov → docs
↓
git push → PR opened
↓
GitHub Actions: validate → tfsec → Checkov → Infracost → plan
↓
PR comment: security report + cost diff + terraform plan
↓
Human review → approve → merge
↓
Terraform apply (with manual gate for production)
This is the Terraform workflow that senior engineers at companies like Stripe, Airbnb, and Coinbase run internally — automated, auditable, and cost-aware at every step.
Learn This in a Live Bootcamp
Want to build this complete AI-powered Terraform pipeline on real AWS, Azure, and GCP infrastructure — with hands-on projects, live mentoring, and career support?
CloudDevOpsHub Batch 42 — 55-day Multi-Cloud + DevOps with AI bootcamp — covers Terraform, Kubernetes, GitOps, AIOps, CI/CD, and AI-integrated DevOps workflows end to end.
👉 Enroll in Batch 42 — CloudDevOpsHub →
SEO Keywords Covered
Terraform AI tutorial, GitHub Copilot Terraform, Terraform IaC AI 2025, write Terraform faster AI, GitHub Copilot IaC, tfsec tutorial, Checkov Terraform, Infracost GitHub Actions, Terraform security scanning, AI infrastructure as code, Terraform CI/CD pipeline, GitHub Copilot DevOps, Terraform best practices 2025, IaC security tools, Terraform cost estimation, terraform validate tfsec checkov, Infracost PR comment, Terraform AWS module Copilot, AI DevOps tools 2025, GitHub Copilot cloud infrastructure
Written by the CloudDevOpsHub team — practical hands-on training in Multi-Cloud, DevOps, and AI for engineers who want high-paying cloud roles. Follow CloudDevOpsHub on Hashnode for weekly deep-dives on Terraform, Kubernetes, and AI-powered DevOps.