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 version

Projektstruktur

terraform-project/
├── main.tf           # Hauptkonfiguration
├── variables.tf      # Variablen-Definitionen
├── outputs.tf        # Output-Definitionen
├── terraform.tfvars  # Variablen-Werte
├── providers.tf      # Provider-Konfiguration
└── modules/          # Wiederverwendbare Module

Grundbefehle

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üfen

Provider

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 apply

Outputs

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 -json

State 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-1234567890abcdef0

Module

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 show

Workspace 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 -recursive

Validierung

terraform validate

Sicherheit

# 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.