Terraform / OpenTofu Cheatsheet
Workflow
terraform init # после изменения providers / backend
terraform init -upgrade # обновить providers
terraform init -reconfigure # пересоздать backend config
terraform fmt -recursive
terraform validate
terraform plan
terraform plan -out=tf.plan
terraform apply tf.plan # применить plan
terraform apply # plan + apply
terraform apply -auto-approve # ⚠️ без confirm
terraform destroy
terraform destroy -target=aws_s3_bucket.b
State
terraform state list
terraform state show aws_s3_bucket.b
terraform state pull > backup.tfstate
terraform state mv aws_s3_bucket.old aws_s3_bucket.new
terraform state rm aws_s3_bucket.b # удалить из state, не из cloud
terraform refresh # синк state с реальностью
Import
# TF 1.5+ декларативно
import {
to = aws_s3_bucket.legacy
id = "existing-bucket-name"
}
# CLI способ
terraform import aws_s3_bucket.legacy existing-bucket-name
Targeting
terraform plan -target=aws_vpc.main
terraform apply -target=aws_vpc.main
terraform destroy -target=module.eks
Workspaces (НЕ для prod environments)
terraform workspace list
terraform workspace new dev
terraform workspace select dev
terraform workspace show
terraform workspace delete dev
Output
terraform output
terraform output vpc_id
terraform output -json | jq
HCL — основы
terraform {
required_version = ">= 1.6"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "my-tf-state"
key = "envs/prod/terraform.tfstate"
region = "eu-west-1"
dynamodb_table = "tf-lock"
encrypt = true
}
}
provider "aws" {
region = "eu-west-1"
}
Variables
variable "name" {
description = "Name prefix"
type = string
default = "myapp"
sensitive = false
validation {
condition = length(var.name) > 0
error_message = "name required"
}
}
variable "tags" {
type = map(string)
default = {}
}
variable "subnets" {
type = list(object({
cidr = string
az = string
}))
}
Передача:
terraform apply -var="name=myapp" -var-file=prod.tfvars
# или через env: TF_VAR_name=myapp terraform apply
Locals и Outputs
locals {
common_tags = {
Environment = var.environment
ManagedBy = "terraform"
}
name_prefix = "${var.project}-${var.environment}"
}
output "vpc_id" {
description = "VPC ID"
value = aws_vpc.main.id
sensitive = false
}
Ресурсы
resource "aws_vpc" "main" {
cidr_block = var.cidr
enable_dns_hostnames = true
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-vpc"
})
lifecycle {
prevent_destroy = true
create_before_destroy = true
ignore_changes = [tags["LastUpdated"]]
}
}
Data sources
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-noble-24.04-amd64-server-*"]
}
}
# Использование
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
}
for_each и count
# for_each (предпочтительнее)
resource "aws_subnet" "private" {
for_each = toset(var.azs)
vpc_id = aws_vpc.main.id
availability_zone = each.key
cidr_block = cidrsubnet(var.cidr, 4, index(var.azs, each.key))
}
# Доступ
output "subnet_ids" {
value = { for k, v in aws_subnet.private : k => v.id }
}
# count
resource "aws_iam_user" "admins" {
count = length(var.admins)
name = var.admins[count.index]
}
Условия (conditional)
# Тернарный
resource "aws_eip" "nat" {
count = var.enable_nat ? 1 : 0
domain = "vpc"
}
# Conditional expressions
locals {
instance_count = var.environment == "prod" ? 3 : 1
}
Модули
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = "myapp-vpc"
cidr = "10.0.0.0/16"
azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
}
# Локальный модуль
module "eks" {
source = "../../modules/eks"
vpc_id = module.vpc.vpc_id
}
# Git модуль
module "shared" {
source = "git::https://github.com/org/modules.git//eks?ref=v1.2.0"
}
Полезные функции
length(list)
keys(map)
values(map)
contains(list, value)
toset(list)
tolist(set)
merge(map1, map2)
concat(list1, list2)
join(",", list)
split(",", string)
cidrsubnet("10.0.0.0/16", 8, 2) # → 10.0.2.0/24
cidrhost("10.0.1.0/24", 5) # → 10.0.1.5
formatdate("YYYY-MM-DD", timestamp())
jsondecode(file("config.json"))
yamldecode(file("config.yaml"))
templatefile("template.tpl", {var = val})
base64encode("string")
base64decode(b64)
md5("string")
sha256("string")
Tooling вокруг
tflint # линтер
tfsec . # security
checkov -d . # security (поддержка K8s, Helm)
terraform-docs markdown . # ★ авто-генерация README
# pre-commit с tflint, tfsec, terraform_fmt:
# https://github.com/antonbabenko/pre-commit-terraform
# Terragrunt
terragrunt run-all plan
terragrunt run-all apply