lesson ── terraform-advanced ── ~16 мин ── 6 шагов
Legacy infrastructure created by hand or by CloudFormation. To take it
over with terraform import means weeks of work and the risk of a
destroy. The blue-green alternative: build a parallel copy with
Terraform, switch the traffic, then tear down the legacy. In this
lesson you simulate that on two S3 buckets, "legacy" and "tf", with a
symbolic traffic switch.
интерактивный sandbox
Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.
stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя
To simulate legacy, we create it with the aws CLI, not with Terraform:
cd /home/student/blue-green
aws --endpoint-url=http://localstack:4566 \
s3api create-bucket --bucket legacy-app-data --region us-east-1
aws --endpoint-url=http://localstack:4566 \
s3api put-bucket-tagging --bucket legacy-app-data \
--tagging 'TagSet=[
{Key=ManagedBy,Value=clickops}, {Key=Owner,Value=unknown}]'
# put some "user" data in place
echo "important data v1" > /tmp/data-1.txt
echo "important data v2" > /tmp/data-2.txt
aws --endpoint-url=http://localstack:4566 s3 cp /tmp/data-1.txt s3://legacy-app-data/
aws --endpoint-url=http://localstack:4566 s3 cp /tmp/data-2.txt s3://legacy-app-data/
aws --endpoint-url=http://localstack:4566 s3 ls s3://legacy-app-data/
The legacy bucket is in the cloud, and Terraform does not manage it.
✓ Legacy is in place. Terraform does not know about it.
cat > main.tf <<'EOF'
resource "aws_s3_bucket" "tf_app_data" {bucket = "tf-app-data"
tags = {ManagedBy = "terraform"
MigratedFrom = "legacy-app-data"
}
}
resource "aws_s3_bucket_versioning" "tf_app_data" {bucket = aws_s3_bucket.tf_app_data.id
versioning_configuration {status = "Enabled"
}
}
output "tf_bucket" {value = aws_s3_bucket.tf_app_data.bucket
}
EOF
terraform init -no-color > /dev/null
terraform apply -auto-approve -no-color > /dev/null
aws --endpoint-url=http://localstack:4566 s3 ls
You see two buckets: legacy-app-data and tf-app-data. Green is
still empty.
✓ Green is created, next to legacy. The tracker is Terraform.
aws --endpoint-url=http://localstack:4566 \
s3 sync s3://legacy-app-data s3://tf-app-data --exact-timestamps
aws --endpoint-url=http://localstack:4566 s3 ls s3://tf-app-data/
The data is in green. This is the most expensive part of a real migration (terabytes of data means hours).
In production it is either a short-window cutover or logical-replication (RDS, MongoDB).
✓ The data is synced. Green is ready to take traffic.
In a real system the switch is DNS/Route53/ALB. On LocalStack we use a proxy for the question "which application bucket?":
# Symlink emulation of the application config
cat > current-target.txt <<EOF
target: legacy-app-data
EOF
cat current-target.txt
# smoke-test of the "application", reads the named bucket
cat > smoke.sh <<'EOF'
#!/bin/bash
TARGET=$(grep '^target' current-target.txt | cut -d' ' -f2)
echo "Reading from $TARGET..."
aws --endpoint-url=http://localstack:4566 s3 ls s3://$TARGET/ | head -2
EOF
chmod +x smoke.sh
./smoke.sh
Right now the application reads legacy. Let us switch it:
sed -i 's/legacy-app-data/tf-app-data/' current-target.txt
./smoke.sh
The real switch goes through Route53 weighted-records (weight 100 to tf-, 0 to legacy-) or an ALB target-group. See the KB tf-blue-green-migration.
✓ Traffic is switched to green. The old bucket is on standby.
In real production this is several days with metrics turned on. Symbolically:
cat > /tmp/monitoring.log <<EOF
day-1: requests=10000 errors=0 | green ok
day-2: requests=15000 errors=2 | green ok (within tolerance)
day-3: requests=12000 errors=0 | green ok
EOF
cat /tmp/monitoring.log
If green showed instability, sed -i 's/tf-app-data/legacy-app-data/' current-target.txt and the rollback is ready. Blue is still intact.
This is the advantage of blue-green: rollback is cheap, and the decision stays reversible until you tear down blue.
✓ The monitoring window passed without incidents. We are ready to remove legacy.
OpenTofu keeps its CLI and state compatible with Terraform for
the commands in this step: migration usually goes through mv .terraform .terraform.bak; tofu init -upgrade. But on the first
switch, back up your state and do a run on a feature-branch, since
the differences cluster in the newer features (variables in the
backend, state-encryption, OCI registry-backed modules). See
tf-opentofu-parity for the full matrix.
# clear the contents (s3 rb needs an empty bucket without --force)
aws --endpoint-url=http://localstack:4566 \
s3 rm s3://legacy-app-data --recursive
aws --endpoint-url=http://localstack:4566 \
s3 rb s3://legacy-app-data
aws --endpoint-url=http://localstack:4566 s3 ls
Legacy is gone. The Terraform state never needed it, tf_app_data
was always under Terraform's management.
The final check, the application still works:
./smoke.sh
It sees the data in tf-app-data. The migration is complete.
No terraform import, no scary state operations. Just building the
new infrastructure alongside and switching over gradually.
✓ Legacy is gone. Green is running. The blue-green migration is complete.
Blue-green is not a silver bullet. Cases where you should do an
import or take another path:
A unique ARN/ID in external systems. An IAM role with a hardcoded ARN in a third-party service, blue-green will not help, the ARN will be different.
Stateful with large data. 100 TB in RDS, the sync is
expensive, the downtime is long. import plus a careful
refactor can be cheaper.
Hard dependencies on specific IPs. An internal ALB with a pinned IP. Blue-green creates a different IP, and external clients will not know about it.
Cross-account or cross-team coupling. If other teams
reference your resource by name, and renaming it requires
coordination, import is clearer (the name does not change).
The decision is made case by case. Blue-green is the default
choice for most stateless resources and small statefuls. import
is for when blue-green is physically expensive or impossible.
The scenario: a legacy bucket exists, created outside Terraform. We do not import it. We create a parallel tf-bucket, sync the data with aws s3 sync, switch DNS (or an alias), then remove the legacy. At every step a rollback is easy.
команды
aws s3 sync s3://legacy s3://tf-managedcopy the data.terraform applydeploy green.aws s3 rb s3://legacy --forcethe final teardown of legacy.концепции