OpenTofu
أداة بنية تحتية كشفرة مفتوحة المصدر وفرع مجتمعي من Terraform.
التثبيت
Section titled “التثبيت”التثبيت حسب المنصة
Section titled “التثبيت حسب المنصة”| الأمر | الوصف |
|---|---|
brew install opentofu | التثبيت على macOS باستخدام Homebrew |
snap install --classic opentofu | التثبيت على Linux باستخدام Snap |
choco install opentofu | التثبيت على Windows باستخدام Chocolatey |
curl -fsSL https://get.opentofu.org/install-opentofu.sh | sh | التثبيت عبر سكريبت shell |
docker run -it ghcr.io/opentofu/opentofu | التشغيل عبر Docker |
tofu --version | عرض الإصدار المثبت |
tofu -help | عرض الأوامر المتاحة |
تثبيت مدير الإصدارات
Section titled “تثبيت مدير الإصدارات”# استخدام tofuenv (مثل tfenv لـ OpenTofu)
git clone https://github.com/tofuutils/tofuenv.git ~/.tofuenv
echo 'export PATH="$HOME/.tofuenv/bin:$PATH"' >> ~/.bashrc
# عرض الإصدارات المتاحة
tofuenv list-remote
# تثبيت إصدار محدد
tofuenv install 1.8.0
tofuenv use 1.8.0
# تثبيت الإصدار في المشروع
echo "1.8.0" > .opentofu-version
الأوامر الأساسية
Section titled “الأوامر الأساسية”أوامر سير العمل
Section titled “أوامر سير العمل”| الأمر | الوصف |
|---|---|
tofu init | تهيئة دليل العمل وتنزيل الموفرين |
tofu init -upgrade | إعادة التهيئة وتحديث إصدارات الموفرين/الوحدات |
tofu init -backend-config=prod.hcl | التهيئة مع ملف تكوين الخلفية |
tofu plan | معاينة تغييرات البنية التحتية |
tofu plan -out=plan.tfplan | حفظ الخطة في ملف للتطبيق لاحقاً |
tofu plan -target=aws_instance.web | تخطيط التغييرات لمورد محدد فقط |
tofu plan -var-file=prod.tfvars | التخطيط مع ملف متغيرات محدد |
tofu apply | تطبيق تغييرات البنية التحتية (يطلب التأكيد) |
tofu apply -auto-approve | تطبيق التغييرات بدون تأكيد |
tofu apply plan.tfplan | تطبيق ملف خطة محفوظ |
tofu destroy | تدمير جميع البنية التحتية المُدارة |
tofu destroy -auto-approve | التدمير بدون تأكيد |
tofu destroy -target=aws_instance.web | تدمير مورد محدد فقط |
أوامر الفحص
Section titled “أوامر الفحص”| الأمر | الوصف |
|---|---|
tofu validate | التحقق من صحة بنية التكوين |
tofu fmt | تنسيق ملفات التكوين |
tofu fmt -check | التحقق من تنسيق الملفات (متوافق مع CI) |
tofu fmt -recursive | تنسيق جميع الملفات في الأدلة الفرعية |
tofu output | عرض جميع قيم المخرجات |
tofu output -json | عرض المخرجات بتنسيق JSON |
tofu output db_password | عرض قيمة مخرج محددة |
tofu show | عرض الحالة الحالية بتنسيق مقروء |
tofu show -json | عرض الحالة بتنسيق JSON |
tofu show plan.tfplan | عرض تفاصيل الخطة المحفوظة |
tofu graph | إنشاء رسم بياني لتبعيات الموارد (تنسيق DOT) |
tofu graph | dot -Tpng > graph.png | تحويل رسم التبعيات إلى PNG |
tofu refresh | مزامنة الحالة مع البنية التحتية الحقيقية |
tofu providers | عرض الموفرين المستخدمين في التكوين |
التكوين (HCL)
Section titled “التكوين (HCL)”تكوين الموفرين
Section titled “تكوين الموفرين”# كتلة الموفرين المطلوبة
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
# تشفير الحالة (ميزة حصرية لـ OpenTofu)
encryption {
method "aes_gcm" "default" {
keys = key_provider.pbkdf2.mykey
}
state {
method = method.aes_gcm.default
enforced = true
}
plan {
method = method.aes_gcm.default
enforced = true
}
key_provider "pbkdf2" "mykey" {
passphrase = var.state_passphrase
}
}
}
# موفر بتكوين افتراضي
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
ManagedBy = "opentofu"
Environment = var.environment
Project = var.project_name
}
}
}
# اسم مستعار للموفر لمتعدد المناطق
provider "aws" {
alias = "west"
region = "us-west-2"
}
# استخدام موفر بالاسم المستعار على مورد
resource "aws_instance" "west_server" {
provider = aws.west
ami = "ami-0abcdef1234567890"
instance_type = "t3.micro"
}
تعريفات الموارد
Section titled “تعريفات الموارد”# مورد أساسي
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = {
Name = "${var.project_name}-web"
}
}
# مصدر بيانات (قراءة الموارد الموجودة)
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
# القيم المحلية
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "opentofu"
}
az_count = length(data.aws_availability_zones.available.names)
}
# Count و for_each
resource "aws_subnet" "public" {
count = local.az_count
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = merge(local.common_tags, {
Name = "${var.project_name}-public-${count.index}"
Type = "public"
})
}
resource "aws_security_group_rule" "ingress" {
for_each = {
http = { port = 80, cidrs = ["0.0.0.0/0"] }
https = { port = 443, cidrs = ["0.0.0.0/0"] }
ssh = { port = 22, cidrs = [var.admin_cidr] }
}
type = "ingress"
from_port = each.value.port
to_port = each.value.port
protocol = "tcp"
cidr_blocks = each.value.cidrs
security_group_id = aws_security_group.main.id
}
المتغيرات والمخرجات
Section titled “المتغيرات والمخرجات”تعريفات المتغيرات
Section titled “تعريفات المتغيرات”| الأمر | الوصف |
|---|---|
variable "name" { type = string } | تعريف متغير نصي |
variable "port" { type = number default = 8080 } | متغير بقيمة افتراضية |
variable "tags" { type = map(string) } | متغير خريطة |
variable "cidrs" { type = list(string) } | متغير قائمة |
variable "config" { type = object({ ... }) } | متغير كائن بهيكل محدد |
variable "db_pass" { sensitive = true } | تعليم المتغير كحساس (مخفي في المخرجات) |
variable "env" { validation { ... } } | متغير مع تحقق مخصص |
تمرير المتغيرات
Section titled “تمرير المتغيرات”| الأمر | الوصف |
|---|---|
tofu plan -var="name=value" | تمرير متغير عبر سطر الأوامر |
tofu plan -var-file="prod.tfvars" | تمرير متغيرات من ملف |
TF_VAR_name=value tofu plan | تمرير عبر متغير بيئة |
إنشاء ملف terraform.tfvars | قيم متغيرات تُحمّل تلقائياً |
إنشاء ملف *.auto.tfvars | ملفات متغيرات تُحمّل تلقائياً |
أمثلة المتغيرات والمخرجات
Section titled “أمثلة المتغيرات والمخرجات”# variables.tf
variable "environment" {
type = string
description = "بيئة النشر"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "يجب أن تكون البيئة dev أو staging أو prod."
}
}
variable "instance_config" {
type = object({
instance_type = string
volume_size = number
encrypted = bool
})
default = {
instance_type = "t3.micro"
volume_size = 20
encrypted = true
}
}
variable "database_password" {
type = string
sensitive = true
}
# outputs.tf
output "vpc_id" {
description = "معرّف VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "قائمة معرّفات الشبكات الفرعية العامة"
value = aws_subnet.public[*].id
}
output "web_url" {
description = "عنوان URL لخادم الويب"
value = "https://${aws_lb.web.dns_name}"
}
output "db_connection_string" {
description = "سلسلة اتصال قاعدة البيانات"
value = "postgresql://${var.db_username}:${var.database_password}@${aws_db_instance.main.endpoint}/${var.db_name}"
sensitive = true
}
ملفات المتغيرات
Section titled “ملفات المتغيرات”# prod.tfvars
environment = "prod"
instance_config = {
instance_type = "t3.large"
volume_size = 100
encrypted = true
}
# dev.tfvars
environment = "dev"
instance_config = {
instance_type = "t3.micro"
volume_size = 20
encrypted = false
}
إدارة الحالة
Section titled “إدارة الحالة”أوامر الحالة
Section titled “أوامر الحالة”| الأمر | الوصف |
|---|---|
tofu state list | عرض جميع الموارد في الحالة |
tofu state show aws_instance.web | عرض تفاصيل مورد محدد |
tofu state mv aws_instance.old aws_instance.new | إعادة تسمية مورد في الحالة |
tofu state rm aws_instance.web | إزالة مورد من الحالة (يحتفظ بالبنية التحتية الحقيقية) |
tofu state pull | سحب الحالة البعيدة إلى stdout |
tofu state push | دفع الحالة المحلية إلى الخلفية البعيدة |
tofu import aws_instance.web i-1234567 | استيراد مورد موجود إلى الحالة |
tofu state replace-provider hashicorp/aws registry.opentofu.org/hashicorp/aws | استبدال الموفر في الحالة |
tofu force-unlock LOCK_ID | تحرير قفل حالة عالق |
تكوين الخلفية
Section titled “تكوين الخلفية”# خلفية S3 (الأكثر شيوعاً)
terraform {
backend "s3" {
bucket = "my-tofu-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "tofu-locks"
encrypt = true
}
}
# خلفية PostgreSQL
terraform {
backend "pg" {
conn_str = "postgres://user:pass@db.example.com/tofu_state?sslmode=require"
}
}
# خلفية HTTP (تعمل مع GitLab وبدائل Terraform Cloud)
terraform {
backend "http" {
address = "https://gitlab.example.com/api/v4/projects/1/terraform/state/prod"
lock_address = "https://gitlab.example.com/api/v4/projects/1/terraform/state/prod/lock"
unlock_address = "https://gitlab.example.com/api/v4/projects/1/terraform/state/prod/lock"
username = "gitlab-ci-token"
password = var.ci_token
}
}
# خلفية محلية (افتراضي، للتطوير)
terraform {
backend "local" {
path = "terraform.tfstate"
}
}
تشفير الحالة (حصري لـ OpenTofu)
Section titled “تشفير الحالة (حصري لـ OpenTofu)”# تشفير الحالة مع AWS KMS
terraform {
encryption {
key_provider "aws_kms" "main" {
kms_key_id = "arn:aws:kms:us-east-1:123456789:key/abcd-1234"
region = "us-east-1"
key_spec = "AES_256"
}
method "aes_gcm" "default" {
keys = key_provider.aws_kms.main
}
state {
method = method.aes_gcm.default
enforced = true
}
}
}
الوحدات
Section titled “الوحدات”استخدام الوحدات
Section titled “استخدام الوحدات”| الأمر | الوصف |
|---|---|
module "vpc" { source = "./modules/vpc" } | استخدام وحدة محلية |
module "vpc" { source = "github.com/org/repo//modules/vpc" } | استخدام وحدة من GitHub |
module "vpc" { source = "registry.opentofu.org/..." } | استخدام وحدة من سجل OpenTofu |
tofu get | تنزيل وتحديث الوحدات |
tofu get -update | فرض تحديث جميع الوحدات |
هيكل الوحدات
Section titled “هيكل الوحدات”modules/
└── vpc/
├── main.tf # الموارد
├── variables.tf # متغيرات الإدخال
├── outputs.tf # قيم المخرجات
├── versions.tf # متطلبات الموفرين
└── README.md # توثيق الاستخدام
مثال على وحدة
Section titled “مثال على وحدة”# modules/vpc/main.tf
resource "aws_vpc" "this" {
cidr_block = var.cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = merge(var.tags, { Name = var.name })
}
resource "aws_subnet" "public" {
count = length(var.public_subnets)
vpc_id = aws_vpc.this.id
cidr_block = var.public_subnets[count.index]
availability_zone = var.azs[count.index]
map_public_ip_on_launch = true
tags = merge(var.tags, {
Name = "${var.name}-public-${count.index}"
})
}
# modules/vpc/variables.tf
variable "name" { type = string }
variable "cidr" { type = string }
variable "azs" { type = list(string) }
variable "public_subnets" { type = list(string) }
variable "tags" { type = map(string) default = {} }
# modules/vpc/outputs.tf
output "vpc_id" { value = aws_vpc.this.id }
output "public_subnet_ids" { value = aws_subnet.public[*].id }
# استخدام الوحدة
module "vpc" {
source = "./modules/vpc"
name = "production"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
tags = local.common_tags
}
الانتقال من Terraform
Section titled “الانتقال من Terraform”التوافق المباشر
Section titled “التوافق المباشر”| الأمر | الوصف |
|---|---|
استبدال terraform بـ tofu في الأوامر | أوامر CLI متوافقة مباشرة |
tofu init -upgrade | إعادة التهيئة مع موفري OpenTofu |
الاحتفاظ بملفات .tf الموجودة دون تغيير | صيغة HCL متوافقة بالكامل |
الاحتفاظ بملفات .tfstate الموجودة | تنسيق الحالة متوافق |
الاحتفاظ بملف .terraform.lock.hcl الموجود | ملف القفل متوافق |
تحديث CI/CD لاستخدام إجراء opentofu/setup-opentofu | تبديل GitHub Actions |
قائمة التحقق من الانتقال
Section titled “قائمة التحقق من الانتقال”الانتقال من Terraform إلى OpenTofu:
1. تثبيت OpenTofu بجانب Terraform
2. في دليل المشروع، تشغيل: tofu init
3. تشغيل: tofu plan
- مقارنة المخرجات مع: terraform plan
- يجب أن تكون متطابقة (بدون تغييرات)
4. استبدال 'terraform' بـ 'tofu' في أنابيب CI/CD
5. تحديث GitHub Actions:
- القديم: hashicorp/setup-terraform
- الجديد: opentofu/setup-opentofu
6. تحديث مصادر الموفرين في ملف القفل (إذا لزم الأمر):
- tofu providers lock
7. تفعيل تشفير الحالة (حصري لـ OpenTofu):
- إضافة كتلة encryption إلى كتلة terraform {}
8. تبديل مراجع سجل الوحدات:
- registry.terraform.io → registry.opentofu.org
- (كلاهما يعمل، لكن سجل OpenTofu مفضّل)
CI/CD مع GitHub Actions
Section titled “CI/CD مع GitHub Actions”name: OpenTofu Plan & Apply
on:
push:
branches: [main]
pull_request:
jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: opentofu/setup-opentofu@v1
with:
tofu_version: 1.8.0
- name: Init
run: tofu init
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Plan
run: tofu plan -out=plan.tfplan -no-color
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Apply (الفرع الرئيسي فقط)
if: github.ref == 'refs/heads/main'
run: tofu apply -auto-approve plan.tfplan
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
الميزات الحصرية لـ OpenTofu
Section titled “الميزات الحصرية لـ OpenTofu”تشفير الحالة
Section titled “تشفير الحالة”تشفير ملفات الحالة أثناء الراحة — غير متوفر في Terraform. يدعم موفري مفاتيح PBKDF2 و AWS KMS و GCP KMS و Azure Key Vault.
تشفير الحالة من جانب العميل
Section titled “تشفير الحالة من جانب العميل”على عكس تشفير Terraform Cloud من جانب الخادم، يقوم OpenTofu بتشفير الحالة قبل مغادرتها جهازك، مما يضمن عدم تمكن أي طرف ثالث من قراءة حالة البنية التحتية الخاصة بك.
استقلالية السجل
Section titled “استقلالية السجل”يحتفظ OpenTofu بسجل الموفرين والوحدات الخاص به في registry.opentofu.org، مما يضمن التوفر على المدى الطويل بشكل مستقل عن قرارات ترخيص HashiCorp.
أفضل الممارسات
Section titled “أفضل الممارسات”-
استخدام حالة بعيدة مع القفل — تكوين خلفية S3 + DynamoDB (أو ما يعادلها) لمنع التعديلات المتزامنة وتمكين التعاون بين الفريق.
-
تفعيل تشفير الحالة — استخدام ميزة التشفير الحصرية لـ OpenTofu مع KMS أو PBKDF2 لحماية البيانات الحساسة في ملفات الحالة.
-
تثبيت إصدارات الموفرين والوحدات — استخدام قيود
~>(مثل~> 5.0) للسماح بتحديثات التصحيح مع منع التغييرات المعطّلة. -
استخدام مساحات العمل أو فصل الأدلة للبيئات — الحفاظ على فصل حالات dev و staging والإنتاج لمنع التغييرات العرضية بين البيئات.
-
كتابة وحدات للأنماط القابلة لإعادة الاستخدام — استخراج الأنماط الشائعة (VPCs وقواعد البيانات ومجموعات Kubernetes) إلى وحدات بمدخلات ومخرجات واضحة.
-
التحقق من المتغيرات بالقيود — إضافة كتل
validationللمتغيرات لالتقاط القيم غير الصالحة قبل وصولها إلى موفر السحابة. -
حفظ الخطط قبل التطبيق — استخدام
tofu plan -out=plan.tfplanثمtofu apply plan.tfplanلضمان تطبيق ما راجعته بالضبط. -
استخدام
default_tagsعلى الموفرين — تعيين العلامات التنظيمية (ManagedBy و Environment و Project) على مستوى الموفر لضمان وسم متسق. -
استيراد الموارد الموجودة — استخدام
tofu importلإدخال البنية التحتية غير المُدارة تحت سيطرة OpenTofu بدلاً من إعادة إنشائها. -
تشغيل
tofu fmt -checkفي CI — فرض تنسيق متسق بين جميع أعضاء الفريق بإفشال بناءات CI على الملفات غير المنسقة. -
استخدام مصادر البيانات للمراجع الخارجية — الإشارة إلى البنية التحتية الموجودة (AMIs و VPCs ومناطق DNS) بكتل
dataبدلاً من ترميز المعرّفات الثابتة. -
مراجعة الخطط بعناية — قراءة مخرجات الخطة دائماً قبل التطبيق. الانتباه لإجراءات
destroyوreplaceالتي قد تسبب توقف الخدمة.