🏗️ Terraform Lektion 2/15 ~9 Min. Einsteiger

HCL Syntax und Grundlagen

Resources, Data Sources, Variables, Outputs.

📝 Meine Notizen

Lernziele

Nach dieser Lektion liest und schreibst du HCL sicher: Du kennst die Blocktypen resource, data, variable und output, verstehst, wie Terraform aus Referenzen einen Abhängigkeitsgraphen baut, und kannst erklären, warum eine Data Source etwas grundlegend anderes ist als eine Resource. Außerdem weißt du, wofür terraform fmt und terraform validate da sind.

Das Problem: eine Sprache zwischen YAML und Programmiersprache

In Lektion 1 hast du gesehen, dass Terraform den Zielzustand deklarativ beschreibt. Aber in welcher Sprache? JSON und YAML wären zu starr — keine Variablen, keine Ausdrücke, keine Kommentare (JSON). Eine volle Programmiersprache wäre zu mächtig — Code mit Seiteneffekten lässt sich nicht mehr statisch analysieren und schwer reviewen.

Die HashiCorp Configuration Language ist der bewusste Mittelweg: deklarative Blöcke mit einer begrenzten Ausdruckssprache obendrauf. Du bekommst Variablen, Funktionen, Konditionale und Schleifen-Konstrukte — aber keine beliebige Logik. Genau diese Einschränkung ist ein Feature: Ein Reviewer kann eine HCL-Datei lesen und weiß, was passiert.

Resources: der wichtigste Block

Eine Resource ist ein Stück Infrastruktur, das Terraform erstellen, ändern und löschen soll:

resource "aws_s3_bucket" "artifacts" {
  bucket = "platprep-artifacts"

  tags = {
    Team        = "platform"
    Environment = "dev"
  }
}

Die Anatomie musst du im Schlaf können: aws_s3_bucket ist der Resource-Typ (definiert vom Provider, dazu Lektion 4), artifacts ist der lokale Name — er existiert nur in deinem Code und im State, nicht in der Cloud. Die Kombination aws_s3_bucket.artifacts ist die Adresse, unter der du die Resource überall referenzierst.

Referenzen sind der Mechanismus, der HCL zusammenhält:

resource "aws_s3_bucket_versioning" "artifacts" {
  bucket = aws_s3_bucket.artifacts.id

  versioning_configuration {
    status = "Enabled"
  }
}

Weil dieser Block aws_s3_bucket.artifacts.id referenziert, weiß Terraform: Erst der Bucket, dann die Versionierung. Aus allen Referenzen baut Terraform einen gerichteten azyklischen Graphen und arbeitet ihn in der richtigen Reihenfolge ab — unabhängige Ressourcen sogar parallel. Du deklarierst Abhängigkeiten also implizit durch Verweise, nicht durch Reihenfolge im Code. Die Reihenfolge der Blöcke in einer Datei ist für Terraform bedeutungslos.

Data Sources: lesen statt verwalten

Eine Data Source sieht ähnlich aus, tut aber das Gegenteil: Sie liest etwas, das bereits existiert und das jemand anderes verwaltet:

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"
}

Der Unterschied ist im Interview ein Klassiker: Eine Resource erscheint im Plan als „wird erstellt/geändert/zerstört” — eine Data Source wird nur abgefragt. terraform destroy löscht das AMI nicht, weil Terraform es nie besessen hat. Data Sources sind das Bindeglied zu Infrastruktur außerhalb deines Projekts: das VPC vom Netzwerk-Team, die aktuelle AMI-ID, eine bestehende DNS-Zone.

Variables und Outputs: die Schnittstelle deiner Konfiguration

Variables parametrisieren deine Konfiguration, statt Werte hart zu codieren:

variable "environment" {
  type        = string
  description = "Zielumgebung (dev, staging, prod)"
  default     = "dev"

  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "environment muss dev, staging oder prod sein."
  }
}

Werte kommen über mehrere Wege herein — terraform.tfvars-Dateien, -var-Flags, Umgebungsvariablen mit dem Präfix TF_VAR_ — und du greifst per var.environment darauf zu. Der validation-Block fängt Tippfehler ab, bevor irgendetwas in der Cloud passiert. Für Secrets gibt es sensitive = true, damit der Wert nicht in der Plan-Ausgabe erscheint (im State landet er trotzdem — das wird in Lektion 5 wichtig).

Outputs sind das Gegenstück: Werte, die deine Konfiguration nach außen gibt — an den Menschen am Terminal, an andere Terraform-Projekte (Lektion 6) oder als Schnittstelle von Modulen (Lektion 7):

output "bucket_name" {
  value       = aws_s3_bucket.artifacts.bucket
  description = "Name des Artifact-Buckets"
}

Dazu kommen locals für berechnete Zwischenwerte (local.name_prefix), die du nutzt, wenn derselbe Ausdruck mehrfach vorkommt — Variables sind Eingaben von außen, Locals sind interne Hilfswerte. Diese Abgrenzung wird gern gefragt.

Praxis: HCL ohne Cloud-Account üben

Der random-Provider eignet sich perfekt, um die Blocktypen gefahrlos durchzuspielen:

mkdir hcl-lab && cd hcl-lab
cat > main.tf <<'EOF'
variable "name_prefix" {
  type    = string
  default = "platprep"
}

resource "random_pet" "server" {
  prefix = var.name_prefix
  length = 2
}

locals {
  server_label = upper(random_pet.server.id)
}

output "server_name" {
  value = random_pet.server.id
}

output "server_label" {
  value = local.server_label
}
EOF

terraform init
terraform fmt        # Formatierung vereinheitlichen
terraform validate   # Syntax und interne Konsistenz prüfen
terraform apply -var="name_prefix=demo"
terraform output server_name
terraform destroy

terraform fmt ist nicht kosmetisch: Es erzwingt den kanonischen Stil und gehört mit -check in jede CI-Pipeline, damit Diffs in Reviews nur echte Änderungen zeigen. terraform validate prüft Syntax und Referenzen, ohne die Cloud zu kontaktieren — die schnellste Qualitätsstufe (mehr dazu in Lektion 11).

Typische Stolperfallen

Resource und Data Source verwechseln: Wer eine bestehende Ressource als resource deklariert statt sie zu lesen oder zu importieren, bekommt entweder einen Namenskonflikt oder ein Duplikat. Regel: resource = ich verwalte es, data = ich lese es nur.

Abhängigkeiten über Reihenfolge erwarten: HCL ist kein Skript. Wenn zwei Ressourcen voneinander abhängen, aber keine Referenz existiert (z. B. eine implizite Abhängigkeit über IAM-Propagation), brauchst du explizit depends_on — sonst legt Terraform beide parallel an und der Apply schlägt sporadisch fehl.

Hartcodierte Werte überall: Region, Namen und Größen direkt in Resource-Blöcken machen die Konfiguration unbrauchbar für eine zweite Umgebung. Eingaben gehören in Variables, Wiederholtes in Locals.

sensitive mit Sicherheit verwechseln: sensitive = true versteckt Werte in der Ausgabe — im State stehen sie im Klartext. Echtes Secret-Handling kommt in Lektion 14.

Interview-Vorbereitung

Auf „Erkläre die wichtigsten HCL-Bausteine” antwortest du strukturiert: Resource (Infrastruktur, die Terraform besitzt und verwaltet) → Data Source (read-only-Blick auf Fremdes) → Variable/Output (Eingabe- und Ausgabeschnittstelle) → der Graph (Referenzen erzeugen Reihenfolge, nicht die Position im Code). Der letzte Punkt hebt dich von Kandidaten ab, die HCL nur als „Config-Format” kennen.

Typische Follow-ups:

  • „Woher weiß Terraform, in welcher Reihenfolge es Ressourcen anlegt?” — Aus den Referenzen entsteht ein Abhängigkeitsgraph; Unabhängiges läuft parallel, für Unsichtbares gibt es depends_on.
  • „Unterschied Variable vs. Local?” — Variable ist Eingabe von außen (parametrisierbar), Local ist ein intern berechneter Wert.
  • „Was prüft terraform validate — und was nicht?” — Syntax, Typen, Referenzen; nicht aber, ob die Cloud die Werte akzeptiert. Das zeigt erst plan/apply.
  • „Wie verhinderst du ungültige Variablenwerte?” — validation-Blöcke mit condition und aussagekräftiger error_message.

Zusammenfassung

HCL beschreibt Infrastruktur in vier zentralen Blöcken: resource (verwalten), data (lesen), variable (Eingabe) und output (Ausgabe), ergänzt um locals für Zwischenwerte. Referenzen zwischen Blöcken bilden den Abhängigkeitsgraphen, der die Ausführungsreihenfolge bestimmt. terraform fmt und terraform validate sichern Stil und Konsistenz, bevor die Cloud ins Spiel kommt.

In der nächsten Lektion setzt du diese Bausteine in Bewegung: der Workflow aus init, plan, apply und destroy.