Зачем понимать ссылки
В HCL почти всё, это ссылки. Имя бакета зависит от региона? Ссылаемся на data.aws_region. Имя зависит от переменной? var.something. Атрибут одного ресурса нужен в другом? aws_s3_bucket.demo.arn.
Без уверенного знания синтаксиса ссылок Terraform превращается в копипаст без понимания. С ним, наоборот.
Главные пять префиксов
| Префикс | Что | Пример |
|---|---|---|
var.имя | Переменная | var.region |
local.имя | Локал | local.name_prefix |
тип.имя.атрибут | Resource | aws_s3_bucket.demo.arn |
data.тип.имя.атрибут | Data-источник | data.aws_region.current.name |
module.имя.output_имя | Output дочернего модуля | module.network.vpc_id |
Плюс есть специальные внутри блоков:
count.index, внутри блока сcount.each.key,each.value, внутри блока сfor_each.self.атрибут, внутриprovisioner(advanced).
Простые примеры
variable "env" { default = "dev" }locals { prefix = "myapp-${var.env}"}
data "aws_region" "current" {}resource "aws_s3_bucket" "logs" { bucket = "${local.prefix}-logs-${data.aws_region.current.name}"# myapp-dev-logs-us-east-1
}
resource "aws_s3_bucket_versioning" "logs" {bucket = aws_s3_bucket.logs.id # ссылка на первый бакет
versioning_configuration {status = "Enabled"
}
}
Внутри ${...} vs без них
В современном HCL ${...} нужны только если выражение, часть строки:
# interpolation внутри строки, обязательны
bucket = "myapp-${var.env}-${local.region}"# выражение целиком, кавычки и ${...} НЕ нужныbucket = local.bucket_name
count = var.instance_count
region = data.aws_region.current.name
Старый стиль "${var.foo}" для одного выражения тоже работает, но Terraform будет ругаться warning'ом «Interpolation-only expressions are deprecated».
Splat operator [*], взять одно поле от всех
Если ресурс создан с count или for_each, он становится list/map. Чтобы получить, например, все arn:
resource "aws_s3_bucket" "logs" {count = 3
bucket = "logs-${count.index}"}
# Все arn'ы списком
output "all_arns" {value = aws_s3_bucket.logs[*].arn
# = [aws_s3_bucket.logs[0].arn, aws_s3_bucket.logs[1].arn, aws_s3_bucket.logs[2].arn]
}
Знак [*] называется splat operator. Применяется к count и for_each одинаково.
Альтернативный синтаксис через for:
output "all_arns" {value = [for b in aws_s3_bucket.logs : b.arn]
}
Адреса с индексом
# count
aws_s3_bucket.logs[0] # первый
aws_s3_bucket.logs[length(aws_s3_bucket.logs) - 1] # последний
# for_each
aws_s3_bucket.regional["us"]
aws_s3_bucket.regional["eu"].arn
# внутри resource с count
resource "aws_s3_bucket" "logs" {count = 3
bucket = "logs-${count.index}" # 0, 1, 2}
# внутри resource с for_each
resource "aws_s3_bucket" "regional" {for_each = toset(["us", "eu"])
bucket = "data-${each.key}" # us, eu tags = { region = each.value } # тот же us/eu, для toset}
Какой атрибут существует у ресурса
Когда вы пишете aws_s3_bucket.demo.X, какие X доступны? Это зависит от провайдера. Список, в документации провайдера:
Раздел "Attributes Reference", что можно прочитать. Раздел "Arguments", что можно задать.
Совет: запомните адресацию terraform.io/providers/<vendor>/<provider>/latest/docs/resources/<resource_name>, пригодится постоянно.
Подводные камни
-
localvslocalsснова. Объявление,locals { ... }, ссылка,local.name. Безs. -
Ресурс с count в state, list. Адрес без индекса работает не везде.
aws_s3_bucket.logs.arn, ошибка если count > 1. Нужноaws_s3_bucket.logs[0].arnилиaws_s3_bucket.logs[*].arn. -
var.имя, для variable, не для locals. Часто новички пишутvar.prefixимея в виду local. Это два разных namespace. -
(known after apply)ломает интуицию. Если вы ссылаетесь на атрибут, который вычисляется на стороне облака. Terraform знает что значение появится, но не показывает его в plan. Это нормально. -
Циклические зависимости, фатальны. Если A ссылается на B, а B на A. Terraform упадёт с «cyclic dependency». Лечится через tf-locals или пересмотром архитектуры.
-
Ссылаться на ресурс из самого блока, нельзя.
resource "x" { foo = self.bar },selfдоступен только вprovisionerблоках. Обычно, никак.