Die Oberfläche ist lokalisiert; Skill-Beschreibungen und -Dokumente werden per KI automatisch in Ihre Sprache übersetzt. Code, Befehle, Pfade und Fachbegriffe bleiben im Original, maßgeblich ist das Dokument des Autors.
Skills entdecken
Bereitstellung und Betrieb · SKILL.md

terraform-skill

terraform-skill

Terraform/OpenTofu best practices, testing strategy, naming conventions, and CI/CD integration. Load BEFORE writing any Terraform module or environment config. Triggers: terraform test, terraform module, terraform naming, IaC best practices, terratest, terraform ci/cd, terraform version, for_each vs count, terraform structure, terraform conventions.

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill-Dokumentation

GitHub

name: terraform-skill description: "Terraform/OpenTofu best practices, testing strategy, naming conventions, and CI/CD integration. Load BEFORE writing any Terraform module or environment config. Triggers: terraform test, terraform module, terraform naming, IaC best practices, terratest, terraform ci/cd, terraform version, for_each vs count, terraform structure, terraform conventions." allowed-tools: Bash, Read, Write, Edit, Glob, Grep metadata: triggers: ["terraform test", "terraform module", "terraform naming", "IaC best practices", "terratest", "terraform ci/cd", "terraform version", "for_each vs count", "terraform structure", "terraform conventions"] related-skills: [gcp-cloud-run, gcp-finops, docker, architecture-design, terraform-module-library] domain: backend role: specialist scope: infrastructure output-format: code last-reviewed: "2026-03-15"

Terraform Skill

Source: terraform-best-practices.com + enterprise Terraform/OpenTofu experience. GCP-primary for this workspace.

Iron Law

BEFORE writing any Terraform:
1. Load this skill for naming, testing, and code structure conventions
2. Dispatch terraform-specialist agent to provision GCP resources
3. Run tfsec/Checkov BEFORE terraform apply — no exceptions
4. State files NEVER go in git — GCS remote state always
5. GitHub Actions only — never generate cloudbuild.yaml

This Skill vs terraform-specialist Agent

TaskUse This SkillUse terraform-specialist Agent
Naming conventions (resources, variables, files)YESNo
Testing strategy (native test vs Terratest)YESNo
count vs for_each decisionYESNo
Resource block ordering standardsYESNo
Version constraint strategyYESNo
CI/CD pipeline for Terraform (GitHub Actions)YESNo
Provision Cloud Run, Cloud SQL, Artifact RegistryNoYES
Multi-environment (staging/prod) setupNoYES
GCS remote state configurationNoYES
Workload Identity FederationNoYES
tfsec/Checkov security scanningNoYES

Load this skill for how to write Terraform. Dispatch terraform-specialist for what to provision.


Pattern Selector

What do you need?
    |
    +-- Writing or reviewing a Terraform module?
    |   -> Apply § Naming Conventions
    |   -> Apply § Code Structure Standards (resource block ordering)
    |   -> Apply § Count vs For_Each decision
    |
    +-- Adding tests to a Terraform module?
    |   -> Use § Testing Strategy decision tree
    |   -> Terraform 1.6+: native test (preferred for this workspace)
    |   -> Terraform 1.0–1.5: Terratest (Go)
    |
    +-- Setting up CI/CD for Terraform?
    |   -> See § CI/CD Integration
    |   -> GitHub Actions ONLY (workspace rule — never cloudbuild.yaml)
    |
    +-- Choosing module or environment structure?
        -> See § Code Structure Philosophy
        -> See docs/workflows/cloud-run-terraform.md for GCP-specific layout
        -> See terraform-module-library skill for GCP module patterns

Code Structure Philosophy

terraform/
├── modules/          # Reusable modules — NO provider {}, NO backend {}
│   ├── cloud-run/    # One resource group per module
│   ├── cloud-sql/
│   ├── artifact-registry/
│   ├── secret-manager/
│   └── wif/
├── environments/     # Environment roots — call modules, configure backends
│   ├── staging/
│   │   ├── main.tf         # Module calls only — no raw resource definitions
│   │   ├── variables.tf
│   │   ├── terraform.tfvars
│   │   └── backend.tf      # GCS remote state
│   └── production/
│       └── ...
└── examples/         # Usage examples — also serve as integration test fixtures
    └── complete/

Rules:

  • Modules do NOT contain provider {} blocks or backend {} — only environment roots do
  • Environment roots contain module calls only, never raw resource definitions
  • examples/ doubles as documentation AND integration test fixture

Naming Conventions

Resource Names

# Use descriptive names for resources used multiple times in a module
resource "google_cloud_run_v2_service" "api" { }
resource "google_cloud_run_v2_service" "worker" { }

# Use "this" ONLY for singleton resources (exactly one per module)
resource "google_artifact_registry_repository" "this" { }
resource "google_sql_database_instance" "this" { }

Variable Names — Context-Prefixed

# Avoid — ambiguous
variable "name" { }
variable "region" { }

# Prefer — context-prefixed
variable "service_name" { }
variable "cloud_run_region" { }
variable "sql_instance_name" { }

File Names

FilePurpose
main.tfPrimary resource definitions
variables.tfAll input variable declarations
outputs.tfAll output declarations
versions.tfterraform {} block + required_providers
data.tfData source lookups only
locals.tfLocal value computations (extract from main.tf when > 5 locals)

Testing Strategy

Decision Tree

What Terraform version?
    |
    +-- 1.7+ → Use native test WITH mock providers (fastest, no real GCP calls)
    |
    +-- 1.6 → Use native test (terraform test command)
    |          Test files: tests/*.tftest.hcl
    |
    +-- 1.0–1.5 → Use Terratest (Go)
                   Test files: tests/*_test.go

Testing Pyramid

LayerToolRuns OnCost
Static analysistflint, terraform validate, tfsecEvery commitFree
Unit (plan)Native test (command = plan)PR open/updateFree
IntegrationNative test (command = apply, mock provider)PR merge to developFree
E2ENative test (command = apply, real provider)Release branch only~$0.01–$1/run

Rule: Run layers 1–2 on every PR. Layer 3 on merge to develop. Layer 4 on release branches only.

Native Test — Preferred (Terraform 1.6+)

# tests/cloud_run_module.tftest.hcl
mock_provider "google" {
  mock_resource "google_cloud_run_v2_service" {
    defaults = {
      uri = "https://test-api-abc123-uc.a.run.app"
    }
  }
  mock_resource "google_cloud_run_v2_service_iam_member" {}
}

variables {
  service_name          = "test-api"
  project_id            = "test-project"
  region                = "us-central1"
  image_url             = "us-central1-docker.pkg.dev/test-project/registry/api:latest"
  service_account_email = "sa@test-project.iam.gserviceaccount.com"
}

run "verify_service_name" {
  command = apply   # Safe — mock provider, no real GCP calls

  assert {
    condition     = google_cloud_run_v2_service.this.name == "test-api"
    error_message = "Service name should match input variable"
  }

  assert {
    condition     = startswith(output.service_url, "https://")
    error_message = "Service URL must be HTTPS"
  }
}

run "verify_name_validation" {
  command = plan

  variables {
    service_name = "this-name-is-way-too-long-for-cloud-run-service-names-xxxx"
  }

  expect_failures = [var.service_name]
}

Code Structure Standards

Resource Block Ordering

resource "google_cloud_run_v2_service" "api" {
  # 1. count / for_each first
  for_each = toset(var.regions)

  # 2. Required arguments (alphabetical within group)
  location = each.key
  name     = "${var.service_name}-${each.key}"
  project  = var.project_id

  # 3. Optional arguments
  template { ... }

  # 4. Labels
  labels = var.labels

  # 5. depends_on (before lifecycle)
  depends_on = [google_project_service.run]

  # 6. lifecycle (last)
  lifecycle {
    ignore_changes = [template[0].containers[0].image]
  }
}

Variable Block Ordering

variable "service_name" {
  description = "Name of the Cloud Run service"   # 1. description (always required)
  type        = string                             # 2. type
  default     = null                              # 3. default (if any)
  validation {                                    # 4. validation (if any)
    condition     = length(var.service_name) <= 49
    error_message = "Cloud Run service names must be <= 49 chars."
  }
  nullable = false                                # 5. nullable
}

Count vs For_Each

Decision Guide

Is the resource conditional (enabled/disabled flag)?
    YES -> count = var.enable_feature ? 1 : 0

Is the set driven by a list that could change order?
    YES -> for_each (stable addressing — prevents destroy/recreate cascade)
    NO (truly static, never reordered) -> count is acceptable

Do items have meaningful identity beyond index?
    YES -> for_each = tomap(...) or toset(...)
    NO  -> count

Example trap: multiple Cloud Run regions
    WRONG: count = 2  (count[0] -> count[1] on reorder = destroy us-central1!)
    RIGHT: for_each = toset(["us-central1", "us-east1"])
# Conditional resource — count
resource "google_monitoring_alert_policy" "latency" {
  count = var.enable_monitoring ? 1 : 0
  ...
}

# Multi-region stable addressing — for_each
resource "google_cloud_run_v2_service" "regional" {
  for_each = toset(var.regions)
  name     = "${var.service_name}-${each.key}"
  location = each.key
}

# IAM members — for_each (never count — order instability)
resource "google_artifact_registry_repository_iam_member" "readers" {
  for_each   = toset(var.reader_service_accounts)
  role       = "roles/artifactregistry.reader"
  member     = "serviceAccount:${each.key}"
  ...
}

Version Management

Constraint Strategy

ComponentConstraintExample
Terraform binaryPin minor~> 1.9.0
google providerPin minor~> 6.0
google-beta providerPin minor~> 6.0
Internal modulesPin exact= 1.2.3
Registry modulesPin minor~> 2.1
# versions.tf
terraform {
  required_version = "~> 1.9.0"

  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 6.0"
    }
    google-beta = {
      source  = "hashicorp/google-beta"
      version = "~> 6.0"
    }
  }
}

Feature Availability by Version

FeatureMin VersionUse For
try() function0.13Safely access nested optional attributes
optional() in object types1.3Optional object keys with defaults
moved {} block1.1Rename resources without destroy
import {} block1.5Declarative resource import
Native terraform test1.6Module unit testing
Mock providers in tests1.7Offline module testing (no real GCP calls)
Cross-variable validation1.9Validate interdependent variable combinations

CI/CD Integration (GitHub Actions Only)

Full workflow: load reference/cicd-github-actions.md

Stage order: validate → test → plan → apply — never skip test stage.

Key jobs: validate (fmt + tflint + tfsec), test (native terraform test with mock providers), plan (PR only, requires WIF auth), apply (main branch only, environment gate).


Reference Files

ReferenceLoad When
reference/index.mdNavigation
reference/cicd-github-actions.mdSetting up GitHub Actions CI/CD for Terraform

Cross-References

  • GCP Infrastructure provisioning: Dispatch terraform-specialist agent
  • GCP module patterns (Cloud Run, Cloud SQL, VPC, WIF): Load terraform-module-library skill
  • Provisioning workflow: docs/workflows/cloud-run-terraform.md
  • Module development workflow: docs/workflows/terraform-module-development.md
  • Security scanning post-apply: Dispatch security-reviewer agent
  • CI/CD application pipeline: docs/workflows/deployment-ci-cd.md
Gespeicherte Skills