أساسيات Terraform

الحالة: قلب Terraform

18 دقيقة الدرس 5 من 30

الحالة: قلب Terraform

في كل مرة ينشئ فيها Terraform بنية تحتية أو يعدّلها أو يحذفها، يُسجّل النتيجة في ملف مرجعي وحيد يُعرف بـالحالة (State). بدون هذا الملف، لن يعرف Terraform ما سبق بناؤه، وسيحاول في كل مرة إنشاء كل شيء من الصفر. ولذلك فإن الحالة ليست ميزة اختيارية، بل هي الآلية التي تُتيح إدارة البنية التحتية بصورة تدريجية وقابلة للتكرار دون تكرار الأعمال..

ما هي الحالة فعليًا؟

الحالة هي مستند JSON (يُسمى عادةً terraform.tfstate) يربط كل مورد في إعداداتك بنظيره الحقيقي عبر واجهة برمجة المزوّد. فمثلاً بالنسبة لنموذج EC2 في AWS، يحتفظ ملف الحالة بمعرّف النموذج والـ AMI والعنوان الخاص ومجموعات الأمان والوسوم وعشرات السمات الأخرى التي لم يكن Terraform يعرفها وقت التخطيط. وتُستخدم هذه السمات المخزّنة من أجل:

  • حساب الفوارق — مقارنة الإعداد المطلوب بالحالة الفعلية في السحابة دون استدعاء واجهة برمجة التطبيقات لكل سمة على حدة.
  • بناء مخطط التبعيات — المراجع العابرة بين الموارد كـ aws_subnet.main.id تُحلَّل من الحالة لا من استدعاءات API مباشرة.
  • اكتشاف الانجراف — عند تعديل البنية التحتية خارج Terraform، تكشف الحالة عن التناقض.
  • تفعيل العمليات المحددة — terraform apply -target=aws_instance.web يعمل لأن Terraform يعرف أي الموارد قائمة بالفعل.
الحالة هي مصدر الحقيقة لما يديره Terraform — لا الكود. ملفات HCL تصف النية، أما الحالة فتصف الواقع كما رصده Terraform آخر مرة. مرحلة التخطيط تُوازن بين الاثنين.

المقارنة الثلاثية

كل terraform plan يُجري مقارنة ثلاثية:

  1. الحالة المطلوبة — ملفات .tf الخاصة بك.
  2. الحالة المُسجَّلةterraform.tfstate.
  3. الحالة الفعلية — قراءة حديثة من واجهة برمجة المزوّد (ما يُعرف بـ "التحديث" أو refresh).

يقارن Terraform المطلوب مع المُسجَّل بعد تحديثه لإنتاج الخطة. ولهذا السبب تُجري terraform plan استدعاءات API حقيقية: فهي تحتاج إلى تحديث الحالة المُسجَّلة قبل إجراء الفارق. يمكنك تخطّي التحديث بـ -refresh=false لتسريع العملية، لكن لا تفعل ذلك إلا عندما تكون متأكدًا من عدم وجود أي تغيير خارجي.

Terraform Three-Way State Reconciliation HCL Config (.tf files) Desired State State File (tfstate) Recorded State Cloud API (AWS / GCP / Azure) Actual State Plan Engine 3-way diff + dep graph Execution Plan + / ~ / - changes listed desired recorded refresh
ثلاثة مدخلات تُغذّي محرك التخطيط: إعداد HCL المطلوب، والحالة المُسجَّلة، وتحديث مباشر من API — وينتج عنها خطة التنفيذ.

تشريح ملف الحالة

لا يُقصد تعديل ملف الحالة يدويًا، لكن قراءته تُعلّمك بالضبط ما يتتبعه Terraform. بعد تشغيل terraform apply على VPC وشبكة فرعية بسيطة، يحتوي الملف على مصفوفة resources يحمل كل إدخال فيها:

  • mode — إما managed (أنشأه Terraform) أو data (مصدر بيانات للقراءة فقط).
  • type وname — مثلاً aws_vpc / main.
  • provider — عنوان المزوّد المسؤول عن هذا المورد.
  • instances — مصفوفة (إدخال واحد لكل نسخة عبر count أو for_each) تحتوي على schema_version وخريطة attributes كاملة.

فحص الحالة بأمان

لا تفتح terraform.tfstate في محرر نصي للفحص الروتيني. استخدم أوامر CLI التي تحلل الحالة وتعرضها بوضوح، وتحترم القفل عند استخدام الحالة البعيدة.

# سرد كل المورد التي يعرفها Terraform terraform state list # فحص مورد واحد بالتفصيل (يعرض جميع السمات) terraform state show aws_vpc.main # سحب الحالة كاملةً بصيغة JSON (للقراءة فقط — آمن للسكريبت) terraform show -json | jq '.values.root_module.resources[] | {address, type, values}' # التحقق من إصدار الحالة ورقم التسلسل terraform show # تحديث الحالة من المزوّد دون إجراء أي تغييرات terraform apply -refresh-only

يبدو ناتج terraform state show aws_vpc.main كالتالي عندما تكون الـ VPC قيد التشغيل:

# aws_vpc.main: resource "aws_vpc" "main" { arn = "arn:aws:ec2:us-east-1:123456789012:vpc/vpc-0abc123" cidr_block = "10.0.0.0/16" default_route_table_id = "rtb-0def456" dhcp_options_id = "dopt-0ghi789" enable_dns_hostnames = true enable_dns_support = true id = "vpc-0abc123" instance_tenancy = "default" main_route_table_id = "rtb-0def456" owner_id = "123456789012" tags = { "Environment" = "production" "Name" = "main" } }
اجعل terraform state list عادة يومية للتحقق السريع. في وحدة جذر كبيرة، رؤية مورد غير متوقع في القائمة — أو غياب مورد متوقع — هو أول إشارة على انجراف أو كسر ناجم عن إعادة هيكلة. شغّلها بعد كل apply في CI وقارن مخرجاتها بالتشغيل السابق.

عمليات الحالة التي يجب أن تعرفها

إلى جانب الفحص، يمنحك الأمر الفرعي terraform state تحكمًا دقيقًا:

  • terraform state mv — إعادة تسمية مورد أو نقل عنوانه في الحالة دون تدميره وإعادة إنشائه. ضروري عند إعادة هيكلة إعداد مسطح إلى وحدات (modules).
  • terraform state rm — حذف مورد من الحالة دون لمس البنية التحتية الفعلية. يُستخدم عندما تريد أن "ينسى" Terraform موردًا ما (مثلاً عند نقله لحالة فريق آخر).
  • terraform import — إدراج مورد سحابي قائم تحت إدارة Terraform بكتابة سماته في الحالة. ضروري للتعامل مع البنية التحتية الموروثة (brownfield).
التلاعب بالحالة دائم ولا يمكن التراجع عنه. خطأ في state rm يليه apply سيُدمّر المورد الحقيقي. وخطأ في state mv قد يُسبّب استبدالًا في الخطة التالية. خذ نسخة احتياطية دائمًا أولاً: terraform state pull > backup.tfstate. في الإنتاج، لا تُشغّل أوامر الحالة الفرعية أبدًا بدون مراجعة زميل وخطة تراجع واضحة.

رقم التسلسل وحارس الإصدار

كل كتابة على الحالة تزيد رقم serial بمقدار واحد. إذا طبّق مهندسان في آنٍ واحد من أجهزة مختلفة — كلاهما بدأ من serial رقم 7 — فإن الثاني الذي ينتهي سيجد أن الحالة البعيدة باتت عند serial رقم 8 وسيرفض الكتابة فوقها. هكذا تعمل الحالة كقفل تزامن متفائل عند استخدام backend بعيد. ملف terraform.tfstate المحلي لا يملك هذه الحماية، وهذا تحديدًا لماذا الحالة المحلية آمنة للمشغّل الفردي فقط.

القيم الحساسة في الحالة

يخزّن Terraform جميع سمات الموارد في الحالة، بما فيها الأسرار ككلمات مرور قواعد البيانات والمفاتيح الخاصة ورموز الوصول. حتى لو وسّمت ناتجًا بـ sensitive = true، تبقى القيمة موجودة بنص صريح داخل ملف الحالة. لهذا تداعيات أمنية مباشرة في الإنتاج:

  • لا تُلزم terraform.tfstate أو terraform.tfstate.backup أبدًا في نظام إدارة الإصدار. أضف كليهما إلى .gitignore فورًا.
  • استخدم backend بعيدًا مع تشفير وقت التخزين (S3 + SSE-KMS أو Terraform Cloud) حتى لا تُخزَّن الحالة أبدًا على جهاز مطوّر.
  • قيّد وصول IAM / RBAC إلى bucket الحالة على دور CI/CD والمهندسين الرئيسيين فقط.
الحالة كمحيط أمني. في شركات كـ Google وMeta وStripe، يُعامَل bucket الحالة الخاص بـ Terraform بنفس ضوابط الوصول المُطبَّقة على خزائن الأسرار. الوصول للقراءة على الحالة = وصول لكل سر كتبه Terraform على الإطلاق. الحالة البعيدة مع IAM دقيق والتشفير من جانب الخادم هي الحد الأدنى؛ والحالة المُشفَّرة في Terraform Cloud مع سجلات التدقيق هي المعيار الذهبي.