OpenTofu
Open-Source Infrastructure-as-Code-Tool und Community-getriebener Fork von Terraform.
Installation
Abschnitt betitelt „Installation“Plattform-Installation
Abschnitt betitelt „Plattform-Installation“| Befehl | Beschreibung |
|---|---|
brew install opentofu | Installation auf macOS mit Homebrew |
snap install --classic opentofu | Installation auf Linux mit Snap |
choco install opentofu | Installation auf Windows mit Chocolatey |
curl -fsSL https://get.opentofu.org/install-opentofu.sh | sh | Installation über Shell-Skript |
docker run -it ghcr.io/opentofu/opentofu | Ausführung über Docker |
tofu --version | Installierte Version anzeigen |
tofu -help | Verfügbare Befehle anzeigen |
Versionsmanager-Installation
Abschnitt betitelt „Versionsmanager-Installation“# tofuenv verwenden (wie tfenv für OpenTofu)
git clone https://github.com/tofuutils/tofuenv.git ~/.tofuenv
echo 'export PATH="$HOME/.tofuenv/bin:$PATH"' >> ~/.bashrc
# Verfügbare Versionen auflisten
tofuenv list-remote
# Bestimmte Version installieren
tofuenv install 1.8.0
tofuenv use 1.8.0
# Version im Projekt festlegen
echo "1.8.0" > .opentofu-version
Kernbefehle
Abschnitt betitelt „Kernbefehle“Workflow-Befehle
Abschnitt betitelt „Workflow-Befehle“| Befehl | Beschreibung |
|---|---|
tofu init | Arbeitsverzeichnis initialisieren und Provider herunterladen |
tofu init -upgrade | Neu initialisieren und Provider-/Modulversionen aktualisieren |
tofu init -backend-config=prod.hcl | Mit Backend-Konfigurationsdatei initialisieren |
tofu plan | Infrastrukturänderungen vorschauen |
tofu plan -out=plan.tfplan | Plan in Datei speichern für späteres Apply |
tofu plan -target=aws_instance.web | Nur Änderungen für bestimmte Ressource planen |
tofu plan -var-file=prod.tfvars | Plan mit bestimmter Variablendatei |
tofu apply | Infrastrukturänderungen anwenden (fragt nach Bestätigung) |
tofu apply -auto-approve | Änderungen ohne Bestätigung anwenden |
tofu apply plan.tfplan | Einen gespeicherten Plan anwenden |
tofu destroy | Gesamte verwaltete Infrastruktur zerstören |
tofu destroy -auto-approve | Ohne Bestätigung zerstören |
tofu destroy -target=aws_instance.web | Nur bestimmte Ressource zerstören |
Inspektionsbefehle
Abschnitt betitelt „Inspektionsbefehle“| Befehl | Beschreibung |
|---|---|
tofu validate | Konfigurationssyntax validieren |
tofu fmt | Konfigurationsdateien formatieren |
tofu fmt -check | Prüfen ob Dateien formatiert sind (CI-freundlich) |
tofu fmt -recursive | Alle Dateien in Unterverzeichnissen formatieren |
tofu output | Alle Ausgabewerte anzeigen |
tofu output -json | Ausgaben im JSON-Format anzeigen |
tofu output db_password | Bestimmten Ausgabewert anzeigen |
tofu show | Aktuellen State in lesbarer Form anzeigen |
tofu show -json | State im JSON-Format anzeigen |
tofu show plan.tfplan | Gespeicherte Plan-Details anzeigen |
tofu graph | Ressourcen-Abhängigkeitsgraph generieren (DOT-Format) |
tofu graph | dot -Tpng > graph.png | Abhängigkeitsgraph als PNG rendern |
tofu refresh | State mit realer Infrastruktur abgleichen |
tofu providers | In der Konfiguration verwendete Provider auflisten |
Konfiguration (HCL)
Abschnitt betitelt „Konfiguration (HCL)“Provider-Konfiguration
Abschnitt betitelt „Provider-Konfiguration“# Block für erforderliche Provider
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
# State-Verschlüsselung (OpenTofu-exklusives Feature)
encryption {
method "aes_gcm" "default" {
keys = key_provider.pbkdf2.mykey
}
state {
method = method.aes_gcm.default
enforced = true
}
plan {
method = method.aes_gcm.default
enforced = true
}
key_provider "pbkdf2" "mykey" {
passphrase = var.state_passphrase
}
}
}
# Provider mit Standardkonfiguration
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
ManagedBy = "opentofu"
Environment = var.environment
Project = var.project_name
}
}
}
# Provider-Alias für Multi-Region
provider "aws" {
alias = "west"
region = "us-west-2"
}
# Aliased Provider auf einer Ressource verwenden
resource "aws_instance" "west_server" {
provider = aws.west
ami = "ami-0abcdef1234567890"
instance_type = "t3.micro"
}
Ressourcendefinitionen
Abschnitt betitelt „Ressourcendefinitionen“# Grundlegende Ressource
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = {
Name = "${var.project_name}-web"
}
}
# Datenquelle (bestehende Ressourcen lesen)
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
# Lokale Werte
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "opentofu"
}
az_count = length(data.aws_availability_zones.available.names)
}
# Count und for_each
resource "aws_subnet" "public" {
count = local.az_count
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = merge(local.common_tags, {
Name = "${var.project_name}-public-${count.index}"
Type = "public"
})
}
resource "aws_security_group_rule" "ingress" {
for_each = {
http = { port = 80, cidrs = ["0.0.0.0/0"] }
https = { port = 443, cidrs = ["0.0.0.0/0"] }
ssh = { port = 22, cidrs = [var.admin_cidr] }
}
type = "ingress"
from_port = each.value.port
to_port = each.value.port
protocol = "tcp"
cidr_blocks = each.value.cidrs
security_group_id = aws_security_group.main.id
}
Variablen & Ausgaben
Abschnitt betitelt „Variablen & Ausgaben“Variablendefinitionen
Abschnitt betitelt „Variablendefinitionen“| Befehl | Beschreibung |
|---|---|
variable "name" { type = string } | Eine String-Variable definieren |
variable "port" { type = number default = 8080 } | Variable mit Standardwert |
variable "tags" { type = map(string) } | Map-Variable |
variable "cidrs" { type = list(string) } | Listen-Variable |
variable "config" { type = object({ ... }) } | Objekt-Variable mit Struktur |
variable "db_pass" { sensitive = true } | Variable als sensitiv markieren (in Ausgabe verborgen) |
variable "env" { validation { ... } } | Variable mit benutzerdefinierter Validierung |
Variablen übergeben
Abschnitt betitelt „Variablen übergeben“| Befehl | Beschreibung |
|---|---|
tofu plan -var="name=value" | Variable über CLI übergeben |
tofu plan -var-file="prod.tfvars" | Variablen aus Datei übergeben |
TF_VAR_name=value tofu plan | Über Umgebungsvariable übergeben |
terraform.tfvars-Datei erstellen | Automatisch geladene Variablenwerte |
*.auto.tfvars-Datei erstellen | Automatisch geladene Variablendateien |
Variablen- & Ausgabebeispiele
Abschnitt betitelt „Variablen- & Ausgabebeispiele“# variables.tf
variable "environment" {
type = string
description = "Deployment environment"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "instance_config" {
type = object({
instance_type = string
volume_size = number
encrypted = bool
})
default = {
instance_type = "t3.micro"
volume_size = 20
encrypted = true
}
}
variable "database_password" {
type = string
sensitive = true
}
# outputs.tf
output "vpc_id" {
description = "The ID of the VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "List of public subnet IDs"
value = aws_subnet.public[*].id
}
output "web_url" {
description = "URL of the web server"
value = "https://${aws_lb.web.dns_name}"
}
output "db_connection_string" {
description = "Database connection string"
value = "postgresql://${var.db_username}:${var.database_password}@${aws_db_instance.main.endpoint}/${var.db_name}"
sensitive = true
}
Variablendateien
Abschnitt betitelt „Variablendateien“# prod.tfvars
environment = "prod"
instance_config = {
instance_type = "t3.large"
volume_size = 100
encrypted = true
}
# dev.tfvars
environment = "dev"
instance_config = {
instance_type = "t3.micro"
volume_size = 20
encrypted = false
}
State-Verwaltung
Abschnitt betitelt „State-Verwaltung“State-Befehle
Abschnitt betitelt „State-Befehle“| Befehl | Beschreibung |
|---|---|
tofu state list | Alle Ressourcen im State auflisten |
tofu state show aws_instance.web | Details einer bestimmten Ressource anzeigen |
tofu state mv aws_instance.old aws_instance.new | Ressource im State umbenennen |
tofu state rm aws_instance.web | Ressource aus State entfernen (behält reale Infrastruktur) |
tofu state pull | Remote-State auf stdout ausgeben |
tofu state push | Lokalen State an Remote-Backend übertragen |
tofu import aws_instance.web i-1234567 | Bestehende Ressource in State importieren |
tofu state replace-provider hashicorp/aws registry.opentofu.org/hashicorp/aws | Provider im State ersetzen |
tofu force-unlock LOCK_ID | Festsitzende State-Sperre aufheben |
Backend-Konfiguration
Abschnitt betitelt „Backend-Konfiguration“# S3-Backend (am häufigsten)
terraform {
backend "s3" {
bucket = "my-tofu-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "tofu-locks"
encrypt = true
}
}
# PostgreSQL-Backend
terraform {
backend "pg" {
conn_str = "postgres://user:pass@db.example.com/tofu_state?sslmode=require"
}
}
# HTTP-Backend (funktioniert mit GitLab, Terraform Cloud-Alternativen)
terraform {
backend "http" {
address = "https://gitlab.example.com/api/v4/projects/1/terraform/state/prod"
lock_address = "https://gitlab.example.com/api/v4/projects/1/terraform/state/prod/lock"
unlock_address = "https://gitlab.example.com/api/v4/projects/1/terraform/state/prod/lock"
username = "gitlab-ci-token"
password = var.ci_token
}
}
# Lokales Backend (Standard, für Entwicklung)
terraform {
backend "local" {
path = "terraform.tfstate"
}
}
State-Verschlüsselung (OpenTofu-exklusiv)
Abschnitt betitelt „State-Verschlüsselung (OpenTofu-exklusiv)“# State-Verschlüsselung mit AWS KMS
terraform {
encryption {
key_provider "aws_kms" "main" {
kms_key_id = "arn:aws:kms:us-east-1:123456789:key/abcd-1234"
region = "us-east-1"
key_spec = "AES_256"
}
method "aes_gcm" "default" {
keys = key_provider.aws_kms.main
}
state {
method = method.aes_gcm.default
enforced = true
}
}
}
Modulverwendung
Abschnitt betitelt „Modulverwendung“| Befehl | Beschreibung |
|---|---|
module "vpc" { source = "./modules/vpc" } | Ein lokales Modul verwenden |
module "vpc" { source = "github.com/org/repo//modules/vpc" } | Ein GitHub-Modul verwenden |
module "vpc" { source = "registry.opentofu.org/..." } | OpenTofu-Registry-Modul verwenden |
tofu get | Module herunterladen und aktualisieren |
tofu get -update | Aktualisierung aller Module erzwingen |
Modulstruktur
Abschnitt betitelt „Modulstruktur“modules/
└── vpc/
├── main.tf # Ressourcen
├── variables.tf # Eingabevariablen
├── outputs.tf # Ausgabewerte
├── versions.tf # Provider-Anforderungen
└── README.md # Nutzungsdokumentation
Modulbeispiel
Abschnitt betitelt „Modulbeispiel“# modules/vpc/main.tf
resource "aws_vpc" "this" {
cidr_block = var.cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = merge(var.tags, { Name = var.name })
}
resource "aws_subnet" "public" {
count = length(var.public_subnets)
vpc_id = aws_vpc.this.id
cidr_block = var.public_subnets[count.index]
availability_zone = var.azs[count.index]
map_public_ip_on_launch = true
tags = merge(var.tags, {
Name = "${var.name}-public-${count.index}"
})
}
# modules/vpc/variables.tf
variable "name" { type = string }
variable "cidr" { type = string }
variable "azs" { type = list(string) }
variable "public_subnets" { type = list(string) }
variable "tags" { type = map(string) default = {} }
# modules/vpc/outputs.tf
output "vpc_id" { value = aws_vpc.this.id }
output "public_subnet_ids" { value = aws_subnet.public[*].id }
# Das Modul verwenden
module "vpc" {
source = "./modules/vpc"
name = "production"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
tags = local.common_tags
}
Migration von Terraform
Abschnitt betitelt „Migration von Terraform“Drop-In-Kompatibilität
Abschnitt betitelt „Drop-In-Kompatibilität“| Befehl | Beschreibung |
|---|---|
terraform durch tofu in Befehlen ersetzen | CLI-Befehle sind Drop-In-kompatibel |
tofu init -upgrade | Neu initialisieren mit OpenTofu-Providern |
Bestehende .tf-Dateien unverändert lassen | HCL-Syntax ist vollständig kompatibel |
Bestehende .tfstate-Dateien beibehalten | State-Format ist kompatibel |
Bestehende .terraform.lock.hcl beibehalten | Lock-Datei ist kompatibel |
CI/CD auf opentofu/setup-opentofu Action aktualisieren | GitHub Actions umstellen |
Migrations-Checkliste
Abschnitt betitelt „Migrations-Checkliste“Migration von Terraform zu OpenTofu:
1. OpenTofu neben Terraform installieren
2. Im Projektverzeichnis ausführen: tofu init
3. Ausführen: tofu plan
- Ausgabe mit terraform plan vergleichen
- Sollte identisch sein (keine Änderungen)
4. 'terraform' durch 'tofu' in CI/CD-Pipelines ersetzen
5. GitHub Actions aktualisieren:
- Alt: hashicorp/setup-terraform
- Neu: opentofu/setup-opentofu
6. Lock-Datei-Provider-Quellen aktualisieren (falls nötig):
- tofu providers lock
7. State-Verschlüsselung aktivieren (OpenTofu-exklusiv):
- Encryption-Block zum terraform {}-Block hinzufügen
8. Modul-Registry-Referenzen umstellen:
- registry.terraform.io → registry.opentofu.org
- (Beide funktionieren noch, aber OpenTofu-Registry wird bevorzugt)
GitHub Actions CI/CD
Abschnitt betitelt „GitHub Actions CI/CD“name: OpenTofu Plan & Apply
on:
push:
branches: [main]
pull_request:
jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: opentofu/setup-opentofu@v1
with:
tofu_version: 1.8.0
- name: Init
run: tofu init
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Plan
run: tofu plan -out=plan.tfplan -no-color
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Apply (nur main)
if: github.ref == 'refs/heads/main'
run: tofu apply -auto-approve plan.tfplan
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
OpenTofu-exklusive Features
Abschnitt betitelt „OpenTofu-exklusive Features“State-Verschlüsselung
Abschnitt betitelt „State-Verschlüsselung“State-Dateien im Ruhezustand verschlüsseln — nicht verfügbar in Terraform. Unterstützt PBKDF2, AWS KMS, GCP KMS und Azure Key Vault Key-Provider.
Clientseitige State-Verschlüsselung
Abschnitt betitelt „Clientseitige State-Verschlüsselung“Im Gegensatz zur serverseitigen Verschlüsselung von Terraform Cloud verschlüsselt OpenTofu den State, bevor er Ihren Rechner verlässt, sodass kein Dritter Ihren Infrastruktur-State lesen kann.
Registry-Unabhängigkeit
Abschnitt betitelt „Registry-Unabhängigkeit“OpenTofu pflegt eine eigene Provider- und Modul-Registry unter registry.opentofu.org, die langfristige Verfügbarkeit unabhängig von HashiCorps Lizenzentscheidungen sicherstellt.
Best Practices
Abschnitt betitelt „Best Practices“-
Remote-State mit Locking verwenden — ein S3 + DynamoDB-Backend (oder Äquivalent) konfigurieren, um gleichzeitige Änderungen zu verhindern und Teamzusammenarbeit zu ermöglichen.
-
State-Verschlüsselung aktivieren — OpenTofus exklusives Verschlüsselungsfeature mit KMS oder PBKDF2 verwenden, um sensible Daten in State-Dateien zu schützen.
-
Provider- und Modulversionen festlegen —
~>-Constraints verwenden (z.B.~> 5.0), um Patch-Updates zu erlauben und Breaking Changes zu verhindern. -
Workspaces oder Verzeichnistrennung für Umgebungen verwenden — Dev-, Staging- und Production-State getrennt halten, um versehentliche umgebungsübergreifende Änderungen zu verhindern.
-
Module für wiederverwendbare Muster schreiben — häufige Muster (VPCs, Datenbanken, Kubernetes-Cluster) in Module mit klaren Ein- und Ausgaben extrahieren.
-
Variablen mit Constraints validieren —
validation-Blöcke zu Variablen hinzufügen, um ungültige Werte abzufangen, bevor sie den Cloud-Provider erreichen. -
Pläne vor dem Anwenden speichern —
tofu plan -out=plan.tfplanund danntofu apply plan.tfplanverwenden, um sicherzustellen, dass genau das angewendet wird, was überprüft wurde. -
default_tagsbei Providern verwenden — organisationsweite Tags (ManagedBy, Environment, Project) auf Provider-Ebene setzen, um konsistentes Tagging sicherzustellen. -
Bestehende Ressourcen importieren —
tofu importverwenden, um nicht verwaltete Infrastruktur unter OpenTofu-Kontrolle zu bringen, anstatt sie neu zu erstellen. -
tofu fmt -checkin CI ausführen — konsistente Formatierung über alle Teammitglieder hinweg erzwingen, indem CI-Builds bei unformatierten Dateien fehlschlagen. -
Datenquellen für externe Referenzen verwenden — bestehende Infrastruktur (AMIs, VPCs, DNS-Zonen) mit
data-Blöcken referenzieren, anstatt IDs hartzucodieren. -
Pläne sorgfältig prüfen — immer die Plan-Ausgabe vor dem Anwenden lesen. Auf
destroy- undreplace-Aktionen achten, die Ausfallzeiten verursachen könnten.