Урок 11-15: Продвинутые функции Terraform
Часть 1: Динамические блоки (Dynamic Blocks)
Проблема без Dynamic блоков
# Много повторяющегося кода
resource "aws_security_group" "web" {
name = "web-sg"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
}
}
Решение с Dynamic блоками
locals {
ingress_rules = [
{
port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
port = 3306
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
}
]
}
resource "aws_security_group" "web" {
name = "web-sg"
dynamic "ingress" {
for_each = local.ingress_rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}
Часть 2: Lifecycle правила
Основные директивы Lifecycle
| Директива | Описание |
|---|---|
| create_before_destroy | Создать новый перед удалением старого |
| prevent_destroy | Запретить удаление ресурса |
| ignore_changes | Игнорировать изменения в определенных атрибутах |
| replace_triggered_by | Пересоздать при изменении другого ресурса |
create_before_destroy - Zero Downtime
resource "aws_autoscaling_group" "web" {
name = "web-asg"
vpc_zone_identifier = aws_subnet.main[*].id
min_size = 2
max_size = 5
desired_capacity = 3
launch_template {
id = aws_launch_template.web.id
version = "$Latest"
}
lifecycle {
create_before_destroy = true # Создать новый ASG перед удалением старого
}
}
prevent_destroy - Защита критичных ресурсов
resource "aws_db_instance" "main" {
identifier = "main-db"
engine = "postgres"
engine_version = "13.0"
instance_class = "db.t3.small"
lifecycle {
prevent_destroy = true # Запретить удаление
}
}
# Попытка destroy выведет ошибку:
# Error: Instance cannot be destroyed
# Resource instance aws_db_instance.main has lifecycle.prevent_destroy set
ignore_changes - Игнорировать внешние изменения
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
lifecycle {
ignore_changes = [
ami, # Игнорировать изменения AMI
instance_type, # Игнорировать изменения типа инстанса
tags # Игнорировать изменения tags
]
}
}
# Или игнорировать все изменения
lifecycle {
ignore_changes = all
}
Часть 3: Outputs - Выходные значения
Объявление Outputs
Файл: outputs.tf
output "instance_id" {
description = "ID of the EC2 instance"
value = aws_instance.web.id
}
output "public_ip" {
description = "Public IP address"
value = aws_instance.web.public_ip
}
output "security_group_id" {
description = "Security Group ID"
value = aws_security_group.web.id
}
output "database_endpoint" {
description = "Database connection string"
value = aws_db_instance.main.endpoint
sensitive = true # Скрывать значение в логах
}
output "all_instance_ips" {
description = "All instance IPs"
value = aws_instance.web[*].public_ip # Массив IPs
}
output "instance_details" {
description = "Complete instance details"
value = {
id = aws_instance.web.id
public_ip = aws_instance.web.public_ip
type = aws_instance.web.instance_type
}
}
Использование Outputs
# Просмотр выходов
terraform output
# Конкретный output
terraform output instance_id
# В JSON формате
terraform output -json
# Сохранить в файл
terraform output -json > outputs.json
Часть 4: Зависимости (depends_on)
Явные зависимости
# Зависимость: Internet Gateway должен быть создан перед EIP
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "main-igw"
}
}
resource "aws_eip" "web" {
domain = "vpc"
depends_on = [aws_internet_gateway.main] # Явная зависимость
tags = {
Name = "web-eip"
}
}
Неявные зависимости (автоматические)
# Terraform автоматически определит зависимость через ссылку
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.web.id] # Автоматическая зависимость
# Terraform знает, что Security Group должен быть создан перед Instance
}
Сложный пример зависимостей
# 1. VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
# 2. Internet Gateway (зависит от VPC)
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
}
# 3. Subnet (зависит от VPC)
resource "aws_subnet" "main" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
}
# 4. Security Group (зависит от VPC)
resource "aws_security_group" "web" {
vpc_id = aws_vpc.main.id
name = "web-sg"
}
# 5. Instance (зависит от Subnet и Security Group)
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = aws_subnet.main.id
vpc_security_group_ids = [aws_security_group.web.id]
}
# 6. EIP (зависит от Instance и Internet Gateway)
resource "aws_eip" "web" {
instance = aws_instance.web.id
domain = "vpc"
depends_on = [aws_internet_gateway.main]
}
График зависимостей
terraform graph | dot -Tsvg > graph.svg
# Просмотр зависимостей
terraform graph
Часть 5: Data Sources
Получение данных о существующих ресурсах
# Получить информацию об AMI
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# Использование в Instance
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id # Использовать найденный AMI
instance_type = "t2.micro"
tags = {
Name = "web-server"
}
}
output "ami_id" {
value = data.aws_ami.amazon_linux.id
}
output "ami_name" {
value = data.aws_ami.amazon_linux.name
}
Другие полезные Data Sources
# Информация о VPC
data "aws_vpc" "default" {
default = true
}
# Список подсетей
data "aws_subnets" "default" {
filter {
name = "vpc-id"
values = [data.aws_vpc.default.id]
}
}
# Информация о доступных зонах
data "aws_availability_zones" "available" {
state = "available"
}
# Использование
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
subnet_id = data.aws_subnets.default.ids[0]
availability_zone = data.aws_availability_zones.available.names[0]
}
Полный пример: Веб-приложение с Advanced функциями
# Получить последний Amazon Linux 2 AMI
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*"]
}
}
# Launch Template
resource "aws_launch_template" "web" {
image_id = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
user_data = base64encode(file("${path.module}/init.sh"))
}
# Auto Scaling Group
resource "aws_autoscaling_group" "web" {
vpc_zone_identifier = data.aws_subnets.default.ids
min_size = 2
max_size = 5
desired_capacity = 3
launch_template {
id = aws_launch_template.web.id
version = "$Latest"
}
lifecycle {
create_before_destroy = true
}
dynamic "tag" {
for_each = {
Name = "web-server"
Environment = "production"
}
content {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
}
# Outputs
output "asg_name" {
description = "Auto Scaling Group name"
value = aws_autoscaling_group.web.name
}
output "current_size" {
description = "Current number of instances"
value = aws_autoscaling_group.web.desired_capacity
}