Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tomevault-io/skills-registry --skill terraform-core명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | terraform-core |
| description | >- Use when this capability is needed. |
This skill covers the Terraform configuration language (HCL): how you declare resources, wire
data between them, and constrain Terraform and provider versions. It does not replace
provider-specific resources (see cloud skills) or state backends (see terraform-state).
Every root module should declare which Terraform CLI versions are supported and pin provider
sources. Use required_version to prevent accidental upgrades on developer laptops and CI runners.
terraform {
required_version = ">= 1.9.0, < 1.12.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
Rules of thumb:
>= 1.9, < 1.12) over a single equality so patch releases flow.ephemeral resource behaviors need modern
Terraform; check blocks and test have minimum versions - consult release notes when adopting.Variables accept primitive types (string, number, bool), collection types (list,
set, map), and structural types (object, tuple, any). Use object types for
structured inputs instead of untyped maps when you want IDE clarity and validation.
variable "service" {
type = object({
name = string
port = number
tags = optional(map(string), {})
})
description = "Service descriptor used by compute and LB resources."
}
variable "cidr_allow" {
type = list(string)
description = "CIDR blocks allowed to reach the ALB."
validation {
condition = alltrue([
for c in var.cidr_allow : can(cidrhost(c, 0))
])
error_message = "Each entry in cidr_allow must be a valid CIDR string."
}
}
optional() inside object types (Terraform 1.3+) lets callers omit keys while you supply defaults
with the second argument to optional.
Locals deduplicate expressions and name intermediate values. Prefer locals over repeating the
same merge() or string templates across many resources.
locals {
base_tags = {
Project = var.project
Environment = var.environment
ManagedBy = "terraform"
}
name_prefix = "${var.project}-${var.environment}"
# Example: normalize AZs to subnet maps elsewhere
az_to_index = { for i, az in var.availability_zones : az => i }
}
Use splat ([*]) and for expressions to reshape collections. merge(flatten([...])) is
common for building tag maps across modules.
Mark variables and outputs sensitive to reduce accidental logging. This does not encrypt state - it only affects CLI redaction in human-oriented output.
variable "api_key" {
type = string
sensitive = true
description = "External API key; never commit."
}
output "database_url" {
value = aws_db_instance.app.address
sensitive = true
description = "DB hostname; still stored in state."
}
Secrets still land in state. See terraform-secrets for external secret stores, ephemeral
patterns, and write-only attributes where applicable.
count indexes resources with count.index. It forces replacement when indices shift (e.g.
shortening a list breaks alignment). for_each maps resources to keys (set or map) and is the
default choice for address-stable fan-out.
resource "aws_subnet" "private" {
for_each = toset(var.availability_zones)
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_cidr, 4, index(var.availability_zones, each.key))
availability_zone = each.key
}
Use count when you truly need a numeric index or a simple boolean toggle (count = var.enable_nat ? 1 : 0). When in doubt, pick for_each.
dynamic generates nested blocks from collections. The label is the block type name inside
the resource (e.g. ingress on a security group).
resource "aws_security_group" "app" {
name = "app"
vpc_id = aws_vpc.this.id
dynamic "ingress" {
for_each = var.ingress_rules
content {
description = ingress.value.description
from_port = ingress.value.from
to_port = ingress.value.to
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidrs
}
}
}
Avoid dynamic when a resource already accepts a list attribute - prefer native list attributes
when available for readability.
Blocks Terraform from planning a destroy (useful for stateful data stores). Combine with IAM to stop console deletes when needed.
resource "aws_db_instance" "app" {
# ...
lifecycle {
prevent_destroy = true
}
}
Stops drift reconciliation for listed attributes - use sparingly when an external system mutates tags or auto-scaling shapes and you accept Terraform ignoring those diffs.
resource "aws_instance" "bastion" {
# ...
lifecycle {
ignore_changes = [ami]
}
}
Orders replacement so the new object is created first when the provider supports it - critical for zero-downtime when type-specific constraints allow.
resource "aws_lb_target_group" "api" {
# ...
lifecycle {
create_before_destroy = true
}
}
Forces replace when other resource instances change, even if argument diffs alone would not.
resource "aws_instance" "worker" {
# ...
lifecycle {
replace_triggered_by = [
aws_launch_template.worker.latest_version
]
}
}
Terraform infers dependencies from references. Add depends_on only when you need an ordering
constraint without referencing attributes - common with IAM attachments or eventual consistency edges.
resource "aws_iam_role_policy_attachment" "lambda_basic" {
role = aws_iam_role.lambda_exec.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
depends_on = [aws_iam_role.lambda_exec]
}
moved renames addresses in state without destroy/create. Prefer this over terraform state mv
when you can encode history in config for reviewers.
moved {
from = aws_security_group.old
to = module.network.aws_security_group.app
}
removed tells Terraform to stop managing an object while optionally leaving it running
(destroy = false).
removed {
from = aws_s3_bucket.legacy_uploads
lifecycle {
destroy = false
}
}
Declare import in configuration to bring existing infrastructure under management with code
review. Pair with a matching to resource block.
import {
to = aws_iam_role.imported_ops
id = "ops-readonly"
}
resource "aws_iam_role" "imported_ops" {
name = "ops-readonly"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { AWS = "arn:aws:iam::111111111111:root" }
Action = "sts:AssumeRole"
}]
})
}
Common families:
format, formatlist, replace, regex, split, joinlength, lookup, merge, concat, distinct, flatten, tolist, tosetjsonencode, jsondecode, yamlencode, base64encodecidrsubnet, cidrhostfile, templatefile, filebase64Example combining templatefile with clean separation of policy JSON:
data "aws_iam_policy_document" "lambda_assume" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
resource "aws_iam_role" "lambda" {
name = "imu-processor-lambda"
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json
}
Expose small, typed variable sets; return outputs that downstream stacks need (IDs, ARNs,
DNS names). Keep provider configuration at the root; children receive configuration_aliases
when multiple regions or accounts are required - see terraform-providers and terraform-modules.
terraform-state.terraform-aws.terraform test, policies) → terraform-testing.for_each: ensure the collection is a map or a set of strings, not a
list of objects (wrap with { for x in var.items : x.name => x }).-target only as a last resort; usually reshape dependencies
or split stacks - see terraform-troubleshooting.Use try for graceful fallbacks when a deep key might be absent; prefer typed object
variables so absence is caught earlier at plan time.
locals {
# Safe nested lookup with default
log_level = try(var.observability.log_level, "INFO")
}
can pairs with for/if for validation and conditional logic without tripping evaluation:
locals {
valid_ports = [
for p in var.ports : p if can(tonumber(p)) && tonumber(p) > 0 && tonumber(p) <= 65535
]
}
Treat null as “unset” in attributes that accept it; merging maps often uses compact() or
{ for k, v in var.m : k => v if v != null } to drop empty values.
Interpolation ${} embeds expressions; directives %{if} enable inline conditionals inside
strings. For multi-line blobs (JSON policies, startup scripts), prefer <<-EOF heredocs and
indentation stripping.
locals {
user_data = <<-EOT
#!/bin/bash
set -euo pipefail
echo "BOOTSTRAP_ENV=${var.environment}" >/etc/instance.env
EOT
}
templatefile(path, vars) keeps large templates out of .tf files and enables reuse across
modules.
Each resource has a type and a logical name: aws_s3_bucket.audit_logs. Data sources use
data. prefix in references. Provisioners (local-exec, remote-exec) are a last resort - prefer
native APIs, user-data, or pipeline jobs.
Besides for_each, count, and lifecycle, resources can set provider aliases to target
alternate configurations. Always pass provider mappings explicitly in modules that need multi-region
or multi-account parallelism - see terraform-providers for configuration_aliases.
Example pattern:
resource "aws_security_group" "peer" {
provider = aws.us_west_2
name = "peer-link"
vpc_id = aws_vpc.west.id
}
Inside module blocks, version = "..." pins Terraform Registry modules. For local paths, omit
version and use source = "../modules/foo". Tag Git modules with semver and reference
ref pins - details in terraform-modules.
Run terraform fmt on every change. For large object variables, align keys vertically in teams
that adopt style guides - Terraform accepts either style, but consistency beats cleverness.
Source: eclosion-labs/terraform-cursor-plugin — distributed by TomeVault.
SOC 직업 분류 기준