"Ποτέ μην αγγίζεις το AWS console στο production" ακούγεται σαν ακραίος κανόνας. Δεν είναι. Είναι η πιο σημαντική επιχειρησιακή πειθαρχία σε μια cloud-native ομάδα και το κόστος της παραβίασής της συσσωρεύεται σιωπηλά μέχρι να προκαλέσει ένα σοβαρό περιστατικό.

Αυτό το post εξηγεί το γιατί και πώς να επιβάλλεις την IaC-first ανάπτυξη σε μια πραγματική ομάδα.

Το Πρόβλημα του State Drift

Το Terraform (και το OpenTofu) διατηρεί ένα state file που αντιπροσωπεύει την υπάρχουσα υποδομή. Όταν κάνεις apply, το Terraform συγκρίνει το state file με τη διαμόρφωσή σου και κάνει το ελάχιστο σύνολο αλλαγών για να ευθυγραμμίσει την πραγματικότητα με τη διαμόρφωση.

Όταν κάνεις κλικ στο AWS console και δημιουργείς ή τροποποιείς πόρους, αλλάζεις την πραγματικότητα χωρίς να αλλάζεις το state file.

Τώρα: Το επόμενο plan θα δείξει drift: Το Terraform θέλει να καταστρέψει ή να αναδημιουργήσει πόρους που "δεν θα έπρεπε να υπάρχουν" (επειδή δεν είναι στη διαμόρφωση).

Ή χειρότερα: Το state του Terraform λέει ότι ο πόρος υπάρχει με τη ρύθμιση A, αλλά το console τον άλλαξε στη ρύθμιση B. Το plan δείχνει "καμία αλλαγή" παρόλο που ο πόρος είναι λάθος ρυθμισμένος.

Ή: κάποιος προσθέτει έναν πόρο στο console, λειτουργεί, αποκτά εξαρτήσεις από άλλους πόρους και μετά ένα apply τρέχει και τον διαγράφει επειδή δεν είναι στο state.

Το console δεν είναι η πηγή αλήθειας. Το IaC σου είναι. Το console είναι ένα ψέμα.

Το Import Workflow: Η Σωστή Απάντηση στο "Υπάρχει Ήδη"

Όταν ένας πόρος υπάρχει στο AWS αλλά όχι στη διαμόρφωση του Terraform (δημιουργήθηκε από κάποιον στο console, δημιουργήθηκε χειροκίνητα μέσω CLI ή μεταφέρθηκε από άλλο εργαλείο), η σωστή ενέργεια είναι να τον κάνεις import:

Εισαγωγή υπάρχοντος πόρου στο Terraform state

./deploy.sh --infra --import aws_s3_bucket.service existing-bucket-name
./deploy.sh --infra --import aws_dynamodb_table.service_files serviceFiles
./deploy.sh --infra --import aws_lambda_function.service_api myapp-service-api

Μετά το import, τρέξε plan:
./deploy.sh --infra

Το plan δείχνει τη διαφορά μεταξύ αυτού που υπάρχει στο AWS και αυτού που λέει η διαμόρφωσή σου. Διόρθωσε τη διαμόρφωση μέχρι το plan να μην δείχνει απροσδόκητες αλλαγές. Μετά κάνε apply για να συγχρονίσεις το state file.

Ποτέ μην διαγράψεις έναν πόρο από το console και να τον ξαναδημιουργήσεις μέσω Terraform. Αυτό καταστρέφει δεδομένα και σπάει εξαρτήσεις. Import → reconcile → apply.

Η Μία Νόμιμη Χρήση του Console

Το console είναι κατάλληλο για:

  • Read-only εξερεύνηση. CloudWatch logs, CloudWatch metrics, ιστορικό κλήσεων Lambda, επιθεώρηση αντικειμένων DynamoDB.
  • Επείγουσες λειτουργίες με άμεσο import. Αν το production είναι κάτω και η επιδιόρθωση απαιτεί αλλαγή στο console, κάνε την αλλαγή, αλλά τεκμηρίωσέ την και κάνε την import στο Terraform μέσα στην ίδια συνεδρία εργασίας.

Αυτό είναι. Οτιδήποτε δημιουργεί, τροποποιεί ή διαγράφει πόρους ανήκει στο IaC.

Το Deploy Script ως Ενιαίο Σημείο Εισόδου

Οι εντολές terraform apply ή tofu apply που εκτελούνται απευθείας στον host είναι επικίνδυνες επειδή παρακάμπτουν την τυπική ροή deploy της ομάδας σου (validation, lint, plan review). Δημιούργησε ένα deploy script που τυλίγει όλες τις λειτουργίες Terraform:

#!/bin/bash
# deploy.sh - ο μόνος τρόπος αλληλεπίδρασης με την υποδομή

case “$1” in –infra) case “$2” in –apply) run_apply ;; –plan) run_plan ;; –validate) run_validate ;; –tflint) run_tflint ;; –import) run_import “$3” “$4” ;; *) run_plan ;; # default: μόνο plan (ασφαλές) esac ;; esac

Βασικές ιδιότητες ενός καλού deploy script:

  • Default σε plan. Η εκτέλεση ./deploy.sh --infra χωρίς δεύτερο όρισμα πρέπει να δείχνει το plan, ποτέ να μην κάνει apply. Το apply απαιτεί ρητή πρόθεση (--apply).
  • Πάντα validate πριν το apply. Τρέξε terraform validate και tflint πριν από κάθε apply. Απέρριψε το deploy αν κάποιο αποτύχει.
  • Πάντα να δείχνεις το plan πριν το apply. Ακόμα και για --apply, δείξε την έξοδο του plan και απαίτησε επιβεβαίωση σε interactive mode.
  • Συνεπές var-file. Πάντα να περιλαμβάνεις το ίδιο var-file (vars/production.tfvars). Ποτέ μην κάνεις apply χωρίς αυτό.

Το Plan Review ως Πύλη

Πριν από οποιοδήποτε infrastructure PR γίνει merge, η έξοδος του plan πρέπει να είναι ορατή στο PR:
## Terraform Plan

Terraform will perform the following actions:
module.service_api.aws_lambda_function.service_api will be updated in-place
~ resource "aws_lambda_function" "service_api" {
~ timeout = 30 -> 60
}

Plan: 0 to add, 1 to change, 0 to destroy.

module.service_api.aws_lambda_function.service_api will be updated in-place

~ resource "aws_lambda_function" "service_api" {
~ timeout = 30 -> 60
}

Plan: 0 to add, 1 to change, 0 to destroy.

Αυτό επιτυγχάνει δύο πράγματα:

  1. Ο reviewer μπορεί να δει ακριβώς τι θα αλλάξει στο production πριν εγκρίνει.
  2. Η έξοδος του plan είναι ένα ιστορικό αρχείο του τι προοριζόταν κατά τη στιγμή του merge.

Ένα plan που δείχνει απροσδόκητα destroys (N to destroy) πρέπει να μπλοκάρει το PR μέχρι ο συγγραφέας να εξηγήσει το γιατί.

Ο Κανόνας No-Exclude

Ένα συνηθισμένο λάθος όταν ένα apply αποτυγχάνει: εξαίρεσε τον πόρο που αποτυγχάνει και κάνε apply ούτως ή άλλως.

# ΛΑΘΟΣ tofu apply -exclude='aws_bedrockagent_knowledge_base.kb'

Αυτό δημιουργεί ένα μερικό apply: κάποιοι πόροι ενημερώθηκαν, κάποιοι όχι. Το state file τώρα αντικατοπτρίζει μια μερικώς εφαρμοσμένη διαμόρφωση. Ο εξαιρούμενος πόρος είναι εκτός συγχρονισμού με όλα όσα εξαρτώνται από αυτόν.

Η σωστή απάντηση σε έναν πόρο που αποτυγχάνει:

  1. Άφησε το apply να τελειώσει. Μην το διακόψεις.
  2. Κατανόησε γιατί ο πόρος απέτυχε.
  3. Διόρθωσε τη διαμόρφωση ή κάνε import τον υπάρχοντα πόρο.
  4. Τρέξε apply ξανά μέχρι μηδέν σφάλματα.

Αν το σφάλμα είναι "resource already exists": κάνε import.
Αν το σφάλμα είναι αναντιστοιχία ρυθμίσεων: διόρθωσε τη διαμόρφωση για να ταιριάζει με τον υπάρχοντα πόρο.
Αν το σφάλμα είναι πρόβλημα σειράς εξαρτήσεων: διόρθωσε το depends_on.

Ποτέ: μην εξαιρείς, σχολιάζεις ή με άλλο τρόπο παρακάμπτεις πόρους. Η διαμόρφωση IaC πρέπει πάντα να αντικατοπτρίζει την πραγματικότητα.

To Force-Unlock

Το Terraform χρησιμοποιεί έναν πίνακα κλειδώματος (DynamoDB) για να αποτρέψει ταυτόχρονα applies. Αν ένα κλείδωμα έχει κολλήσει (η Lambda έκανε timeout στη μέση του apply, η διεργασία τερματίστηκε), θα δεις:

Error: Error locking state: Error acquiring the state lock

Η δελεαστική λύση: terraform force-unlock. Αυτό είναι επικίνδυνο αν ένα άλλο apply τρέχει πραγματικά - το force-unlock ενός ζωντανού apply προκαλεί καταστροφή.

Η σωστή απάντηση:

  1. Έλεγξε αν κάποιο apply τρέχει πραγματικά (έλεγξε το CloudWatch για το deploy Lambda, ρώτησε τους συναδέλφους).
  2. Αν κανένα apply δεν τρέχει και είσαι βέβαιος ότι το κλείδωμα είναι stale, τότε force-unlock.
  3. Τεκμηρίωσε ότι έκανες force-unlock - είναι ένα σήμα για να διερευνήσεις γιατί το προηγούμενο apply δεν καθάρισε σωστά.

Ποτέ μην κάνεις force-unlock ως πρώτη απάντηση. Πάντα να επαληθεύεις ότι δεν υπάρχει ζωντανό apply πρώτα.

Ανίχνευση Drift

Ακόμα και με αυστηρή πειθαρχία IaC, το drift συσσωρεύεται. Το AWS μπορεί να τροποποιεί αυτόματα πόρους (π.χ., το Cognito να ενημερώνει μια συσχέτιση Lambda trigger), ή ένα μέλος της ομάδας μπορεί να κάνει μια επείγουσα αλλαγή στο console και να ξεχάσει να την κάνει import.

Προγραμμάτισε ένα τακτικό plan run (εβδομαδιαία ή μετά από περιόδους σημαντικής δραστηριότητας) χωρίς apply:

# Στο CI/CD: εβδομαδιαίο προγραμματισμένο plan ./deploy.sh --infra --plan 2>&1 | tee plan-output.txt # Ειδοποίηση αν το plan δείχνει απροσδόκητες αλλαγές

Αν το plan δείχνει αλλαγές που δεν θα έπρεπε να υπάρχουν, διερεύνησε πριν το επόμενο apply τις καταστρέψει.

Βασικά Συμπεράσματα

  • Το AWS console είναι για διάβασμα, όχι για γράψιμο. Κάθε αλλαγή υποδομής ανήκει στο IaC.
  • Όταν ένας πόρος υπάρχει στο AWS αλλά όχι στο Terraform: κάνε τον import, reconcile τη διαμόρφωση, μετά apply.
  • Ποτέ μην χρησιμοποιείς -exclude για να παρακάμψεις έναν πόρο που αποτυγχάνει. Διόρθωσε τη βασική αιτία.
  • Τύλιξε όλες τις λειτουργίες Terraform σε ένα deploy script. Default σε plan; απαίτησε ρητή πρόθεση για apply.
  • Βάλε την έξοδο του plan σε κάθε infrastructure PR. Τα απροσδόκητα destroys πρέπει να μπλοκάρουν το PR.
  • Ποτέ μην κάνεις force-unlock χωρίς να επαληθεύσεις ότι δεν τρέχει ζωντανό apply.
  • Τρέξε προγραμματισμένους ελέγχους plan για έγκαιρη ανίχνευση drift.