---
title: GitHub Actions تسلسل أدوار (Role Chaining) AWS OIDC عبر حسابات متعددة (2026)
description: 'شرح تطبيقي: النشر إلى حسابات AWS متعددة من GitHub Actions باستخدام دور OIDC hub واحد، و sts:TagSession، و role-chaining: true، والحد الأقصى لمدة ساعة واحدة (2026).'
url: https://nerdleveltech.com/ar/github-actions-multi-account-aws-oidc-role-chaining-tutorial
language: ar
date: '2026-05-30'
category: cloud-devops
tags:
  - github actions
  - oidc
  - aws
  - iam
  - role chaining
  - multi-account
  - sts
  - ci-cd
  - terraform
  - security
markdown_url: https://nerdleveltech.com/ar/github-actions-multi-account-aws-oidc-role-chaining-tutorial.md
attribution: 'Source: NerdLevelTech — https://nerdleveltech.com/ar/github-actions-multi-account-aws-oidc-role-chaining-tutorial'
terms: https://nerdleveltech.com/llms.txt
---

# GitHub Actions تسلسل أدوار (Role Chaining) AWS OIDC عبر حسابات متعددة (2026)

## ملخص

إن النشر في حسابات AWS الخاصة ببيئة الاختبار (staging) والإنتاج (production) من نفس سير عمل GitHub Actions هو مشكلة "مركز وأطراف" (hub-and-spoke): دور واحد موثوق به عبر OIDC في حساب "المركز" (hub)، ودور نشر واحد لكل حساب عمل (spoke)، واستخدام `role-chaining: true` في `aws-actions/configure-aws-credentials@v6.1.3` للتنقل بينهما. يوضح هذا الدليل التعليمي لعام 2026 النمط الكامل، بما في ذلك إذن `sts:TagSession` الذي ينساه الجميع، والحد الأقصى لـ AWS البالغ **ساعة واحدة** للجلسات المتسلسلة، واستخدام بيئات GitHub كبوابة للنشر، والتوافق المستقبلي لمطالبة الموضوع غير القابلة للتغيير (immutable subject claim) التي سيتم تفعيلها للمستودعات الجديدة في **18 يونيو 2026**.

**موجز الإجابة:** ضع دور مركز واحد موثوق به عبر OIDC في حساب AWS مركزي، وامنح كل حساب عمل دور طرف (spoke) يثق في المركز باستخدام `sts:AssumeRole` و `sts:TagSession`، ثم استدعِ `aws-actions/configure-aws-credentials@v6.1.3` مرتين — مرة عبر OIDC، ومرة باستخدام `role-chaining: true`. الجلسات المتسلسلة محدودة بساعة واحدة كحد أقصى.

## ما ستتعلمه

- إنشاء **دور IAM للمركز (hub)** في حساب AWS مركزي يثق في GitHub Actions OIDC لمستودعك.
- إنشاء **دورين IAM للأطراف (spokes)** — `deploy-staging` و `deploy-production` — في حسابات عمل منفصلة يمكن لدور المركز تقمصها عبر حدود الحسابات.
- سير عمل GitHub Actions يقوم بتشغيل `terraform apply` ضد إما بيئة الاختبار أو الإنتاج بناءً على بيئة GitHub المختارة للوظيفة.
- سياسة ثقة للأطراف تمنح كلاً من `sts:AssumeRole` و `sts:TagSession` لدور المركز — والحل البديل `role-skip-session-tagging: true` لأدوار الخدمات المشتركة حيث لا يمكنك منح `sts:TagSession`.
- تحديد سقف `role-duration-seconds: 3600` مدمج في سير العمل بحيث تفشل عمليات النشر بسرعة بدلاً من التوقف المفاجئ عند حدود الجلسة المتسلسلة.
- التوافق المستقبلي مع مطالبة الموضوع غير القابلة للتغيير التي ستطبق على المستودعات الجديدة في 2026-06-18.

يستغرق الأمر حوالي 40-60 دقيقة من البداية للنهاية.

## المتطلبات الأساسية

- ثلاثة حسابات AWS: واحد "للمركز" (مرساة الثقة لـ OIDC) وحسابا عمل "أطراف" لبيئة الاختبار والإنتاج. لا يشترط أن يكون المركز هو حساب إدارة AWS Organization الخاص بك — حساب `shared-services` أو `security-tooling` مخصص هو الخيار التقليدي.
- إعداد نسخة الحساب الواحد من هذا الدليل التعليمي وتفعيلها: [GitHub Actions OIDC إلى AWS: Terraform بدون مفاتيح (2026)](https://nerdleveltech.com/GitHub-actions-oidc-aws-terraform-keyless-deploys-tutorial). دور المركز الذي ستبنيه هنا هو نفس شكل دور الحساب الواحد في ذلك المنشور — هذا الدليل يضيف طبقة التسلسل (chaining) فوقه.
- خلفية S3 لحالة Terraform مع قفل أصلي (native locking) — يوضح دليل [Terraform S3 native state locking](https://nerdleveltech.com/terraform-s3-native-state-locking-tutorial) كيفية توفير واحدة بدون DynamoDB.
- يعمل `aws-actions/configure-aws-credentials@v6.1.3` على `actions/runner` الإصدار v2.327.1 أو أحدث[^v6]؛ مشغلات `ubuntu-latest` المستضافة على GitHub تجاوزت هذا الحد الأدنى في أوائل عام 2026، لذا لا حاجة لتجاوز يدوي هناك.
- Terraform 1.15.5[^tf] و AWS CLI v2.34+[^awscli] على المشغل (يتم توفيرهما بواسطة `hashicorp/setup-terraform@v4.0.1`[^setupterraform]).
- مستودع GitHub مع توفر صلاحية `id-token: write` (أي خطة — OIDC يعمل على Free و Pro و Team و Enterprise Cloud[^oidc]).

## الخطوة 1: لماذا نمط المركز والأطراف (وليس "دور OIDC واحد لكل حساب")

النهج البديهي هو تسجيل مزود OIDC الخاص بـ GitHub في كل حساب عمل وإنشاء دور واحد موثوق به عبر OIDC لكل حساب. هذا ينجح، لكنه يمنحك N من النسخ لنفس إعدادات OIDC التي يجب مزامنتها — كل تغيير في تنسيق مطالبة `sub`، كل ميزة جديدة في GitHub، كل عملية تدقيق. كما يجعل إضافة حساب رابع أو خامس عملية إعداد لكل حساب على حدة.

نمط المركز والأطراف يحول N إلى 1 + N: مزود OIDC واحد في حساب المركز، ودور واحد موثوق به عبر OIDC في حساب المركز، ودور بسيط يعتمد على `sts:AssumeRole` فقط في كل طرف. سياسة ثقة دور المركز هي المورد الوحيد المدرك لـ OIDC — أما أدوار الأطراف فتثق فقط في كيان AWS (ARN الخاص بدور المركز)، وهو نفس النمط التقليدي عبر الحسابات الذي توفره AWS منذ عام 2011.

كما يمنحك لوحة تدقيق واحدة: كل عملية نشر عبر كل حساب تبدأ بنفس دور المركز، وأدوار الأطراف تحتاج فقط لتسجيل "هل قام هذا الـ ARN بتقمص دوري، ولأي مدة" — وهو سؤال يجيب عليه CloudTrail بوضوح بالفعل.

المقايضة: الجلسات المتسلسلة محدودة بساعة واحدة (الخطوة 6 تغطي ما يعنيه ذلك عملياً). بالنسبة لعملية Terraform apply تستغرق 20 دقيقة ضد بضع مئات من الموارد، فهذه ليست مشكلة. أما بالنسبة لعملية ترحيل بنية تحتية تستغرق 90 دقيقة، فستحتاج إلى تصميم مختلف.

## الخطوة 2: إعداد حساب المركز — مزود OIDC ودور المركز

يتم تشغيل هذا مرة واحدة في حساب المركز (سنسميه `123456789012` طوال الوقت). يخبر مزود IAM OpenID Connect شركة AWS بالوثوق في رموز JWT الموقعة من قبل `token.actions.githubusercontent.com`.

```bash
# Run against the hub account
aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com
```

لاحظ عدم وجود `--thumbprint-list`. منذ يوليو 2023، تتحقق AWS من شهادة TLS لنقطة نهاية JWKS مقابل مراجع التصديق الجذرية المضمنة لديها وتتجاهل أي بصمة (thumbprint) تمررها لمزود GitHub؛ الأدلة التعليمية التي لا تزال تثبت البصمة القديمة `6938fd4d…` تكرر طقوساً ما قبل عام 2023 لا تفعل شيئاً اليوم[^thumbprint].

الآن قم بإنشاء دور المركز. احفظ سياسة الثقة باسم `hub-trust.json`:

```json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
      },
      "StringLike": {
        "token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:*"
      }
    }
  }]
}
```

تثبيت `:aud=sts.amazonaws.com` يطابق مدخل `audience` الافتراضي لـ `aws-actions/configure-aws-credentials@v6.1.3`[^v6]. تثبيت `:sub=repo:your-org/your-repo:*` باستخدام `StringLike` يقيد الدور برموز JWT الصادرة لمستودعك المحدد (أي فرع، أي بيئة)؛ ستقوم الخطوة 5 بتشديد هذا القيد أكثر.

قم بإنشاء الدور بسياسة أذونات دنيا. يحتاج دور المركز إلى قدرة واحدة فقط: `sts:AssumeRole` على الـ ARNs الخاصة بالأطراف.

```bash
# Hub account
aws iam create-role \
  --role-name GitHub-actions-hub \
  --assume-role-policy-document file://hub-trust.json \
  --max-session-duration 3600
```

ثم أرفق سياسة مضمنة تسرد الـ ARNs الخاصة بالأطراف التي يمكنه تقمصها:

```bash
cat > hub-permissions.json <<'EOF'
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": [
      "arn:aws:iam::222222222222:role/deploy-staging",
      "arn:aws:iam::333333333333:role/deploy-production"
    ]
  }]
}
EOF

aws iam put-role-policy \
  --role-name GitHub-actions-hub \
  --policy-name AssumeSpokes \
  --policy-document file://hub-permissions.json
```

ملاحظتان حول التصميم. أولاً، تم ضبط `--max-session-duration` لدور المركز على 3600 ثانية. الافتراضي هو 3600 على أي حال، لكن تثبيته صراحةً يزيل الإغراء لأي شخص لزيادته لاحقاً وافتراض أن ذلك سينتقل عبر السلسلة — وهو ما لا يحدث (الخطوة 6). ثانياً، دور المركز لديه صفر من أذونات مستوى البيانات في AWS: لا يمكنه قراءة كائن S3، أو سرد مثيل EC2، أو التحدث إلى RDS. نصف قطر الانفجار الوحيد في حال تسربت بيانات اعتماده هو "يمكن للمهاجم محاولة تقمص أدوار الأطراف، والتي بدورها تقيد الوصول حسب الـ ARN المصدر".

## الخطوة 3: توفير أدوار الأطراف باستخدام sts:TagSession

هذا هو المكان الذي تخطئ فيه معظم الشروحات التعليمية الخاصة بالحسابات المتعددة. يجب أن تمنح سياسة الثقة الخاصة بحساب الـ spoke صلاحية `sts:AssumeRole` وأيضاً `sts:TagSession` لدور الـ hub؛ فبدون `sts:TagSession`، ستفشل عملية AssumeRole المتسلسلة لأن `aws-actions/configure-aws-credentials` يمرر وسوم الجلسة (`GitHub`، `Repository`، `Workflow`، `Action`، `Actor`، `Commit`، `Branch`) بشكل افتراضي، وترفض AWS الطلب عندما يكون إذن تمرير الوسوم مفقوداً[^tagsession].

احفظ هذا كـ `spoke-trust.json` وقم بتطبيقه على كل من حسابي staging و production:

```json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "AWS": "arn:aws:iam::123456789012:role/GitHub-actions-hub"
    },
    "Action": [
      "sts:AssumeRole",
      "sts:TagSession"
    ]
  }]
}
```

الـ `Principal` هو الـ ARN الخاص بدور الـ hub، وليس جذر الحساب (root). إن تحديد الـ principal بـ ARN الدور بدقة يعني أنه حتى لو تم إنشاء دور آخر بطريقة ما في حساب الـ hub، فلن يتمكن من تقمص دور الـ spoke الخاص بك — فقط `GitHub-actions-hub` هو من يستطيع[^principal]. (لا تزال بعض الأنماط القديمة تستخدم `"AWS": "arn:aws:iam::123456789012:root"`. وهذا أقل أماناً بشكل صارم ولا يوجد سبب للقيام بذلك.)

قم بتهيئة كلا الـ spokes. من حساب staging (`222222222222`):

```bash
# Run against the staging spoke account
aws iam create-role \
  --role-name deploy-staging \
  --assume-role-policy-document file://spoke-trust.json \
  --max-session-duration 3600

aws iam attach-role-policy \
  --role-name deploy-staging \
  --policy-arn arn:aws:iam::aws:policy/PowerUserAccess
# Replace PowerUserAccess with your least-privilege policy in real life.
```

كرر العملية ضد `333333333333` من أجل `deploy-production`. ملف سياسة الثقة متطابق؛ ما يختلف لكل spoke هو سياسة الأذونات (عادةً ما يكون الإنتاج production أكثر تقييداً من staging).

إذا رفض فريق الأمان لديك منح `sts:TagSession` لـ principal عبر الحسابات، فلديك مخرج واحد — توضحه الخطوة 4.

## الخطوة 4: كتابة سير عمل التسلسل باستخدام role-chaining: true

الآن سير العمل. الهدف الكامل من `role-chaining: true` يتلخص في وصفه الخاص من بيان الإجراء (action manifest): *"استخدم أوراق الاعتماد الموجودة من البيئة لتقمص دور جديد، بدلاً من تقديم أوراق الاعتماد كمدخلات."*[^rolechaining] بدون هذا العلم، ستحاول خطوة `configure-aws-credentials` الثانية تنفيذ `sts:AssumeRoleWithWebIdentity` ضد دور الـ spoke — لكن دور الـ spoke لا يثق بمزود OIDC؛ بل يثق بـ ARN دور الـ hub. ستحصل على هذا الخطأ المألوف:

```
Error: Could not assume role with OIDC: Not authorized to perform sts:AssumeRoleWithWebIdentity
```

هذا الخطأ هو العلامة رقم 1 على أنك نسيت علم التسلسل (chaining flag)[^391]. مع `role-chaining: true`، تستخدم الخطوة الثانية أوراق الاعتماد التي صدرتها الخطوة الأولى إلى بيئة المشغل (runner) كـ principal يستدعي `sts:AssumeRole` — وهو التدفق العادي عبر الحسابات.

احفظه كـ `.GitHub/workflows/deploy.yml`:

```yaml
name: Deploy

on:
  workflow_dispatch:
    inputs:
      target:
        description: 'Deploy target'
        required: true
        default: 'staging'
        type: choice
        options: [staging, production]

permissions:
  id-token: write
  contents: read

jobs:
  apply:
    runs-on: ubuntu-latest
    environment: ${{ inputs.target }}
    steps:
      - uses: actions/checkout@v6.0.2

      - name: Assume hub role via OIDC
        uses: aws-actions/configure-aws-credentials@v6.1.3
        with:
          aws-region: us-east-1
          role-to-assume: arn:aws:iam::123456789012:role/GitHub-actions-hub
          role-session-name: gh-hub-${{ GitHub.run_id }}
          role-duration-seconds: 3600

      - name: Chain into the spoke deploy role
        uses: aws-actions/configure-aws-credentials@v6.1.3
        with:
          aws-region: us-east-1
          role-to-assume: ${{ inputs.target == 'production'
              && 'arn:aws:iam::333333333333:role/deploy-production'
              || 'arn:aws:iam::222222222222:role/deploy-staging' }}
          role-session-name: gh-spoke-${{ inputs.target }}-${{ GitHub.run_id }}
          role-chaining: true
          role-duration-seconds: 3600

      - uses: hashicorp/setup-terraform@v4.0.1
        with:
          terraform_version: 1.15.5

      - run: terraform init
      - run: terraform plan -out=tfplan
      - run: terraform apply -auto-approve tfplan
```

هناك بعض الأشياء التي تستحق التنويه. يقوم الإجراء بتقمص دور الـ hub باستخدام رمز GitHub OIDC (رمز `ACTIONS_ID_TOKEN_REQUEST_TOKEN` الذي يوفره المشغل والمستبدل برمز STS)، ثم يصدر `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY`/`AWS_SESSION_TOKEN` إلى بيئة المشغل. الخطوة الثانية ليس لها مدخل role-to-assume يشير إلى OIDC — بل يخبره `role-chaining: true` أن "أوراق الاعتماد في بيئتي هي بالفعل principal صالح؛ استخدمها لاستدعاء `sts:AssumeRole`." يتم اختيار ARN الـ spoke بواسطة تعبير ثلاثي (ternary) بناءً على مدخل `target`، ويربط `environment: ${{ inputs.target }}` الوظيفة ببيئة GitHub (سنضيف قواعد حماية في الخطوة 5).

إذا لم تتمكن من منح `sts:TagSession` على الـ spoke (على سبيل المثال، لأن مسار الحوكمة لديك يشحن فقط قالب سياسة ثقة ثابتاً لا يتضمنه)، فقم بتعيين `role-skip-session-tagging: true` في خطوة التسلسل[^skiptag]:

```yaml
      - name: Chain into the shared-services role
        uses: aws-actions/configure-aws-credentials@v6.1.3
        with:
          aws-region: us-east-1
          role-to-assume: arn:aws:iam::444444444444:role/shared-services
          role-session-name: gh-shared-${{ GitHub.run_id }}
          role-chaining: true
          role-skip-session-tagging: true
          role-duration-seconds: 3600
```

المقايضة هنا هي أن الجلسة المتسلسلة تفقد وسوم `GitHub`/`Repository`/`Workflow`/`Commit`/`Branch` — لا يزال CloudTrail يسجل حدث assume-role، لكن تدقيق حساب عبء العمل لم يعد بإمكانه الإجابة على "أي تشغيل لسير العمل قام بهذا؟" من الجلسة نفسها. إذا سلكت هذا المسار، فقم بتعيين اسم الجلسة ليكون شيئاً قابلاً للبحث (يستخدم سير العمل أعلاه `gh-shared-<run_id>`) واعتمد على الربط بين أحداث CloudTrail في حساب الـ hub وحساب الـ spoke بدلاً من ذلك.

## الخطوة 5: ربط أدوار الـ spoke ببيئات GitHub

تسمح سياسة ثقة الـ hub في الخطوة 2 بأي فرع وأي بيئة في مستودعك. هذا جيد بالنسبة للـ hub — فنطاق تأثيره هو مجرد "القدرة على استدعاء AssumeRole على الـ spokes" — ولكن يجب أن تكون أدوار الـ spoke أكثر إحكاماً. على وجه الخصوص، يجب ألا يقبل دور الـ production إلا تسلسلاً نشأ من تشغيل سير عمل مر عبر بيئة GitHub مع قواعد الحماية الصحيحة (المراجعون المطلوبون، مؤقت الانتظار، فروع المصدر المقيدة)[^env].

لا يمكنك القيام بذلك في سياسة ثقة الـ spoke نفسها — فالـ spoke يثق بـ ARN دور الـ hub، وليس بمصدر OIDC الخاص بـ GitHub، لذا فليس لديه وصول إلى مطالبة `token.actions.githubusercontent.com:sub`. يجب أن يتم الربط في طبقة الـ hub، حيث تكون مفاتيح شروط OIDC مرئية.

قم بتشديد `hub-trust.json` لاستخدام `StringLike` على مطالبة الموضوع (subject claim) التي تطابق كلاً من أنماط `environment:staging` و `environment:production`، ولا شيء غيرهما:

```json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
      },
      "StringLike": {
        "token.actions.githubusercontent.com:sub": [
          "repo:your-org/your-repo:environment:staging",
          "repo:your-org/your-repo:environment:production"
        ]
      }
    }
  }]
}
```

مطالبة الموضوع في وظيفة سير عمل مع `environment: production` هي بالضبط `repo:your-org/your-repo:environment:production` (فهي تحل محل مطالبة الفرع/المرجع بالكامل عند تعيين بيئة)[^subjectenv]. عمليات الدفع (pushes) إلى فروع الميزات التي لا تشير إلى بيئة لا يمكنها سك JWT يطابق أي من النمطين، لذا لا يمكنها تقمص دور الـ hub على الإطلاق — مما يعني أنها لا تستطيع الوصول إلى أي من الـ spokes، بغض النظر عما تحاول فعله.

الآن اذهب إلى **Settings → Environments** في المستودع، وقم بإنشاء `staging` و `production`، وفي بيئة `production` قم بتمكين **Required reviewers** (شخص أو شخصان موثوقان)، و **Wait timer** (على سبيل المثال 5 دقائق)، و **Deployment branches** مقيدة بـ `main`. الوظيفة في الخطوة 4 لديها بالفعل `environment: ${{ inputs.target }}`، لذا سيتوقف سير العمل عند تلك القواعد قبل أن يبدأ المشغل في سك OIDC JWT[^env].

قم بتطبيق سياسة الثقة الجديدة:

```bash
aws iam update-assume-role-policy \
  --role-name GitHub-actions-hub \
  --policy-document file://hub-trust.json
```

## الخطوة 6: حد الساعة الواحدة (وماذا تفعل حيال ذلك)

الصفحة الأكثر تخطياً في كل شرح تعليمي لـ AWS متعدد الحسابات هي تلك التي تذكر هذا: **المدة القصوى لجلسة دور متسلسلة هي ساعة واحدة. نقطة.** مرجع AWS STS `AssumeRole` API لا لبس فيه — *"إذا قمت بتقمص دور باستخدام تسلسل الأدوار وقدمت قيمة لمعلمة DurationSeconds أكبر من ساعة واحدة، فستفشل العملية."*[^1hour] تعيين `--max-session-duration` إلى 12 ساعة في دور الـ spoke لا يغير هذا. كما أن رفع `role-duration-seconds` إلى 7200 في سير العمل لا يغير هذا أيضاً؛ سيعيد استدعاء AssumeRole خطأ `DurationSeconds exceeds the MaxSessionDuration set for this role` لأن الحد الأقصى الفعلي للاستدعاء *المتسلسل* هو 3600.

بالنسبة لمعظم عمليات النشر، لا يمثل هذا مشكلة: فعملية `terraform apply` نموذجية على بضع مئات من الموارد تستغرق من 5 إلى 25 دقيقة. ولكن هناك ثلاثة أشياء قد تسبب لك المتاعب:

1. **عمليات Terraform apply الطويلة.** عملية ترحيل (migration) مدتها 90 دقيقة تقوم بإنشاء نسخ RDS للقراءة، وتشغيل تغييرات في المخطط (schema)، وتدوير مجموعات الأمان ستصطدم بالحد الأقصى في منتصف عملية `apply`. تنتهي صلاحية بيانات الاعتماد، ويبدأ موفر AWS في تلقي أخطاء 401، ويتركك Terraform بحالة جزئية (القفل لا يزال محتجزاً، ونصف الموارد تم تغييرها). الحل هو تقسيم النشر إلى استدعاءات `terraform apply` أصغر يتناسب كل منها مع الساعة الواحدة، أو إعادة التصميم بحيث لا تحتاج العملية الطويلة إلى بيانات اعتماد جديدة (يجب تشغيل معظم عمليات ترحيل المخطط داخل التطبيق، وليس داخل Terraform).
2. **منتظرو العمليات غير المتزامنة (Async waiters).** بعض استدعاءات `aws ec2 wait …` أو `aws cloudformation wait stack-update-complete` تقوم بالاستعلام المتكرر (polling) حتى تنتهي عملية طويلة الأمد. إذا استغرقت العملية المنتظرة أكثر من ساعة، سيفشل المنتظر بخطأ `ExpiredToken` وستخرج خطوة سير العمل برمز خطأ (non-zero) على الرغم من نجاح عملية AWS في النهاية. استخدم خطوة استعلام أصغر تخرج بسرعة وتتتبع الاكتمال خارج الجلسة المتسلسلة.
3. **إعادة استخدام بيانات الاعتماد عبر الخطوات.** إذا قمت باستدعاء `configure-aws-credentials` مرة واحدة في بداية المهمة ثم كان لديك عشر خطوات لاحقة تعتمد على متغيرات البيئة المصدرة، فإن جميع الخطوات العشر تشترك في توقيت الجلسة الواحدة. خطوة اختبار مدتها 50 دقيقة تليها خطوة نشر مدتها 15 دقيقة ستفشل عند النشر. إما أن تعيد تشغيل `configure-aws-credentials` مع تفعيل `role-chaining: true` في بداية كل مرحلة طويلة الأمد (وهي عملية غير مكلفة؛ حيث تعيد التسلسل من بيانات اعتماد المحور "hub" التي لا تزال في البيئة) أو قم بالتقسيم إلى مهام منفصلة.

إن تعيين `role-duration-seconds: 3600` صراحةً في كلتا الخطوتين في سير عمل الخطوة 4 هو خيار "الفشل السريع": فهو يخبرك مسبقاً أن لديك ساعة واحدة، بدلاً من التظاهر بأن لديك اثنتي عشرة ساعة.

## الخطوة 7: التوافق المستقبلي لمطالبة الموضوع غير القابلة للتغيير بتاريخ 2026-06-18

أعلنت GitHub عن طرح "مطالبة الموضوع غير القابلة للتغيير" (immutable-subject-claim) في 23 أبريل 2026. بالنسبة لجميع المستودعات التي تم إنشاؤها في أو بعد **18 يونيو 2026** (بالإضافة إلى أي إعادة تسمية للمستودع أو نقل يحدث في ذلك التاريخ أو بعده)، لن تبدو مطالبة موضوع OIDC كما يلي:

```
repo:your-org/your-repo:environment:production
```

بل ستبدو هكذا:

```
repo:your-org-1234567/your-repo-89012345:environment:production
```

يتم إلحاق المعرفات الرقمية للحماية من هجمات إعادة استخدام الأسماء حيث يمكن إعادة تسجيل اسم `org/repo` مُعاد تدويره واستخدامه لإصدار رموز مميزة تطابق سياسة ثقة موجودة[^immutable]. المستودعات الحالية لا تتأثر إلا إذا اخترت ذلك، والتغيير خاص بـ `GitHub.com` فقط — وليس GitHub Enterprise Server.

إذا كان مستودعك أقدم من تاريخ الطرح، فسيستمر سير العمل المذكور أعلاه في العمل. ولكن يجب عليك تحديث سياسة ثقة المحور (hub) الآن لقبول كل من التنسيقات القديمة وغير القابلة للتغيير، بحيث لا ينكسر أي شيء في الساعة 3 صباحاً في اليوم الذي يطبق فيه التغيير على مستودعك (بسبب إعادة التسمية أو النقل أو لأنك اخترت ذلك):

```json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
      },
      "StringLike": {
        "token.actions.githubusercontent.com:sub": [
          "repo:your-org/your-repo:environment:staging",
          "repo:your-org/your-repo:environment:production",
          "repo:your-org-*/your-repo-*:environment:staging",
          "repo:your-org-*/your-repo-*:environment:production"
        ]
      }
    }
  }]
}
```

تطابق علامة النجمة `*` في `StringLike` المعرف الرقمي اللاحق دون إضعاف بقية المسار: المهاجم الذي يسجل مساحة اسم `your-org` في المستقبل لا يمكنه مطابقة أي من النمطين لأنه لا يمكنه إصدار رمز مميز يبدأ `sub` الخاص به بـ `repo:your-org-<your-actual-id>/`.

## التحقق

أسرع فحص للسلامة هو تشغيل سير العمل ضد بيئة staging من واجهة مستخدم GitHub (**Actions → Deploy → Run workflow → target: staging**) ومراقبة كلتا خطوتي `configure-aws-credentials` وهما تسجلان علامة اختيار خضراء ومعرف حساب AWS مخفي. ثم تحقق من CloudTrail في كل حساب فرعي (spoke):

```bash
# في حساب staging الفرعي
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRole \
  --max-results 5 \
  --query 'Events[*].[EventTime,Username,Resources[?ResourceType==`AWS::IAM::Role`].ResourceName | [0]]' \
  --output table
```

يجب أن ترى حدثاً في الحساب الفرعي حيث يكون `Username` (الأصيل) هو `GitHub-actions-hub/gh-hub-<run_id>` (اسم جلسة دور المحور من الخطوة 4)، والمورد هو `deploy-staging`. هذا هو التسلسل الذي يعمل من البداية إلى النهاية: قام OIDC JWT بإنشاء جلسة المحور في الحساب `123456789012`، وقامت جلسة المحور بانتحال دور `deploy-staging` في الحساب `222222222222`.

إذا قمت بتغيير مدخل `target` إلى `production` من فرع PR، فيجب أن يفشل التشغيل في مرحلة "انتظار المراجعة" — وليس في طبقة IAM — لأن قواعد حماية البيئة تمنعه قبل أن يحاول المشغل (runner) البدء.

## الأخطاء الشائعة

**خطأ `Not authorized to perform sts:AssumeRoleWithWebIdentity` في الخطوة الثانية.** لقد نسيت `role-chaining: true` في خطوة `configure-aws-credentials` الثانية. عاد الإجراء افتراضياً إلى تدفق OIDC ضد الدور الفرعي، والذي لا يثق في موفر OIDC[^391].

**خطأ `User: arn:aws:sts::123456789012:assumed-role/GitHub-actions-hub/... is not authorized to perform: sts:TagSession on resource: arn:aws:iam::222222222222:role/deploy-staging`.** سياسة ثقة الدور الفرعي تفتقد إلى `sts:TagSession`. إما أن تضيفها إلى سياسة ثقة الدور الفرعي (المسار المفضل في الخطوة 3) أو قم بتعيين `role-skip-session-tagging: true` في خطوة التسلسل (خيار الطوارئ في الخطوة 4)[^tagsession].

**خطأ `The requested DurationSeconds exceeds the MaxSessionDuration set for this role`** في خطوة التسلسل، على الرغم من أنك قمت بتعيين `--max-session-duration 3600` لكلا الدورين. من المؤكد تقريباً أنك قمت بتعيين `role-duration-seconds` بقيمة أعلى من `3600` في خطوة التسلسل. الجلسات المتسلسلة محدودة بساعة واحدة بغض النظر عن `MaxSessionDuration` لأي من الدورين[^1hour]. اضبطها على `3600` (أو احذفها — فـ `3600` هي القيمة الافتراضية).

**الرموز التي تنتهي صلاحيتها بعد 55 دقيقة.** الوقت الفعلي لمهمتك من "إصدار رمز OIDC" إلى "الخطوة التي تحتاج إلى بيانات الاعتماد" أطول مما تعتقد — مؤقتات انتظار البيئة، والمراجعون المطلوبون، والمشغلون في قائمة الانتظار، كلهم يستهلكون من نفس الساعة. أعد تشغيل `configure-aws-credentials` مع `role-chaining: true` في بداية أي مرحلة تبدأ بعد أكثر من 30 دقيقة من خطوة المحور.

**فشل سير العمل فوراً بخطأ `Error: Unable to retrieve OIDC ID token: NotFound`.** المهمة تفتقد إلى `permissions: id-token: write`. تمنحها الكتلة البرمجية في سير عمل الخطوة 4 على مستوى سير العمل؛ إذا كنت تحدد الأذونات لكل مهمة على حدة، فإن كل مهمة تستدعي `configure-aws-credentials` تحتاج إلى هذا السطر[^oidc].

## الخطوات التالية

لديك الآن مرساة ثقة OIDC واحدة وعدد N من الأدوار الفرعية، محمية ببيئات GitHub ومتوافقة مستقبلاً مع طرح 18 يونيو. ثلاثة توسعات طبيعية:

- **إضافة دور فرعي رابع لحساب تطوير منفصل.** كل شيء في الخطوة 3 يعتمد على قالب `account_id`؛ أضف بيئة `dev` في GitHub، ونمط `:environment:development` في سياسة ثقة المحور، ودور `deploy-development` في الحساب الجديد. لا حاجة لموارد جديدة في جانب المحور.
- **استبدال قائمة AssumeRole المضمنة في `hub-permissions.json` بشرط يعتمد على العلامات (tags).** بدلاً من إدراج كل ARN فرعي صراحةً، قم بإرفاق علامة مثل `Purpose=GitHubDeploy` بكل دور فرعي واكتب سياسة المحور كـ `"Resource": "*"` مع `"Condition": {"StringEquals": {"iam:ResourceTag/Purpose": "GitHubDeploy"}}`. ستحتاج الأدوار الفرعية الجديدة فقط إلى العلامة.
- **اربط هذا بحاجز حماية "الفشل المفتوح" (fail-open) في عملية النشر نفسها.** إذا كان الحساب الفرعي غير متاح لفترة وجيزة، فيجب أن يفشل النشر بوضوح بدلاً من التوقف — أنماط [البرمجيات الوسيطة للفشل المفتوح مقابل الفشل المغلق](https://nerdleveltech.com/fail-open-vs-fail-closed-hono-middleware-Redis-tutorial) يمكن تعميمها على حواجز حماية CI أيضاً.

نمط الـ hub-and-spoke يتوسع خطياً مع الحسابات ويظل قابلاً للمراجعة والتدقيق مع نموه. الأجزاء التي يخطئ فيها معظم الناس — `sts:TagSession`، و `role-chaining: true`، والحد الأقصى للساعة الواحدة — هي بالضبط الأجزاء التي حددها هذا الدليل بمصادر موثقة، لذا عندما يسألك المهندس التالي في فريقك "لماذا يوجد 3600 في سير عمل النشر الخاص بنا؟" سيكون لديك إجابة لا تبدأ بـ "أعتقد أن..."

[^v6]: `aws-actions/configure-aws-credentials` v6.1.3 release notes (2026-05-27); v6.0.0 release notes (2026-02-04) document the node24 runtime bump and the GitHub Actions Runner v2.327.1 minimum. <https://GitHub.com/aws-actions/configure-aws-credentials/releases>

[^tf]: HashiCorp Terraform releases — 1.15.5 is the current stable per the authoritative release directory. <https://releases.hashicorp.com/terraform/>

[^awscli]: AWS CLI v2 installation reference. <https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html>

[^setupterraform]: `hashicorp/setup-terraform` v4.0.1 release notes (2026-05-12). <https://GitHub.com/hashicorp/setup-terraform/releases/tag/v4.0.1>

[^oidc]: GitHub OpenID Connect reference — minting JWTs, the `id-token: write` permission, audience and subject claim formats. <https://docs.GitHub.com/en/actions/concepts/security/openid-connect>

[^thumbprint]: "Obtain the thumbprint for an OpenID Connect identity provider" — AWS IAM docs explicitly state the thumbprint is no longer required for the GitHub provider and is ignored if supplied. <https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html>

[^tagsession]: "Pass session tags in AWS STS" — every role connected to the identity provider passing tags must have `sts:TagSession` in its trust policy, or the AssumeRole operation fails. <https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html>

[^principal]: "How to use trust policies with IAM roles" — AWS Security Blog on pinning the trust principal to the exact role ARN instead of the account root. <https://aws.amazon.com/blogs/security/how-to-use-trust-policies-with-iam-roles/>

[^rolechaining]: `aws-actions/configure-aws-credentials` action manifest, `role-chaining` input description: *"Use existing credentials from the environment to assume a new role, rather than providing credentials as input."* <https://GitHub.com/aws-actions/configure-aws-credentials/blob/main/action.yml>

[^391]: Issue #391 on `aws-actions/configure-aws-credentials` — the canonical write-up of "I forgot role-chaining: true and got AssumeRoleWithWebIdentity not authorized." <https://GitHub.com/aws-actions/configure-aws-credentials/issues/391>

[^skiptag]: Issue #1396 — the credentials-chaining README example does not work without either `sts:TagSession` on the spoke trust policy OR `role-skip-session-tagging: true` on the chaining step. <https://GitHub.com/aws-actions/configure-aws-credentials/issues/1396>

[^env]: GitHub Environments and protection rules — required reviewers, wait timers, branch restrictions. <https://docs.GitHub.com/en/actions/how-tos/manage-workflow-runs/manage-environments>

[^subjectenv]: GitHub OIDC reference — the subject claim is `repo:ORG-NAME/REPO-NAME:environment:ENVIRONMENT-NAME` when a job references an environment, replacing the branch `ref:` form. <https://docs.GitHub.com/actions/reference/openid-connect-reference>

[^1hour]: AWS STS `AssumeRole` API reference — "When you use role chaining, your new credentials are limited to a maximum duration of one hour." <https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html>

[^immutable]: "Immutable subject claims for GitHub Actions OIDC tokens" — GitHub Changelog, 2026-04-23. New repositories adopt the immutable format automatically on 2026-06-18; existing repositories must opt in. GitHub.com only. <https://GitHub.blog/changelog/2026-04-23-immutable-subject-claims-for-GitHub-actions-oidc-tokens/>
