19

The timestamp() function in the interpolation syntax will return an ISO 8601 formatted string, which looks like this 2019-02-06T23:22:28Z. However, I want to have a string which looks like this 20190206232240706500000001. A string with only numbers (integers) and no hyphens, white spaces, colon, Z or T. What is a simple and elegant way to achieve this?

It works if I replace every a single character class at the time hyphens, white spaces, colon Z and T:

locals {
  timestamp = "${timestamp()}"
  timestamp_no_hyphens = "${replace("${local.timestamp}", "-", "")}"
  timestamp_no_spaces = "${replace("${local.timestamp_no_hyphens}", " ", "")}"
  timestamp_no_t = "${replace("${local.timestamp_no_spaces}", "T", "")}"
  timestamp_no_z = "${replace("${local.timestamp_no_t}", "Z", "")}"
  timestamp_no_colons = "${replace("${local.timestamp_no_z}", ":", "")}"
  timestamp_sanitized = "${local.timestamp_no_colons}"
}

output "timestamp" {
  value = "${local.timestamp_sanitized}"
}

The resulting output is in the desired format, except the string is significantly shorter:

Outputs:

timestamp = 20190207000744

However, this solution is very ugly. Is there another way of doing the same thing in a more elegant way as well as producing a string with the same length as the example string 20190206232240706500000001?

Jeeppler
  • 473
  • 1
  • 5
  • 9

3 Answers3

41

Terraform 0.12.0 introduced a new function formatdate which can make this more readable:

output "timestamp" {
  value = formatdate("YYYYMMDDhhmmss", timestamp())
}

At the time of writing, formatdate's smallest supported unit is whole seconds, so this won't give exactly the same result as the regexp approach, but can work if the nearest second is accurate enough for the use-case.

Martin Atkins
  • 62,420
  • 8
  • 120
  • 138
13

The current interpolate function timestamp() has been hardcoded with output format RFC3339 in the source code:

https://github.com/hashicorp/terraform/blob/master/config/interpolate_funcs.go#L1521

return time.Now().UTC().Format(time.RFC3339), nil

So there is nothing wrong with your way, however we can improve it a little bit.

locals {
  timestamp = "${timestamp()}"
  timestamp_sanitized = "${replace("${local.timestamp}", "/[- TZ:]/", "")}"

}

Reference:

https://github.com/google/re2/wiki/Syntax

replace(string, search, replace) - Does a search and replace on the given string. All instances of search are replaced with the value of replace. If search is wrapped in forward slashes, it is treated as a regular expression. If using a regular expression, replace can reference subcaptures in the regular expression by using $n where n is the index or name of the subcapture. If using a regular expression, the syntax conforms to the re2 regular expression syntax.

BMW
  • 42,880
  • 12
  • 99
  • 116
  • Can you explain what the pipe | symbol does? Is this just an `OR` in re2 syntax? – Jeeppler Feb 07 '19 at 16:35
  • mostly. Recommend to take some Regex (regular expression) tutorial, then you can easily understand these characters' usage – BMW Feb 07 '19 at 22:47
  • thanks. There are different variations of regular expression syntax. Re2 is probably the most academic version of a Deterministic Finite Automaton (DFA) and regular language . The `|` symbol means nothing more than `or` in re2 syntax. However, what is not so clear is that the Terraform `replace` method replaces any occurrence of the regular expression string. – Jeeppler Feb 22 '19 at 19:13
  • 1
    The `/` are used to indicate the beginning of a regular expression and the `[]` group search terms together. A written explanation of `[-| |T|Z|:]` would be search for any occurrence of either single dash `-` or single space ` ` or single `T` or single `Z` or single colon `:` and replace it with nothing `""`. – Jeeppler Feb 22 '19 at 19:16
  • nice, and you can play regex at https://regex101.com/ to prove if you can get the expect result. – BMW Feb 23 '19 at 11:12
  • 2
    @Jeepler or anyone who may come by here, `[-| |T|Z|:]` is incorrect as the pipe symbol inside square brackets does not function as `OR` and will match the character `|`. The character set inside square brackets is already a choose any, so you just need `[- TZ:]` meaning you don't need the `|`. – remorath Nov 14 '19 at 15:11
6

This answer just shows an example of @BMW's answer which wasn't obvious to someone that's new to Terraform.

$ cat main.tf
locals {
  timestamp = "${timestamp()}"
  timestamp_sanitized = "${replace("${local.timestamp}", "/[-| |T|Z|:]/", "")}"

}

output "timestamp" {
  value = "${local.timestamp_sanitized}"
}

Example runs

Run #1:

$ terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

timestamp = 20190221205825

Run #2:

$ terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

timestamp = 20190221205839
slm
  • 15,396
  • 12
  • 109
  • 124