Complete terraform toolkit with generation and validation capabilities
93
Quality
93%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advisory
Suggest reviewing before use
terraform-project/
├── main.tf # Primary resource definitions
├── variables.tf # Input variable declarations
├── outputs.tf # Output value declarations
├── versions.tf # Terraform and provider version constraints
├── terraform.tfvars # Variable values (gitignored if sensitive)
├── backend.tf # Backend configuration (optional)
├── locals.tf # Local values (optional)
├── data.tf # Data source definitions (optional)
└── modules/ # Local modules (optional)
└── networking/
├── main.tf
├── variables.tf
└── outputs.tfterraform-project/
├── modules/ # Reusable modules
│ └── vpc/
├── environments/ # Environment-specific configurations
│ ├── dev/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf
│ ├── staging/
│ └── production/
└── shared/ # Shared resources# Good
resource "aws_instance" "web_server" {}
resource "aws_security_group" "web_server_sg" {}
# Avoid
resource "aws_instance" "aws_instance_web" {}
resource "aws_security_group" "sg" {}# Good
variable "instance_count" {}
variable "backup_retention_days" {}
variable "enable_encryption" {}
# Avoid
variable "count" {}
variable "retention" {}
variable "encrypt" {}terraform {
required_version = ">= 1.8, < 2.0" # Use 1.8+ for modern features
}terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # Allow patch versions
}
random = {
source = "hashicorp/random"
version = "3.5.1" # Pin exact version if needed
}
}
}module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.0" # Always pin module versions
# ...
}# Modern S3 backend with native locking (Terraform 1.11+)
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "project/terraform.tfstate"
region = "us-east-1"
encrypt = true
use_lockfile = true # S3-native locking (recommended for 1.11+)
kms_key_id = "alias/terraform-state"
}
}
# Legacy S3 backend with DynamoDB locking (Terraform < 1.11)
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "project/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks" # Deprecated in 1.11+
kms_key_id = "alias/terraform-state"
}
}Always use state locking for remote backends:
use_lockfile = true (Terraform 1.11+) or DynamoDB table (legacy)variable "instance_type" {
description = "EC2 instance type for web servers"
type = string
default = "t3.micro"
validation {
condition = can(regex("^t[23]\\.", var.instance_type))
error_message = "Instance type must be from t2 or t3 family."
}
}
variable "tags" {
description = "Common tags to apply to all resources"
type = map(string)
default = {}
}
variable "availability_zones" {
description = "List of availability zones"
type = list(string)
validation {
condition = length(var.availability_zones) >= 2
error_message = "At least 2 availability zones required."
}
}variable "database_password" {
description = "Password for database admin user"
type = string
sensitive = true
}
output "connection_string" {
value = "postgresql://user:${var.database_password}@${aws_db_instance.main.endpoint}"
sensitive = true
}TF_VAR_name)terraform.tfvars fileterraform.tfvars.json file*.auto.tfvars files (alphabetical order)-var and -var-file command-line flags# Implicit dependency (preferred)
resource "aws_eip" "example" {
instance = aws_instance.web.id # Implicit dependency
}
# Explicit dependency (when needed)
resource "aws_instance" "web" {
# ...
depends_on = [
aws_iam_role_policy.example
]
}resource "aws_instance" "web" {
# ...
lifecycle {
create_before_destroy = true # Create replacement before destroying
prevent_destroy = true # Prevent accidental deletion
ignore_changes = [ # Ignore external changes
tags["LastModified"],
user_data,
]
}
}resource "aws_instance" "web" {
# ...
provisioner "local-exec" {
command = "echo ${self.private_ip} >> private_ips.txt"
on_failure = continue # Continue if provisioner fails
}
provisioner "remote-exec" {
connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx",
]
}
}# Fetch latest AMI
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# Reference in resource
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
}
# Fetch availability zones
data "aws_availability_zones" "available" {
state = "available"
}
# Use in resources
resource "aws_subnet" "private" {
count = length(data.aws_availability_zones.available.names)
availability_zone = data.aws_availability_zones.available.names[count.index]
# ...
}locals {
# Common tags
common_tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "Terraform"
CostCenter = var.cost_center
}
# Computed values
az_count = length(data.aws_availability_zones.available.names)
subnet_count = var.subnet_count != null ? var.subnet_count : local.az_count
# Complex expressions
instance_name = "${var.project_name}-${var.environment}-web"
# Conditional values
instance_type = var.environment == "production" ? "t3.large" : "t3.micro"
# Map transformations
subnet_cidrs = {
for idx, az in data.aws_availability_zones.available.names :
az => cidrsubnet(var.vpc_cidr, 8, idx)
}
}# Dynamic ingress rules
resource "aws_security_group" "web" {
name_prefix = "web-"
vpc_id = aws_vpc.main.id
dynamic "ingress" {
for_each = var.ingress_rules
content {
description = ingress.value.description
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
tags = local.common_tags
}
# Variable definition
variable "ingress_rules" {
type = list(object({
description = string
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
default = [
{
description = "HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
description = "HTTPS"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
]
}# Create multiple similar resources
resource "aws_instance" "web" {
count = var.instance_count
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
subnet_id = element(aws_subnet.private[*].id, count.index)
tags = merge(
local.common_tags,
{
Name = "${local.instance_name}-${count.index + 1}"
}
)
}# Create resources from map
resource "aws_iam_user" "users" {
for_each = toset(var.user_names)
name = each.value
tags = {
Team = lookup(var.user_teams, each.value, "default")
}
}
# Create resources with different configurations
variable "environments" {
type = map(object({
instance_type = string
instance_count = number
}))
default = {
dev = {
instance_type = "t3.micro"
instance_count = 1
}
prod = {
instance_type = "t3.large"
instance_count = 3
}
}
}
resource "aws_instance" "env_servers" {
for_each = var.environments
ami = data.aws_ami.ubuntu.id
instance_type = each.value.instance_type
count = each.value.instance_count
tags = {
Name = "${each.key}-server"
Environment = each.key
}
}module/
├── main.tf # Main resources
├── variables.tf # Input variables
├── outputs.tf # Output values
├── versions.tf # Version constraints
├── README.md # Documentation
└── examples/ # Usage examples
└── complete/
├── main.tf
└── variables.tf# modules/vpc/variables.tf
variable "name" {
description = "Name to be used on all resources"
type = string
}
variable "cidr" {
description = "CIDR block for VPC"
type = string
validation {
condition = can(cidrhost(var.cidr, 0))
error_message = "Must be valid IPv4 CIDR."
}
}
variable "enable_nat_gateway" {
description = "Should be true to provision NAT Gateways"
type = bool
default = true
}
variable "tags" {
description = "A map of tags to add to all resources"
type = map(string)
default = {}
}# modules/vpc/outputs.tf
output "vpc_id" {
description = "The ID of the VPC"
value = aws_vpc.this.id
}
output "vpc_cidr_block" {
description = "The CIDR block of the VPC"
value = aws_vpc.this.cidr_block
}
output "private_subnet_ids" {
description = "List of IDs of private subnets"
value = aws_subnet.private[*].id
}
output "public_subnet_ids" {
description = "List of IDs of public subnets"
value = aws_subnet.public[*].id
}module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.0"
name = "${var.project_name}-vpc"
cidr = "10.0.0.0/16"
azs = data.aws_availability_zones.available.names
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
enable_vpn_gateway = false
tags = local.common_tags
}
# Reference module outputs
resource "aws_instance" "web" {
subnet_id = module.vpc.private_subnet_ids[0]
# ...
}# NEVER hardcode secrets
# BAD
resource "aws_db_instance" "database" {
password = "supersecretpassword" # NEVER DO THIS
}
# GOOD - Use variables
variable "db_password" {
type = string
sensitive = true
}
resource "aws_db_instance" "database" {
password = var.db_password
}
# BETTER - Use secrets management service
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "prod/database/password"
}
resource "aws_db_instance" "database" {
password = jsondecode(data.aws_secretsmanager_secret_version.db_password.secret_string)["password"]
}# Enable encryption by default
resource "aws_s3_bucket" "data" {
bucket = "my-data-bucket"
}
resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
bucket = aws_s3_bucket.data.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.data.arn
}
}
}
# Encrypt EBS volumes
resource "aws_instance" "web" {
# ...
root_block_device {
encrypted = true
kms_key_id = aws_kms_key.data.arn
volume_type = "gp3"
}
}# Use least privilege principle
data "aws_iam_policy_document" "lambda_execution" {
statement {
effect = "Allow"
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
resources = [
"arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/${var.function_name}:*"
]
}
statement {
effect = "Allow"
actions = [
"s3:GetObject"
]
resources = [
"${aws_s3_bucket.data.arn}/*"
]
}
}
resource "aws_iam_policy" "lambda_execution" {
name = "${var.function_name}-execution"
policy = data.aws_iam_policy_document.lambda_execution.json
}variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "instance_count" {
type = number
validation {
condition = var.instance_count > 0 && var.instance_count <= 10
error_message = "Instance count must be between 1 and 10."
}
}Use terraform fmt and terraform validate in pre-commit hooks:
#!/bin/bash
# .git/hooks/pre-commit
terraform fmt -check -recursive || exit 1
terraform validate || exit 1# Create VPC for application infrastructure
# This VPC uses a /16 CIDR block to accommodate multiple subnets
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true # Required for ECS task networking
enable_dns_support = true
tags = merge(
local.common_tags,
{
Name = "${var.project_name}-vpc"
}
)
}Include in project README:
Terraform automatically parallelizes resource creation when possible. Help it by:
terraform apply -target=resource-refresh=false when appropriate# ~/.terraformrc or terraform.rc
plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"