Terraform AWS источник данных aws_su bnet возвращает не найденные подходящие подсети - PullRequest
1 голос
/ 21 марта 2020

У меня есть модуль, который я пытаюсь найти внутри подсети AWS, а затем использую / возвращаю. Это называется так:

module "smurf_subnet_grp" {
   source            = "../../modules/networking/subnet_grp_per_az-test"
   vpc_id            = "${module.networking_uswe2.vpc_id}"
   azs               = "${local.az_list_uswe2}"
   private_subnets   = "${var.private_subnets_uswe2}"
}

Код для модуля:

variable "azs"              { type = "list" }
variable "private_subnets"  { type = "list" }
variable "vpc_id"           {}

# ========== remove special subnets ==============

locals {
  cnt      = "${length(var.private_subnets) - 3}"
  prv_subs = "${slice(var.private_subnets, 0, local.cnt)}"
}

# ========== get subnet details ==================

data "aws_subnet" "self" {
  count         = "${length(local.prv_subs)}"
  vpc_id        = "${var.vpc_id}"
  cidr_block    = "${local.prv_subs[count.index]}"
}

# ========== get subnets by AZ ===================

locals {
  prv_subs0 = "${matchkeys(data.aws_subnet.self.*.id, data.aws_subnet.self.*.availability_zone, list(var.azs[0]))}"
  prv_subs1 = "${matchkeys(data.aws_subnet.self.*.id, data.aws_subnet.self.*.availability_zone, list(var.azs[1]))}"
  prv_subs2 = "${matchkeys(data.aws_subnet.self.*.id, data.aws_subnet.self.*.availability_zone, list(var.azs[2]))}"
}

# ========== select 1 subnet per AZ ==============

resource "random_shuffle" "prv_sub0" {
    input        = ["${local.prv_subs0}"]
    result_count = 1
}
resource "random_shuffle" "prv_sub1" {
    input        = ["${local.prv_subs1}"]
    result_count = 1
}
resource "random_shuffle" "prv_sub2" {
    input        = ["${local.prv_subs2}"]
    result_count = 1
}

# ========== put selected into 1 list ============

locals {
  prv_sub_az = [
       "${random_shuffle.prv_sub0.result}", 
       "${random_shuffle.prv_sub1.result}", 
       "${random_shuffle.prv_sub2.result}"
  ]
}

output "prv_subnet_grp" {
  value = "${local.prv_sub_az}"
}

Что выдает это:

Error: Error refreshing state: 1 error occurred:
    * module.smurf_subnet_grp.data.aws_subnet.self: 6 errors occurred:
    * module.smurf_subnet_grp.data.aws_subnet.self[5]: data.aws_subnet.self.5: no matching subnet found
    * module.smurf_subnet_grp.data.aws_subnet.self[3]: data.aws_subnet.self.3: no matching subnet found
    * module.smurf_subnet_grp.data.aws_subnet.self[0]: data.aws_subnet.self.0: no matching subnet found
    * module.smurf_subnet_grp.data.aws_subnet.self[1]: data.aws_subnet.self.1: no matching subnet found
    * module.smurf_subnet_grp.data.aws_subnet.self[2]: data.aws_subnet.self.2: no matching subnet found
    * module.smurf_subnet_grp.data.aws_subnet.self[4]: data.aws_subnet.self.4: no matching subnet found

Если я введу depends_on для aws_subnet поставщик данных:

data "aws_subnet" "self" {
  count         = "${length(local.prv_subs)}"
  vpc_id        = "${var.vpc_id}"
  cidr_block    = "${local.prv_subs[count.index]}"
  depends_on    = ["null_resource.module_depends_on"]
}

Он будет работать как положено, но затем будет каждый раз воссоздавать его.

ОБНОВЛЕНИЕ # 1

В попытке обойти В этом я попытался реализовать предложение, найденное здесь на дискуссионных форумах Hashicorp под названием: СОВЕТЫ: ​​Как реализовать модуль зависящий от эмуляции . Теория заключается в том, что проблема, с которой я столкнулся, была проблема порядка / зависимости.

Код, который я использую для реализации depends_on, приведен ниже для моего subnet_grp_per_az-test модуля:

/*
    Add the following line to the resource in this module that depends on the completion of external module components:

    depends_on = ["null_resource.module_depends_on"]

    This will force Terraform to wait until the dependant external resources are created before proceeding with the creation of the
    resource that contains the line above.

    This is a hack until Terraform officially support module depends_on.
*/

variable "module_depends_on" {
  default = [""]
}

resource "null_resource" "module_depends_on" {
  triggers = {
    value = "${length(var.module_depends_on)}"
  }
}

1 Ответ

0 голосов
/ 23 марта 2020

Обратите внимание, что это мое понимание. Надеемся, М. Аткинс сможет подтвердить.

Почему у Terraform нет зависимости_ для модуля

В качестве подготовки сначала хотелось бы прояснить недоразумение, которое у нас может быть (которое у меня было) относительно Terraform module .

Цель состоит в том, чтобы sh установить, что нет никакой зависимости от модуля TF к другому модулю TF . Тот факт, что объявление модуля A предшествует модулю B, как в файле Root module tf ниже, не означает, что создание ресурса в модуле B не произойдет, пока все ресурсы в модуле A не будут завершены.

Что бы произошло, если бы у нас было два модуля A и B, и они зависели друг от друга?

enter image description here

Root module

resource "aws_vpc" "this" {
  cidr_block = var.vpc_cidr
}
#--------------------------------------------------------------------------------
# Create PRIVATE subnets but create EC2 in PUBLIC subnets (cross module reference)
#--------------------------------------------------------------------------------
module "private_subnet_public_ec2" {
  source               = "../private_subnet_public_ec2"
  vpc_id               = aws_vpc.this.id
  private_subnet_cidr_blocks = var.private_subnet_cidr_blocks
  public_subnet_ids    = module.public_subnet_private_ec2.public_subnet_ids
  ami_id               = data.aws_ami.this.id
}
#--------------------------------------------------------------------------------
# Create PUBLIC subnets but create EC2 in PRIVATE subnets (cross module reference)
#--------------------------------------------------------------------------------
module "public_subnet_private_ec2" {
  source              = "../public_subnet_private_ec2"
  vpc_id              = aws_vpc.this.id
  public_subnet_cidr_blocks = var.public_subnet_cidr_blocks
  private_subnet_ids  = module.private_subnet_public_ec2.private_subnet_ids
  ami_id              = data.aws_ami.this.id
}

Модуль A

Создает частные подсети и EC2 в общедоступных c подсетях, созданных в модуле B.

resource "aws_subnet" "private" {
  count = length(var.private_subnet_cidr_blocks)
  vpc_id     = var.vpc_id
  cidr_block = var.private_subnet_cidr_blocks[count.index]
}
output "private_subnet_ids" {
  value = aws_subnet.private[*].id
}

resource "aws_instance" "public_ec2" {
  count = length(var.public_subnet_ids)
  subnet_id = var.public_subnet_ids[count.index]
  ami           = var.ami_id
  instance_type = "t2.micro"
  tags = {
    Name = "PublicEC2${count.index}}"
  }
  provisioner "local-exec" {
    command = <<EOF
echo "Public EC2 ${count.index} ID is ${self.id}"
EOF
  }
}

Модуль B

Создает pubi c подсетей и EC2 в частных подсетях, созданных в модуле A.

resource "aws_subnet" "public" {
  count = length(var.public_subnet_cidr_blocks)
  vpc_id     = var.vpc_id
  cidr_block = var.public_subnet_cidr_blocks[count.index]
}
output "public_subnet_ids" {
  value = aws_subnet.public[*].id
}

resource "aws_instance" "private_ec2" {
  count = length(var.private_subnet_ids)
  subnet_id = var.private_subnet_ids[count.index]
  ami           = var.ami_id
  instance_type = "t2.micro"
  tags = {
    Name = "privateEC2${count.index}}"
  }
  provisioner "local-exec" {
    command = <<EOF
echo "private EC2 ${count.index} ID is ${self.id}"
EOF
  }
}

Результат выполнения:

$ terraform apply --auto-approve
Apply complete! Resources: 13 added, 0 changed, 0 destroyed.

Итак, нам нужно иметь в виду то, что на самом деле видит Terraform, это сплющенный мир без модулей, где существуют только ресурсы. Terraform создает DAG из ресурсов, и в этой группе нет module как узла .

Вот почему мы не можем использовать ависимый_он от модуля, потому что в DAG Terraform модуль не является узлом , для которого можно создавать вершины для представления зависимостей.

enter image description here

Механизм монитора синхронизации

Terraform выполняет несколько потоков для одновременного создания ресурсов. Нам нужна синхронизация monitor , в которой потоки могут ожидать, чтобы эти потоки запускались только после создания ресурса зависимости.

Механизм мониторинга в Terraform (кроме оператора HCL depen_on) использует атрибут (ы) созданного ресурса (или ссылку через локальный).

enter image description here

В СОВЕТАХ: Как реализовать модуль зависит от эмуляции , Мартин Аткинс показал пример использования переменной модуля "vm_depends_on", ссылающейся на атрибут созданного ресурса брандмауэра, модуль . fw_core01.firewall , чтобы эти ресурсы в модуле "example" могли быть созданы только после создания брандмауэра.

enter image description here


Problem

В моем понимании причина проблемы в этом вопросе - отсутствие monitor , ссылки на атрибут ресурса, который должен был быть уже создан при запуске потока выполнение data "aws_subnet" "self".

Поскольку у меня нет полного источника исходного вопроса, вот пример для воспроизведения ошибки.

enter image description here

Root модуль

variable "vpc_cidr" {
    default = "10.5.0.0/20"
}
variable "private_subnet_cidr_blocks" {
  default = ["10.5.3.0/24","10.5.4.0/24","10.5.5.0/24"]
}

resource "aws_vpc" "this" {
  cidr_block = var.vpc_cidr
}
module "private_subnet" {
  source          = "../private_subnet"
  vpc_id          = aws_vpc.this.id
  private_subnet_cidr_blocks = var.private_subnet_cidr_blocks
}
module "private_ec2" {
  source          = "../private_ec2"
  vpc_id          = aws_vpc.this.id
  private_subnet_cidr_blocks = var.private_subnet_cidr_blocks
  ami_id          = data.aws_ami.this.id
}

Модуль Су bnet

variable "vpc_id" {}
variable "private_subnet_cidr_blocks" {
  type = list(string)
}

resource "aws_subnet" "private" {
  count = length(var.private_subnet_cidr_blocks)
  vpc_id     = var.vpc_id
  cidr_block = var.private_subnet_cidr_blocks[count.index]
}

Модуль EC2

variable "vpc_id" {}
variable "private_subnet_cidr_blocks" {
  type = list(string)
}
variable "ami_id" {}

data aws_subnet "private" {
  count      = length(var.private_subnet_cidr_blocks)
  vpc_id     = var.vpc_id
  cidr_block = var.private_subnet_cidr_blocks[count.index]
}

resource "aws_instance" "private_ec2" {
  count         = length(data.aws_subnet.private[*].id)
  subnet_id     = data.aws_subnet.private[count.index].id
  ami           = var.ami_id
  instance_type = "t2.micro"
  tags          = {
    Name = "privateEC2${count.index}}"
  }
  provisioner "local-exec" {
    command = <<EOF
echo "private EC2 ${count.index} ID is ${self.id}"
EOF
  }
}

Результат выполнения:

$ terraform apply --auto-approve
...
Error: no matching subnet found

  on ../private_ec2/main.tf line 1, in data "aws_subnet" "private":
   1: data aws_subnet "private" {

Причина

Причиной является передача частного su bnet CIDR с использованием константных переменных, а НЕ атрибутов созданного AWS su bnet.

Если используются атрибуты созданного ресурса AWS su bnet, то он работает как монитор, на котором будет ожидать поток, выполняющий data "aws_subnet" "self".

Root module

variable "private_subnet_cidr_blocks" {
  default = ["10.5.3.0/24","10.5.4.0/24","10.5.5.0/24"]
}
module "private_ec2" {
  source          = "../private_ec2"
  vpc_id          = aws_vpc.this.id
  private_subnet_cidr_blocks = var.private_subnet_cidr_blocks # <----- Here
  ami_id          = data.aws_ami.this.id
}

Поскольку нет никакой зависимости между ресурсом в модуле Su bnet и ресурсом в модуле EC2, создание ресурса в обоих модулях выполняется параллельно.

В первоначальном вопросе

Я полагаю, что причиной первоначального вопроса является передаваемая переменная private_subnets , а не атрибуты фактически созданного ресурса AWS su bnet.

module "smurf_subnet_grp" {
   source            = "../../modules/networking/subnet_grp_per_az-test"
   vpc_id            = "${module.networking_uswe2.vpc_id}"
   azs               = "${local.az_list_uswe2}"

   private_subnets   = "${var.private_subnets_uswe2}" <----- Here

}

Следовательно, data "aws_subnet" "self" выполняется одновременно, пока AWS su bnet создается или еще не создан.


Fix

Исправление для примера:

enter image description here

Root module

resource "aws_vpc" "this" {
  cidr_block = var.vpc_cidr
}
module "private_subnet" {
  source          = "../private_subnet"
  vpc_id          = aws_vpc.this.id
  private_subnet_cidr_blocks = var.private_subnet_cidr_blocks
}
module "private_ec2" {
  source          = "../private_ec2"
  vpc_id          = aws_vpc.this.id

  #--------------------------------------------------------------------------------
  # Pass the attributes of created aws_subnet resource attributes
  #--------------------------------------------------------------------------------
  #private_subnet_cidr_blocks = var.private_subnet_cidr_blocks
  private_subnet_cidr_blocks = module.private_subnet.private_subnet_cidr_blocks  # <--- Here
  #--------------------------------------------------------------------------------

  ami_id          = data.aws_ami.this.id
}

Module su bnet

resource "aws_subnet" "private" {
  count = length(var.private_subnet_cidr_blocks)
  vpc_id     = var.vpc_id
  cidr_block = var.private_subnet_cidr_blocks[count.index]
}
#--------------------------------------------------------------------------------
# Output the cidr_block attributes of the AWS subnet resources created
#--------------------------------------------------------------------------------
output "private_subnet_cidr_blocks" {
  value = aws_subnet.private[*].cidr_block    # <----- Here
}

Результат

$ terraform apply --auto-approve
data.aws_availability_zones.all: Refreshing state...
aws_vpc.this: Refreshing state... [id=vpc-0b338898d18a5986e]
data.aws_ami.this: Refreshing state...
data.aws_region.current: Refreshing state...
module.private_subnet.data.aws_ami.ubuntu: Refreshing state...
module.private_subnet.aws_subnet.private[2]: Refreshing state... [id=subnet-0cf916b6b9003f71f]
module.private_subnet.aws_subnet.private[1]: Refreshing state... [id=subnet-0b39beb22b23eef5d]
module.private_subnet.aws_subnet.private[0]: Refreshing state... [id=subnet-0c80c92f4023ba893]
aws_vpc.this: Creating...
aws_vpc.this: Still creating... [10s elapsed]
aws_vpc.this: Still creating... [20s elapsed]
aws_vpc.this: Creation complete after 24s [id=vpc-00069d144b5f76182]
module.private_subnet.aws_subnet.private[1]: Creating...
module.private_subnet.aws_subnet.private[2]: Creating...
module.private_subnet.aws_subnet.private[0]: Creating...
module.private_subnet.aws_subnet.private[2]: Creation complete after 5s [id=subnet-0252c6047cd56abac]
module.private_subnet.aws_subnet.private[1]: Creation complete after 6s [id=subnet-019f8cbd30db10edb]
module.private_subnet.aws_subnet.private[0]: Creation complete after 6s [id=subnet-0a1028bf17d7d81be]
module.private_ec2.data.aws_subnet.private[1]: Refreshing state...
module.private_ec2.data.aws_subnet.private[2]: Refreshing state...
module.private_ec2.data.aws_subnet.private[0]: Refreshing state...
module.private_ec2.aws_instance.private_ec2[2]: Creating...
module.private_ec2.aws_instance.private_ec2[1]: Creating...
module.private_ec2.aws_instance.private_ec2[0]: Creating...
module.private_ec2.aws_instance.private_ec2[2]: Still creating... [10s elapsed]
module.private_ec2.aws_instance.private_ec2[1]: Still creating... [10s elapsed]
module.private_ec2.aws_instance.private_ec2[0]: Still creating... [10s elapsed]
module.private_ec2.aws_instance.private_ec2[2]: Still creating... [20s elapsed]
module.private_ec2.aws_instance.private_ec2[1]: Still creating... [20s elapsed]
module.private_ec2.aws_instance.private_ec2[0]: Still creating... [20s elapsed]
module.private_ec2.aws_instance.private_ec2[2]: Still creating... [30s elapsed]
module.private_ec2.aws_instance.private_ec2[1]: Still creating... [30s elapsed]
module.private_ec2.aws_instance.private_ec2[0]: Still creating... [30s elapsed]
module.private_ec2.aws_instance.private_ec2[2]: Still creating... [40s elapsed]
module.private_ec2.aws_instance.private_ec2[1]: Still creating... [40s elapsed]
module.private_ec2.aws_instance.private_ec2[0]: Still creating... [40s elapsed]
module.private_ec2.aws_instance.private_ec2[1]: Provisioning with 'local-exec'...
module.private_ec2.aws_instance.private_ec2[1] (local-exec): Executing: ["/bin/sh" "-c" "echo \"private EC2 1 ID is i-0ced265565dfec85c\"\n"]
module.private_ec2.aws_instance.private_ec2[1] (local-exec): private EC2 1 ID is i-0ced265565dfec85c
module.private_ec2.aws_instance.private_ec2[1]: Creation complete after 46s [id=i-0ced265565dfec85c]
module.private_ec2.aws_instance.private_ec2[0]: Provisioning with 'local-exec'...
module.private_ec2.aws_instance.private_ec2[0] (local-exec): Executing: ["/bin/sh" "-c" "echo \"private EC2 0 ID is i-0f6ce62c29376c6fe\"\n"]
module.private_ec2.aws_instance.private_ec2[0] (local-exec): private EC2 0 ID is i-0f6ce62c29376c6fe
module.private_ec2.aws_instance.private_ec2[0]: Creation complete after 47s [id=i-0f6ce62c29376c6fe]
module.private_ec2.aws_instance.private_ec2[2]: Provisioning with 'local-exec'...
module.private_ec2.aws_instance.private_ec2[2] (local-exec): Executing: ["/bin/sh" "-c" "echo \"private EC2 2 ID is i-03be32b7b803eb0cc\"\n"]
module.private_ec2.aws_instance.private_ec2[2] (local-exec): private EC2 2 ID is i-03be32b7b803eb0cc
module.private_ec2.aws_instance.private_ec2[2]: Creation complete after 50s [id=i-03be32b7b803eb0cc]

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

Исправить исходный вопрос

Полагаю, что приведенное ниже исправит проблему.

module "smurf_subnet_grp" {
   source            = "../../modules/networking/subnet_grp_per_az-test"
   vpc_id            = "${module.networking_uswe2.vpc_id}"
   azs               = "${local.az_list_uswe2}"

   #--------------------------------------------------------------------------------
   # Pass the cidr_block attribute of aws_subnet resource created in module.networking_uswe2
   #------------------------------------------------------------------------------   
   #private_subnets   = "${var.private_subnets_uswe2}"
   private_subnets   = module.networking_uswe2.private_subnet_cidr_blocks  # <---- Here
   #------------------------------------------------------------------------------   
}
...