Terraform: условное создание ресурса на основе переменной в .tfvars

У меня есть ресурсы, определенные в .tfфайлы, общие для нескольких приложений. Я заполняю многие поля через.tfvarsфайл. Мне нужно исключить некоторые ресурсы, полностью основанные на переменных в.tfvars.

Например, если у меня есть ресурс вроде:

resource "cloudflare_record" "record" {
  zone_id = "${data.cloudflare_zones.domain.zones[0].id}"
  name    = "${var.subdomain}"
  value   = "${var.origin_server}"
  type    = "CNAME"
  ttl     = 1
  proxied = true
}

Но тогда я заявляю что-то вроде cloudflare = false в моем .tfvars файл, я бы хотел сделать что-то вроде этого:

if var.cloudflare {
  resource "cloudflare_record" "record" {
    zone_id = "${data.cloudflare_zones.domain.zones[0].id}"
    name    = "${var.subdomain}"
    value   = "${var.origin_server}"
    type    = "CNAME"
    ttl     = 1
    proxied = true
 }
}

Я посмотрел на динамические блоки, но похоже, что вы можете использовать их только для редактирования полей и блоков внутри ресурса. Мне нужно иметь возможность игнорировать весь ресурс.

5 ответов

Решение

На самом деле это очень просто. Добавитьcount параметр с тернарным условным выражением с использованием переменной, объявленной в .tfvars нравится:

resource "cloudflare_record" "record" {
  count = var.cloudflare ? 1 : 0
  zone_id = "${data.cloudflare_zones.domain.zones[0].id}"
  name    = "${var.subdomain}"
  value   = "${var.origin_server}"
  type    = "CNAME"
  ttl     = 1
  proxied = true
}

В этом примере var.cloudflare является логическим значением, объявленным в .tfvarsфайл. Если это правда, счет 1recordбудет создан. Если это ложь, подсчет 0record будет создан.

Расширение ответа @Joel Guerra после использованияcountчтобы определить, следует ли развертывать ресурс или нет, вы можете использоватьone()для обращения к ресурсу без индекса (т.е. без использования ).

Например, после определения ресурса, как показано ниже

      resource "cloudflare_record" "record" {
  count = var.cloudflare ? 1 : 0
}

Определите локальную переменную, как показано ниже.

      locals {
  cloudflare_record_somefield = one(cloudflare_record.record[*].some_field)
}

Теперь вместоcloudflare_record.record[0].some_field, вы можете использовать

      local.cloudflare_record_somefield

Если счетчик равен 0 (например,var.cloudflareявляетсяfalseи ресурс не был создан) тогдаlocal.cloudflare_record_somefieldвернетсяnull(вместо того, чтобы возвращать ошибку при индексировании с помощью[0]).

Ссылка: https://developer.hashicorp.com/terraform/language/functions/one

Проблема, с которой я сталкиваюсь, заключается в том, что если ресурс, который вы пытаетесь создать, уже использует for_each, то вы не можете использовать в ресурсе как count, так и for_each. Я все еще пытаюсь найти ответ на этот вопрос, если я найду что-то лучше, он будет обновлен.

Пример сценария. Возможно, вы захотите создать или не создавать (переключить/использовать флаг/создать по условию) виртуальную машину. Но наряду с виртуальной машиной вам также может потребоваться создать или не создавать ее балансировщик нагрузки, целевую группу, группу безопасности и т. д.

Проблема с другими ответами в Интернете заключается в том, что когда вы используете тернарный оператор для ресурса и пытаетесь сослаться на него на каком-либо другом ресурсе, вы всегда получаете ошибку ссылки, ошибку индекса или ошибку пустого кортежа.

Чтобы решить эту проблему при указании на условный ресурс, вы можете использоватьсинтаксис

      resource "aws_vpc" "main" {
  count = var.test_flag ? 1 : 0
  cidr_block       = var.vpc_cidr
  instance_tenancy = var.vpc_instance_tenancy

  tags = {
     Name = "${var.cluster_name}-vpc"
  }

}

resource "aws_internet_gateway" "gw" {
  count =   try(var.test_flag ? 1 : 0, 0) // try block is important here because it has a dependency, here the VPC, but VPC might not need a try block because it is the parent.

  vpc_id = aws_vpc.main[0].id

  tags = {
    Name = "${var.cluster_name}-IG"
  }
}

Я столкнулся с аналогичной проблемой создания ресурсов AWS на основе переменной.

Запрос:

  • для производственной среды используйте импортированный сертификат;
  • для среды разработки запросите сертификат ACM и проверьте его через DNS, создав записи Route53.

Я пробую код, как показано ниже. В производственной среде он жалуется, что выданный AWS файл aws_acm_certificate.alb[] пуст. Я надеялся, что тернарный оператор «?:» ярлык.

Любое предложение о том, как решить мою проблему?

Спасибо,

Тигр

      # alb Cert
data "aws_acm_certificate" "vault-alb-imported" {
  counti = var.vault-cert-arn == "" ? 0 : 1 
  domain = "vault-alb.${var.domain}"
  types  = ["IMPORTED"]
}

resource "aws_acm_certificate" "alb" {
  count             = var.cert-arn == "" ? 1 : 0 
  domain_name       = "alb.${var.domain}"
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }

  tags = merge(var.default-tags, tomap({
    Name = "alb"
  }))
}

resource "aws_route53_record" "alb-cert" {
  for_each = var.cert-arn == "" ? {} : { 
    for dvo in aws_acm_certificate.alb[0].domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }
  allow_overwrite = true
  name            = each.value.name
  records         = [each.value.record]
  ttl             = 60
  type            = each.value.type
  zone_id         = var.public-hosted-zone-id
} 
Другие вопросы по тегам