Mastering Terraform Functions

String Functions

chomp

The chomp function removes any newline character sequence (either \n or \r\n) that appears at the very end of the given string. If the string does not end with a newline sequence, the function returns the string unchanged.

Syntax: chomp(string)

The string argument is the string you want to remove trailing newlines from.

Example:

chomp("hello\n")
# returns "hello"

chomp("hello\r\n")
# returns "hello"

chomp("hello")
# returns "hello"

chomp("hello\n\n")
# returns "hello\n"

endswith

The endswith function determines if a given string ends with a specified suffix. It returns true if the string ends with the suffix, and false otherwise. The comparison is case-sensitive.

Syntax: endswith(string, suffix)

The string argument is the string you want to check. The suffix argument is the string you are looking for at the end of the string.

Example:

endswith("hello world", "world")
# returns true

endswith("hello world", "World")
# returns false

endswith("terraform", "form")
# returns true

endswith("terraform", "cloud")
# returns false

format

The format function produces a formatted string using a format string and a variadic list of arguments. It behaves similarly to C’s sprintf or Go’s fmt.Sprintf. The format string can contain verbs that specify how subsequent arguments should be formatted.

Syntax: format(format_string, args...)

The format_string argument is a string containing zero or more formatting verbs. The args... are the values to be formatted according to the format_string. Common verbs include:

  • %s: The value is formatted as a string.
  • %d: The value is formatted as a decimal integer.
  • %f: The value is formatted as a floating-point number.
  • %t: The value is formatted as true or false for booleans.
  • %%: A literal percent sign.

Example:

format("Hello %s, your age is %d.", "Alice", 30)
# returns "Hello Alice, your age is 30."

format("The value is %.2f", 123.456)
# returns "The value is 123.46"

format("Is it true? %t", true)
# returns "Is it true? true"

format("Percent sign: %%")
# returns "Percent sign: %"

formatlist

The formatlist function produces a list of formatted strings. It takes a format string and a variadic number of lists or tuples of arguments. For each element in the input lists (or tuples), it applies the format string, producing a new string, and then collects all these new strings into a single list. This is useful for constructing lists of strings where each string follows a specific pattern based on corresponding elements from other lists.

Syntax: formatlist(format_string, args...)

The format_string argument is a string containing zero or more formatting verbs, similar to the format function. The args... are a series of lists (or tuples) where corresponding elements from each list will be used as arguments for the format_string to generate each output string. All input lists must have the same number of elements.

Example:

formatlist("Hello %s, your age is %d.", ["Alice", "Bob"], [30, 25])
# returns ["Hello Alice, your age is 30.", "Hello Bob, your age is 25."]

formatlist("Resource name: %s-%s", ["app", "db"], ["server", "instance"])
# returns ["Resource name: app-server", "Resource name: db-instance"]

formatlist("Item: %s", ["apple", "banana", "cherry"])
# returns ["Item: apple", "Item: banana", "Item: cherry"]

indent

The indent function adds a specified number of spaces to the beginning of every line in a given string, except for the first line. This is particularly useful for formatting multi-line strings, such as YAML or JSON content, to fit within a larger configuration block.

Syntax: indent(number_of_spaces, string)

The number_of_spaces argument is the integer number of spaces to add to the beginning of each line (except the first). The string argument is the multi-line string to which the indentation will be applied.

Example:

indent(2, "line1\nline2\nline3")
# returns 
# line1
#   line2
#   line3

indent(4, "resource:\n  type: aws_s3_bucket")
# returns 
# resource:
#       type: aws_s3_bucket

indent(0, "no indent here")
# returns 
# no indent here

join

The join function concatenates the elements of a given list of strings into a single string, using a specified delimiter string between each element.

Syntax: join(delimiter, list)

The delimiter argument is the string that will be inserted between each element of the list. The list argument is a list of strings to be joined.

Example:

join("-", ["a", "b", "c"])
# returns "a-b-c"

join(", ", ["apple", "banana", "cherry"])
# returns "apple, banana, cherry"

join("", ["hello", "world"])
# returns "helloworld"

join(" ", [])
# returns ""

lower

The lower function converts all uppercase letters in a given string to their corresponding lowercase letters. Other characters, such as numbers, symbols, and spaces, remain unchanged.

Syntax: lower(string)

The string argument is the string that you want to convert to lowercase.

Example:

lower("HELLO WORLD")
# returns "hello world"

lower("Terraform 1.0")
# returns "terraform 1.0"

lower("aBcDeFg")
# returns "abcdefg"

lower("123!@#")
# returns "123!@#"

replace

The replace function searches for all occurrences of a specified substring within a given string and replaces them with another specified replacement string.

Syntax: replace(string, substring, replacement)

The string argument is the original string in which replacements will occur. The substring argument is the string to be found and replaced. The replacement argument is the string that will be inserted in place of each substring occurrence.

Example:

replace("hello world", "world", "terraform")
# returns "hello terraform"

replace("foo bar foo baz", "foo", "qux")
# returns "qux bar qux baz"

replace("one two three", "two", "")
# returns "one  three"

replace("apple", "p", "x")
# returns "axle"

split

The split function divides a given string into a list of substrings using a specified delimiter. Each occurrence of the delimiter marks a new element in the resulting list.

Syntax: split(delimiter, string)

The delimiter argument is the string that will be used to break the input string into parts. The string argument is the string to be split.

Example:

split("-", "a-b-c")
# returns ["a", "b", "c"]

split(",", "apple,banana,cherry")
# returns ["apple", "banana", "cherry"]

split(" ", "hello world")
# returns ["hello", "world"]

split("", "terraform")
# returns ["t", "e", "r", "r", "a", "f", "o", "r", "m"]

split("-", "no_delimiter_here")
# returns ["no_delimiter_here"]

startswith

The startswith function determines if a given string begins with a specified prefix. It returns true if the string starts with the prefix, and false otherwise. The comparison is case-sensitive.

Syntax: startswith(string, prefix)

The string argument is the string you want to check. The prefix argument is the string you are looking for at the beginning of the string.

Example:

startswith("hello world", "hello")
# returns true

startswith("Hello world", "hello")
# returns false

startswith("terraform", "ter")
# returns true

startswith("aws_instance", "aws_")
# returns true

strcontains

The strcontains function checks if a given string contains a specified substring. It returns true if the substring is found anywhere within the string, and false otherwise. The comparison is case-sensitive.

Syntax: strcontains(string, substring)

The string argument is the string to search within. The substring argument is the string you are looking for.

Example:

strcontains("hello world", "world")
# returns true

strcontains("Hello World", "world")
# returns false

strcontains("terraform", "form")
# returns true

strcontains("aws_s3_bucket", "s3")
# returns true

strcontains("my string", "xyz")
# returns false

strrev

The strrev function returns a new string with the characters of the given string in reverse order.

Syntax: strrev(string)

The string argument is the string to be reversed.

Example:

strrev("hello")
# returns "olleh"

strrev("terraform")
# returns "mrofarret"

strrev("12345")
# returns "54321"

strrev("")
# returns ""

substr

The substr function extracts a portion of a string. It returns a substring starting at a specified offset and extending for a given length.

Syntax: substr(string, offset, length)

The string argument is the original string from which to extract the substring. The offset argument is the starting position of the substring (0-indexed). The length argument is the number of characters to include in the substring. If the offset plus length exceeds the string length, the function returns the characters from the offset to the end of the string.

Example:

substr("hello world", 0, 5)
# returns "hello"

substr("hello world", 6, 5)
# returns "world"

substr("terraform", 4, 3)
# returns "for"

substr("abcdef", 2, 10)
# returns "cdef" (length exceeds string, returns to end)

substr("example", 0, 0)
# returns ""

title

The title function converts the first letter of each word in a given string to uppercase, and all subsequent letters in each word to lowercase. Words are typically delimited by spaces.

Syntax: title(string)

The string argument is the string to be converted to title case.

Example:

title("hello world")
# returns "Hello World"

title("terraform provider aws")
# returns "Terraform Provider Aws"

title("this is A TEST string")
# returns "This Is A TEST String"

title("123 test")
# returns "123 Test"

trim

The trim function removes all leading and trailing whitespace characters (spaces, tabs, newlines, etc.) from a given string.

Syntax: trim(string)

The string argument is the string from which leading and trailing whitespace should be removed.

Example:

trim("  hello world  ")
# returns "hello world"

trim("\n\tterraform\n")
# returns "terraform"

trim("no_leading_or_trailing_spaces")
# returns "no_leading_or_trailing_spaces"

trim(" leading and trailing ")
# returns "leading and trailing"

trimprefix

The trimprefix function removes a specified prefix from the beginning of a string. If the string does not start with the given prefix, the function returns the original string unchanged.

Syntax: trimprefix(string, prefix)

The string argument is the string from which the prefix should be removed. The prefix argument is the string that will be removed if it appears at the beginning of the string.

Example:

trimprefix("prefix_hello", "prefix_")
# returns "hello"

trimprefix("terraform", "ter")
# returns "raform"

trimprefix("aws_instance", "google_")
# returns "aws_instance"

trimprefix("another_example", "another_example")
# returns ""

trimsuffix

The trimsuffix function removes a specified suffix from the end of a string. If the string does not end with the given suffix, the function returns the original string unchanged.

Syntax: trimsuffix(string, suffix)

The string argument is the string from which the suffix should be removed. The suffix argument is the string that will be removed if it appears at the end of the string.

Example:

trimsuffix("hello_suffix", "_suffix")
# returns "hello"

trimsuffix("terraform", "form")
# returns "terra"

trimsuffix("aws_s3_bucket", "_instance")
# returns "aws_s3_bucket"

trimsuffix("example.txt", ".txt")
# returns "example"

trimspace

The trimspace function is an alias for the trim function. It removes all leading and trailing whitespace characters (spaces, tabs, newlines, etc.) from a given string.

Syntax: trimspace(string)

The string argument is the string from which leading and trailing whitespace should be removed.

Example:

trimspace("  hello world  ")
# returns "hello world"

trimspace("\n\tterraform\n")
# returns "terraform"

trimspace("no_leading_or_trailing_spaces")
# returns "no_leading_or_trailing_spaces"

trimspace(" leading and trailing ")
# returns "leading and trailing"

upper

The upper function converts all lowercase letters in a given string to their corresponding uppercase letters. Other characters, such as numbers, symbols, and spaces, remain unchanged.

Syntax: upper(string)

The string argument is the string that you want to convert to uppercase.

Example:

upper("hello world")
# returns "HELLO WORLD"

upper("Terraform 1.0")
# returns "TERRAFORM 1.0"

upper("aBcDeFg")
# returns "ABCDEFG"

upper("123!@")
# returns "123!@"

Numeric Functions

abs

The abs function returns the absolute value of the given number. If the number is zero or positive, the function returns the value as-is, but if it is negative, it is multiplied by -1 to make it positive before returning it1.

Syntax: abs(number)

The number argument is the number you want the absolute value of. The number can be an integer or floating point.

Example:

abs(26)
# returns 26

abs(0)
# returns 0

abs(-17.1)
# returns 17.1

ceil

The ceil function computes the smallest integer value that is greater than or equal to the given number. This effectively rounds the number up to the nearest whole number.

Syntax: ceil(number)

The number argument is the number you want to round up. It can be an integer or floating point.

Example:

ceil(3.14)
# returns 4

ceil(5.0)
# returns 5

ceil(-2.7)
# returns -2

ceil(0.0)
# returns 0

floor

The floor function computes the largest integer value that is less than or equal to the given number. This effectively rounds the number down to the nearest whole number.

Syntax: floor(number)

The number argument is the number you want to round down. It can be an integer or floating point.

Example:

floor(3.8)
# returns 3

floor(5.0)
# returns 5

floor(-2.1)
# returns -3

floor(0.0)
# returns 0

log

The log function computes the logarithm of a given number in a specified base.

Syntax: log(number, base)

The number argument is the value for which you want to calculate the logarithm. The base argument is the base of the logarithm. Both number and base must be positive.

Example:

log(50, 10)
# returns 1.6989700043360185

log(16, 2)
# returns 4

ceil(log(15, 2))
# returns 4

ceil(log(17, 2))
# returns 5

max

The max function returns the greatest of the given numbers. It can accept any number of arguments, which can be provided individually or by expanding a list or set of numbers using the ... operator. All arguments must be of a number type (integer or float).

Syntax: max(number1, number2, ...) or max(list_or_set_of_numbers...)

The number1, number2, etc., are the numbers among which you want to find the maximum value. If using a list or set, list_or_set_of_numbers is a collection whose elements will be expanded into individual arguments.

Example:

max(10, 30, 20)
# returns 30

max(5.5, 2.1, 8.9)
# returns 8.9

max(-5, -1, -10)
# returns -1

max([1, 5, 2]...)
# returns 5

max(7)
# returns 7

min

The min function returns the smallest of the given numbers. It can accept any number of arguments, which can be provided individually or by expanding a list or set of numbers using the ... operator. All arguments must be of a number type (integer or float).

Syntax: min(number1, number2, ...) or min(list_or_set_of_numbers...)

The number1, number2, etc., are the numbers among which you want to find the minimum value. If using a list or set, list_or_set_of_numbers is a collection whose elements will be expanded into individual arguments.

Example:

min(10, 30, 20)
# returns 10

min(5.5, 2.1, 8.9)
# returns 2.1

min(-5, -1, -10)
# returns -10

min([1, 5, 2]...)
# returns 1

min(7)
# returns 7

parseint

The parseint function converts a string representation of a number into its integer equivalent, interpreting the string according to a specified numerical base. This is particularly useful when dealing with values that might be encoded in binary, hexadecimal, or other bases. The base must be an integer between 2 and 62, inclusive.

Syntax: parseint(string, base)

The string argument is the text you wish to convert into an integer. The base argument specifies the numerical system (e.g., 2 for binary, 10 for decimal, 16 for hexadecimal) that the string is represented in.

Example:

parseint("7B", 16)
# returns 123

parseint("1101", 2)
# returns 13

parseint("42", 10)
# returns 42

parseint("-F", 16)
# returns -15

pow

The pow function calculates the result of raising a base number to a specified exponent. It returns the power of the base number.

Syntax: pow(base, exponent)

The base argument is the number that will be multiplied by itself. The exponent argument is the number of times the base will be multiplied by itself. Both arguments can be integers or floating-point numbers.

Example:

pow(2, 3)
# returns 8 (2 * 2 * 2)

pow(5, 2)
# returns 25 (5 * 5)

pow(4, 0.5)
# returns 2 (square root of 4)

pow(10, -1)
# returns 0.1

signum

The signum function returns an integer indicating the sign of the given number. It returns 1 for positive numbers, -1 for negative numbers, and 0 for zero.

Syntax: signum(number)

The number argument is the value whose sign you want to determine. It can be an integer or a floating-point number.

Example:

signum(12.5)
# returns 1

signum(-7)
# returns -1

signum(0)
# returns 0

signum(999)
# returns 1

Collection Functions

alltrue

The alltrue function checks if all elements in a given collection (list or set) of boolean values are true. It returns true if every element is true, or if the collection is empty. It returns false if at least one element is false.

Syntax: alltrue(collection)

The collection argument is a list or set containing boolean values.

Example:

alltrue([true, true, true])
# returns true

alltrue([true, false, true])
# returns false

alltrue([])
# returns true

alltrue(toset([true, true, true]))
# returns true

alltrue(toset([true, false]))
# returns false

anytrue

The anytrue function checks if at least one element in a given collection (list or set) of boolean values is true. It returns true if one or more elements are true. It returns false if all elements are false, or if the collection is empty.

Syntax: anytrue(collection)

The collection argument is a list or set containing boolean values.

Example:

anytrue([false, false, true])
# returns true

anytrue([false, false, false])
# returns false

anytrue([])
# returns false

anytrue(toset([true, false]))
# returns true

anytrue(toset([false, false]))
# returns false

chunklist

The chunklist function divides a single list into multiple sub-lists (or “chunks”), each containing a specified maximum number of elements. The last chunk may contain fewer elements if the total number of elements in the original list is not evenly divisible by the chunk size.

Syntax: chunklist(list, size)

The list argument is the original list that you want to divide into smaller chunks. The size argument is an integer specifying the maximum number of elements each new sub-list should contain.

Example:

chunklist(["a", "b", "c", "d", "e", "f"], 2)
# returns [["a", "b"], ["c", "d"], ["e", "f"]]

chunklist(["one", "two", "three", "four", "five"], 3)
# returns [["one", "two", "three"], ["four", "five"]]

chunklist([1, 2, 3], 5)
# returns [[1, 2, 3]]

chunklist([], 2)
# returns []

coalesce

The coalesce function returns the first non-null argument from a series of arguments. It evaluates arguments in order and returns the value of the first argument that is not null. If all arguments are null, it returns null. This function is useful for providing fallback values when a primary value might be undefined.

Syntax: coalesce(arg1, arg2, ...)

The arg1, arg2, etc., are the values to be evaluated. They can be of any type.

Example:

coalesce(null, "fallback_value", "another_option")
# returns "fallback_value"

coalesce("primary_value", null, "secondary_value")
# returns "primary_value"

coalesce(null, null, 123)
# returns 123

coalesce(null, null, null)
# returns null

coalescelist

The coalescelist function returns the first list from a series of arguments that is not empty. It evaluates arguments in order and returns the value of the first argument that is a non-empty list. If all arguments are null or empty lists, it returns an empty list. This function is useful for providing fallback lists.

Syntax: coalescelist(list1, list2, ...)

The list1, list2, etc., are the lists to be evaluated. They should primarily be lists, but null values are also handled.

Example:

coalescelist([], ["a", "b"], ["c"])
# returns ["a", "b"]

coalescelist(["primary"], [], ["secondary"])
# returns ["primary"]

coalescelist([], [], null, ["final"])
# returns ["final"]

coalescelist(null, [], [])
# returns []

compact

The compact function takes a list of strings and returns a new list with all empty strings and null values removed. It preserves the order of the non-empty, non-null strings.

Syntax: compact(list)

The list argument is a list of strings that you want to filter.

Example:

compact(["a", "", "b", null, "c"])
# returns ["a", "b", "c"]

compact(["", "empty", null, " "])
# returns ["empty", " "]

compact(["only", "valid", "items"])
# returns ["only", "valid", "items"]

compact([null, "", null])
# returns []

concat

The concat function concatenates two or more lists into a single new list. The order of elements from the input lists is preserved, with elements from subsequent lists appended to the end of the preceding ones.

Syntax: concat(list1, list2, ...)

The list1, list2, etc., are the lists that you want to join together. All arguments must be lists of elements of the same type.

Example:

concat(["a", "b"], ["c", "d"])
# returns ["a", "b", "c", "d"]

concat([1, 2], [3], [4, 5])
# returns [1, 2, 3, 4, 5]

concat([], ["item"])
# returns ["item"]

concat(["first"], [])
# returns ["first"]

concat([1, 2], [3], [4, 5], [3,6])
# returns [1, 2, 3, 4, 5, 3, 6]

contains

The contains function checks if a given list contains a specific value. It returns true if the value is found in the list, and false otherwise. The comparison is type-sensitive, meaning that a number 5 is different from a string “5”.

Syntax: contains(list, value)

The list argument is the list to be searched. The value argument is the specific value you are looking for within the list.

Example:

contains(["apple", "banana", "cherry"], "banana")
# returns true

contains([10, 20, 30], 25)
# returns false

contains(["a", "b", "c"], "A")
# returns false (case-sensitive)

contains(["true", false], true)
# returns false (type-sensitive)

distinct

The distinct function takes a list and returns a new list containing only the unique elements from the original list. The order of the elements in the returned list is not guaranteed to be the same as the original, but generally, it preserves the first occurrence’s position.

Syntax: distinct(list)

The list argument is the list from which you want to extract unique elements.

Example:

distinct(["a", "b", "a", "c", "b"])
# returns ["a", "b", "c"] (order may vary in practice)

distinct([1, 2, 2, 3, 1])
# returns [1, 2, 3] (order may vary in practice)

distinct(["apple", "orange", "apple"])
# returns ["apple", "orange"] (order may vary in practice)

distinct(["unique", "items"])
# returns ["unique", "items"]

element

The element function retrieves a single element from a list at a specified zero-based index. If the index is out of bounds (i.e., less than 0 or greater than or equal to the list’s length), Terraform will produce an error.

Syntax: element(list, index)

The list argument is the list from which you want to retrieve an element. The index argument is the zero-based position of the desired element within the list.

Example:

element(["red", "green", "blue"], 1)
# returns "green"

element([10, 20, 30, 40], 0)
# returns 10

element(["single_item"], 0)
# returns "single_item"

flatten

The flatten function takes a list of lists (or a list containing nested lists) and converts it into a single, flat list containing all the elements from the nested lists. It can handle multiple levels of nesting, flattening them all into one dimension.

Syntax: flatten(list)

The list argument is a list that may contain other lists as its elements, and you wish to combine all elements into a single flat list.

Example:

flatten([["a", "b"], ["c", "d"]])
# returns ["a", "b", "c", "d"]

flatten([["one"], ["two", "three"], ["four"]])
# returns ["one", "two", "three", "four"]

flatten([["x", ["y", "z"]], "p"])
# returns ["x", "y", "z", "p"] (handles nested lists)

flatten([])
# returns []

index

The index function finds the index of the first occurrence of a given value within a list. It returns the zero-based index if the value is found. If the value is not found in the list, Terraform will produce an error. This function is an alias for element when a single value is returned based on index, but index is more typically used for searching a value’s position.

Syntax: index(list, value)

The list argument is the list to be searched. The value argument is the specific value you are looking for.

Example:

index(["apple", "banana", "cherry"], "banana")
# returns 1

index([10, 20, 30, 20], 20)
# returns 1 (returns the first occurrence's index)

index(["red", "green", "blue"], "red")
# returns 0

keys

The keys function takes a map, object, or tuple of objects and returns a list of all the keys (or attribute names) present in that collection. The order of the keys in the returned list is not guaranteed and may vary.

Syntax: keys(collection)

The collection argument is the map, object, or tuple of objects from which you want to extract the keys or attribute names.

Example:

keys({"name" = "Alice", "age" = 30, "city" = "New York"})
# returns ["name", "age", "city"] (order may vary)

keys({
  instance = {
    id   = "i-123"
    type = "t2.micro"
  }
})
# returns ["instance"]

keys({})
# returns []

length

The length function returns the number of elements in a given collection (list, map, set, or tuple), the number of characters in a string, or the number of bytes in an opaque object (like a file content).

Syntax: length(collection_or_string)

The collection_or_string argument is the list, map, set, tuple, or string whose length you want to determine.

Example:

length(["a", "b", "c"])
# returns 3

length({"name" = "Bob", "id" = "xyz"})
# returns 2

length("hello")
# returns 5

length(toset([1, 2, 3, 2]))
# returns 3 (sets only count unique items)

length([])
# returns 0

lookup

The lookup function retrieves the value associated with a given key from a map or object. If the specified key does not exist in the map, it returns a supplied default value instead of producing an error. This makes it a safer alternative to direct map access when a key might be optional.

Syntax: lookup(map, key, default)

The map argument is the map or object from which you want to retrieve a value. The key argument is the string key whose value you want to look up. The default argument is the value to return if the key is not found in the map.

Example:

lookup({"name" = "Alice", "age" = 30}, "name", "N/A")
# returns "Alice"

lookup({"name" = "Alice", "age" = 30}, "city", "Unknown")
# returns "Unknown"

lookup({"env" = "dev"}, "env", "prod")
# returns "dev"

lookup({}, "anything", "default_value")
# returns "default_value"

matchkeys

The matchkeys function reorders and filters a list of values based on a corresponding list of keys, so that the values align with a specified desired order of keys. It is particularly useful for synchronizing two lists that represent keys and their corresponding values when the order or presence of items in one list needs to dictate the other.

Syntax: matchkeys(values_list, keys_list, desired_keys_list)

The values_list argument is a list of values that you want to reorder or filter. The keys_list argument is a list of keys that corresponds positionally to the values_list. The desired_keys_list argument is the list of keys in the order you want the values_list to be returned, also implicitly filtering for only the keys present in this list.

Example:

matchkeys(
  ["v1", "v2", "v3"],
  ["k1", "k2", "k3"],
  ["k3", "k1"]
)
# returns ["v3", "v1"] (values for k3 then k1)

matchkeys(
  ["apple", "banana", "cherry"],
  ["A", "B", "C"],
  ["C", "A", "D"]
)
# returns ["cherry", "apple"] (D is ignored as it has no corresponding value)

matchkeys(
  [10, 20],
  ["small", "large"],
  ["large", "small"]
)
# returns [20, 10]

merge

The merge function takes two or more maps or objects and combines them into a single new map or object. If multiple input maps contain the same key, the value from the rightmost (last) map in the argument list takes precedence.

Syntax: merge(map1, map2, ...)

The map1, map2, etc., are the maps or objects you want to combine.

Example:

merge({"a" = 1, "b" = 2}, {"b" = 3, "c" = 4})
# returns {"a" = 1, "b" = 3, "c" = 4}

merge({"name" = "Alice"}, {"city" = "New York"}, {"age" = 30})
# returns {"name" = "Alice", "city" = "New York", "age" = 30}

merge({"env" = "dev", "debug" = true}, {"debug" = false})
# returns {"env" = "dev", "debug" = false}

merge({}, {"key" = "value"})
# returns {"key" = "value"}

range

The range function generates a list of numbers. It can produce sequences in a few ways:

  • With one argument, it creates a list of integers from 0 up to (but not including) that number.
  • With two arguments, it creates a list of integers from the first number up to (but not including) the second number.
  • With three arguments, it creates a list of integers starting from the first, up to (but not including) the second, with a step increment defined by the third argument.

Syntax:

  • range(limit)
  • range(lower, upper)
  • range(lower, upper, step)

The arguments limit, lower, upper, and step must be integers.

Example:

range(5)
# returns [0, 1, 2, 3, 4]

range(2, 7)
# returns [2, 3, 4, 5, 6]

range(1, 10, 2)
# returns [1, 3, 5, 7, 9]

range(5, 0, -1)
# returns [5, 4, 3, 2, 1]

range(0)
# returns []

reverse

The reverse function takes a list and returns a new list with the elements in reverse order.

Syntax: reverse(list)

The list argument is the list whose elements you want to reverse.

Example:

reverse(["a", "b", "c"])
# returns ["c", "b", "a"]

reverse([1, 2, 3, 4, 5])
# returns [5, 4, 3, 2, 1]

reverse(["single"])
# returns ["single"]

reverse([])
# returns []

setintersection

The setintersection function computes the intersection of two or more sets. It returns a new set containing only the elements that are present in all of the input sets.

Syntax: setintersection(set1, set2, ...)

The set1, set2, etc., are the sets for which you want to find the intersection. All arguments must be sets.

Example:

setintersection(toset(["a", "b", "c"]), toset(["b", "c", "d"]))
# returns toset(["b", "c"])

setintersection(toset([1, 2, 3]), toset([3, 4, 5]), toset([2, 3, 6]))
# returns toset([3])

setintersection(toset(["apple", "banana"]), toset(["orange"]))
# returns toset([])

setintersection(toset(["only_one"]))
# returns toset(["only_one"])

setproduct

The setproduct function computes the Cartesian product of two or more sets. It returns a set of lists, where each list contains one element from each of the input sets, covering all possible combinations. The order of elements within the inner lists corresponds to the order of the input sets.

Syntax: setproduct(set1, set2, ...)

The set1, set2, etc., are the sets for which you want to compute the Cartesian product. All arguments must be sets.

Example:

setproduct(toset(["a", "b"]), toset(["1", "2"]))
# returns toset([["a", "1"], ["a", "2"], ["b", "1"], ["b", "2"]])

setproduct(toset(["red"]), toset(["small", "large"]), toset(["circle", "square"]))
# returns toset([
#   ["red", "small", "circle"],
#   ["red", "small", "square"],
#   ["red", "large", "circle"],
#   ["red", "large", "square"]
# ])

setproduct(toset([1, 2]), toset([]))
# returns toset([])

setproduct(toset(["item"]))
# returns toset([["item"]])

setsubtract

The setsubtract function computes the set difference between two sets. It returns a new set containing all the elements from the first set that are not present in the second set.

Syntax: setsubtract(set1, set2)

The set1 argument is the set from which elements will be subtracted. The set2 argument is the set containing elements to be removed from set1. Both arguments must be sets.

Example:

setsubtract(toset(["a", "b", "c"]), toset(["b", "d"]))
# returns toset(["a", "c"])

setsubtract(toset([1, 2, 3, 4]), toset([3, 4, 5]))
# returns toset([1, 2])

setsubtract(toset(["apple", "banana"]), toset(["apple", "banana"]))
# returns toset([])

setsubtract(toset(["full"]), toset([]))
# returns toset(["full"])

setunion

The setunion function computes the union of two or more sets. It returns a new set containing all unique elements that are present in any of the input sets. Duplicate elements across the input sets will only appear once in the resulting set.

Syntax: setunion(set1, set2, ...)

The set1, set2, etc., are the sets for which you want to compute the union. All arguments must be sets.

Example:

setunion(toset(["a", "b"]), toset(["b", "c", "d"]))
# returns toset(["a", "b", "c", "d"])

setunion(toset([1, 2]), toset([3, 4]), toset([2, 5]))
# returns toset([1, 2, 3, 4, 5])

setunion(toset(["apple", "banana"]), toset([]))
# returns toset(["apple", "banana"])

setunion(toset(["only"]))
# returns toset(["only"])

slice

The slice function extracts a portion of a list, creating a new list that includes elements from a specified starting index up to (but not including) a specified ending index.

Syntax: slice(list, start_index, end_index)

The list argument is the original list from which you want to extract a slice. The start_index argument is the zero-based index of the first element to include in the new list. The end_index argument is the zero-based index after the last element to include. If start_index is negative, it counts from the end of the list. If end_index is negative, it counts from the end of the list. If start_index is greater than or equal to end_index, an empty list is returned.

Example:

slice(["a", "b", "c", "d", "e"], 1, 4)
# returns ["b", "c", "d"]

slice([10, 20, 30, 40, 50], 0, 2)
# returns [10, 20]

slice(["one", "two", "three"], 1, 1)
# returns []

slice(["red", "green", "blue"], 0, 5)
# returns ["red", "green", "blue"] (end_index beyond length returns to end)

slice(["x", "y", "z"], -2, 3)
# returns ["y", "z"] (start_index from end)

sort

The sort function sorts the elements of a given list of strings in lexicographical (alphabetical) order. It returns a new list with the sorted elements. This function is designed for lists of strings; attempting to sort lists with mixed types or numbers directly may lead to unexpected results or errors.

Syntax: sort(list)

The list argument is the list of strings that you want to sort.

Example:

sort(["c", "a", "b"])
# returns ["a", "b", "c"]

sort(["apple", "cherry", "banana"])
# returns ["apple", "banana", "cherry"]

sort(["10", "2", "1"])
# returns ["1", "10", "2"] (lexicographical sort, not numeric)

sort([])
# returns []

sum

The sum function calculates the sum of all elements in a given collection of numbers (list or set). It can handle both integers and floating-point numbers.

Syntax: sum(collection)

The collection argument is a list or set containing numbers (integers or floats) that you want to add together.

Example:

sum([1, 2, 3, 4])
# returns 10

sum([10.5, 20.0, 5.5])
# returns 36.0

sum(toset([7, 3, 5]))
# returns 15

sum([])
# returns 0

transpose

The transpose function takes a map of lists and reorganizes it by swapping keys and values, effectively “transposing” the data. It’s useful for converting data structures where keys represent categories and values are lists of items into a structure where items become keys and categories become values, or vice-versa, allowing for different ways to group and access data.

The function expects a map where each value is a list of strings. It returns a map of lists where the original values (from the inner lists) become the new top-level keys, and the original top-level keys become the elements in the new inner lists.

Syntax: transpose(map_of_lists)

The map_of_lists argument is a map where all values are lists of strings of the same length.

Example:

transpose({
  "role_a" = ["user1", "user2"],
  "role_b" = ["user3", "user1"]
})
# returns {
#   "user1" = ["role_a", "role_b"],
#   "user2" = ["role_a"],
#   "user3" = ["role_b"]
# }

transpose({
  "server_group_1" = ["server_a", "server_b"],
  "server_group_2" = ["server_c", "server_d"]
})
# returns {
#   "server_a" = ["server_group_1"],
#   "server_b" = ["server_group_1"],
#   "server_c" = ["server_group_2"],
#   "server_d" = ["server_group_2"]
# }

values

The values function takes a map or object and returns a list containing all the values (or attribute values) from that collection. The order of the values in the returned list is not guaranteed and may vary.

Syntax: values(collection)

The collection argument is the map or object from which you want to extract the values or attribute values.

Example:

values({"name" = "Bob", "age" = 25, "city" = "London"})
# returns ["Bob", 25, "London"] (order may vary)

values({
  item = {
    id    = "xyz"
    price = 99.99
  }
})
# returns [{"id" = "xyz", "price" = 99.99}]

values({})
# returns []

zipmap

The zipmap function creates a new map by combining two lists: one list provides the keys, and the other provides the corresponding values. Both lists must be of the same length.

Syntax: zipmap(keys_list, values_list)

The keys_list argument is a list of strings that will be used as the keys for the new map. The values_list argument is a list of values (of any type) that will be used as the corresponding values in the new map.

Example:

zipmap(["name", "age", "city"], ["Alice", 30, "New York"])
# returns {"name" = "Alice", "age" = 30, "city" = "New York"}

zipmap(["id", "type"], ["i-12345", "t2.micro"])
# returns {"id" = "i-12345", "type" = "t2.micro"}

zipmap(["key1", "key2"], [100, true])
# returns {"key1" = 100, "key2" = true}

zipmap([], [])
# returns {}

Filesystem Functions

abspath

The abspath function converts a given path into an absolute path. This means it resolves any relative components (like . or ..) and ensures the path starts from the root directory of the filesystem. The result is a canonical, absolute path.

Syntax: abspath(path)

The path argument is the path string you want to convert to an absolute path.

Example:

# Assuming the current working directory is /home/user/project
abspath("module/child.tf")
# returns "/home/user/project/module/child.tf"

abspath("../another_dir/file.txt")
# returns "/home/user/another_dir/file.txt"

abspath("/var/log/app.log")
# returns "/var/log/app.log"

abspath("./current_file.txt")
# returns "/home/user/project/current_file.txt"

dirname

The dirname function extracts the directory portion of a given path. It effectively removes the last element (file or directory name) from the path string.

Syntax: dirname(path)

The path argument is the path string from which you want to extract the directory name.

Example:

dirname("/home/user/documents/file.txt")
# returns "/home/user/documents"

dirname("module/sub_module/")
# returns "module"

dirname("my_file.txt")
# returns "." (current directory)

dirname("/var/log/")
# returns "/var/log"

pathexpand

The pathexpand function expands a path string that contains a tilde (~) into an absolute path, replacing the tilde with the current user’s home directory. If the path does not start with a tilde, it is returned unchanged.

Syntax: pathexpand(path)

The path argument is the string path that you want to expand.

Example:

# Assuming the current user's home directory is /home/developer
pathexpand("~/configs/app.conf")
# returns "/home/developer/configs/app.conf"

pathexpand("~")
# returns "/home/developer"

pathexpand("/var/log/sys.log")
# returns "/var/log/sys.log" (no tilde, so path is unchanged)

pathexpand("~user/project") # Note: This form is typically not expanded by pathexpand in Terraform,
                             # as it's specifically for the *current* user's home directory.
                             # It would likely return "~user/project" itself.
# returns "~user/project"

basename

The basename function extracts the last element (file or directory name) from a given path. It effectively removes the directory prefix and any trailing slashes from the path string.

Syntax: basename(path)

The path argument is the path string from which you want to extract the base name.

Example:

basename("/home/user/documents/report.pdf")
# returns "report.pdf"

basename("module/sub_module/")
# returns "sub_module"

basename("/var/log")
# returns "log"

basename("my_file.txt")
# returns "my_file.txt"

basename("/")
# returns "/"

file

The file function reads the contents of a file at the given path and returns them as a string. The path is interpreted relative to the current working directory of the Terraform configuration. This function is typically used to embed file content directly into a resource argument, such as a script, a configuration file, or user data for a virtual machine.

Syntax: file(path)

The path argument is a string representing the path to the file you want to read.

Example:

# Assuming a file named 'script.sh' exists in the current directory with content "echo 'Hello, Terraform!'"
file("script.sh")
# returns "echo 'Hello, Terraform!'"

# Assuming a file named 'config/settings.txt' exists
file("config/settings.txt")
# returns the content of settings.txt as a string

# Using an absolute path (though relative is more common in Terraform configs)
file("/etc/hosts")
# returns the content of the /etc/hosts file (if accessible)

fileexists

The fileexists function checks if a file or directory exists at the given path. It returns true if a file or directory is found at the specified path, and false otherwise. The path is interpreted relative to the current working directory of the Terraform configuration.

Syntax: fileexists(path)

The path argument is a string representing the path to the file or directory you want to check for existence.

Example:

# Assuming a file named 'main.tf' exists in the current directory
fileexists("main.tf")
# returns true

# Assuming a directory named 'modules' exists in the current directory
fileexists("modules")
# returns true

# Assuming 'non_existent_file.txt' does not exist
fileexists("non_existent_file.txt")
# returns false

# Checking for a path that might be a file or directory
fileexists("README.md")
# returns true or false depending on its existence

fileset

The fileset function returns a set of paths to files that match a given pattern within a specified directory. The paths are relative to the directory containing the Terraform configuration, and the pattern uses the standard glob syntax (e.g., * for any sequence of characters, ? for any single character, ** for any directory path recursively).

Syntax: fileset(path, pattern)

The path argument is a string representing the base directory to search within. The pattern argument is a glob pattern string to match against file paths.

Example:

# Assuming the following structure:
# .
# └── templates/
#     ├── config.tpl
#     ├── server.conf
#     └── web/
#         └── index.html

fileset("templates", "*.tpl")
# returns toset(["templates/config.tpl"])

fileset("templates", "**/*.html")
# returns toset(["templates/web/index.html"])

fileset("templates", "*")
# returns toset(["templates/config.tpl", "templates/server.conf"])

fileset("non_existent_dir", "*.txt")
# returns toset([])

filebase64

The filebase64 function reads the contents of a file at the given path and then encodes those contents as a Base64 string. This is commonly used when a service API expects file contents to be Base64-encoded, such as user data for cloud instances. The path is interpreted relative to the current working directory of the Terraform configuration.

Syntax: filebase64(path)

The path argument is a string representing the path to the file whose contents you want to read and Base64 encode.

Example:

# Assuming a file named 'script.sh' exists with content "echo 'Hello!'"
filebase64("script.sh")
# returns "ZWNobyAnSGVsbG8hJw=="

# For an empty file
filebase64("empty.txt")
# returns ""

# Using it with a hypothetical YAML config file
# filebase64("config/app_config.yaml")
# returns a Base64 encoded string of app_config.yaml's content

templatefile:

The templatefile function reads the content of a file as a template, processes it using a simple template syntax, and returns the rendered content as a string. This function allows for dynamic generation of file contents by injecting variables into a template. The template syntax supports variable interpolation (${var_name}) and simple for loops and if conditionals.

Syntax: templatefile(path, vars)

The path argument is a string representing the path to the template file. The vars argument is a map of key-value pairs that will be available as variables within the template.

Example:

Given a template file named instance.conf.tpl with the following content:

server {
  listen ${port};
  hostname = "${hostname}";
  % for user in users ~}
  allow user ${user};
  % endfor ~}
}

And a Terraform configuration:

Terraform

locals {
  rendered_config = templatefile("${path.module}/instance.conf.tpl", {
    port     = 8080
    hostname = "web-server-1"
    users    = ["admin", "dev"]
  })
}

output "config_output" {
  value = local.rendered_config
}

Result of output "config_output":

server {
  listen 8080;
  hostname = "web-server-1";
  allow user admin;
  allow user dev;
}

Date & Time Functions

formatdate

The formatdate function converts a timestamp string into a different date and time format. It takes an input timestamp and a format string, and returns a new string representing the timestamp in the desired format. The input timestamp should be in RFC3339 format (e.g., YYYY-MM-DDTHH:MM:SSZ).

Syntax: formatdate(format_string, timestamp)

The format_string argument is a string that specifies the desired output format using a subset of the Go time.Format function layout constants. Common format characters include:

  • YYYY: four-digit year
  • MM: two-digit month (01-12)
  • DD: two-digit day of month (01-31)
  • HH: two-digit hour (00-23)
  • MM: two-digit minute (00-59)
  • SS: two-digit second (00-59)
  • Z: UTC offset (e.g., +00:00 or Z for zero offset)

The timestamp argument is the input timestamp string in RFC3339 format.

Example:

formatdate("YYYY-MM-DD HH:MM:SS", "2025-06-29T17:55:00Z")
# returns "2025-06-29 17:55:00"

formatdate("DD-MM-YYYY", "2025-01-15T09:00:00Z")
# returns "15-01-2025"

formatdate("HH:MM", "2025-07-04T22:30:00+05:30")
# returns "22:30"

formatdate("YYYYMMDD", "2024-12-31T00:00:00Z")
# returns "20241231"

plantimestamp

The plantimestamp function returns the current date and time as a string, formatted according to RFC 3339. This timestamp represents the exact moment when the Terraform plan was started. It’s useful for scenarios where you need to mark resources or logs with a consistent time of plan execution.

Syntax: plantimestamp()

This function takes no arguments.

Example:

# The exact return value will depend on when the Terraform plan is executed.
# If the plan runs at, for example, 2025-06-29 18:00:00 UTC
plantimestamp()
# returns "2025-06-29T18:00:00Z" (example value; precise second and timezone will vary)

timeadd

The timeadd function adds a specified duration to a given timestamp and returns the new timestamp as an RFC3339-formatted string. This is useful for calculating future or past dates and times relative to a starting point.

Syntax: timeadd(timestamp, duration)

The timestamp argument is the starting timestamp string, which must be in RFC3339 format (e.g., 2025-06-29T17:55:00Z). The duration argument is a string representing the amount of time to add, using units like ns (nanoseconds), us (microseconds), ms (milliseconds), s (seconds), m (minutes), h (hours), d (days), and M (months). You can combine units (e.g., “1h30m”).

Example:

timeadd("2025-06-29T12:00:00Z", "24h")
# returns "2025-06-30T12:00:00Z" (adding 24 hours)

timeadd("2025-06-29T10:00:00Z", "7d")
# returns "2025-07-06T10:00:00Z" (adding 7 days)

timeadd("2025-06-29T15:00:00Z", "2h30m")
# returns "2025-06-29T17:30:00Z" (adding 2 hours and 30 minutes)

timeadd("2025-06-01T00:00:00Z", "1M")
# returns "2025-07-01T00:00:00Z" (adding 1 month)

timeadd("2025-06-29T17:56:18Z", "-1h") # subtracting 1 hour from current time
# returns "2025-06-29T16:56:18Z"

timecmp

The timecmp function compares two timestamps and returns an integer indicating their relative order. This is useful for conditional logic or sorting based on time.

Syntax: timecmp(timestamp1, timestamp2)

The timestamp1 and timestamp2 arguments are the two timestamp strings to compare. Both timestamps must be in RFC3339 format (e.g., YYYY-MM-DDTHH:MM:SSZ).

The function returns:

  • -1 if timestamp1 is earlier than timestamp2
  • 0 if timestamp1 is the same as timestamp2
  • 1 if timestamp1 is later than timestamp2

Example:

timecmp("2025-06-29T10:00:00Z", "2025-06-29T12:00:00Z")
# returns -1

timecmp("2025-07-01T00:00:00Z", "2025-06-30T23:59:59Z")
# returns 1

timecmp("2025-06-29T15:30:00Z", "2025-06-29T15:30:00Z")
# returns 0

timecmp("2025-01-01T00:00:00Z", "2024-12-31T23:59:59Z")
# returns 1

timestamp

The timestamp function returns the current date and time as a string, formatted according to RFC 3339. This timestamp represents the exact moment the function is called during the Terraform execution. This can be slightly different from plantimestamp if a long time passes between plan and apply.

Syntax: timestamp()

This function takes no arguments.

Example:

# The exact return value will depend on when this function is evaluated during Terraform's apply phase.
# If the function is called at, for example, 2025-06-29 17:57:12 UTC
timestamp()
# returns "2025-06-29T17:57:12Z" (example value; precise second and timezone will vary based on execution time)

Encoding Functions

base64decode

The base64decode function decodes a Base64-encoded string back into its original form. This is the inverse of base64encode and filebase64. It’s commonly used to retrieve original content that was stored or passed as a Base64-encoded string, such as encrypted secrets or user data.

Syntax: base64decode(string)

The string argument is the Base64-encoded string that you want to decode.

Example:

base64decode("SGVsbG8sIFdvcmxkIQ==")
# returns "Hello, World!"

base64decode("Zm9vYmFy")
# returns "foobar"

base64decode("")
# returns ""

base64encode

The base64encode function encodes a given string into a Base64 format. Base64 encoding is often used to transmit binary data over mediums that primarily handle text, ensuring data integrity. The resulting string is suitable for embedding in formats that might not directly support arbitrary binary data.

Syntax: base64encode(string)

The string argument is the input string that you want to encode into Base64.

Example:

base64encode("Hello, Terraform!")
# returns "SGVsbG8sIFRlcnJhZm9ybSE="

base64encode("username:password")
# returns "dXNlcm5hbWU6cGFzc3dvcmQ="

base64encode("")
# returns ""

base64gzip

The base64gzip function first compresses a given string using gzip compression and then encodes the resulting compressed binary data into a Base64 string. This is useful for reducing the size of data before transmitting or storing it, especially when dealing with larger strings like user data scripts or configuration files, and when the receiving system expects Base64-encoded compressed data.

Syntax: base64gzip(string)

The string argument is the input string that you want to compress and then Base64 encode.

Example:

# The exact Base64 string will vary slightly due to compression metadata,
# but the decompressed result will be the original string.
base64gzip("This is a long string that will benefit from gzip compression.")
# returns "H4sIAAAAAAAA/yvPLy4pLkksSk5OTUlLTvRLzs9xSsvPS0zNzEvOzysuAQDD4aL2IAAAAA==" (example Base64)

base64gzip("short")
# returns "H4sIAAAAAAAA/yvLLC4pAQD8494yBAAAAA==" (example Base64)

base64gzip("")
# returns "H4sIAAAAAAAA/wEAAP//AAAAAAAAAAA=" (example Base64 for an empty string)

csvdecode

The csvdecode function parses a CSV (Comma Separated Values) formatted string and converts it into a list of objects. Each object in the list represents a row from the CSV, and its attributes correspond to the column headers from the first row of the CSV.

Syntax: csvdecode(string)

The string argument is the CSV-formatted string that you want to parse. The first line of the CSV string is treated as the header row, defining the keys for the objects in the resulting list.

Example:

csvdecode("name,age,city\nAlice,30,New York\nBob,25,London")
# returns [
#   {"name" = "Alice", "age" = "30", "city" = "New York"},
#   {"name" = "Bob", "age" = "25", "city" = "London"}
# ]

csvdecode("product,price\nLaptop,1200.50\nMouse,25")
# returns [
#   {"product" = "Laptop", "price" = "1200.50"},
#   {"product" = "Mouse", "price" = "25"}
# ]

csvdecode("header1,header2")
# returns [{"header1" = "", "header2" = ""}] (for a CSV with only headers and an empty data row)

csvdecode("")
# returns []

jsondecode

The jsondecode function parses a JSON (JavaScript Object Notation) formatted string and converts it into a native Terraform value, such as an object, list, string, number, or boolean. This is useful for integrating data from external systems or APIs that provide output in JSON format.

Syntax: jsondecode(string)

The string argument is the JSON-formatted string that you want to parse.

Example:

jsondecode("{\"name\": \"Alice\", \"age\": 30, \"isActive\": true}")
# returns {
#   "name" = "Alice",
#   "age" = 30,
#   "isActive" = true
# }

jsondecode("[1, 2, \"three\", null]")
# returns [1, 2, "three", null]

jsondecode("\"hello world\"")
# returns "hello world"

jsondecode("true")
# returns true

jsondecode("{}")
# returns {}

jsonencode

The jsonencode function converts a native Terraform value (such as an object, list, string, number, or boolean) into a JSON-formatted string. This is the inverse of jsondecode and is commonly used when Terraform needs to pass complex data structures to external systems or APIs that expect JSON input, or to store complex data in a string attribute.

Syntax: jsonencode(value)

The value argument is the Terraform value that you want to convert into a JSON string.

Example:

jsonencode({"name" = "Bob", "age" = 28, "roles" = ["admin", "user"]})
# returns "{\"name\":\"Bob\",\"age\":28,\"roles\":[\"admin\",\"user\"]}"

jsonencode(["item1", {"key" = "value"}, 123])
# returns "[\"item1\",{\"key\":\"value\"},123]"

jsonencode("a plain string")
# returns "\"a plain string\""

jsonencode(true)
# returns "true"

jsonencode(null)
# returns "null"

textdecodebase64

The textdecodebase64 function decodes a Base64-encoded string into a plain text string, interpreting the bytes according to a specified character encoding. This is particularly useful when dealing with strings that might not always be UTF-8, or when explicitly ensuring a specific encoding is used during decoding.

Syntax: textdecodebase64(string, encoding)

The string argument is the Base64-encoded string that you want to decode. The encoding argument is a string specifying the character encoding to use for the decoded bytes (e.g., “UTF-8”, “UTF-16LE”, “windows-1252”).

Example:

textdecodebase64("SGVsbG8gRnJvbSBUZXJyYWZvcm0h", "UTF-8")
# returns "Hello From Terraform!"

# Example with a different encoding (though UTF-8 is most common in practice)
# Assuming a Base64 string that, when decoded, represents "éà" in ISO-8859-1
textdecodebase64("6WDD", "ISO-8859-1")
# returns "éà"

textdecodebase64("", "UTF-8")
# returns ""

textencodebase64

The textencodebase64 function encodes a given plain text string into a Base64 string, first converting the text into bytes using a specified character encoding. This function is useful when you need to explicitly define the text encoding before Base64 encoding, especially for strings containing non-ASCII characters, ensuring consistent interpretation.

Syntax: textencodebase64(string, encoding)

The string argument is the input text string that you want to encode. The encoding argument is a string specifying the character encoding to use when converting the text to bytes (e.g., “UTF-8”, “UTF-16LE”, “windows-1252”).

Example:

textencodebase64("Hello From Terraform!", "UTF-8")
# returns "SGVsbG8gRnJvbSBUZXJyYWZvcm0h"

# Example with non-ASCII characters and UTF-8 encoding
textencodebase64("你好世界", "UTF-8") # "Hello World" in Chinese
# returns "5L2g5aW95L2g5aW9"

# Example with a different encoding (use with caution, UTF-8 is dominant)
# textencodebase64("éà", "ISO-8859-1")
# returns "6WDD" (example Base64 representation)

textencodebase64("", "UTF-8")
# returns ""

urlencode

The urlencode function encodes a string for use in a URL query string. This means it replaces characters that are not allowed or have special meaning in a URL with their percent-encoded equivalents (e.g., spaces become %20). This is crucial for constructing valid URLs when passing dynamic data as query parameters.

Syntax: urlencode(string)

The string argument is the text that you want to encode for a URL.

Example:

urlencode("hello world")
# returns "hello%20world"

urlencode("param with / slashes & ampersands")
# returns "param%20with%20%2F%20slashes%20%26%20ampersands"

urlencode("user@example.com")
# returns "user%40example.com"

urlencode("key=value with spaces")
# returns "key%3Dvalue%20with%20spaces"

yamldecode

The yamldecode function parses a YAML (YAML Ain’t Markup Language) formatted string and converts it into a native Terraform value, such as an object (map), list, string, number, or boolean. This is particularly useful for integrating data from configuration files written in YAML, which is often more human-readable than JSON.

Syntax: yamldecode(string)

The string argument is the YAML-formatted string that you want to parse.

Example:

yamldecode("
name: Alice
age: 30
isActive: true
hobbies:
  - reading
  - hiking
")
# returns {
#   "name" = "Alice",
#   "age" = 30,
#   "isActive" = true,
#   "hobbies" = ["reading", "hiking"]
# }

yamldecode("[item1, item2, {key: value}]")
# returns ["item1", "item2", {"key" = "value"}]

yamldecode("just a string")
# returns "just a string"

yamldecode("true")
# returns true

yamldecode("{}")
# returns {}

yamlencode

The yamlencode function converts a native Terraform value (such as an object, list, string, number, or boolean) into a YAML-formatted string. This is the inverse of yamldecode and is commonly used when Terraform needs to generate configuration files in YAML format, or to pass complex data structures to external systems that expect YAML input.

Syntax: yamlencode(value)

The value argument is the Terraform value that you want to convert into a YAML string.

Example:

yamlencode({
  name = "Bob"
  age  = 28
  roles = ["admin", "user"]
  settings = {
    debug = true
    level = "info"
  }
})
# returns a multi-line string like:
# "age: 28
# name: Bob
# roles:
# - admin
# - user
# settings:
#   debug: true
#   level: info"

yamlencode(["item1", {"key" = "value"}, 123])
# returns a multi-line string like:
# "- item1
# - key: value
# - 123"

yamlencode("a plain string")
# returns "\"a plain string\"" (strings that need quotes in YAML will get them)

yamlencode(true)
# returns "true"

yamlencode(null)
# returns "null"

IP Networking Functions

cidrhost

The cidrhost function calculates a full IPv4 host address for a given host number within a CIDR prefix. It’s commonly used to assign specific IP addresses from a defined network block.

Syntax: cidrhost(prefix, hostnum)

The prefix argument is a string containing an IPv4 address prefix in CIDR notation (e.g., “10.0.0.0/16”). The hostnum argument is an integer representing the host number within that prefix. Host number 0 typically refers to the network address itself, and the largest host number refers to the broadcast address. Therefore, the first usable host IP is usually hostnum 1, and the last usable is hostnum (total hosts – 2).

Example:

cidrhost("10.0.0.0/8", 0) # Network address
# returns "10.0.0.0"

cidrhost("10.0.0.0/16", 1) # First usable host IP in the /16 network
# returns "10.0.0.1"

cidrhost("192.168.1.0/24", 10)
# returns "192.168.1.10"

cidrhost("172.16.0.0/20", 254)
# returns "172.16.0.254"

# Example calculating the broadcast address for a /24
# A /24 network has 2^8 = 256 addresses.
# Host number 255 is the broadcast address.
cidrhost("192.168.1.0/24", 255)
# returns "192.168.1.255"

cidrnetmask

The cidrnetmask function converts a CIDR prefix length into an IPv4 dotted-decimal netmask. This is useful for systems or configurations that expect a subnet mask in the traditional format rather than CIDR prefix length.

Syntax: cidrnetmask(prefix_length)

The prefix_length argument is an integer representing the CIDR prefix length (e.g., 24 for /24). It must be an integer between 0 and 32, inclusive.

Example:

cidrnetmask(24)
# returns "255.255.255.0"

cidrnetmask(16)
# returns "255.255.0.0"

cidrnetmask(8)
# returns "255.0.0.0"

cidrnetmask(27)
# returns "255.255.255.224"

cidrnetmask(32)
# returns "255.255.255.255"

cidrnetmask(0)
# returns "0.0.0.0"

cidrsubnet

The cidrsubnet function calculates a CIDR subnet within a given IP address prefix. This is commonly used to divide a larger network block into smaller, non-overlapping subnets, which is a fundamental task in network design.

Syntax: cidrsubnet(prefix, new_bits, netnum)

The prefix argument is a string containing an IPv4 address prefix in CIDR notation (e.g., “10.0.0.0/16”). The new_bits argument is an integer representing the number of additional bits to use for the new subnet mask (i.e., how many bits to borrow from the host portion of the original prefix). This value increases the prefix length and thus makes the subnet smaller. The netnum argument is an integer representing the desired subnet number among the possible subnets created by the new_bits.

Example:

# Original prefix: 10.0.0.0/16 (16 bits for network, 16 for host)
# new_bits: 8 (take 8 more bits, making it a /24)
# netnum: 0 (the first /24 subnet)
cidrsubnet("10.0.0.0/16", 8, 0)
# returns "10.0.0.0/24"

# Original prefix: 10.0.0.0/16
# new_bits: 8
# netnum: 1 (the second /24 subnet)
cidrsubnet("10.0.0.0/16", 8, 1)
# returns "10.0.1.0/24"

# Original prefix: 192.168.0.0/20
# new_bits: 4 (making it a /24)
# netnum: 5 (the sixth /24 subnet, as netnum is 0-indexed)
cidrsubnet("192.168.0.0/20", 4, 5)
# returns "192.168.5.0/24"

# Original prefix: 172.16.0.0/22 (22 bits for network, 10 for host)
# new_bits: 3 (making it a /25)
# netnum: 0
cidrsubnet("172.16.0.0/22", 3, 0)
# returns "172.16.0.0/25"

# Original prefix: 172.16.0.0/22
# new_bits: 3
# netnum: 1
cidrsubnet("172.16.0.0/22", 3, 1)
# returns "172.16.0.128/25"

cidrsubnets

The cidrsubnets function calculates a list of non-overlapping CIDR subnets from a given IP address prefix. Unlike cidrsubnet which returns a single subnet, cidrsubnets generates multiple subnets simultaneously, making it ideal for provisioning an array of network blocks.

Syntax: cidrsubnets(prefix, new_bits_1, new_bits_2, ...)

The prefix argument is a string containing an IPv4 address prefix in CIDR notation (e.g., “10.0.0.0/16”). The new_bits_1, new_bits_2, etc., are integers representing the number of additional bits to use for each new subnet’s mask. For each new_bits argument provided, a corresponding subnet will be returned. Terraform will find the next available subnet number for each requested size.

Example:

# Original prefix: 10.0.0.0/16
# Requesting three subnets: one /20, one /20, and one /24
cidrsubnets("10.0.0.0/16", 4, 4, 8)
# returns [
#   "10.0.0.0/20",   # first /20 subnet
#   "10.0.16.0/20",  # second /20 subnet, immediately following the first
#   "10.0.32.0/24"   # first available /24 subnet after the /20s
# ]

# Original prefix: 192.168.0.0/20
# Requesting two /24 subnets
cidrsubnets("192.168.0.0/20", 4, 4)
# returns [
#   "192.168.0.0/24",
#   "192.168.1.0/24"
# ]

# Original prefix: 172.16.0.0/16
# Requesting one /18 and one /20
cidrsubnets("172.16.0.0/16", 2, 4)
# returns [
#   "172.16.0.0/18",
#   "172.16.64.0/20"
# ]

# If no new_bits are provided, it's an error. At least one new_bits argument is expected.
# cidrsubnets("10.0.0.0/16")
# This would cause an error.

Type Conversion Functions

can

The can function evaluates a given expression and returns a boolean value (true or false) indicating whether the expression successfully produced a result without any errors during its evaluation. This function is particularly useful for handling situations where a certain value or attribute might not exist, allowing for graceful error handling within your configuration.

Syntax: can(expression)

The expression argument is any Terraform expression that you want to evaluate.

Example:

locals {
  # Example map where "bar" exists, but "boop" does not
  my_map = {
    "bar" = "baz"
  }

  # Check if my_map.bar can be evaluated without error
  can_access_bar = can(local.my_map.bar)

  # Check if my_map.boop can be evaluated without error
  can_access_boop = can(local.my_map.boop)

  # Attempt to reference a non-existent local value (will result in an error during planning,
  # but 'can' can be used on expressions that would dynamically error)
  # can_access_nonexist = can(local.nonexist)
}

output "can_access_bar_output" {
  value = local.can_access_bar
}

output "can_access_boop_output" {
  value = local.can_access_boop
}

Results of the example:

  • output "can_access_bar_output" will return true
  • output "can_access_boop_output" will return false

Important Note: The can function will not catch errors related to constructs that are demonstrably invalid even before dynamic evaluation, such as syntactical errors or references to undeclared top-level objects (e.g., can(local.nonexist) where nonexist is never declared will still produce a planning error). It is designed to handle errors that occur during the runtime evaluation of an expression.

ephemeralasnull

The ephemeralasnull function takes an ephemeral value as input and returns null. This function is useful for explicitly nullifying ephemeral values, including those nested within complex data structures like objects or maps. It can be used to ensure that ephemeral data (which is not intended to be persisted or sensitive) is not inadvertently exposed or used where only persistent values are expected. This function is available in Terraform v1.10 and later.

Syntax: ephemeralasnull(value)

The value argument is the ephemeral value you wish to convert to null. This value typically comes from a variable or local that has been marked as ephemeral = true.

Example:

variable "session_token" {
  type      = string
  default   = "some-secret-token"
  ephemeral = true # Mark this variable as ephemeral
}

variable "user_config" {
  type = map(string)
  default = {
    "env" = "production"
    "region" = "us-east-1"
  }
}

locals {
  # Merge a non-ephemeral map with an ephemeral value
  merged_settings = merge(
    var.user_config,
    {
      "token" = var.session_token
    }
  )

  # Use ephemeralasnull on the ephemeral variable directly
  nullified_token = ephemeralasnull(var.session_token)

  # Use ephemeralasnull on the merged map to nullify any nested ephemeral values
  sanitized_settings = ephemeralasnull(local.merged_settings)
}

output "nullified_token_output" {
  value = local.nullified_token
  description = "Shows the ephemeral token as null."
}

output "sanitized_settings_output" {
  value = local.sanitized_settings
  description = "Shows the merged settings with ephemeral values nullified."
}

Results of the example (during terraform apply):

Outputs:

nullified_token_output = tostring(null) # Explicitly null because var.session_token is ephemeral
sanitized_settings_output = {
  "env"    = "production"
  "region" = "us-east-1"
  "token"  = tostring(null) # The ephemeral "token" from merged_settings becomes null
}

issensitive

The issensitive function inspects a given value and returns true if Terraform treats that value as sensitive, and false otherwise. A value is considered sensitive if it originates from an input variable explicitly marked as sensitive = true, or if it is the result of an expression where one or more of its inputs are sensitive. Sensitive values are redacted from Terraform’s console output and state files to protect confidential information. This function is available in Terraform v1.8 and later.

Syntax: issensitive(value)

The value argument is any Terraform value whose sensitivity you want to check.

Example:

# Assuming var.my_sensitive_value is declared with sensitive = true
# issensitive(var.my_sensitive_value)
# returns true

issensitive("a plain string")
# returns false

issensitive(sensitive("this is a secret"))
# returns true

issensitive(123)
# returns false

issensitive(true)
# returns false

issensitive(["item1", sensitive("secret_item")])
# returns true (if any part of a collection is sensitive, the collection is sensitive)

issensitive({"key" = "value", "secret" = sensitive("my_password")})
# returns true (if any part of an object is sensitive, the object is sensitive)

nonsensitive

The nonsensitive function takes a sensitive value and returns a copy of that value with the sensitive marking removed, thus revealing the value in outputs and state. This function is typically used when a sensitive value (e.g., a password or token) needs to be exposed for a very specific, controlled purpose where its sensitivity is no longer relevant or has been handled by another mechanism (e.g., being passed to a local script that consumes it immediately). This function is available in Terraform v0.15 and later.

Syntax: nonsensitive(value)

The value argument is the sensitive Terraform value you want to treat as non-sensitive. It will produce an error if the input value is not marked as sensitive, as its use would be redundant.

Example:

# Assuming local.my_sensitive_token is a sensitive value
# nonsensitive(local.my_sensitive_token)
# returns "actual_secret_token_value"

nonsensitive(sensitive("explicitly_made_sensitive"))
# returns "explicitly_made_sensitive"

# nonsensitive("a_plain_string")
# Error: Invalid function argument
# Invalid value for "value" parameter: the given value is not sensitive, so this call is redundant.

# nonsensitive(123)
# Error: Invalid function argument
# Invalid value for "value" parameter: the given value is not sensitive, so this call is redundant.

sensitive

The sensitive function takes any Terraform value and returns a copy of that value explicitly marked as sensitive. This has the same effect as marking an input variable as sensitive = true: the value will be redacted from Terraform’s console output (plan and apply) and from the state file. This function is useful for explicitly treating derived or computed values as sensitive, even if their direct inputs were not inherently marked as such. This function is available in Terraform v0.15 and later.

Syntax: sensitive(value)

The value argument is any Terraform value that you want to mark as sensitive.

Example:

sensitive("my_database_password")
# returns <sensitive>

sensitive(12345)
# returns <sensitive>

sensitive(true)
# returns <sensitive>

sensitive(["secret_item_1", "secret_item_2"])
# returns <sensitive>

sensitive({"api_key" = "abc", "secret_id" = "xyz"})
# returns <sensitive>

tobool

The tobool function converts its argument to a boolean value (true or false). This explicit type conversion is generally not needed for most operations in Terraform, as the language often performs automatic type coercion when necessary. However, tobool can be useful for normalizing data types, particularly when processing module outputs or data from external sources where the exact type might be uncertain.

Syntax: tobool(value)

The value argument is the expression or literal that you want to convert to a boolean.

  • Strings "true" and "false" (case-insensitive) are converted to true and false respectively.
  • The boolean values true and false are returned as themselves.
  • null is converted to null.
  • Any other string or a number will result in an error.

Example:

tobool(true)
# returns true

tobool(false)
# returns false

tobool("true")
# returns true

tobool("False")
# returns false

tobool(null)
# returns null

tobool("yes")
# Error: Invalid function argument
# Invalid value for "v" parameter: cannot convert "yes" to bool: only the strings "true" or "false" are allowed.

tobool(1)
# Error: Invalid function argument
# Invalid value for "v" parameter: cannot convert number to bool.

tolist

The tolist function converts its argument to a list value. If the argument is already a list, it is returned unchanged. If the argument is a set, it returns a list containing all the elements of the set. The order of elements in the resulting list when converting from a set is not guaranteed. Explicit type conversions like tolist are rarely necessary in Terraform because it performs automatic type conversion where required. Use them primarily to normalize types returned by module outputs.

Syntax: tolist(value)

The value argument is the expression or literal that you want to convert to a list.

Example:

tolist(toset(["apple", "banana", "cherry"]))
# returns ["apple", "banana", "cherry"] (order not guaranteed)

tolist(["a", "b", "c"])
# returns ["a", "b", "c"]

tolist(null)
# returns null

# tolist("hello")
# Error: Invalid function argument
# Invalid value for "v" parameter: cannot convert string to list of string.

# tolist({"key" = "value"})
# Error: Invalid function argument
# Invalid value for "v" parameter: cannot convert object to list of string.

tomap

The tomap function converts its argument to an object (map) value. If the argument is already an object, it is returned unchanged. If the argument is a null value, it is also returned unchanged. This function is typically used for explicit type conversion, especially when data structures might have ambiguous types (e.g., from dynamic inputs or external data sources).

Syntax: tomap(value)

The value argument is the expression or literal that you want to convert to an object (map). The input value must be representable as a map; typically, this means it should be a known object value. Other types, such as lists, numbers, or booleans, cannot be directly converted to maps and will result in an error.

Example:

tomap({"name" = "Alice", "age" = 30})
# returns {"name" = "Alice", "age" = 30}

tomap(null)
# returns null

# tomap(["a", "b", "c"])
# Error: Invalid function argument
# Invalid value for "v" parameter: cannot convert list of string to object.

# tomap("hello")
# Error: Invalid function argument
# Invalid value for "v" parameter: cannot convert string to object.

tonumber

The tonumber function converts its argument to a number. If the argument is already a number (either integer or floating-point), it is returned unchanged. If the argument is a string that represents a valid number, it will be converted. This function is useful for ensuring that values are treated numerically, especially when they might originate as strings from inputs or external data sources.

Syntax: tonumber(value)

The value argument is the expression or literal that you want to convert to a number.

  • Strings containing valid integer or floating-point numbers are converted.
  • Numbers (integers or floats) are returned as themselves.
  • null is converted to null.
  • Any other string (e.g., “hello”) or a boolean will result in an error.

Example:

tonumber(123)
# returns 123

tonumber(3.14)
# returns 3.14

tonumber("45")
# returns 45

tonumber("7.89")
# returns 7.89

tonumber(null)
# returns null

# tonumber("abc")
# Error: Invalid function argument
# Invalid value for "v" parameter: cannot convert string to number.

# tonumber(true)
# Error: Invalid function argument
# Invalid value for "v" parameter: cannot convert bool to number.

toset

The toset function converts its argument to a set value. A set is an unordered collection of unique elements. If the argument is already a set, it is returned unchanged. If the argument is a list or a tuple, it returns a set containing all the unique elements from that collection; duplicate values are automatically removed. The order of elements in the resulting set is not guaranteed. Explicit type conversions like toset are rarely necessary in Terraform as it often performs automatic type conversion. Use them primarily to normalize types returned by module outputs or to ensure uniqueness of elements.

Syntax: toset(value)

The value argument is the expression or literal that you want to convert to a set.

Example:

toset(["a", "b", "c"])
# returns toset(["a", "b", "c"]) (order not guaranteed)

toset(["a", "b", "a", "c"]) # Duplicate "a" is removed
# returns toset(["a", "b", "c"]) (order not guaranteed)

toset(toset(["x", "y"]))
# returns toset(["x", "y"]) (order not guaranteed)

toset(null)
# returns null

# toset({"key" = "value"})
# Error: Invalid function argument
# Invalid value for "v" parameter: cannot convert object to set of string.

# toset("hello")
# Error: Invalid function argument
# Invalid value for "v" parameter: cannot convert string to set of string.

tostring

The tostring function converts its argument to a string value. This is a very flexible conversion function, as most Terraform primitive types (numbers, booleans, null) and some complex types can be meaningfully represented as strings. When converting a complex type (like a list or object), it typically produces a string representation similar to how it would appear in Terraform’s console output.

Syntax: tostring(value)

The value argument is the expression or literal that you want to convert to a string.

Example:

tostring(123)
# returns "123"

tostring(3.14)
# returns "3.14"

tostring(true)
# returns "true"

tostring(false)
# returns "false"

tostring(null)
# returns tostring(null)

tostring(["a", "b", "c"])
# returns Invalid value for "v" parameter: cannot convert tuple to string.

tostring({"key" = "value", "num" = 1})
# returns Invalid value for "v" parameter: cannot convert object to string.

type

The type function returns a string representing the type of its argument. This function is useful for inspecting values, particularly when dealing with dynamic data from external sources or complex expressions, to understand their underlying data type. The returned string will be one of the primitive type names (e.g., “string”, “number”, “bool”, “list”, “map”, “set”, “tuple”, “object”, “null”).

Syntax: type(value)

The value argument is the expression or literal whose type you want to determine.

Example:

type("hello")
# returns "string"

type(123)
# returns "number"

type(3.14)
# returns "number"

type(true)
# returns "bool"

type(null)
# returns "null"

type(["a", "b"])
# returns "list"

type({"key" = "value"})
# returns "object"

type(toset(["x", "y"]))
# returns "set"

try

The try function evaluates multiple expressions in sequence and returns the result of the first expression that does not produce an error. This is particularly useful for handling situations where data structures might have varying shapes or optional attributes, especially when integrating with external systems that return data in formats like JSON or YAML where fields might be absent. It allows for a graceful fallback mechanism.

Syntax: try(expr1, expr2, ...)

The expr1, expr2, ... arguments are the expressions that try will attempt to evaluate, in the order they are provided. It proceeds to the next expression only if the current one results in an error.

Example:

# Example 1: Normalizing data from an external system with potentially missing fields
locals {
  # Assume 'example.yaml' contains:
  # name: "MyService"
  # # groups might be missing, or could be null
  # groups: ["admins", "users"]

  # raw_value would be the parsed content of example.yaml
  # For demonstration, let's represent a possible scenario where groups might be null or missing:
  raw_value_scenario_1 = {
    name = "ServiceA"
    groups = ["group1", "group2"]
  }
  raw_value_scenario_2 = {
    name = "ServiceB"
    groups = null # explicit null
  }
  raw_value_scenario_3 = {
    name = "ServiceC"
    # groups field entirely missing
  }

  normalized_value_1 = {
    name   = tostring(try(local.raw_value_scenario_1.name, null))   # try(ServiceA, null) # returns "ServiceA"
    groups = try(local.raw_value_scenario_1.groups, []) # try(["group1", "group2"], []) # returns ["group1", "group2"]
  }

  normalized_value_2 = {
    name   = tostring(try(local.raw_value_scenario_2.name, null))   # try(ServiceB, null) # returns "ServiceB"
    groups = try(local.raw_value_scenario_2.groups, []) # try(null, []) # returns []
  }

  normalized_value_3 = {
    name   = tostring(try(local.raw_value_scenario_3.name, null))   # try(ServiceC, null) # returns "ServiceC"
    groups = try(local.raw_value_scenario_3.groups, []) # try(error_accessing_missing_groups, []) # returns []
  }
}

# Example 2: Handling different forms of input for a variable
variable "flexible_input" {
  type = any
  # default = "single_string" # Scenario 1
  # default = ["list_item_1", "list_item_2"] # Scenario 2
  # default = null # Scenario 3
}

locals {
  # This 'try' block attempts to normalize the flexible_input variable.
  # If flexible_input is a single string, it puts it in a list.
  # If flexible_input is already a list, it converts it to a list (no-op or cast).
  # If neither, it defaults to an empty list.
  normalized_flexible_input = try(
    [tostring(var.flexible_input)],      # First attempt: treat as single string, wrap in list
    tolist(var.flexible_input),          # Second attempt: treat as a list directly
    []                                   # Fallback: empty list
  )
}

Author

Debjeet Bhowmik

Experienced Cloud & DevOps Engineer with hands-on experience in AWS, GCP, Terraform, Ansible, ELK, Docker, Git, GitLab, Python, PowerShell, Shell, and theoretical knowledge on Azure, Kubernetes & Jenkins. In my free time, I write blogs on ckdbtech.com

Leave a Comment