Урок 16-21: Продвинутые функции Terraform

Pavel 08.12.2025 18:58 4 просмотров

Часть 1: Count и For Each - Циклы

Count - Простой счетчик

variable "instance_count" {
  type    = number
  default = 3
}

resource "aws_instance" "web" {
  count         = var.instance_count
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  tags = {
    Name = "web-server-${count.index + 1}"
  }
}

# Обращение к ресурсам
output "instance_ids" {
  value = aws_instance.web[*].id
}

output "first_instance_ip" {
  value = aws_instance.web[0].public_ip
}

For Each - Итерация по коллекциям

variable "instances" {
  type = map(object({
    instance_type = string
    availability_zone = string
  }))
  default = {
    web1 = {
      instance_type = "t2.micro"
      availability_zone = "us-west-2a"
    }
    web2 = {
      instance_type = "t2.small"
      availability_zone = "us-west-2b"
    }
    web3 = {
      instance_type = "t2.medium"
      availability_zone = "us-west-2c"
    }
  }
}

resource "aws_instance" "web" {
  for_each          = var.instances
  ami               = "ami-0c55b159cbfafe1f0"
  instance_type     = each.value.instance_type
  availability_zone = each.value.availability_zone

  tags = {
    Name = each.key
  }
}

# Обращение к ресурсам
output "instance_ids" {
  value = { for k, v in aws_instance.web : k => v.id }
}

output "web1_ip" {
  value = aws_instance.web["web1"].public_ip
}

Count vs For Each

Параметр Count For Each
Использование Простые массивы Сложные структуры
Добавление элемента Сдвигает индексы Не влияет на остальные
Читаемость Хуже Лучше
Производительность Быстрее Медленнее на больших данных
Best for 1-2 экземпляра Именованные ресурсы

Часть 2: Условия (If)

Простое условие

variable "enable_monitoring" {
  type    = bool
  default = true
}

resource "aws_cloudwatch_log_group" "app" {
  count             = var.enable_monitoring ? 1 : 0
  name              = "/aws/lambda/app-logs"
  retention_in_days = 7
}

output "log_group_name" {
  value = var.enable_monitoring ? aws_cloudwatch_log_group.app[0].name : "monitoring-disabled"
}

Условие в переменных

variable "environment" {
  type = string
}

locals {
  instance_type = var.environment == "prod" ? "t2.large" : "t2.micro"

  enable_ebs_optimization = contains(["prod", "staging"], var.environment)

  backup_retention = var.environment == "prod" ? 30 : 7
}

resource "aws_instance" "web" {
  ami                  = "ami-0c55b159cbfafe1f0"
  instance_type        = local.instance_type
  ebs_optimized        = local.enable_ebs_optimization

  tags = {
    Name = "web-${var.environment}"
  }
}

Условие в Dynamic блоках

variable "create_nat_gateway" {
  type    = bool
  default = true
}

resource "aws_subnet" "private" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-west-2a"
}

resource "aws_route_table" "private" {
  vpc_id = aws_vpc.main.id

  dynamic "route" {
    for_each = var.create_nat_gateway ? [1] : []
    content {
      cidr_block     = "0.0.0.0/0"
      nat_gateway_id = aws_nat_gateway.main.id
    }
  }
}

Часть 3: Локальные команды (local-exec)

Выполнение локальных скриптов

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  provisioner "local-exec" {
    command = "echo ${self.public_ip} >> /tmp/instance_ips.txt"
  }

  provisioner "local-exec" {
    when    = destroy
    command = "echo 'Instance destroyed' >> /tmp/destroyed.log"
  }
}

Запуск Python скрипта

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  provisioner "local-exec" {
    command = "python3 register_instance.py --ip=${self.public_ip} --name=${self.tags.Name}"
  }
}

Запуск Bash скрипта

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  provisioner "local-exec" {
    command = "bash configure_instance.sh"
    environment = {
      INSTANCE_ID = self.id
      REGION      = var.aws_region
    }
  }
}

Часть 4: Terraform Import

Импорт существующих ресурсов

# Импортировать существующий EC2 инстанс
terraform import aws_instance.web i-1234567890abcdef0

# Импортировать Security Group
terraform import aws_security_group.web sg-12345678

# Импортировать S3 бакет
terraform import aws_s3_bucket.main my-bucket-name

# Импортировать VPC
terraform import aws_vpc.main vpc-12345678

# Импортировать RDS базу
terraform import aws_db_instance.main mydbinstance

Как это работает

# 1. Сначала создать блок в коде
resource "aws_instance" "web" {
  # Оставить пустым - будет заполнено из существующего ресурса
}

# 2. Импортировать ресурс
terraform import aws_instance.web i-1234567890abcdef0

# 3. Terraform обновит state файл
# 4. Вручную обновить код с нужными атрибутами

resource "aws_instance" "web" {
  ami                    = "ami-0c55b159cbfafe1f0"
  instance_type          = "t2.micro"
  vpc_security_group_ids = ["sg-12345678"]

  tags = {
    Name = "web-server"
  }
}

Импорт большого количества ресурсов

#!/bin/bash
# Импортировать все EC2 инстансы с тегом Environment=production

INSTANCES=$(aws ec2 describe-instances \
  --filters "Name=tag:Environment,Values=production" \
  --query 'Reservations[*].Instances[*].InstanceId' \
  --output text)

for instance_id in $INSTANCES; do
  terraform import aws_instance.${instance_id} ${instance_id}
done

Часть 5: Terraform Taint и Untaint

Пересоздание ресурса - Taint

# Отметить ресурс для пересоздания
terraform taint aws_instance.web

# Пересоздать при следующем apply
terraform apply

# Отменить taint
terraform untaint aws_instance.web

Использование в коде

#!/bin/bash
# Если инстанс вышел из строя, пересоздать его

INSTANCE_STATUS=$(aws ec2 describe-instance-status \
  --instance-ids i-1234567890abcdef0 \
  --query 'InstanceStatuses[0].InstanceStatus.Status' \
  --output text)

if [ "$INSTANCE_STATUS" != "ok" ]; then
  terraform taint aws_instance.web
  terraform apply -auto-approve
fi

Часть 6: Conditions и Lookups

Использование Conditions

variable "environment" {
  type = string
}

variable "enable_backups" {
  type    = bool
  default = false
}

locals {
  # Условия
  is_production = var.environment == "prod"
  needs_backup  = local.is_production && var.enable_backups
}

resource "aws_db_instance" "main" {
  allocated_storage    = local.is_production ? 100 : 20
  storage_type         = local.is_production ? "gp3" : "gp2"
  backup_retention_period = local.needs_backup ? 30 : 0
  multi_az             = local.is_production ? true : false
}

Lookups - Поиск в словарях

locals {
  instance_types = {
    dev     = "t2.micro"
    staging = "t2.small"
    prod    = "t2.large"
  }

  ami_names = {
    amazon_linux = "amzn2-ami-hvm-*-x86_64-gp2"
    ubuntu       = "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"
    centos       = "CentOS Linux 8 x86_64 HVM EBS*"
  }
}

variable "environment" {
  type = string
}

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = lookup(local.instance_types, var.environment, "t2.micro")

  tags = {
    Name = "web-${var.environment}"
  }
}

# С значением по умолчанию
output "instance_type" {
  value = lookup(local.instance_types, var.environment, "t2.micro")
}

Полный пример: Multi-Environment Infrastructure

# variables.tf
variable "environment" {
  type = string
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod"
  }
}

variable "instance_count" {
  type = object({
    dev     = number
    staging = number
    prod    = number
  })
  default = {
    dev     = 1
    staging = 2
    prod    = 3
  }
}

# main.tf
locals {
  instance_count = lookup(var.instance_count, var.environment)
  instance_type = var.environment == "prod" ? "t2.large" : "t2.micro"

  common_tags = {
    Environment = var.environment
    ManagedBy   = "Terraform"
  }
}

resource "aws_instance" "web" {
  count         = local.instance_count
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = local.instance_type

  tags = merge(
    local.common_tags,
    {
      Name = "${var.environment}-web-${count.index + 1}"
    }
  )

  provisioner "local-exec" {
    command = "echo '${self.public_ip}' >> /tmp/${var.environment}_instances.txt"
  }

  lifecycle {
    create_before_destroy = true
  }
}

# outputs.tf
output "instance_ids" {
  value = aws_instance.web[*].id
}

output "instance_ips" {
  value = aws_instance.web[*].public_ip
}

Использование

# Разработка
terraform apply -var="environment=dev"

# Staging
terraform apply -var="environment=staging"

# Production
terraform apply -var="environment=prod"

Комментарии (0)

Для добавления комментария необходимо войти в аккаунт

Войти / Зарегистрироваться