Blog

Modulparameter-Standardwerte mit dem Terraform-Objekttyp

Lotte-Sara Laan

Aktualisiert Oktober 21, 2025
7 Minuten

In diesem Beitrag gehe ich auf die Herausforderungen ein, mit denen ich bei der Übernahme des Objekttyps in meine Terraform 0.12-Module konfrontiert war, und auf die Lösungen, die ich gefunden habe, um diese Probleme zu umgehen. Als Beispiel werde ich die Ressource google_storage_bucket verwenden, die Teil eines der von mir erstellten Module ist.

Der Terraform Objekttyp

Mit der Einführung von Terraform 0.12 im Mai 2019 wurden viele neue coole Funktionen eingeführt. Im Vergleich zu Terraform 0.11, wo Sie eine Menge Code wiederholen mussten, können Sie jetzt die neue for_each-Funktionalität und den Objekttyp nutzen, um saubereren Code zu schreiben. Wenn Sie den neuen Objekttyp noch nicht entdeckt haben, werden Sie vielleicht von seinem Potenzial überrascht sein. Er ist einer der beiden komplexen Typen, die Terraform bietet, und gibt Ihnen die Möglichkeit, Objektstrukturen zu beschreiben. Dieser neue Typ hat zwar viele Vorteile, wie z.B. das Mischen von Typen und das Definieren von mehrschichtigen Strukturen, aber er hat auch ein paar Nachteile, die ich weiter unten erläutern werde.

Der folgende Inhalt stammt aus den Terraform-Dokumenten selbst: object(...) Das Schema für Objekttypen ist { <key> = <type>, <key> = <type>, ... }</type></key></type></key> - ein Paar geschweifte Klammern, die eine durch Komma getrennte Reihe von <key> = <type></type></key> Paaren enthalten. Werte, die dem Objekttyp entsprechen, müssen alle angegebenen Schlüssel enthalten, und der Wert für jeden Schlüssel muss dem angegebenen Typ entsprechen. (Werte mit zusätzlichen Schlüsseln können immer noch einem Objekttyp entsprechen, aber die zusätzlichen Attribute werden bei der Typkonvertierung verworfen).

Beispiel Objektstruktur:

my_object = {
  a_string  = example,
  a_number  = 1,
  a_boolean = true,
  a_map = {
    type1 = 1,
    type2 = 2,
    type3 = 3
  }
}

Modul-Standard-Parameter mit Objekt bereitstellen

Wie versprochen, werde ich in diesem Blogbeitrag erklären, wie ich den Objekttyp in meinem benutzerdefinierten Terraform-Modul verwendet habe. Im folgenden Beispiel habe ich den Objekttyp verwendet, um die unterstützten Einstellungen des GCP-Speicher-Buckets zu definieren.

variable bucket_settings {
  type = object({
    location           = string
    storage_class      = string
    versioning_enabled = bool
    bucket_policy_only = bool
    lifecycle_rules = map(object({
      action = map(string)
      condition = object({
        age                   = number
        with_state            = string
        created_before        = string
        matches_storage_class = list(string)
        num_newer_versions    = number
      })
    }))
  })
}

Früher mussten Sie beim Schreiben Ihres Terraform-Moduls für jede Einstellung, die Sie an Ihre Ressource übergeben wollten, eine Variable erstellen. Sicher, Sie hatten Maps und Listen, aber eine Map konnte nur Werte desselben Typs enthalten, was ihre Verwendung erheblich einschränkte. Wenn wir den Objekttyp verwenden, können wir diese Einstellungen in einer komplexen Struktur kombinieren. Damit kann ich meinen Bucket befüllen, indem ich alle diese Einstellungen in einer Variablen bereitstelle (bucket_settings in der Auflistung unten).

bucket_settings = {
  location           = europe-west4
  storage_class      = REGIONAL
  versioning_enabled = true
  bucket_policy_only = true
  lifecycle_rules    = {}
}

Da ich bestimmte Einstellungen habe, die ich auf alle meine Buckets anwenden möchte, möchte ich, dass die meisten dieser Einstellungen für mich standardmäßig festgelegt werden. Zu diesem Zweck können wir in der Variablendefinition selbst einen default Wert für das bucket_settings Objekt angeben:

variable bucket_settings {
  type = object({
    location           = string
    storage_class      = string
    versioning_enabled = bool
    bucket_policy_only = bool
    lifecycle_rules = map(object({
      action = map(string)
      condition = object({
        age                   = number
        with_state            = string
        created_before        = string
        matches_storage_class = list(string)
        num_newer_versions    = number
      })
    }))
  })

  default = {
    location           = europe-west4
    storage_class      = REGIONAL
    versioning_enabled = true
    bucket_policy_only = true
    lifecycle_rules    = {}
  }
}

Wenn ich nun meinen Bucket erstelle, kann ich das Modul einfach so importieren:

module my_bucket {
  source = path.to/my/module
  name   = bucket-with-defaults
}

Und da ich die Standardeinstellungen festgelegt habe, brauche ich die Variable bucket_settings gar nicht mehr anzugeben. ## Ändern Sie eine Einstellung, und die Standardwerte verschwinden

Aber was ist, wenn ich nur eine der Standardeinstellungen überschreiben und den Rest beibehalten möchte? Im folgenden Beispiel versuche ich, mit meinem Modul eine Lebenszyklusrichtlinie für den Bucket festzulegen. Beachten Sie, dass ich die bucket_settings als Parameter übergebe.

module my_bucket {
  source = ./modules/bucket
  name   = bucket-with-defaults

  bucket_settings = {
    lifecycle_rules = {
      delete rule = {
        action = { type = Delete }
        condition = {
          age        = 30
          with_state = ANY
        }
      }
    }
  }
}

Leider führt dies zu mehreren Fehlern: Fehlende Attribute

The given value is not suitable for child module variable bucket_settings
defined at modules/bucket/main.tf:6,1-27: attributes bucket_policy_only,
location, storage_class, and versioning_enabled are required.

Da ich die bucket_settings für dieses Modul zur Verfügung gestellt habe, wird es die gesamte Voreinstellung überschreiben, die ich für die Variable festgelegt habe. Ich muss die Schlüssel wie , , und angeben. Aber selbst nachdem ich diese Einstellungen vorgenommen habe, erhalte ich einen neuen Fehler:

The given value is not suitable for child module variable bucket_settings
defined at modules/bucket/main.tf:6,1-27: attribute lifecycle_rules: element
delete rule: attribute condition: attributes created_before,
matches_storage_class, and num_newer_versions are required.

Für die lifecycle_rule conditionhabe ich nur die age und with_state da ich mich nicht um die created_before, matches_storage_class oder num_newer_versions. Aber da ich sie in der Objektdefinition definiert habe, wird Terraform sich beschweren und mir sagen, dass ich sie ebenfalls bereitstellen muss. Damit dies mit dem oben beschriebenen Modul-Setup funktioniert, muss ich also alle Einstellungen erneut bereitstellen. Wie Sie sehen, wird das ziemlich mühsam, vor allem, wenn Sie mehrere Buckets mit nur wenigen Unterschieden in der Bucket-Konfiguration erstellen möchten. # Lösung mit einer defaults Variable und Zusammenführung

Da ich nur die Einstellungen bereitstellen möchte, die von den Standardeinstellungen abweichen, habe ich eine Lösung dafür entwickelt. Wenn ich eine separate Variable für die Standardeinstellungen erstelle, kann ich die bereitgestellten mit dieser Variable zusammenführen, bevor ich den eigentlichen Bucket Provider aufrufe. Das Ergebnis sieht wie folgt aus:

Modul-Code-Schnipsel

variable name {
  type = string
  description = "The name of the bucket"
}

variable bucket_defaults {
  type = object({
    location           = string
    storage_class      = string
    versioning_enabled = bool
    bucket_policy_only = bool
    lifecycle_rules = map(object({
      action = map(string)
      condition = object({
        age                   = number
        with_state            = string
        created_before        = string
        matches_storage_class = list(string)
        num_newer_versions    = number
      })
    }))
  })

  default = {
    location           = europe-west4
    storage_class      = REGIONAL
    versioning_enabled = true
    bucket_policy_only = true
    lifecycle_rules    = {}
  }
}

variable bucket_settings {
  description = "Map of bucket settings to be applied, which will be merged with the bucket_defaults. Allowed keys are the same as defined for bucket_defaults."
}

locals {
  merged_bucket_settings = merge(var.bucket_defaults, var.bucket_settings)
}

Ich kann das Modul nun verwenden, indem ich nur die Lebenszyklusregel eingebe, die ich für meinen Bucket festlegen möchte, und alle anderen Standardeinstellungen werden weiterhin angewendet. Verwendung des Moduls

module my_bucket {
  source = path.to/my/module
  name   = bucket-with-defaults

  bucket_settings = {
    lifecycle_rules = {
    delete rule = {
        action = { type = Delete }
        condition = {
          age          = 30
          with_state = ANY
        }
      }
    }
  }
}

Fazit

Mir gefällt die Tatsache, dass ich jetzt Objekte mit gemischten Typen definieren kann, um komplexere Strukturen zu erstellen, aber es gibt ein paar Dinge, die meiner Meinung nach verbessert werden könnten:

  • Die Art und Weise, wie Variablenvorgaben gehandhabt werden. Es wäre toll, wenn diese mit dem bereitgestellten Wert verschmolzen würden oder zumindest eine Einstellung hätten, die dieses Verhalten zulässt.
  • Sie sollten die Möglichkeit haben, optionale Schlüssel für Ihre Objekte zu definieren. Die Notwendigkeit dafür wird jedoch weniger dringend, wenn der erste Punkt erledigt ist.

Ich hoffe, dass dieser Blogbeitrag Ihnen hilft, den Objekttyp und seine Grenzen zu verstehen und Ihnen gleichzeitig eine Idee davon gibt, wie Sie ihn optimal nutzen können. In meinem nächsten Blogbeitrag werde ich Ihnen mehr über die for_each-Funktionalität erzählen und wie Sie diese in Ihren Terraform-Modulen einsetzen können.

Verfasst von

Lotte-Sara Laan

Contact

Let’s discuss how we can support your journey.