Blog

Eingabevalidierung - fordern Sie Ihre Zeit von Terraform zurück!

Lotte-Sara Laan

Lotte-Sara Laan

Aktualisiert Oktober 20, 2025
7 Minuten

Ich verwende Terraform jetzt seit über zwei Jahren und habe von Anfang an festgestellt, dass eine angemessene Eingabevalidierung notwendig ist. Obwohl Terraform mit HCL 2.0 Variablentypen und sogar Regeln für die Validierung von Eingabevariablen in Terraform CLI v0.13.0 hinzugefügt hat, scheint dies immer noch eine Herausforderung zu sein.

Warum Eingabeüberprüfung?

Es gibt mehrere Gründe, warum die Eingabevalidierung eine gute Idee ist, aber der wichtigste ist die Zeitersparnis!
Es ist schon oft vorgekommen, dass eine plan Ausgabe für mich gut aussah und Terraform selbst keine Fehler fand, aber wenn ich apply ausführte, brach der Code trotzdem ab. Nicht alle Anbieter scheinen Ihnen im Voraus zu sagen, dass eine bestimmte Kombination von Einstellungen nicht korrekt ist. Es kann auch vorkommen, dass der Name der Ressource nicht validiert wird, aber das wird in der Phase plan nicht erkannt. Insbesondere bei der Bereitstellung von GKE-Clustern oder Diensten wie Composer in der Google Cloud können Sie mehr als 30 Minuten warten, bis diese Art von Fehlern auftaucht. Und dann können Sie weitere 30 Minuten warten, bis die Ressourcen wieder deleted sind.
Sie sehen also, dass Sie viel Zeit sparen können, wenn Sie im Voraus über diese Einstellungsfehler informiert werden.

Benutzerdefinierte Assertions verwenden

Eines der ersten Dinge, die wir in unseren benutzerdefinierten Modulen implementiert haben, ist die Verwendung von Assertions. Die Sicherstellung, dass Terraform eine korrekte Fehlermeldung anzeigt, war eine Herausforderung, da es dafür keine vordefinierte Funktion gibt. Hinzu kommt, dass das Hinzufügen von benutzerdefinierten Prüfungen, um aus dem Code auszubrechen, von HCL nicht unterstützt wird. Beispiel:

# variables.tf
variable "prefix" {
  description = "Company naming prefix, ensures uniqueness of bucket names"
  type        = string
  default     = "binx"
}
variable "project" {
  description = "Company project name."
  type        = string
  default     = "blog"
}
variable "environment" {
  description = "Company environment for which the resources are created (e.g. dev, tst, acc, prd, all)."
  type        = string
  default     = "dev"
}
variable "purpose" {
  description = "Bucket purpose, will be used as part of the bucket name"
  type        = string
}

# main.tf
locals {
  bucket_name = format("%s-%s-%s-%s", var.prefix, var.project, var.environment, var.purpose)
}

resource "google_storage_bucket" "demo" {
  provider = google-beta
  name     = local.bucket_name
}

Wenn Sie terraform plan mit diesem Beispielcode ausführen und eine purpose mit ungültigen Zeichen eingeben, erhalten Sie die folgende Ausgabe:

var.purpose
  Bucket purpose, will be used as part of the bucket name
  Enter a value: test^&asd

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # google_storage_bucket.demo will be created
  + resource "google_storage_bucket" "demo" {
      + bucket_policy_only          = (known after apply)
      + force_destroy               = false
      + id                          = (known after apply)
      + location                    = "US"
      + name                        = "binx-blog-dev-test^&asd"
      + project                     = (known after apply)
      + self_link                   = (known after apply)
      + storage_class               = "STANDARD"
      + uniform_bucket_level_access = (known after apply)
      + url                         = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Wie Sie sehen können, prüft Terraform die Zeichen im Bucket-Namen nicht und lässt Sie glauben, dass alles in Ordnung ist. Wenn Sie jedoch versuchen, diesen Code unter apply einzugeben, erhalten Sie bei der Erstellung des Buckets eine Fehlermeldung, dass der Name ungültig ist:

google_storage_bucket.demo: Creating...
╷
│ Error: googleapi: Error 400: Invalid bucket name: 'binx-blog-dev-test^&asd', invalid
│
│   with google_storage_bucket.demo,
│   on test.tf line 30, in resource "google_storage_bucket" "demo":
│   30: resource "google_storage_bucket" "demo" {
│
╵

Wir möchten dies eigentlich in der plan Phase erkennen, um den Entwickler darauf hinzuweisen, dass er seinen Code korrigieren soll, bevor er ihn in Master für das Rollout zusammenführt.
Wie haben wir also dieses Problem gelöst? Es scheint, dass wir die Funktion file missbrauchen können, um eine angemessene Fehlermeldung anzuzeigen, wenn unsere Prüfungen fehlschlagen. Das Schöne an dieser Funktion ist, dass sie Ihnen eine Fehlermeldung anzeigt, wenn die Datei, die Sie zu laden versuchen, nicht existiert. Wir können dies zu unserem Vorteil nutzen, indem wir eine Fehlerbeschreibung als Dateinamen angeben. Beispiel:

# main.tf
locals {
  regex_bucket_name = "(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])" # See https://cloud.google.com/storage/docs/naming-buckets

  bucket_name       = format("%s-%s-%s-%s", var.prefix, var.project, var.environment, var.purpose)
  bucket_name_check = length(regexall("^${local.regex_bucket_name}$", local.bucket_name)) == 0 ? file(format("Bucket [%s]'s generated name [%s] does not match regex ^%s$", var.purpose, local.bucket_name, local.regex_bucket_name)) : "ok"
}

resource "google_storage_bucket" "demo" {
  provider = google-beta
  name     = local.bucket_name
}

Wenn wir das obige Beispiel verwenden und Terraform plan mit der gleichen purpose Eingabe wie zuvor ausführen, erhalten wir die folgende Meldung:

│ Error: Invalid function argument
│
│   on main.tf line 5, in locals:
│    5:   bucket_name_check = length(regexall("^${local.regex_bucket_name}$", local.bucket_name)) == 0 ? file(format("Bucket [%s]'s generated name [%s] does not match regex ^%s$", var.purpose, local.bucket_name, local.regex_bucket_name)) : "ok"
│     ├────────────────
│     │ local.bucket_name is "binx-blog-dev-test^&asd"
│     │ local.regex_bucket_name is "(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])"
│     │ var.purpose is "test^&asd"
│
│ Invalid value for "path" parameter: no file exists at Bucket [test^&asd]'s generated name [binx-blog-dev-test^&asd] does not match regex ^(([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9]).)*([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])$; this function works only with files that are distributed as part of the configuration source code, so if this file will be created by a resource in this configuration you must instead obtain this result
│ from an attribute of that resource.

Wie Sie sehen können, versucht Terraform, eine Datei namens:

Bucket [test^&asdf]'s generated name [lot-blog-dev-test^&asdf] does not match regex:
^(([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9]).)*([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])$

Dieser Dateiname ist eigentlich die Fehlermeldung, die wir anzeigen wollen. Offensichtlich existiert diese Datei nicht, so dass der Vorgang fehlschlägt und diese Meldung auf der Konsole angezeigt wird. Schön! Wir haben also eine Möglichkeit, dem Entwickler im Voraus mitzuteilen, dass die eingegebene Datei nicht gültig ist. Aber wie können wir die Meldung noch deutlicher machen? Wir können Zeilenumbrüche verwenden! Sehen Sie sich das folgende Beispiel an:

# main.tf
locals {
  regex_bucket_name = "(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])" # See https://cloud.google.com/storage/docs/naming-buckets

  assert_head = "nn-------------------------- /!\ CUSTOM ASSERTION FAILED /!\ --------------------------nn"
  assert_foot = "nn-------------------------- /!\ ^^^^^^^^^^^^^^^^^^^^^^^ /!\ --------------------------nn"

  bucket_name       = format("%s-%s-%s-%s", var.prefix, var.project, var.environment, var.purpose)
  bucket_name_check = length(regexall("^${local.regex_bucket_name}$", local.bucket_name)) == 0 ? file(format("%sBucket [%s]'s generated name [%s] does not match regex:n^%s$%s", local.assert_head, var.purpose, local.bucket_name, local.regex_bucket_name, local.assert_foot)) : "ok"
}

resource "google_storage_bucket" "demo" {
  provider = google-beta
  name     = local.bucket_name
}

Wir erhalten nun die folgende Fehlerausgabe:

│ Error: Invalid function argument
│
│   on main.tf line 8, in locals:
│    8:   bucket_name_check = length(regexall("^${local.regex_bucket_name}$", local.bucket_name)) == 0 ? file(format("%sBucket [%s]'s generated name [%s] does not match regex:n^%s$%s", local.assert_head, var.purpose, local.bucket_name, local.regex_bucket_name, local.assert_foot)) : "ok"
│     ├────────────────
│     │ local.assert_foot is "nn-------------------------- /!\ ^^^^^^^^^^^^^^^^^^^^^^^ /!\ --------------------------nn"
│     │ local.assert_head is "nn-------------------------- /!\ CUSTOM ASSERTION FAILED /!\ --------------------------nn"
│     │ local.bucket_name is "binx-blog-dev-test^&asd"
│     │ local.regex_bucket_name is "(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])"
│     │ var.purpose is "test^&asd"
│
│ Invalid value for "path" parameter: no file exists at
│
│ -------------------------- /! CUSTOM ASSERTION FAILED /! --------------------------
│
│ Bucket [test^&asd]'s generated name [binx-blog-dev-test^&asd] does not match regex:
│ ^(([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9]).)*([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])$
│
│ -------------------------- /! ^^^^^^^^^^^^^^^^^^^^^^^ /! --------------------------
│
│ ; this function works only with files that are distributed as part of the configuration source code, so if this file will be created by a resource in this configuration you must instead obtain this result from an attribute of that resource.

Konklusion

Wie Sie sehen, eröffnet die Verwendung der Funktion file zur Generierung von Assertions viele Möglichkeiten für die Eingabevalidierung. Weitere Beispiele für die Verwendung dieser Funktion finden Sie in unseren eigenen Terraform-Modulen , die Sie in der Terraform Registry finden. Zu jedem dieser Module gehört eine asserts.tf-Datei, in der Assertions mit der oben beschriebenen Methode definiert werden.Ich fände es immer noch schön, wenn Terraform eine Funktion für uns erstellen würde, mit der wir benutzerdefinierte Fehler erzeugen können. Der Workaround, den ich in diesem Blogpost gezeigt habe, kann Sie leicht verwirren, da Terraform Ihnen no file exists mitteilt, während Sie gar nicht versuchen, irgendwelche Dateien zu lesen.

Verfasst von

Lotte-Sara Laan

Contact

Let’s discuss how we can support your journey.