Часть 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
}
}
}
Импорт существующих ресурсов
# Импортировать существующий 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
Пересоздание ресурса - 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"