linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
home/terraform/lessons/tf-advanced-05-blue-green-migration

lesson ── terraform-advanced ── ~16 мин ── 6 шагов

Blue-green migration of legacy infrastructure in Terraform

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 минут, без регистрации.

запустить sandbox →

stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя

Шаги

  1. 01

    The legacy bucket, created by hand

    To simulate legacy, we create it with the aws CLI, not with Terraform:

    bash
    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.

  2. 02

    Create the tf-bucket (green) alongside

    bash
    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.

  3. 03

    Sync the data blue to green

    bash
    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.

  4. 04

    Simulate the traffic switch

    In a real system the switch is DNS/Route53/ALB. On LocalStack we use a proxy for the question "which application bucket?":

    bash
    # 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:

    bash
    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.

  5. 05

    The monitoring window before teardown

    In real production this is several days with metrics turned on. Symbolically:

    bash
    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.

    The same thing on OpenTofu

    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.

    • → OpenTofu parity
  6. 06

    Tear down legacy

    bash
    # 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:

    bash
    ./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.

    When blue-green does not fit

    Blue-green is not a silver bullet. Cases where you should do an import or take another path:

    1. 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.

    2. Stateful with large data. 100 TB in RDS, the sync is expensive, the downtime is long. import plus a careful refactor can be cheaper.

    3. 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.

    4. 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.

    • → Blue-green end to end
    • → When import is justified

Что ты узнал

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.

концепции

  • · Blue (legacy) and green (Terraform) live side by side for days or weeks
  • · Switch via DNS / alias / traffic weight, not via import plus apply
  • · Rollback means flipping back while blue is still alive

← предыдущий

Mock providers, tests without the cloud

следующий →

Update: change an attribute, read the diff

Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies