من حذف المورد؟ تتبع حذف EC2 باستخدام CloudTrail Event History

استيقظت صباحاً لتجد أن instance كانت تشغّل خدمة إنتاجية قد اختفت — لا تنبيه، لا سبب واضح، فقط حالة terminated في Console. السؤال الأول الذي يتبادر إلى الذهن: من فعل هذا؟ تتبع حذف EC2 instance عبر CloudTrail Event History هو الطريقة الأسرع للوصول إلى إجابة دقيقة دون الحاجة إلى أدوات خارجية.

ملخص سريع (TL;DR) — تتبع حذف EC2

الخطوةالإجراءالهدف
1فتح CloudTrail Event Historyتحديد حدث TerminateInstances
2تصفية بـ Instance ID أو اسم الحدثتضييق نطاق البحث
3فحص تفاصيل الحدثاستخراج userIdentity وعنوان IP
4التحقق من IAM Principalتحديد المستخدم أو الـ Role المسؤول
5مراجعة السياق الكاملهل كان مقصوداً أم خطأ أم اختراقاً؟

كيف يعمل CloudTrail في تسجيل أحداث EC2

CloudTrail يسجّل كل API call موجّه إلى AWS — بما فيها TerminateInstances الصادرة عن Console أو CLI أو SDK أو أي خدمة AWS أخرى. كل حدث يحتوي على: من أصدر الطلب (userIdentity)، من أين (sourceIPAddress)، ومتى (eventTime)، وماذا طلب بالضبط (requestParameters).

Event History في CloudTrail يحتفظ بالأحداث لمدة 90 يوماً تلقائياً دون أي إعداد مسبق، وهو مقيّد بالـ management events فقط — أي الأحداث التي تغيّر حالة الموارد مثل الإنشاء والحذف والتعديل. هذا يعني أن TerminateInstances ستكون موجودة دائماً طالما لم يمرّ على الحذف أكثر من 90 يوماً.

graph LR A["API Call TerminateInstances"] --> B["EC2 API"] B --> C["Instance Terminated"] A --> D["CloudTrail"] D --> E["Event History 90 يوماً"] D --> F["S3 Bucket إذا كان Trail مفعّلاً"] D --> G["CloudWatch Logs إذا كان Trail مفعّلاً"] E --> H["Console / CLI للتحقيق"] F --> H G --> H
  1. API Call: أي طلب TerminateInstances — سواء من Console أو CLI أو SDK — يصل إلى EC2 API.
  2. CloudTrail Capture: CloudTrail يلتقط الحدث فوراً ويخزّنه في Event History.
  3. Event Record: السجل يحتوي على userIdentity وتفاصيل الطلب والاستجابة.
  4. التحقيق: يمكنك الوصول إلى هذا السجل عبر Console أو CLI خلال 90 يوماً.

الخطوة 1: البحث في Event History عبر AWS Console

ابدأ من Console لأنه يعطيك نظرة سريعة على الحدث قبل الغوص في CLI. افتح CloudTrail من قائمة الخدمات، ثم اختر Event History من القائمة الجانبية.

في حقل التصفية، اختر Event name وأدخل TerminateInstances. إذا كنت تعرف Instance ID، غيّر التصفية إلى Resource name وأدخل المعرّف مباشرة — هذا يضيّق النتائج فوراً إذا كان لديك بيئة كبيرة فيها عمليات حذف متعددة.

انقر على الحدث لفتح تفاصيله الكاملة. ستجد قسم Event record يحتوي على JSON كامل للحدث — هذا هو المصدر الحقيقي للمعلومات.

الخطوة 2: تحليل Event Record وفهم userIdentity

الـ JSON الخاص بالحدث هو المكان الذي تجد فيه الإجابة. الحقل الأهم هو userIdentity — وهو يأخذ أشكالاً مختلفة بحسب نوع الـ Principal:

🔽 أمثلة على أنواع userIdentity المختلفة
// IAM User مباشر
{
  "userIdentity": {
    "type": "IAMUser",
    "principalId": "AIDAEXAMPLEID",
    "arn": "arn:aws:iam::123456789012:user/ahmed.ali",
    "accountId": "123456789012",
    "userName": "ahmed.ali"
  }
}

// IAM Role (مثل EC2 instance profile أو Lambda)
{
  "userIdentity": {
    "type": "AssumedRole",
    "principalId": "AROAEXAMPLEID:session-name",
    "arn": "arn:aws:sts::123456789012:assumed-role/DevOpsRole/session-name",
    "accountId": "123456789012",
    "sessionContext": {
      "sessionIssuer": {
        "type": "Role",
        "principalId": "AROAEXAMPLEID",
        "arn": "arn:aws:iam::123456789012:role/DevOpsRole",
        "accountId": "123456789012",
        "userName": "DevOpsRole"
      }
    }
  }
}

// AWS Service (مثل Auto Scaling)
{
  "userIdentity": {
    "type": "AWSService",
    "invokedBy": "autoscaling.amazonaws.com"
  }
}

الفرق بين IAMUser وAssumedRole مهم جداً في التحقيق. إذا كان النوع AssumedRole، فأنت تحتاج إلى النظر في sessionContext.sessionIssuer لمعرفة الـ Role الأصلي، وفي principalId لمعرفة اسم الـ session — وهذا غالباً يكشف عن الـ pipeline أو الـ automation التي نفّذت الأمر.

الـ session name في AssumedRole يشبه بصمة الأصابع — إذا كان اسمه GitHubActions أو terraform-run-12345، فأنت تعرف فوراً من أين جاء الطلب.

الخطوة 3: البحث عبر AWS CLI — أسرع وأكثر دقة

Console مفيد للنظرة الأولى، لكن CLI يتيح لك تصفية أدق وتصدير النتائج. الأمر التالي يجلب أحداث TerminateInstances من آخر 90 يوماً:

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=TerminateInstances \
  --region us-east-1 \
  --query 'Events[*].{Time:EventTime,User:Username,Event:CloudTrailEvent}' \
  --output json

إذا كنت تعرف Instance ID بالضبط، صفّ مباشرة على المورد لتجنّب النتائج الكثيرة:

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=ResourceName,AttributeValue=i-0abc123def456789 \
  --region us-east-1 \
  --output json

لاستخراج تفاصيل userIdentity مباشرة من النتائج باستخدام jq:

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=TerminateInstances \
  --region us-east-1 \
  --output json | jq '.Events[].CloudTrailEvent | fromjson | {time: .eventTime, user: .userIdentity, ip: .sourceIPAddress, instances: .requestParameters.instancesSet}'

هذا الأمر يعطيك مباشرة: وقت الحدث، من نفّذه، عنوان IP المصدر، وقائمة الـ instances التي تم إنهاؤها.

الخطوة 4: قراءة requestParameters لتأكيد الـ Instance المحذوف

حتى لو وجدت الحدث، تأكّد أن الـ instance ID الموجود في requestParameters يطابق ما تبحث عنه. في بعض الأحيان يكون هناك أكثر من instance تم إنهاؤه في نفس الطلب:

// مثال على requestParameters لـ TerminateInstances
{
  "requestParameters": {
    "instancesSet": {
      "items": [
        {"instanceId": "i-0abc123def456789"},
        {"instanceId": "i-0xyz987fed654321"}
      ]
    }
  }
}

وجود أكثر من instance في نفس الطلب يشير غالباً إلى automation أو script — وليس إجراءً يدوياً من Console.

تجربة حقيقية: التشخيص الخاطئ الشائع

في إحدى الحوادث، اتُّهم مطوّر بحذف instance إنتاجية بعد أن ظهر اسمه في Event History. التحقيق الأعمق كشف أن userIdentity.type كان AssumedRole وأن الـ session name كان jenkins-deploy-job-447 — أي أن Jenkins pipeline هو من نفّذ الأمر باستخدام Role مرتبط بذلك المطوّر.

الخطأ كان في pipeline يحذف instances قديمة بناءً على tag معيّن، لكن instance الإنتاج كانت تحمل نفس الـ tag بالخطأ. المطوّر لم يلمس Console أصلاً — لكن Role الخاص به هو من نفّذ الحذف.

الدرس: userIdentity.arn وحده لا يكفي. دائماً افحص sessionContext وsourceIPAddress معاً لتفهم السياق الكامل.

graph TD A["Instance مفقودة"] --> B["فحص Event History"] B --> C["userIdentity.arn يشير لمستخدم"] C --> D{"type = AssumedRole?"} D -- لا --> E["IAM User مباشر تحقق من آخر تسجيل دخول"] D -- نعم --> F["فحص sessionContext و session name"] F --> G["session name: jenkins-deploy-job-447"] G --> H["Jenkins Pipeline هو المصدر الحقيقي"] H --> I["فحص requestParameters والـ tags المستخدمة"] I --> J["tag خاطئ على instance الإنتاج"] J --> K["الإصلاح: تصحيح الـ tag + تقييد صلاحيات الـ Role"]
  1. الأعراض: instance مفقودة، لا تنبيه مسبق.
  2. التشخيص الخاطئ: اتهام المستخدم بناءً على اسمه في الحدث فقط.
  3. السبب الحقيقي: Jenkins pipeline يستخدم Role المستخدم لحذف instances بناءً على tag.
  4. الإصلاح: تصحيح الـ tag على instance الإنتاج + تقييد صلاحيات الـ Role.

الخطوة 5: البحث في CloudWatch Logs إذا تجاوز الحدث 90 يوماً

إذا كان الحذف قد حدث منذ أكثر من 90 يوماً، Event History لن يساعدك. هنا تحتاج إلى CloudTrail Trail مُعدّ مسبقاً ليرسل الأحداث إلى CloudWatch Logs أو S3. إذا كان Trail موجوداً، يمكنك البحث في CloudWatch Logs Insights:

fields eventTime, userIdentity.arn, userIdentity.type, sourceIPAddress, requestParameters
| filter eventName = 'TerminateInstances'
| filter requestParameters like 'i-0abc123def456789'
| sort eventTime desc
| limit 20

هذا الاستعلام يُشغَّل في CloudWatch Logs Insights على Log Group الخاص بـ CloudTrail Trail. إذا لم يكن Trail مُعدّاً، فلا يوجد سجل تاريخي يتجاوز 90 يوماً — وهذا سبب كافٍ لتفعيل Trail دائم في بيئات الإنتاج.

IAM Permissions المطلوبة للتحقيق

للوصول إلى Event History وتنفيذ الأوامر السابقة، تحتاج إلى الصلاحيات التالية كحد أدنى:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CloudTrailReadOnly",
      "Effect": "Allow",
      "Action": [
        "cloudtrail:LookupEvents",
        "cloudtrail:GetTrailStatus",
        "cloudtrail:DescribeTrails"
      ],
      "Resource": "*"
    },
    {
      "Sid": "CloudWatchLogsQuery",
      "Effect": "Allow",
      "Action": [
        "logs:StartQuery",
        "logs:GetQueryResults",
        "logs:DescribeLogGroups"
      ],
      "Resource": "*"
    }
  ]
}

لاحظ أن cloudtrail:LookupEvents يتطلب Resource: * لأنه لا يدعم تقييد الموارد على مستوى ARN محدد — هذا موثّق في AWS Service Authorization Reference.

الوقاية: لا تنتظر حادثة لتفعيل التسجيل الصحيح

Event History يعطيك 90 يوماً فقط وبدون إعداد. لبيئات الإنتاج، الحد الأدنى المطلوب هو:

  • تفعيل CloudTrail Trail متعدد المناطق يرسل الأحداث إلى S3 محمي بـ Object Lock.
  • إرسال الأحداث إلى CloudWatch Logs لتمكين البحث الفوري.
  • إعداد CloudWatch Metric Filter لرصد TerminateInstances وإرسال تنبيه فوري.
  • تفعيل AWS Config لتتبع تغييرات حالة الموارد بشكل مستقل.

تفعيل Trail لا يعني فقط الاحتفاظ بسجل أطول — بل يعني أيضاً القدرة على الاستجابة الفورية بدلاً من التحقيق اللاحق.

الخلاصة والخطوات التالية لتتبع حذف EC2

CloudTrail Event History هو نقطة البداية الصحيحة لأي تحقيق في حذف مورد AWS. الحدث TerminateInstances يحتوي على كل ما تحتاجه: من نفّذ الطلب، من أين، ومتى، وأي instances تأثّرت. الخطأ الشائع هو الاكتفاء بـ userIdentity.arn دون فحص sessionContext — وهو ما يؤدي إلى اتهام خاطئ في حالات الـ automation.

للبيئات التي تحتاج إلى سجل يتجاوز 90 يوماً أو تنبيهات فورية، راجع توثيق إنشاء CloudTrail Trail وتكامل CloudTrail مع CloudWatch Logs.

مسرد المصطلحات

المصطلحالتعريف
Event Historyسجل مدمج في CloudTrail يحتفظ بأحداث الـ management events لمدة 90 يوماً دون إعداد.
userIdentityحقل في سجل CloudTrail يصف الـ Principal الذي أصدر الطلب (IAM User أو Role أو Service).
AssumedRoleنوع من userIdentity يشير إلى أن الطلب جاء من جلسة مؤقتة عبر STS AssumeRole.
Management Eventsالأحداث التي تغيّر حالة موارد AWS مثل الإنشاء والحذف والتعديل — مقابل Data Events التي تتعلق بالبيانات.
CloudTrail Trailإعداد يرسل أحداث CloudTrail باستمرار إلى S3 أو CloudWatch Logs للاحتفاظ بها لفترات أطول.

تعليقات

المشاركات الشائعة من هذه المدونة

استرجاع معرّف نسخة EC2 عبر خدمة البيانات الوصفية IMDSv2

أوضاع سعة DynamoDB: متى تختار On-Demand ومتى تختار Provisioned؟

فهم مهلة الرؤية في SQS: لماذا تُعالَج رسائلك مرتين؟