122 Jan. 25, 2024, 11:16 a.m.

Почему for_each, а не count при создании ресурсов terraform

Задача: переписать код с ипользованием count на for_each

Исходно имеем такой код модуля, который создает ssh-key и ec2.

resource "aws_instance" "module" {
  count                  = length(var.names)
  ami                    = var.ami
  instance_type          = var.instance_type
  subnet_id              = var.subnet_id
  vpc_security_group_ids = var.vpc_security_group_ids
  user_data              = file(var.user_data)
  key_name               = aws_key_pair.module[count.index].id
  root_block_device {
    volume_size = var.volume_size
    volume_type = var.volume_type
  }
  # cpu_options {
  #   core_count       = var.core_count
  #   threads_per_core = var.threads_per_core
  # }
  tags = {
    Name = var.names[count.index]
  }
}

resource "aws_key_pair" "module" {
  count      = length(var.names)
  key_name   = "key-for-${var.names[count.index]}-ec2"
  public_key = var.public_key
}

Файл переменных

variable "latest_image" {
  type    = string
  default = "debian-12-amd64-*"
}

variable "ami" {
  type    = string
  default = ""
}

variable "instance_type" {
  type    = string
  default = "t3a.nano"
}

variable "subnet_id" {
  type    = string
  default = ""
}

variable "vpc_security_group_ids" {
  type    = list(any)
  default = []
}

variable "user_data" {
  type    = string
  default = "user_data.sh"
}

variable "volume_size" {
  type    = number
  default = 16
}

variable "volume_type" {
  type    = string
  default = "gp2"
}

# variable "core_count" {
#     type = number
#     default = 1
# }

# variable "threads_per_core" {
#     type = number
#     default = 1
# }

variable "names" {
  type    = list(string)
  default = []
}

variable "public_key" {
  type    = string
  default = ""
}

Файл вывода

output "ec2_id" {
  value = aws_instance.module[*].id
}

output "ec2_public_ip" {
  value = aws_instance.module[*].public_ip
}

output "ec2_private_ip" {
  value = aws_instance.module[*].private_ip
}

С одной стороны кажется что всё хорошо, если создадим массив ["one", "two", "three"]. Если мы захотим пересоздать или поменять название у two тогда все последующие ресурсы после него будут пересозданы, что приведёт к неудобным последсвиям. Поэтому предлагаю такой код, он выполняет всё тоже самое но при изменении одного элемента массива не приведёт к тому что будут задеты другие.

resource "aws_instance" "module" {
  for_each                = toset(var.names)
  ami                     = var.ami
  instance_type           = var.instance_type
  subnet_id               = var.subnet_id
  vpc_security_group_ids  = var.vpc_security_group_ids
  user_data               = file(var.user_data)
  key_name                = aws_key_pair.module[each.key].id

  root_block_device {
    volume_size = var.volume_size
    volume_type = var.volume_type
  }

  tags = {
    Name = each.key
  }
}

resource "aws_key_pair" "module" {
  for_each    = toset(var.names)
  key_name    = "key-for-${each.key}-ec2"
  public_key  = var.public_key
}
variable "latest_image" {
  type    = string
  default = "debian-12-amd64-*"
}

variable "ami" {
  type    = string
  default = ""
}

variable "instance_type" {
  type    = string
  default = "t3a.nano"
}

variable "subnet_id" {
  type    = string
  default = ""
}

variable "vpc_security_group_ids" {
  type    = list(any)
  default = []
}

variable "user_data" {
  type    = string
  default = "user_data.sh"
}

variable "volume_size" {
  type    = number
  default = 16
}

variable "volume_type" {
  type    = string
  default = "gp2"
}

# variable "core_count" {
#     type = number
#     default = 1
# }

# variable "threads_per_core" {
#     type = number
#     default = 1
# }

variable "names" {
  type    = list(string)
  default = []
}

variable "public_key" {
  type    = string
  default = ""
}
output "ec2_id" {
value = { for name, instance in aws_instance.module : name => instance.id }
}

output "ec2_public_ip" {
  value = { for name, instance in aws_instance.module : name => instance.public_ip }
}

output "ec2_private_ip" {
  value = { for name, instance in aws_instance.module : name => instance.private_ip }
}