Terraform IaC drift AWS DevOps

Terraform plan drift po manualnej zmianie w konsoli AWS/Azure/GCP

Naprawa Terraform drift po manualnych zmianach w konsoli: identyfikacja zmienionych zasobów, import lub reset do pożądanego stanu, wdrożenie wykrywania drift i policy.

Jerzy Kopaczewski ·
Uruchamiasz terraform plan i widzisz 47 zmian, których nikt nie commitował. Security group ma dodatkową regułę, RDS ma zmieniony parametr, ktoś ręcznie dodał tag. Pipeline CI/CD blokuje się, bo plan nie jest czysty. Ten runbook pokazuje jak rozwiązać drift i zapobiec jego powstawaniu.

Jeśli Twój problem to zablokowany state lock (nie drift), zobacz runbook o zablokowanym Terraform state. Dla ogólnego zarządzania state, zobacz runbook o drift detection i force-unlock.

Objaw

terraform plan pokazuje zmiany, których nie ma w kodzie:

# Typowy output po manualnej zmianie w konsoli:
terraform plan

# aws_security_group.app:
#   ~ ingress {
#     + cidr_blocks = ["10.0.5.0/24"]  # <-- ktoś dodał ręcznie
#       from_port   = 443
#       protocol    = "tcp"
#       to_port     = 443
#   }

# aws_db_instance.main:
#   ~ parameter_group_name = "default.postgres15" -> "custom-pg15" # zmieniono w konsoli

# aws_instance.web:
#   ~ tags = {
#     + "CostCenter" = "marketing"  # tag dodany ręcznie
#   }

Plan chce „naprawić” zasoby do stanu z kodu - ale te zmiany mogą być celowe (hotfix, ręczna interwencja podczas incydentu).

Przyczyna

  1. Ręczna zmiana w konsoli AWS/Azure/GCP - ktoś z zespołu zmienił coś przez UI zamiast przez kod
  2. Hotfix podczas incydentu - inżynier otworzył port w SG lub zmienił parametr DB podczas awarii i nie wrócił do kodu
  3. Auto-scaling lub automatyczne zmiany AWS - niektóre zasoby AWS modyfikują się same (np. ASG desired count, Lambda concurrent executions)
  4. Inny pipeline Terraform - dwa workspace operują na tym samym zasobie
  5. Manualna zmiana w state - ktoś użył terraform state rm lub terraform import bez odpowiedniego kodu

Naprawa

A) Zidentyfikuj, co się zmieniło i dlaczego

# Szczegółowy plan z pełnym diff
terraform plan -detailed-exitcode 2>&1 | tee plan-output.txt
# Kod wyjścia 2 = są zmiany do wprowadzenia

# Dla konkretnego zasobu - pokaż aktualny stan vs kod
terraform state show aws_security_group.app

# Sprawdź, kto zmienił (AWS CloudTrail)
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=ResourceName,AttributeValue=sg-0123456789abcdef0 \
  --start-time $(date -u -v-7d +%Y-%m-%dT%H:%M:%S) \
  --query 'Events[].{Time:EventTime,User:Username,Event:EventName}'

B) Decyzja: zaakceptować zmianę, czy cofnąć?

Dla każdego zasobu z odchyleniem (drift) podejmij decyzję:

Sytuacja Akcja
Zmiana była celowa i powinna zostać Zaktualizuj kod Terraform, aby odzwierciedlał zmianę
Zmiana była tymczasowa i powinna wrócić Uruchom terraform apply - Terraform przywróci stan z kodu
Zmiana dotyczy zasobu, którego Terraform nie powinien zarządzać Dodaj do lifecycle { ignore_changes = [...] }
Nie wiesz co to zmiana Nie rób apply. Zbadaj najpierw.

C) Zaakceptuj zmianę (zaktualizuj kod)

# Jeśli zmiana w konsoli była celowa - dodaj ją do kodu:

resource "aws_security_group" "app" {
  # ... istniejące reguły ...

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["10.0.5.0/24"]  # Dodane: dostęp z nowej sieci
    description = "Added during incident INC-1234, formalized in TF"
  }
}

D) Cofnij zmianę (terraform apply)

# UWAGA: to przywróci stan zasobu do definicji w kodzie
# Upewnij się, że cofnięcie zmiany nie spowoduje incydentu!

# Najpierw plan, sprawdź co się stanie:
terraform plan -target=aws_security_group.app

# Jeśli jesteś pewien:
terraform apply -target=aws_security_group.app

E) Ignoruj zmianę (lifecycle ignore)

Dla zasobów, które zmieniają się poza Terraform (np. ASG desired count, tags dodawane przez AWS):

resource "aws_autoscaling_group" "app" {
  # ...
  desired_capacity = 3

  lifecycle {
    ignore_changes = [
      desired_capacity,  # Zmieniane przez autoscaling policy
      target_group_arns, # Zarządzane przez inny moduł
    ]
  }
}

resource "aws_instance" "web" {
  # ...
  lifecycle {
    ignore_changes = [
      tags["CostCenter"],  # Tag zarządzany przez AWS Organizations
    ]
  }
}

F) Import niezarządzanego zasobu

Jeśli ktoś utworzył zasób w konsoli, który powinien być zarządzany przez Terraform:

# 1. Napisz blok resource w kodzie (pusty lub z szacowanymi wartościami)
# 2. Import istniejącego zasobu do state
terraform import aws_security_group.new_sg sg-0abcdef1234567890

# 3. Uruchom plan i uzupełnij brakujące atrybuty w kodzie
terraform plan
# Wynik pokaże, co trzeba dodać lub zmienić w kodzie HCL

Zapobieganie

Wykrywanie drift w CI/CD

# GitHub Actions - codzienny drift check
name: Terraform Drift Detection
on:
  schedule:
    - cron: '0 6 * * *'  # Codziennie o 6:00 UTC

jobs:
  drift-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3

      - name: Terraform Init
        run: terraform init

      - name: Terraform Plan (drift detection)
        id: plan
        run: terraform plan -detailed-exitcode -no-color
        continue-on-error: true

      - name: Alert on drift
        if: steps.plan.outcome == 'failure'
        run: |
          curl -X POST "$SLACK_WEBHOOK" -d '{
            "text": "Terraform drift detected in production. Run terraform plan to review changes."
          }'

AWS Config Rules

# Włącz AWS Config rule, która wykrywa zmiany w security groups
aws configservice put-config-rule --config-rule '{
  "ConfigRuleName": "sg-no-manual-changes",
  "Source": {
    "Owner": "AWS",
    "SourceIdentifier": "EC2_SECURITY_GROUP_ATTACHED_TO_ENI_PERIODIC"
  }
}'

SCP (Service Control Policy) blokujący ręczne zmiany

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Deny",
    "Action": [
      "ec2:AuthorizeSecurityGroupIngress",
      "ec2:RevokeSecurityGroupIngress"
    ],
    "Resource": "*",
    "Condition": {
      "StringNotLike": {
        "aws:PrincipalArn": "arn:aws:iam::*:role/terraform-*"
      }
    }
  }]
}

Weryfikacja

# Po naprawie - plan powinien być czysty
terraform plan
# "No changes. Your infrastructure matches the configuration."

# Jeśli nadal są zmiany - sprawdź, czy to nie atrybuty zarządzane automatycznie
# i dodaj ignore_changes tam gdzie potrzeba

Jeśli terraform plan zwraca „No changes” po naprawie, odchylenie rozwiązane. Wdróż wykrywanie drift w CI/CD, aby wyłapywać przyszłe zmiany w ciągu 24h zamiast przy kolejnym wdrożeniu.

Odchylenia (drift) to nieunikniony efekt uboczny środowisk, w których ludzie mają dostęp do konsoli. Rozwiązanie nie polega na zabraniu dostępu (potrzebny podczas incydentów), ale na automatycznym wykrywaniu i sygnalizowaniu zmian. Codzienna weryfikacja drift w CI/CD + SCP ograniczający zmiany do ról Terraform = 90% redukcja nieautoryzowanych odchyleń.
Jerzy Kopaczewski

Terraform drift wymknął się spod kontroli?

Umów bezpłatną 30-minutową rozmowę. Pomożemy oczyścić state i wdrożyć automatyczne wykrywanie odchyleń.