تعلّم أساسيات صياغة Terraform خلال 20 دقيقة
مقدمة سريعة إلى لغة Terraform
إذا كنت تبدأ رحلتك مع Terraform، فستحتاج أولاً إلى فهم البنية الأساسية لملفات الإعداد قبل الانتقال إلى بناء بنية تحتية فعلية على السحابة. يقدّم هذا الدليل شرحاً عملياً ومكثفاً لأهم عناصر الصياغة في Terraform، مع مثال تطبيقي على إنشاء مثيل EC2 في AWS.
الهدف هنا ليس تغطية كل تفصيلة في التوثيق الرسمي، بل تقديم نقطة انطلاق واضحة تساعدك على قراءة ملفات HCL، وهي اختصار لـ HashiCorp Configuration Language، وفهم كيفية استخدام الكتل والمتغيرات والمخرجات ووسائط التحكم الخاصة.

قبل البدء، تأكد من تثبيت Terraform على جهازك المحلي، وأن لديك صلاحية الوصول إلى لوحة إدارة AWS، بالإضافة إلى إعداد مستخدم IAM مخصص لتنفيذ أوامر Terraform.
فهم الكتل والوسائط في Terraform
تعتمد صياغة Terraform على مفهومين أساسيين: الكتل Blocks والوسائط Arguments. الكتلة تمثل وحدة تنظيمية تحمل إعدادات مرتبطة بنوع معين، بينما تُستخدم الوسائط لتحديد القيم الخاصة بخصائص هذه الكتلة.
provider "aws" {
region = "us-west-1"
}
resource "aws_instance" "myec2" {
ami = "ami-12345qwert"
instance_type = "t2.micro"
}
في المثال السابق لدينا كتلتان:
- كتلة
providerلتحديد مزوّد الخدمة السحابية. - كتلة
resourceلتعريف المورد الفعلي الذي سيُنشأ.
تحتوي كتلة provider على الوسيط region الذي يحدد المنطقة المستهدفة. أما كتلة resource فتضم وسيطين أساسيين هما ami وinstance_type. القيم النصية في Terraform تُكتب بين علامتي اقتباس.
لكل كتلة نوع ومعرّفات إدخال labels. على سبيل المثال:
- الكتلة
provider "aws"تستخدم تسمية واحدة تحدد اسم المزوّد. - الكتلة
resource "aws_instance" "myec2"تستخدم تسميتين: نوع المورد واسم المورد.
كيفية إنشاء مثيل EC2 باستخدام Terraform
إنشاء ملفات المشروع
ابدأ بإنشاء مجلد خاص بالمشروع، ثم أنشئ داخله ملفاً باسم main.tf. يتعامل Terraform تلقائياً مع جميع الملفات التي تحمل الامتدادات .tf أو .tf.json داخل المجلد نفسه على أنها جزء من الإعدادات.
تعريف المزوّد المطلوب
بما أننا سننشئ مورداً على AWS، يجب أولاً تعريف المزوّد المطلوب وإصداره:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}
provider "aws" {
region = "us-west-1"
}
هنا تظهر كتلتان مهمتان:
terraform: كتلة عليا تُستخدم لتحديد مزوّدات المشروع والمتطلبات العامة.provider: تحدد إعدادات الاتصال بمزوّد السحابة.
وجود كتلة terraform ليس إلزامياً دائماً، لكنه ممارسة ممتازة خصوصاً عندما تعمل على مشاريع أكبر أو تستخدم إدارة حالة state عن بُعد.
ما هو دور Providers في Terraform؟
تثبيت Terraform وحده لا يكفي. فالأداة تعتمد على بنية إضافات plugins، حيث تمثل نواة Terraform المحرك الأساسي، بينما يتوفر لكل مزوّد سحابي مكوّن إضافي خاص به.
عند تنفيذ الأمر terraform init، يقوم Terraform بتنزيل الإضافات المطلوبة استناداً إلى ما تم تعريفه في required_providers. لكل provider موارده ومصادر بياناته وخيارات ضبطه الخاصة، ولهذا يُعد الرجوع إلى سجل Terraform Registry خطوة مهمة أثناء العمل.
فهم Resources في Terraform
تمثل الموارد resources العناصر الفعلية التي سيُنشئها Terraform على البنية التحتية السحابية. في حالة AWS، فإن المورد aws_instance يمثل مثيل EC2.
لإنشاء مثيل فعلي، أضف الكود التالي إلى ملف main.tf:
resource "aws_instance" "demo" {
ami = "ami-00831fc7c1e3ddc60"
instance_type = "t2.micro"
tags = {
name = "Demo System"
}
}
في هذا المثال:
amiيحدد صورة النظام المستخدمة.instance_typeيحدد مواصفات المثيل.tagsيضيف وسوماً تعريفية تساعد في تنظيم الموارد.
بعد حفظ الملف، يمكنك تشغيل الأوامر التالية بالتسلسل:
terraform initterraform planterraform apply
الأمر plan يعرض ما سيحدث قبل التنفيذ، بينما يقوم apply بتطبيق الإعدادات فعلياً.
استخدام المتغيرات لجعل الإعدادات أكثر مرونة
يعمل Terraform كلغة تصريحية declarative، أي أنك تصف الحالة النهائية المطلوبة، وهو يتكفل بخطوات الوصول إليها. لكن هذا لا يمنع من جعل الإعدادات أكثر مرونة باستخدام المتغيرات variables.
المتغيرات مفيدة جداً عندما تريد إعادة استخدام قيم معينة في أكثر من موضع، أو عندما ترغب في تعديل البيئة دون المساس بمنطق الإعداد بالكامل.
variable "region" {
default = "us-west-1"
description = "AWS Region"
}
variable "ami" {
default = "ami-00831fc7c1e3ddc60"
description = "Amazon Machine Image ID for Ubuntu Server 20.04"
}
variable "type" {
default = "t2.micro"
description = "Size of VM"
}
يمكن استدعاء قيمة أي متغير باستخدام الصيغة var.<name>.
تحديث الإعدادات لاستخدام المتغيرات
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}
provider "aws" {
region = var.region
}
variable "region" {
default = "us-west-1"
description = "AWS Region"
}
variable "ami" {
default = "ami-00831fc7c1e3ddc60"
description = "Amazon Machine Image ID for Ubuntu Server 20.04"
}
variable "type" {
default = "t2.micro"
description = "Size of VM"
}
resource "aws_instance" "demo" {
ami = var.ami
instance_type = var.type
tags = {
name = "Demo System"
}
}
output "instance_id" {
value = aws_instance.demo.id
}
بعد تشغيل terraform plan ستلاحظ أن Terraform يعرض قيمة الخرج output على أنها معروفة بعد التنفيذ فقط known after apply.
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ instance_id = (known after apply)
بعد تنفيذ terraform apply ستتمكن من رؤية قيمة instance_id. وعند الانتهاء من التجربة، احرص على تشغيل terraform destroy لتجنب استهلاك موارد غير ضرورية.
ما هي Output Values ولماذا تُستخدم؟
تسمح لك المخرجات outputs بإرجاع معلومات من التهيئة بعد اكتمال التنفيذ، مثل عنوان IP أو معرّف المثيل أو اسم المورد. هذه القيم مفيدة عند ربط Terraform بأدوات أخرى أو عند تمرير نتائج التنفيذ إلى واجهات خارجية.
يدعم Terraform أيضاً المتغيرات المحلية locals، وهي قيم مؤقتة تُستخدم داخل الإعدادات لتبسيط التعبيرات وتحسين القراءة.
Provisioners في Terraform: متى نستخدمها؟
بعد تجهيز الجهاز أو المثيل، قد تحتاج أحياناً إلى تنفيذ خطوات أولية مثل تثبيت تحديثات أو إعداد برامج أساسية. هنا يظهر دور provisioners.
مع ذلك، من المهم فهم أن Terraform ليس بديلاً كاملاً لأدوات الإدارة التكوينية مثل Ansible أو Chef أو Salt Stack. الأفضل استخدامه لتنفيذ الخطوات الأولية فقط، مثل:
- تثبيت حزم أساسية.
- تنزيل وكيل مراقبة أو أداة إدارة.
- تهيئة أولية للوصول أو السياسات.
أفضل طريقة لتنظيم ملفات Terraform
كلما كبر المشروع، أصبح تقسيم الشيفرة إلى ملفات متعددة أكثر أهمية من ناحية الصيانة والوضوح. من الشائع تنظيم المشروع بالشكل التالي:
variables.tf: يحتوي على المتغيرات والمخرجات.provider.tf: يضم تعريفاتproviders.main.tf: يضم الموارد الفعلية.
صحيح أن Terraform يتعامل مع كل ملفات المجلد كتهيئة واحدة، لكن التنظيم الجيد يقلل الأخطاء ويسهّل التعاون داخل الفرق.
فهم Meta-Arguments في Terraform
تُعد meta-arguments إعدادات خاصة تُستخدم داخل كتل الموارد للتحكم في سلوك Terraform عند إنشاء الموارد أو تعديلها. وهي مفيدة جداً في البيئات العملية التي تتطلب مرونة إضافية.
استخدام Provider Meta-Argument
عند الحاجة إلى العمل مع أكثر من إعداد للمزوّد نفسه، مثل منطقتين مختلفتين في AWS، يمكنك تعريف أكثر من كتلة provider باستخدام alias.
provider "aws" {
alias = "aws_west"
region = var.region_west
}
provider "aws" {
alias = "aws_east"
region = var.region_east
}
ثم عرّف المتغيرات المرتبطة بالمناطق:
variable "region_west" {
default = "us-west-1"
description = "AWS West Region"
}
variable "region_east" {
default = "us-east-1"
description = "AWS East Region"
}
وبعدها يمكنك ربط المورد بإعداد محدد من خلال الوسيط provider:
resource "aws_instance" "demo" {
provider = aws.aws_west
ami = var.ami
instance_type = var.type
tags = {
name = "Demo System"
}
}
يمكنك التأكد من صحة الإعداد عبر الأمر terraform validate.
استخدام Lifecycle Meta-Argument
يتحكم الوسيط lifecycle في طريقة تعامل Terraform مع دورة حياة الموارد. ومن أشهر إعداداته:
create_before_destroy: ينشئ المورد الجديد أولاً قبل حذف القديم.prevent_destroy: يمنع حذف المورد ويُظهر خطأ عند محاولة ذلك.ignore_changes: يتجاهل تغييرات محددة على بعض الخصائص.
يفيد هذا الوسيط في حماية الموارد الحساسة أو تقليل أثر التعديلات الحرجة على البنية التحتية.
استخدام Depends On
في أغلب الحالات، يستطيع Terraform اكتشاف الاعتماديات بين الموارد تلقائياً. لكن عندما تكون العلاقة غير واضحة، يمكن استخدام depends_on لفرض ترتيب التنفيذ.
هذا الوسيط يقبل قائمة بمعرّفات الموارد التي يجب أن تعتمد عليها الكتلة الحالية، ويُستخدم خصوصاً عندما تكون الاعتمادية منطقية وليست بنيوية بشكل مباشر.
استخدام Count لإنشاء موارد متعددة
إذا أردت إنشاء عدة موارد متشابهة من نفس النوع، فيمكنك الاستفادة من count:
resource "aws_instance" "demo" {
count = 3
provider = aws.aws_west
ami = var.ami
instance_type = var.type
tags = {
name = "Demo System"
}
}
بهذا سيُنشئ Terraform ثلاثة مثيلات متطابقة. لكن ستحتاج أيضاً إلى تعديل المخرجات، لأن المورد أصبح متعدد النسخ. هنا يظهر دور splat expression:
output "instance_id" {
value = aws_instance.demo[*].id
}
هذا التعبير يعيد قائمة تحتوي على معرّفات كل المثيلات المنشأة.
استخدام For Each لتخصيص الموارد المتعددة
الوسيط for_each يشبه count من حيث إنشاء أكثر من مورد، لكنه أكثر مرونة. بدلاً من تكرار مورد بعدد ثابت فقط، يسمح لك بإنشاء موارد استناداً إلى map أو list.
resource "aws_instance" "demo" {
for_each = {
fruit = "apple"
vehicle = "car"
continent = "Europe"
}
provider = aws.aws_west
ami = var.ami
instance_type = var.type
tags = {
name = "${each.key}: ${each.value}"
}
}
يمنحك الكائن each إمكانية الوصول إلى each.key وeach.value. وهكذا يمكن إنشاء موارد متشابهة مع اختلافات مخصصة في الوسوم أو الأسماء.
وبما أن for_each هنا يعتمد على map، فإن تعبير splat لن يكون مناسباً للمخرجات. البديل هو استخدام تعبير for:
output "instance_id" {
value = [for b in aws_instance.demo : b.id]
}
التعبيرات Expressions في Terraform
التعبيرات expressions هي ما يجعل شيفرة Terraform أكثر ديناميكية ومرونة. بعضها بسيط، مثل إسناد قيمة مباشرة إلى وسيط، وبعضها أكثر تقدماً مثل تعبيرات for وsplat والتعامل مع القوائم والخرائط.
كلما توسّع مشروعك، ستعتمد أكثر على التعبيرات لتقليل التكرار وجعل الإعدادات أوضح وأسهل صيانة.
الدوال Functions في Terraform
يوفر Terraform مجموعة كبيرة من الدوال المضمنة التي تساعد على معالجة النصوص والأرقام والملفات والتواريخ والتحويل بين الأنواع وغيرها. وعند دمج الدوال مع التعبيرات، يصبح من السهل جداً بناء إعدادات بنية تحتية ذكية وقابلة للتوسع.
من الناحية العملية، تظهر أهمية الدوال عندما تحتاج إلى:
- تنسيق النصوص.
- قراءة ملفات خارجية.
- معالجة القوائم والخرائط.
- تنفيذ تحويلات على أنواع البيانات.
نصائح عملية قبل تشغيل Terraform في بيئة حقيقية
- استخدم
terraform validateقبلplanلاكتشاف الأخطاء الصياغية مبكراً. - راجع مخرجات
terraform planبدقة قبل التنفيذ. - قسّم المشروع إلى ملفات منطقية منذ البداية.
- احرص على استخدام المتغيرات بدلاً من القيم الثابتة قدر الإمكان.
- بعد الاختبارات، نفّذ
terraform destroyلتفادي التكاليف غير الضرورية.
الخلاصة التقنية
يُعد Terraform من أقوى أدوات Infrastructure as Code بفضل بساطة صياغته ومرونته العالية في إدارة الموارد السحابية. فهمك للكتل blocks، والمتغيرات variables، والمخرجات outputs، ووسائط التحكم مثل count وfor_each وlifecycle يمنحك أساساً متيناً لبناء بنية تحتية قابلة للتوسع والصيانة. من وجهة نظر تقنية، أفضل بداية مع Terraform هي التركيز على كتابة إعدادات صغيرة وواضحة، ثم تطويرها تدريجياً مع الالتزام بتنظيم الملفات ومراجعة التنفيذ قبل التطبيق الفعلي.