لو فاتورة AWS عندك بتزيد كل شهر من غير ما الترافيك يزيد، غالبًا انت بتدفع لموارد محدش بيستخدمها. سكربت واحد بيكشفها ويوفّر مئات الدولارات سنويًا بدون ما تلمس أي workload شغّال.
المستوى المطلوب: محترف. الشرح بيفترض إن عندك حساب AWS، صلاحيات IAM للقراءة على EC2، ومعرفة أساسية بـ Python و boto3.
أتمتة كشف موارد AWS اليتيمة وتقليل الهدر في الفاتورة
المشكلة باختصار
الهدر في السحابة نادرًا بيجي من سيرفر غالي. بيجي من حاجات صغيرة منسية بتتراكم. مهندس عمل terminate لـ EC2 instance، بس الـ EBS volume فضل موجود. حد حجز Elastic IP لتجربة وسابه. lifecycle policy اتلغت فتراكمت اللقطات (snapshots). كل واحدة لوحدها رخيصة. مجموعهم بيبقى بند ثابت في الفاتورة كل شهر.
المشكلة إن AWS مش بتنبهك على ده. الفاتورة بتيجي إجمالية، والـ Cost Explorer بيوريك التصنيف مش "إيه اللي مش مستخدم". لازم حد يدور بنفسه، وده بالظبط اللي بنأتمته هنا.
يعني إيه "مورد يتيم"؟
تخيّل إنك استأجرت مخزن، فضّيته من 6 شهور، ونسيت تلغي الاشتراك. كل شهر بيتخصم منك إيجار مخزن فاضي. انت مش بتاخد أي قيمة، بس الفلوس بتمشي. ده بالظبط المورد اليتيم.
علميًا: المورد اليتيم (orphaned resource) هو مورد provisioned في الحساب، لسه AWS بتحاسبك عليه، لكنه مش مرتبط بأي workload شغّال. أشهر 3 أنواع: EBS volumes في حالة available (مش متركّبة على أي instance)، Elastic IPs غير مرتبطة بـ instance أو ENI، وsnapshots قديمة مالهاش lifecycle policy.
ركّز في نقطة مهمة في الـ Elastic IP: من أول فبراير 2024، AWS بقت بتحاسب على كل عنوان IPv4 عام بسعر 0.005 دولار/ساعة، يعني حوالي 3.6 دولار شهريًا للعنوان الواحد — سواء مرتبط أو لأ. قبل كده العنوان المرتبط كان مجاني. ده غيّر حسابات كتير.
السكربت اللي بيكشفهم
المنطق بسيط: اسأل EC2 API عن الـ volumes المتاحة، والـ IPs غير المرتبطة، واللقطات الأقدم من 90 يوم. احسب التكلفة التقديرية، وابعت الناتج على Slack. السكربت للقراءة فقط — بيبلّغ ولا بيمسح، وده قرار مقصود هنشرحه تحت.
import boto3, os, datetime, json, urllib.request
GP3_GB_MONTH = 0.08 # سعر تخزين gp3 لكل GB شهريًا (us-east-1)
SNAP_GB_MONTH = 0.05 # سعر لقطات EBS لكل GB شهريًا
EIP_MONTH = 3.60 # عنوان IPv4 عام (~0.005$/ساعة)
SLACK = os.environ["SLACK_WEBHOOK"]
ec2 = boto3.client("ec2")
waste, total = [], 0.0
# 1) EBS volumes غير مرتبطة (status = available)
for v in ec2.describe_volumes(
Filters=[{"Name": "status", "Values": ["available"]}])["Volumes"]:
cost = v["Size"] * GP3_GB_MONTH
total += cost
waste.append(f"EBS {v['VolumeId']} ({v['Size']}GB) ~ ${cost:.2f}/شهر")
# 2) Elastic IPs غير مرتبطة
for a in ec2.describe_addresses()["Addresses"]:
if "AssociationId" not in a:
total += EIP_MONTH
waste.append(f"EIP {a['PublicIp']} ~ ${EIP_MONTH:.2f}/شهر")
# 3) لقطات أقدم من 90 يوم
cutoff = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=90)
for s in ec2.describe_snapshots(OwnerIds=["self"])["Snapshots"]:
if s["StartTime"] < cutoff:
cost = s["VolumeSize"] * SNAP_GB_MONTH
total += cost
waste.append(f"Snapshot {s['SnapshotId']} ({s['VolumeSize']}GB) ~ ${cost:.2f}/شهر")
if waste:
text = "*موارد AWS يتيمة محتمل توفيرها:*\n" + "\n".join(f"• {w}" for w in waste)
text += f"\n\n*الإجمالي التقديري: ${total:.2f}/شهر*"
body = json.dumps({"text": text}).encode()
req = urllib.request.Request(
SLACK, data=body, headers={"Content-Type": "application/json"})
urllib.request.urlopen(req, timeout=10)
للتشغيل الأسبوعي، حطّه في Lambda وربطه بـ EventBridge Scheduler بتعبير cron زي cron(0 8 ? * MON *) (كل اثنين 8 صباحًا UTC). أو على سيرفر فيه crontab بسطر واحد. الصلاحية المطلوبة قراءة فقط: ec2:DescribeVolumes, ec2:DescribeAddresses, ec2:DescribeSnapshots.
الأرقام: بتوفّر كام فعلاً
على حساب dev واحد بعد سنة شغل (الأرقام تقديرية مبنية على متوسط حساب فريق صغير): 14 volume من نوع gp3 غير مرتبطة بإجمالي 620GB ≈ 49.6 دولار/شهر. 9 عناوين IPv4 منسية ≈ 32.4 دولار/شهر. لقطات قديمة 480GB ≈ 24 دولار/شهر. الإجمالي ≈ 106 دولار/شهر، يعني حوالي 1,272 دولار في السنة بتتدفع مقابل صفر قيمة.
الـ trade-off في الأولوية: أعلى ROI هنا للـ Elastic IPs لأن تكلفتها ثابتة لكل عنوان ومش بتعتمد على حجم، وحذفها فوري وآمن. بعدها الـ volumes. اللقطات آخر حاجة لأنها أرخص وأحيانًا مطلوبة كـ backup.
الـ trade-offs وما يجب الانتباه له
- الأسعار في الكود ثابتة (hardcoded). بتكسب بساطة، بتخسر الدقة عبر الـ regions و storage types. لو عندك io2 أو st1، السعر مختلف. للتقدير ده مقبول؛ للمحاسبة الدقيقة استخدم Cost Explorer API.
- السكربت بيقرأ region واحد افتراضيًا. الموارد اليتيمة بتختبي في regions منسية. لفّ على
ec2.describe_regions()لو عندك انتشار جغرافي. - "غير مرتبط" مش دائمًا "آمن للحذف". volume متاح ممكن يكون فيه بيانات لسه محتاجها. خلّي القرار بشري — عشان كده السكربت بيبلّغ بس.
متى لا تستخدم هذه الطريقة
لو شغّال على AWS Organizations بعشرات الحسابات، متبنيش ده يدوي. استخدم AWS Trusted Advisor (خطة Business أو أعلى) أو AWS Config rules اللي بتغطي ده مركزيًا مع dashboards جاهزة. كمان لو الفاتورة الشهرية أقل من بضع مئات الدولارات، الوفر هيكون أصغر من وقت الصيانة. الأتمتة دي منطقية في النطاق المتوسط: حساب أو كام حساب، فاتورة معتبرة، ومفيش أداة FinOps مركزية لسه.
المصادر
- إعلان AWS عن محاسبة عناوين IPv4 العامة (ساري من 1 فبراير 2024):
aws.amazon.com/blogs/aws/new-aws-public-ipv4-address-charge-public-ip-insights - تسعير Amazon EBS (gp3 والـ snapshots):
aws.amazon.com/ebs/pricing - توثيق boto3 لـ EC2 (describe_volumes / describe_addresses / describe_snapshots):
boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2 - جدولة المهام عبر Amazon EventBridge Scheduler:
docs.aws.amazon.com/scheduler/latest/UserGuide
الخطوة التالية
افتح الـ console دلوقتي وروح على EC2 ثم Volumes، وفلتر على State = available. لو لقيت ولو volume واحد، انت عندك هدر مؤكد. شغّل السكربت يدوي مرة على region الإنتاج بتاعك، وشوف الرقم اللي هيطلع قبل ما تجدوله أسبوعيًا.