Terraform ist das führende Tool für Infrastructure as Code. Es ermöglicht die deklarative Definition und Verwaltung von Cloud-Infrastruktur über verschiedene Provider hinweg.
Grundlagen
Installation
# Debian/Ubuntu
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list
apt update && apt install terraform
# Version prüfen
terraform versionProjektstruktur
terraform-project/
├── main.tf # Hauptkonfiguration
├── variables.tf # Variablen-Definitionen
├── outputs.tf # Output-Definitionen
├── terraform.tfvars # Variablen-Werte
├── providers.tf # Provider-Konfiguration
└── modules/ # Wiederverwendbare ModuleGrundbefehle
terraform init # Provider initialisieren
terraform plan # Änderungen vorschauen
terraform apply # Änderungen anwenden
terraform destroy # Ressourcen löschen
terraform fmt # Code formatieren
terraform validate # Syntax prüfenProvider
AWS Provider
# providers.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "eu-central-1"
# Oder via Umgebungsvariablen:
# AWS_ACCESS_KEY_ID
# AWS_SECRET_ACCESS_KEY
}Hetzner Cloud Provider
terraform {
required_providers {
hcloud = {
source = "hetznercloud/hcloud"
version = "~> 1.45"
}
}
}
provider "hcloud" {
token = var.hcloud_token
}Multiple Provider
provider "aws" {
alias = "us"
region = "us-east-1"
}
provider "aws" {
alias = "eu"
region = "eu-central-1"
}
resource "aws_instance" "us_server" {
provider = aws.us
# ...
}Ressourcen
Hetzner Cloud Server
# main.tf
resource "hcloud_server" "web" {
name = "web-server"
image = "ubuntu-22.04"
server_type = "cx21"
location = "nbg1"
ssh_keys = [hcloud_ssh_key.default.id]
labels = {
environment = "production"
role = "web"
}
}
resource "hcloud_ssh_key" "default" {
name = "default"
public_key = file("~/.ssh/id_rsa.pub")
}AWS EC2 Instance
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.web.id]
subnet_id = aws_subnet.public.id
key_name = aws_key_pair.default.key_name
root_block_device {
volume_size = 20
volume_type = "gp3"
}
tags = {
Name = "web-server"
}
}AWS VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "main-vpc"
}
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "eu-central-1a"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}Variablen
Definition
# variables.tf
variable "environment" {
description = "Environment name"
type = string
default = "development"
}
variable "server_count" {
description = "Number of servers"
type = number
default = 1
}
variable "enable_monitoring" {
description = "Enable monitoring"
type = bool
default = true
}
variable "allowed_ips" {
description = "Allowed IP addresses"
type = list(string)
default = ["0.0.0.0/0"]
}
variable "tags" {
description = "Resource tags"
type = map(string)
default = {}
}
variable "hcloud_token" {
description = "Hetzner Cloud API Token"
type = string
sensitive = true
}Werte zuweisen
# terraform.tfvars
environment = "production"
server_count = 3
allowed_ips = ["10.0.0.0/8", "192.168.0.0/16"]
tags = {
project = "webapp"
owner = "devops"
}Kommandozeile
terraform apply -var="environment=staging"
terraform apply -var-file="production.tfvars"Umgebungsvariablen
export TF_VAR_hcloud_token="your-token"
terraform applyOutputs
Definition
# outputs.tf
output "server_ip" {
description = "Server IP address"
value = hcloud_server.web.ipv4_address
}
output "server_ids" {
description = "All server IDs"
value = hcloud_server.web[*].id
}
output "connection_string" {
description = "SSH connection string"
value = "ssh root@${hcloud_server.web.ipv4_address}"
sensitive = false
}Ausgabe
terraform output
terraform output server_ip
terraform output -jsonState Management
Remote State (S3)
terraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "prod/terraform.tfstate"
region = "eu-central-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}State-Befehle
# State anzeigen
terraform state list
terraform state show aws_instance.web
# Ressource umbenennen
terraform state mv aws_instance.web aws_instance.webserver
# Ressource entfernen (nicht löschen)
terraform state rm aws_instance.web
# State importieren
terraform import aws_instance.web i-1234567890abcdef0Module
Modul erstellen
# modules/webserver/main.tf
resource "hcloud_server" "this" {
count = var.server_count
name = "${var.name}-${count.index}"
image = var.image
server_type = var.server_type
location = var.location
ssh_keys = var.ssh_keys
}
# modules/webserver/variables.tf
variable "name" {
type = string
}
variable "server_count" {
type = number
default = 1
}
variable "image" {
type = string
default = "ubuntu-22.04"
}
variable "server_type" {
type = string
default = "cx21"
}
variable "location" {
type = string
default = "nbg1"
}
variable "ssh_keys" {
type = list(string)
}
# modules/webserver/outputs.tf
output "server_ips" {
value = hcloud_server.this[*].ipv4_address
}Modul verwenden
module "webservers" {
source = "./modules/webserver"
name = "web"
server_count = 3
server_type = "cx31"
ssh_keys = [hcloud_ssh_key.default.id]
}
output "web_ips" {
value = module.webservers.server_ips
}Registry-Module
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["eu-central-1a", "eu-central-1b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
enable_nat_gateway = true
}Loops und Conditions
count
resource "hcloud_server" "web" {
count = var.server_count
name = "web-${count.index}"
image = "ubuntu-22.04"
server_type = "cx21"
}for_each (Map)
variable "servers" {
default = {
web1 = "cx21"
web2 = "cx31"
db = "cx41"
}
}
resource "hcloud_server" "servers" {
for_each = var.servers
name = each.key
server_type = each.value
image = "ubuntu-22.04"
}for_each (Set)
variable "users" {
default = ["alice", "bob", "charlie"]
}
resource "aws_iam_user" "users" {
for_each = toset(var.users)
name = each.value
}Conditional
resource "aws_instance" "web" {
count = var.create_instance ? 1 : 0
ami = "ami-12345"
instance_type = "t3.micro"
}
resource "aws_eip" "web" {
count = var.environment == "production" ? 1 : 0
instance = aws_instance.web[0].id
}Dynamic Blocks
resource "aws_security_group" "web" {
name = "web-sg"
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}Data Sources
Bestehende Ressourcen
# Existierende VPC finden
data "aws_vpc" "existing" {
filter {
name = "tag:Name"
values = ["production-vpc"]
}
}
# Neuestes AMI
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
subnet_id = data.aws_vpc.existing.id
}Provisioner
Remote-Exec
resource "hcloud_server" "web" {
name = "web"
image = "ubuntu-22.04"
server_type = "cx21"
provisioner "remote-exec" {
inline = [
"apt update",
"apt install -y nginx",
"systemctl start nginx"
]
connection {
type = "ssh"
user = "root"
private_key = file("~/.ssh/id_rsa")
host = self.ipv4_address
}
}
}Local-Exec
resource "hcloud_server" "web" {
name = "web"
# ...
provisioner "local-exec" {
command = "echo ${self.ipv4_address} >> inventory.txt"
}
}File
provisioner "file" {
source = "config/nginx.conf"
destination = "/etc/nginx/nginx.conf"
connection {
type = "ssh"
user = "root"
private_key = file("~/.ssh/id_rsa")
host = self.ipv4_address
}
}Workspaces
Workspace-Management
# Workspaces anzeigen
terraform workspace list
# Neuen Workspace erstellen
terraform workspace new staging
terraform workspace new production
# Workspace wechseln
terraform workspace select staging
# Aktueller Workspace
terraform workspace showWorkspace in Config
resource "hcloud_server" "web" {
name = "web-${terraform.workspace}"
server_type = terraform.workspace == "production" ? "cx31" : "cx21"
}
locals {
env_config = {
production = {
server_type = "cx31"
count = 3
}
staging = {
server_type = "cx21"
count = 1
}
}
config = local.env_config[terraform.workspace]
}Best Practices
Dateiformatierung
terraform fmt -recursiveValidierung
terraform validateSicherheit
# Sensible Variablen
variable "db_password" {
type = string
sensitive = true
}
# .gitignore
*.tfvars
*.tfstate*
.terraform/Locking
# DynamoDB für State Locking
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}Zusammenfassung
| Befehl | Funktion | |--------|----------| | terraform init | Initialisieren | | terraform plan | Änderungen anzeigen | | terraform apply | Anwenden | | terraform destroy | Löschen | | terraform state list | State anzeigen | | terraform import | Importieren |
| Block | Verwendung | |-------|------------| | resource | Ressource erstellen | | data | Daten lesen | | variable | Eingabevariable | | output | Ausgabewert | | module | Modul einbinden | | locals | Lokale Werte |
Fazit
Terraform ist das Standardwerkzeug für Infrastructure as Code. Die deklarative Syntax macht Infrastruktur versionierbar und reproduzierbar. Mit Modulen wird Code wiederverwendbar, und der State ermöglicht inkrementelle Änderungen. Remote State und Workspaces unterstützen Team-Arbeit und Multiple Environments. Die große Provider-Vielfalt ermöglicht Multi-Cloud-Strategien.