A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://developer.hashicorp.com/terraform/language/v1.6.x/expressions/type-constraints below:

Type Constraints - Configuration Language | Terraform

Terraform module authors and provider developers can use detailed type constraints to validate user-provided values for their input variables and resource arguments. This requires some additional knowledge about Terraform's type system, but allows you to build a more resilient user interface for your modules and resources.

Type constraints are expressed using a mixture of type keywords and function-like constructs called type constructors.

Type constraints look like other kinds of Terraform expressions, but are a special syntax. Within the Terraform language, they are only valid in the type argument of an input variable.

A primitive type is a simple type that isn't made from any other types. All primitive types in Terraform are represented by a type keyword. The available primitive types are:

Conversion of Primitive Types

The Terraform language will automatically convert number and bool values to string values when needed, and vice-versa as long as the string contains a valid representation of a number or boolean value.

A complex type is a type that groups multiple values into a single value. Complex types are represented by type constructors, but several of them also have shorthand keyword versions.

There are two categories of complex types: collection types (for grouping similar values), and structural types (for grouping potentially dissimilar values).

Collection Types

A collection type allows multiple values of one other type to be grouped together as a single value. The type of value within a collection is called its element type. All collection types must have an element type, which is provided as the argument to their constructor.

For example, the type list(string) means "list of strings", which is a different type than list(number), a list of numbers. All elements of a collection must always be of the same type.

The three kinds of collection type in the Terraform language are:

Structural Types

A structural type allows multiple values of several distinct types to be grouped together as a single value. Structural types require a schema as an argument, to specify which types are allowed for which elements.

The two kinds of structural type in the Terraform language are:

For example: an object type of object({ name=string, age=number }) would match a value like the following:

{
  name = "John"
  age  = 52
}

Also, an object type of object({ id=string, cidr_block=string }) would match the object produced by a reference to an aws_vpc resource, like aws_vpc.example_vpc; although the resource has additional attributes, they would be discarded during type conversion.

Finally, a tuple type of tuple([string, number, bool]) would match a value like the following:

Complex Type Literals

The Terraform language has literal expressions for creating tuple and object values, which are described in Expressions: Literal Expressions as "list/tuple" literals and "map/object" literals, respectively.

Terraform does not provide any way to directly represent lists, maps, or sets. However, due to the automatic conversion of complex types (described below), the difference between similar complex types is almost never relevant to a normal user, and most of the Terraform documentation conflates lists with tuples and maps with objects. The distinctions are only useful when restricting input values for a module or resource.

Conversion of Complex Types

Similar kinds of complex types (list/tuple/set and map/object) can usually be used interchangeably within the Terraform language, and most of Terraform's documentation glosses over the differences between the kinds of complex type. This is due to two conversion behaviors:

For example: if a module argument requires a value of type list(string) and a user provides the tuple ["a", 15, true], Terraform will internally transform the value to ["a", "15", "true"] by converting the elements to the required string element type. Later, if the module uses those elements to set different resource arguments that require a string, a number, and a bool (respectively), Terraform will automatically convert the second and third strings back to the required types at that time, since they contain valid representations of a number and a bool.

On the other hand, automatic conversion will fail if the provided value (including any of its element values) is incompatible with the required type. If an argument requires a type of map(string) and a user provides the object {name = ["Kristy", "Claudia", "Mary Anne", "Stacey"], age = 12}, Terraform will raise a type mismatch error, since a tuple cannot be converted to a string.

Warning: any is very rarely the correct type constraint to use. Do not use any just to avoid specifying a type constraint. Always write an exact type constraint unless you are truly handling dynamic data.

The keyword any is a special construct that serves as a placeholder for a type yet to be decided. any is not itself a type: when interpreting a value against a type constraint containing any, Terraform will attempt to find a single actual type that could replace the any keyword to produce a valid result.

The only situation where it's appropriate to use any is if you will pass the given value directly to some other system without directly accessing its contents. For example, it's okay to use a variable of type any if you use it only with jsonencode to pass the full value directly to a resource, as shown in the following example:

variable "settings" {
  type = any
}
 
resource "aws_s3_object" "example" {
  # ...
 
  # This is a reasonable use of "any" because this module
  # just writes any given data to S3 as JSON, without
  # inspecting it further or applying any constraints
  # to its type or value.
  content = jsonencode(var.settings)
}

If any part of your module accesses elements or attributes of the value, or expects it to be a string or number, or any other non-opaque treatment, it is incorrect to use any. Write the exact type that your module is expecting instead.

any with Collection Types

All of the elements of a collection must have the same type, so if you use any as the placeholder for the element type of a collection then Terraform will attempt to find a single exact element type to use for the resulting collection.

For example, given the type constraint list(any), Terraform will examine the given value and try to choose a replacement for the any that would make the result valid.

If the given value were ["a", "b", "c"] -- whose physical type is tuple([string, string, string]) -- Terraform analyzes this as follows:

If the elements of the given tuple are not all of the same type then Terraform will attempt to find a single type that they can all convert to. Terraform will consider various conversion rules as described in earlier sections.

Although the above examples use list(any), a similar principle applies to map(any) and set(any).

Terraform typically returns an error when it does not receive a value for specified object attributes. When you mark an attribute as optional, Terraform instead inserts a default value for the missing attribute. This allows the receiving module to describe an appropriate fallback behavior.

To mark attributes as optional, use the optional modifier in the object type constraint. The following example creates optional attribute b and optional attribute with a default value c.

variable "with_optional_attribute" {
  type = object({
    a = string                # a required attribute
    b = optional(string)      # an optional attribute
    c = optional(number, 127) # an optional attribute with default value
  })
}

The optional modifier takes one or two arguments.

An optional attribute with a non-null default value is guaranteed to never have the value null within the receiving module. Terraform will substitute the default value both when a caller omits the attribute altogether and when a caller explicitly sets it to null, thereby avoiding the need for additional checks to handle a possible null value.

Terraform applies object attribute defaults top-down in nested variable types. This means that Terraform applies the default value you specify in the optional modifier first and then later applies any nested default values to that attribute.

Example: Nested Structures with Optional Attributes and Defaults

The following example defines a variable for storage buckets that host a website. This variable type uses several optional attributes, including website, which is itself an optional object type that has optional attributes and defaults.

variable "buckets" {
  type = list(object({
    name    = string
    enabled = optional(bool, true)
    website = optional(object({
      index_document = optional(string, "index.html")
      error_document = optional(string, "error.html")
      routing_rules  = optional(string)
    }), {})
  }))
}

The following example terraform.tfvars file specifies three bucket configurations for var.buckets.

The production bucket does not specify the index and error documents, and the archived bucket omits the website configuration entirely. Terraform will use the default values specified in the bucket type constraint.

buckets = [
  {
    name = "production"
    website = {
      routing_rules = <<-EOT
      [
        {
          "Condition" = { "KeyPrefixEquals": "img/" },
          "Redirect"  = { "ReplaceKeyPrefixWith": "images/" }
        }
      ]
      EOT
    }
  },
  {
    name = "archived"
    enabled = false
  },
  {
    name = "docs"
    website = {
      index_document = "index.txt"
      error_document = "error.txt"
    }
  },
]

This configuration produces the following variable values.

tolist([
  {
    "enabled" = true
    "name" = "production"
    "website" = {
      "error_document" = "error.html"
      "index_document" = "index.html"
      "routing_rules" = <<-EOT
      [
        {
          "Condition" = { "KeyPrefixEquals": "img/" },
          "Redirect"  = { "ReplaceKeyPrefixWith": "images/" }
        }
      ]

      EOT
    }
  },
  {
    "enabled" = false
    "name" = "archived"
    "website" = {
      "error_document" = "error.html"
      "index_document" = "index.html"
      "routing_rules" = tostring(null)
    }
  },
  {
    "enabled" = true
    "name" = "docs"
    "website" = {
      "error_document" = "error.txt"
      "index_document" = "index.txt"
      "routing_rules" = tostring(null)
    }
  },
])
Example: Conditionally setting an optional attribute

Sometimes the decision about whether or not to set a value for an optional argument needs to be made dynamically based on some other data. In that case, the calling module block can use a conditional expression with null as one of its result arms to represent dynamically leaving the argument unset.

With the variable "buckets" declaration shown in the previous section, the following example conditionally overrides the index_document and error_document settings in the website object based on a new variable var.legacy_filenames:

variable "legacy_filenames" {
  type     = bool
  default  = false
  nullable = false
}

module "buckets" {
  source = "./modules/buckets"

  buckets = [
    {
      name = "maybe_legacy"
      website = {
        error_document = var.legacy_filenames ? "ERROR.HTM" : null
        index_document = var.legacy_filenames ? "INDEX.HTM" : null
      }
    },
  ]
}

When var.legacy_filenames is set to true, the call will override the document filenames. When it is false, the call will leave the two filenames unspecified, thereby allowing the module to use its specified default values.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4