| --- |
| page_title: defaults - Functions - Configuration Language |
| description: The defaults function can fill in default values in place of null values. |
| --- |
| |
| # `defaults` Function |
| |
| -> **Note:** This function is available only in Terraform 0.15 and later. |
| |
| ~> **Experimental:** This function is part of |
| [the optional attributes experiment](/language/expressions/type-constraints#experimental-optional-object-type-attributes) |
| and is only available in modules where the `module_variable_optional_attrs` |
| experiment is explicitly enabled. |
| |
| The `defaults` function is a specialized function intended for use with |
| input variables whose type constraints are object types or collections of |
| object types that include optional attributes. |
| |
| When you define an attribute as optional and the caller doesn't provide an |
| explicit value for it, Terraform will set the attribute to `null` to represent |
| that it was omitted. If you want to use a placeholder value other than `null` |
| when an attribute isn't set, you can use the `defaults` function to concisely |
| assign default values only where an attribute value was set to `null`. |
| |
| ``` |
| defaults(input_value, defaults) |
| ``` |
| |
| The `defaults` function expects that the `input_value` argument will be the |
| value of an input variable with an exact [type constraint](/language/expressions/types) |
| (not containing `any`). The function will then visit every attribute in |
| the data structure, including attributes of nested objects, and apply the |
| default values given in the defaults object. |
| |
| The interpretation of attributes in the `defaults` argument depends on what |
| type an attribute has in the `input_value`: |
| |
| * **Primitive types** (`string`, `number`, `bool`): if a default value is given |
| then it will be used only if the `input_value`'s attribute of the same |
| name has the value `null`. The default value's type must match the input |
| value's type. |
| * **Structural types** (`object` and `tuple` types): Terraform will recursively |
| visit all of the attributes or elements of the nested value and repeat the |
| same defaults-merging logic one level deeper. The default value's type must |
| be of the same kind as the input value's type, and a default value for an |
| object type must only contain attribute names that appear in the input |
| value's type. |
| * **Collection types** (`list`, `map`, and `set` types): Terraform will visit |
| each of the collection elements in turn and apply defaults to them. In this |
| case the default value is only a single value to be applied to _all_ elements |
| of the collection, so it must have a type compatible with the collection's |
| element type rather than with the collection type itself. |
| |
| The above rules may be easier to follow with an example. Consider the following |
| Terraform configuration: |
| |
| ```hcl |
| terraform { |
| # Optional attributes and the defaults function are |
| # both experimental, so we must opt in to the experiment. |
| experiments = [module_variable_optional_attrs] |
| } |
| |
| variable "storage" { |
| type = object({ |
| name = string |
| enabled = optional(bool) |
| website = object({ |
| index_document = optional(string) |
| error_document = optional(string) |
| }) |
| documents = map( |
| object({ |
| source_file = string |
| content_type = optional(string) |
| }) |
| ) |
| }) |
| } |
| |
| locals { |
| storage = defaults(var.storage, { |
| # If "enabled" isn't set then it will default |
| # to true. |
| enabled = true |
| |
| # The "website" attribute is required, but |
| # it's here to provide defaults for the |
| # optional attributes inside. |
| website = { |
| index_document = "index.html" |
| error_document = "error.html" |
| } |
| |
| # The "documents" attribute has a map type, |
| # so the default value represents defaults |
| # to be applied to all of the elements in |
| # the map, not for the map itself. Therefore |
| # it's a single object matching the map |
| # element type, not a map itself. |
| documents = { |
| # If _any_ of the map elements omit |
| # content_type then this default will be |
| # used instead. |
| content_type = "application/octet-stream" |
| } |
| }) |
| } |
| |
| output "storage" { |
| value = local.storage |
| } |
| ``` |
| |
| To test this out, we can create a file `terraform.tfvars` to provide an example |
| value for `var.storage`: |
| |
| ```hcl |
| storage = { |
| name = "example" |
| |
| website = { |
| error_document = "error.txt" |
| } |
| documents = { |
| "index.html" = { |
| source_file = "index.html.tmpl" |
| content_type = "text/html" |
| } |
| "error.txt" = { |
| source_file = "error.txt.tmpl" |
| content_type = "text/plain" |
| } |
| "terraform.exe" = { |
| source_file = "terraform.exe" |
| } |
| } |
| } |
| ``` |
| |
| The above value conforms to the variable's type constraint because it only |
| omits attributes that are declared as optional. Terraform will automatically |
| populate those attributes with the value `null` before evaluating anything |
| else, and then the `defaults` function in `local.storage` will substitute |
| default values for each of them. |
| |
| The result of this `defaults` call would therefore be the following object: |
| |
| ``` |
| storage = { |
| "documents" = tomap({ |
| "error.txt" = { |
| "content_type" = "text/plain" |
| "source_file" = "error.txt.tmpl" |
| } |
| "index.html" = { |
| "content_type" = "text/html" |
| "source_file" = "index.html.tmpl" |
| } |
| "terraform.exe" = { |
| "content_type" = "application/octet-stream" |
| "source_file" = "terraform.exe" |
| } |
| }) |
| "enabled" = true |
| "name" = "example" |
| "website" = { |
| "error_document" = "error.txt" |
| "index_document" = "index.html" |
| } |
| } |
| ``` |
| |
| Notice that `enabled` and `website.index_document` were both populated directly |
| from the defaults. Notice also that the `"terraform.exe"` element of |
| `documents` had its `content_type` attribute populated from the `documents` |
| default, but the default value didn't need to predict that there would be an |
| element key `"terraform.exe"` because the default values apply equally to |
| all elements of the map where the optional attributes are `null`. |
| |
| ## Using `defaults` elsewhere |
| |
| The design of the `defaults` function depends on input values having |
| well-specified type constraints, so it can reliably recognize the difference |
| between similar types: maps vs. objects, lists vs. tuples. The type constraint |
| causes Terraform to convert the caller's value to conform to the constraint |
| and thus `defaults` can rely on the input to conform. |
| |
| Elsewhere in the Terraform language it's typical to be less precise about |
| types, for example using the object construction syntax `{ ... }` to construct |
| values that will be used as if they are maps. Because `defaults` uses the |
| type information of `input_value`, an `input_value` that _doesn't_ originate |
| in an input variable will tend not to have an appropriate value type and will |
| thus not be interpreted as expected by `defaults`. |
| |
| We recommend using `defaults` only with fully-constrained input variable values |
| in the first argument, so you can use the variable's type constraint to |
| explicitly distinguish between collection and structural types. |