Skillshub terraform-style-guide
Generate Terraform HCL code following HashiCorp's official style conventions and best practices. Use when writing, reviewing, or generating Terraform configurations.
install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/hashicorp/agent-skills/terraform-style-guide" ~/.claude/skills/comeonoliver-skillshub-terraform-style-guide && rm -rf "$T"
manifest:
skills/hashicorp/agent-skills/terraform-style-guide/SKILL.mdsource content
Terraform Style Guide
Generate and maintain Terraform code following HashiCorp's official style conventions and best practices.
Reference: HashiCorp Terraform Style Guide
Code Generation Strategy
When generating Terraform code:
- Start with provider configuration and version constraints
- Create data sources before dependent resources
- Build resources in dependency order
- Add outputs for key resource attributes
- Use variables for all configurable values
File Organization
| File | Purpose |
|---|---|
| Terraform and provider version requirements |
| Provider configurations |
| Primary resources and data sources |
| Input variable declarations (alphabetical) |
| Output value declarations (alphabetical) |
| Local value declarations |
Example Structure
# terraform.tf terraform { required_version = ">= 1.7" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } # variables.tf variable "environment" { description = "Target deployment environment" type = string validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "Environment must be dev, staging, or prod." } } # locals.tf locals { common_tags = { Environment = var.environment ManagedBy = "Terraform" } } # main.tf resource "aws_vpc" "main" { cidr_block = var.vpc_cidr enable_dns_hostnames = true tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-vpc" }) } # outputs.tf output "vpc_id" { description = "ID of the created VPC" value = aws_vpc.main.id }
Code Formatting
Indentation and Alignment
- Use two spaces per nesting level (no tabs)
- Align equals signs for consecutive arguments
resource "aws_instance" "web" { ami = "ami-0c55b159cbfafe1f0" instance_type = "t2.micro" subnet_id = "subnet-12345678" tags = { Name = "web-server" Environment = "production" } }
Block Organization
Arguments precede blocks, with meta-arguments first:
resource "aws_instance" "example" { # Meta-arguments count = 3 # Arguments ami = "ami-0c55b159cbfafe1f0" instance_type = "t2.micro" # Blocks root_block_device { volume_size = 20 } # Lifecycle last lifecycle { create_before_destroy = true } }
Naming Conventions
- Use lowercase with underscores for all names
- Use descriptive nouns excluding the resource type
- Be specific and meaningful
- Resource names must be singular, not plural
- Default to
for resources where a specific descriptive name is redundant or unavailable, provided only one instance existsmain
# Bad resource "aws_instance" "webAPI-aws-instance" {} resource "aws_instance" "web_apis" {} variable "name" {} # Good resource "aws_instance" "web_api" {} resource "aws_vpc" "main" {} variable "application_name" {}
Variables
Every variable must include
type and description:
variable "instance_type" { description = "EC2 instance type for the web server" type = string default = "t2.micro" validation { condition = contains(["t2.micro", "t2.small", "t2.medium"], var.instance_type) error_message = "Instance type must be t2.micro, t2.small, or t2.medium." } } variable "database_password" { description = "Password for the database admin user" type = string sensitive = true }
Outputs
Every output must include
description:
output "instance_id" { description = "ID of the EC2 instance" value = aws_instance.web.id } output "database_password" { description = "Database administrator password" value = aws_db_instance.main.password sensitive = true }
Dynamic Resource Creation
Prefer for_each over count
# Bad - count for multiple resources resource "aws_instance" "web" { count = var.instance_count tags = { Name = "web-${count.index}" } } # Good - for_each with named instances variable "instance_names" { type = set(string) default = ["web-1", "web-2", "web-3"] } resource "aws_instance" "web" { for_each = var.instance_names tags = { Name = each.key } }
count for Conditional Creation
resource "aws_cloudwatch_metric_alarm" "cpu" { count = var.enable_monitoring ? 1 : 0 alarm_name = "high-cpu-usage" threshold = 80 }
Security Best Practices
When generating code, apply security hardening:
- Enable encryption at rest by default
- Configure private networking where applicable
- Apply principle of least privilege for security groups
- Enable logging and monitoring
- Never hardcode credentials or secrets
- Mark sensitive outputs with
sensitive = true
Example: Secure S3 Bucket
resource "aws_s3_bucket" "data" { bucket = "${var.project}-${var.environment}-data" tags = local.common_tags } resource "aws_s3_bucket_versioning" "data" { bucket = aws_s3_bucket.data.id versioning_configuration { status = "Enabled" } } 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.s3.arn } } } resource "aws_s3_bucket_public_access_block" "data" { bucket = aws_s3_bucket.data.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true }
Version Pinning
terraform { required_version = ">= 1.7" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" # Allow minor updates } } }
Version constraint operators:
- Exact version= 1.0.0
- Greater than or equal>= 1.0.0
- Allow rightmost component to increment~> 1.0
- Version range>= 1.0, < 2.0
Provider Configuration
provider "aws" { region = "us-west-2" default_tags { tags = { ManagedBy = "Terraform" Project = var.project_name } } } # Aliased provider for multi-region provider "aws" { alias = "east" region = "us-east-1" }
Version Control
Never commit:
,terraform.tfstateterraform.tfstate.backup
directory.terraform/*.tfplan
files with sensitive data.tfvars
Always commit:
- All
configuration files.tf
(dependency lock file).terraform.lock.hcl
Validation Tools
Run before committing:
terraform fmt -recursive terraform validate
Additional tools:
- Linting and best practicestflint
/checkov
- Security scanningtfsec
Code Review Checklist
- Code formatted with
terraform fmt - Configuration validated with
terraform validate - Files organized according to standard structure
- All variables have type and description
- All outputs have descriptions
- Resource names use descriptive nouns with underscores
- Version constraints pinned explicitly
- Sensitive values marked with
sensitive = true - No hardcoded credentials or secrets
- Security best practices applied
Based on: HashiCorp Terraform Style Guide