Skill v1.0.1
currentAutomated scan100/100+2 new
version: "1.0.1" name: terraform-style-guide description: Generate Terraform HCL code following HashiCorp's official style conventions and best practices. Use when writing, reviewing, or generating Terraform configurations. metadata: lifecycle-status: active
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.tf | Terraform and provider version requirements | |
providers.tf | Provider configurations | |
main.tf | Primary resources and data sources | |
variables.tf | Input variable declarations (alphabetical) | |
outputs.tf | Output value declarations (alphabetical) | |
locals.tf | Local value declarations |
Example Structure
# terraform.tfterraform {required_version = ">= 1.14"required_providers {aws = {source = "hashicorp/aws"version = "~> 6.0"}}}# variables.tfvariable "environment" {description = "Target deployment environment"type = stringvalidation {condition = contains(["dev", "staging", "prod"], var.environment)error_message = "Environment must be dev, staging, or prod."}}# locals.tflocals {common_tags = {Environment = var.environmentManagedBy = "Terraform"}}# main.tfresource "aws_vpc" "main" {cidr_block = var.vpc_cidrenable_dns_hostnames = truetags = merge(local.common_tags, {Name = "${var.project_name}-${var.environment}-vpc"})}# outputs.tfoutput "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-argumentscount = 3# Argumentsami = "ami-0c55b159cbfafe1f0"instance_type = "t2.micro"# Blocksroot_block_device {volume_size = 20}# Lifecycle lastlifecycle {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
mainfor resources where a specific descriptive name is redundant or unavailable, provided only one instance exists
# Badresource "aws_instance" "webAPI-aws-instance" {}resource "aws_instance" "web_apis" {}variable "name" {}# Goodresource "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 = stringdefault = "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 = stringsensitive = 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.passwordsensitive = true}
Dynamic Resource Creation
Prefer for_each over count
# Bad - count for multiple resourcesresource "aws_instance" "web" {count = var.instance_counttags = { Name = "web-${count.index}" }}# Good - for_each with named instancesvariable "instance_names" {type = set(string)default = ["web-1", "web-2", "web-3"]}resource "aws_instance" "web" {for_each = var.instance_namestags = { Name = each.key }}
count for Conditional Creation
resource "aws_cloudwatch_metric_alarm" "cpu" {count = var.enable_monitoring ? 1 : 0alarm_name = "high-cpu-usage"threshold = 80}
Security Best Practices
Refer to SECURITY.md. It includes guidance on encrypting resources, preventing sensitive data in state, and secure configurations.
Version Pinning
terraform {required_version = ">= 1.14"required_providers {aws = {source = "hashicorp/aws"version = "~> 6.0"}}}
Use the latest major version of each provider and the latest minor version of Terraform, unless otherwise constrained by a dependency lock file or by other modules used by the configuration.
Version constraint operators:
= 1.0.0- Exact version>= 1.0.0- Greater than or equal~> 1.0- Allow rightmost component to increment>= 1.0, < 2.0- Version range
Provider Configuration
provider "aws" {region = "us-west-2"default_tags {tags = {ManagedBy = "Terraform"Project = var.project_name}}}# Aliased provider for multi-regionprovider "aws" {alias = "east"region = "us-east-1"}
Version Control
Never commit:
terraform.tfstate,terraform.tfstate.backup.terraform/directory*.tfplan.tfvarsfiles with sensitive data
Always commit:
- All
.tfconfiguration files .terraform.lock.hcl(dependency lock file)
Validation Tools
Run before committing:
terraform fmt -recursiveterraform validate
Additional tools:
tflint- Linting and best practicescheckov/tfsec- Security scanning
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](https://developer.hashicorp.com/terraform/language/style)