skills/terraform-style-guide/SKILL.md
Write and review OpenTofu-first HCL using current style conventions and safe infrastructure patterns. Use whenever editing Terraform or OpenTofu configuration, modules, variables, outputs, providers, or imports. Prefer OpenTofu commands and OpenTofu documentation unless the user explicitly needs Terraform-only behavior.
npx skillsauth add ederheisler/agent-skills terraform-style-guideInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Write HCL that is correct, readable, and OpenTofu-first.
In this repository, OpenTofu is the default runtime. Command examples, validation steps, and workflow choices should therefore use tofu, not terraform, unless the user explicitly asks for Terraform.
terraform/, not from the repository root.cd terraform
tofu fmt -recursive
tofu validate
tofu plan -var-file=terraform.tfvars
Follow the repository's existing file structure instead of imposing a generic layout.
For this repo, the expected root layout is:
providers.tf for terraform {} version/provider requirements and provider configurationmain.tf for root wiring and normalizationvariables.tf for input schema and validationoutputs.tf for exported valuesterraform/modules/*If a file already exists for a concern, extend that file instead of creating a parallel convention.
tofu fmt define the final formatting.tofu fmt preserves alignment.resource "oci_core_instance" "api" {
availability_domain = var.availability_domain
compartment_id = var.compartment_id
display_name = var.display_name
shape = var.shape
}
resource "oci_core_instance" "api" {
for_each = var.instances
availability_domain = each.value.availability_domain
compartment_id = each.value.compartment_id
display_name = each.value.display_name
shape = each.value.shape
source_details {
source_type = "image"
source_id = each.value.image_id
}
lifecycle {
create_before_destroy = true
}
}
instance_instance or server_resource.network, server, public_ip, shape_profiles, or normalized_servers.locals before feeding modules.In this repo specifically:
project_name is a global name prefix.servers is the map of real server definitions.server_defaults holds values shared by most servers.compartments and compartment_ocids should centralize compartment selection instead of scattering raw OCIDs across resources.for_each over countUse for_each when instances have meaningful keys or when stable addressing matters.
module "server" {
for_each = local.normalized_servers
source = "./modules/server"
project_name = var.project_name
server_name = each.key
compute_compartment_id = each.value.compartment_ocid
}
Use count mainly for simple on/off creation where identity is unimportant.
description and explicit type.description.sensitive = true.variable "shape_profile" {
description = "Logical shape profile name resolved through shape_profiles."
type = string
validation {
condition = contains(keys(var.shape_profiles), var.shape_profile)
error_message = "shape_profile must reference a key in shape_profiles."
}
}
When adopting existing resources:
import blocks when the workflow is going into version control.tofu import only when a one-off state attachment is more practical than keeping import blocks in code.Before finishing a change to OpenTofu code in this repository:
tofu fmt -recursive from terraform/.tofu validate from terraform/.tofu plan -var-file=terraform.tfvars when local credentials and tfvars are available.development
Extracts what the user actually wants instead of what they think they should want. Achieves this through one-question-at-a-time interview until ~95% confidence about the underlying intent. Use when an ask is underspecified ("build me X" without "for whom" or "why now"), when the user explicitly invokes ("interview me", "grill me", "are we sure?", "stress-test my thinking"), or when you catch yourself silently filling in ambiguous requirements before any plan, spec, or code exists.
testing
Refines raw ideas into sharp, actionable concepts through structured divergent and convergent thinking. Use when an idea is still vague, when you need to stress-test assumptions before committing to a plan, or when you want to expand options before converging on one. Triggers on "ideate", "refine this idea", or "stress-test my plan".
documentation
Compact the current conversation into a handoff document for another agent to pick up.
testing
Interview the user relentlessly about a plan or design until reaching shared understanding, resolving each branch of the decision tree. Use when user wants to stress-test a plan, get grilled on their design, or mentions "grill me".