Где видны эти функции
Любое HCL-выражение может вызывать функцию: внутри resource, data, variable.default, locals, output. Синтаксис обычный: func(arg1, arg2).
Все строковые функции чистые, не делают сетевых запросов, не пишут на диск, выполняются на этапе plan. Это значит, их можно класть в любые выражения без побочных эффектов.
format, как printf
Шаблон + аргументы. Возвращает форматированную строку.
format("user-%s-%03d", "alice", 7) # "user-alice-007"format("%.2f USD", 19.999) # "19.99 USD"format("https://%s:%d", "api.example", 8080) # "https://api.example:8080"Полезные модификаторы:
%s, строка%d, целое число%f,%.2f, float, два знака после точки%t, bool%v, что угодно (как есть)
Есть ещё formatlist, применяет format к массиву:
formatlist("https://%s.example.com", ["api", "www", "admin"])# ["https://api.example.com", "https://www.example.com", "https://admin.example.com"]
join и split
Превращение массива в строку и обратно.
join(", ", ["a", "b", "c"]) # "a, b, c"join("/", ["my", "bucket", "path"]) # "my/bucket/path"split(",", "us-east-1,us-east-2,eu-central-1")# ["us-east-1", "us-east-2", "eu-central-1"]
Типичный кейс, собрать список CIDR'ов в строку для description:
description = "Allowed: ${join(", ", var.cidrs)}"replace, подстрока или regex
Две формы. По умолчанию, подстрока:
replace("hello-world", "-", "_") # "hello_world"С regex, поиск/замена паттерна. Признак regex, символ / в начале и конце:
replace("v1.2.3-rc.4", "/-rc\\.\\d+$/", "") # "v1.2.3"replace("ARN-12345-XXX", "/[0-9]+/", "###") # "ARN-###-XXX"Группы захвата с $N:
replace("user@example.com", "/^(.+)@(.+)$/", "$1 at $2") # "user at example.com"Если regex невалидный, terraform validate упадёт с сообщением. Сначала отлаживайте в terraform console.
regex и regexall
Достать совпадение, а не заменять:
regex("[0-9]+", "version-42-build-7") # "42", первое совпадениеregexall("[0-9]+", "version-42-build-7") # ["42", "7"], всеПолезно когда нужно вытащить число из имени ресурса или AMI.
lower, upper, title
lower("AWS") # "aws"upper("us-east-1") # "US-EAST-1"title("hello world") # "Hello World"Частый случай, нормализация имён ресурсов:
bucket = lower("${var.project}-${var.env}-${random_id.suffix.hex}")# AWS не любит верхний регистр в bucket names
trim и его варианты
Убрать символы из начала/конца:
trimspace(" hello ") # "hello" , пробелы и переводы строкtrim("xxxhelloxxx", "x") # "hello" , символы x с обеих сторонtrimprefix("https://api.example", "https://") # "api.example"trimsuffix("logs.json", ".json") # "logs"substr, подстрока
substr("hello world", 0, 5) # "hello"substr("abc-12345", 4, 5) # "12345"Третий аргумент, длина, не индекс конца.
length для строки
Хоть length обычно про коллекции, для строки он считает символы (не байты!):
length("hello") # 5length("привет") # 6Это важно при работе с UTF-8 ограничениями AWS (bucket name 3-63 символов).
Конкатенация: лучше interpolation, чем +
В HCL нет оператора + для строк. Используйте interpolation:
# Так не работает:
# "hello" + "world"
# Так, да:
"${var.a}-${var.b}"Если конкатенация сложная, выносите в locals (см. tf-locals).
Подводные камни
-
formatстрогий по типу.format("%d", "42")упадёт,"42"это string, нуженformat("%s", "42")илиparseint("42", 10). -
regex backslash экранируется дважды. В
"/\\d+/", это паттерн\d+. Один\HCL съест как escape-символ строки. -
replaceбез/.../, это подстрока.replace("a.b", ".", "/")заменит точку, не «любой символ». Для regex обязательны слеши:replace("a.b", "/./", "/"). -
upperиlowerне работают на массивах.lower(["A", "B"])упадёт. Используйте[for s in arr : lower(s)](см. tf-functions-collection). -
title, наивный.title("hello WORLD")→"Hello World". Каждое слово получает заглавную, остальное, нижний регистр. Для имён собственных может не подойти. -
lengthстроки в символах, не байтах. На многобайтовых строках может удивить. Если нужны байты, конвертация черезbase64encode+length(грубый хак). -
format/printf-like не интерполирует HCL-выражения.
format("%s", var.x)ок.format("Hello, ${var.name}"), лишнее, выражение можно просто"Hello, ${var.name}".