Ohne Lock-File wählt terraform init bei jedem frischen Checkout die neueste Provider-Version, die zu deinem Constraint passt. Laptop und CI können dann mit unterschiedlichen Ständen planen, ohne dass eine Zeile im Diff steht. Abgrenzung: Den einen Satz zum Lock-File aus „Infrastructure as Code absichern: die häufigsten Terraform-Fehlkonfigurationen“ baut dieser Beitrag zur Mechanik aus. Die Fehlkonfigurationen stehen im Beitrag vom 31.07., der State samt State-Locking im Beitrag vom 17.06..

Was im Lock-File steht

Terraform legt .terraform.lock.hcl beim init im Arbeitsverzeichnis des Root-Moduls an. Ein Eintrag sieht so aus (Hashes teils gekürzt):

provider "registry.terraform.io/hashicorp/random" {
version = "3.6.3"
constraints = "~> 3.6.0"
hashes = [
"h1:In4XBRMdhY89yUoTUyar3wDF28RJlDpQzdjahp59FAk=",
"zh:04ceb65210251339f07cd4611885d242cd4d0c7306e86dda97853968...",
"zh:448f56199f3e99ff75d5c0afacae867ee795e4dfda6cb5f8e3b2a72e...",
]
}

version ist die gewählte Version. constraints hält fest, welche Einschränkungen dabei galten, Terraform nutzt das laut Doku aber nicht für Installationsentscheidungen. hashes sind Prüfsummen, und zwei Schemata kommen vor: zh: ist der SHA256 der offiziellen .zip-Pakete aus der Registry, h1: ein SHA256 über den Inhalt des Pakets und das bevorzugte Schema. Bei jeder Installation prüft Terraform, ob das Paket zu mindestens einer aufgezeichneten Prüfsumme passt, sonst bricht es ab. Die Doku nennt das „trust on first use“.

Terraform-Provider pinnen: Constraint und Lock-File

Der Constraint steht in required_providers:

terraform {
required_providers {
random = {
source = "hashicorp/random"
version = "~> 3.6.0"
}
}
}

~> erlaubt nur, dass die letzte angegebene Stelle steigt. ~> 3.6.0 bleibt bei 3.6.x, ~> 3.6 erlaubt jede 3.x ab 3.6. Die Doku empfiehlt für Root-Module ein ~> mit unterer und oberer Grenze. Existiert ein Eintrag im Lock-File, wählt init genau diese Version, auch wenn eine neuere passt. Ein Upgrade ist eine bewusste Aktion:

terraform init -upgrade
terraform providers lock -platform=linux_amd64 -platform=linux_arm64 \
-platform=darwin_arm64 -platform=windows_amd64

Bei einem Versionswechsel ersetzt Terraform die Hashes durch die des neuen Pakets, und nach init -upgrade steht für die lokale Plattform ein h1:-Eintrag im File, zusätzlich zu den zh:-Einträgen. providers lock trägt die Prüfsummen aller genannten Plattformen nach, sodass ein Mac-Laptop und ein Linux-Runner dasselbe File nutzen. Ohne das ergänzt Terraform h1:-Werte erst, wenn jemand auf der jeweiligen Plattform installiert, und jede Ergänzung wird ein Diff im Repo. Bei Installation über einen Mirror kann Terraform die Prüfsummen anderer Plattformen laut Doku gar nicht ermitteln.

In der Pipeline verhindert -lockfile=readonly, dass init das File stillschweigend ändert:

terraform init -lockfile=readonly

Der Modus prüft die Prüfsummen gegen das aufgezeichnete File, schreibt aber nichts. Passt ein Constraint nicht mehr zur gelockten Version, endet init mit einem Fehler. Kombinieren lässt sich das nicht mit -upgrade.

Module: das Lock-File deckt sie nicht ab

Das Lock-File verfolgt laut Doku nur Provider. Für Registry-Module merkt sich Terraform keine Auswahl, mit einem Bereich als Constraint gilt bei jedem Neuaufbau die neueste passende Version. Deshalb steht bei Modulen die Version im Code:

module "label" {
source = "cloudposse/label/null"
version = "0.25.0"
name = "app"
}
module "label_git" {
source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=488ab91e34a24a86957e397d9f7262ec5925586a"
name = "app"
}

Die Modulnamen sind austauschbare Beispiele. Ein exakter Constraint wählt immer dieselbe Version. Bei Git-Quellen nimmt Terraform ohne ref den Standard-Branch, mit ?ref= geht laut Doku alles, was git checkout versteht: Branch, Tag oder SHA-1. Ein Branch bewegt sich mit jedem Push, ein Tag lässt sich nachträglich auf einen anderen Commit setzen, ein SHA nicht. Bei Drittmodulen ist der Commit deshalb die festere Wahl. Ein Modul kann außerdem einen Provider mitbringen, den dein Code nie genannt hat. Dann erscheint im Lock-File ein neuer Provider-Block, den du im Review sehen willst.

Was das Lock-File nicht sagt

Die Prüfsummen zeigen, dass ein Paket zu dem passt, was beim ersten Mal aufgezeichnet wurde. Ob es vertrauenswürdig ist, sagen sie nicht. Die Doku sagt es für providers lock ausdrücklich: Der Befehl kann nicht automatisch prüfen, ob Provider vertrauenswürdig sind und zu deinen Vorgaben passen, du sollst die Signaturschlüssel in der Ausgabe prüfen, bevor du das File committest.

Aus der Registry installierte Provider sind signiert, Terraform prüft die Signatur bei der Installation. Es gibt drei Arten. Bei Providern von HashiCorp baut, signiert und betreut HashiCorp selbst. Bei Partnern verifiziert HashiCorp den Besitz des Schlüssels und stellt eine Vertrauenskette bereit. Bei selbstsignierten Providern gibt es diese Kette nicht. Die Registry weist außerdem Stufen wie Official, Partner und Community und Namespaces aus, die den Herausgeber erkennbar machen. Signiert heißt damit: Das Paket stammt vom Inhaber des Schlüssels. Was der Provider-Code tut, ist damit nicht geprüft, und er läuft als Programm in deinem Terraform-Lauf mit dessen Zugangsdaten.

Prüfe deshalb beim ersten Hinzufügen den Namespace im source, lies die Zeile signed by ... der init-Ausgabe und kläre, wer der Herausgeber ist. Das Lock-File hält die Entscheidung danach fest.

Lock-File-Diffs im Pull Request

Die Doku empfiehlt, das File einzuchecken, damit Änderungen an externen Abhängigkeiten im Code-Review besprochen werden. Ein Diff liest du so:

  • Neue h1:-Zeile bei gleicher version: üblich, Terraform ergänzt Hashes laut Doku nur, wenn das Paket zu einem bereits bekannten passt.
  • Geänderte version: Ist das ein gewolltes Upgrade, und passt der Constraint dazu? Bei neuer Version werden in der Regel alle Hashes ersetzt, das ist normal.
  • Andere Hashes bei gleicher version: klären, bevor gemergt wird.
  • Neuer provider-Block: Wer ist der Herausgeber, und welche Änderung hat ihn hereingebracht?

Einordnung

Das Lock-File (auch Lockfile geschrieben) macht Provider-Installationen wiederholbar und meldet ein verändertes Paket für eine bereits gewählte Version. Module deckt es nicht ab, und die Frage, ob ein Provider oder Modul vertrauenswürdig ist, beantwortet es nicht. Die bleibt eine Prüfung beim ersten Hinzufügen und im Review. Wie andere Ökosysteme mit Lockfiles umgehen, steht in „Lockfiles richtig nutzen“.

Fragen oder Anmerkungen zu diesem Setup: mm@mhm-dl.de.

Über das Anmeldeformular für den Newsletter trägst du deine E-Mail-Adresse ein, kreuzt die Einwilligung an und bestätigst die Anmeldung danach per Mail. Mit der Anmeldung willigst du ein, dass der Newsletter unter anderem folgende Themen behandelt: Schulungen zu Docker, Kubernetes, CI/CD, Git-Workflows und DevSecOps-Werkzeugen sowie die Anforderungen aus NIS2, CRA und DORA und deren technische Umsetzung.

Hinterlasse einen Kommentar

Diese Seite verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden..