โ† Semua pembelajaran / Laravel Nol โ†’ Enterprise
Fase 9 ยท Deploy di AWS

CI/CD tanpa access key

Access key di dalam secret repositori adalah kredensial abadi yang bisa dipakai siapa pun dengan akses tulis ke pipeline. OIDC menggantinya dengan token berumur menit yang hanya berlaku untuk repositori dan cabang tertentu.

Intisari

  • GitHub menerbitkan token OIDC; AWS menukarnya dengan kredensial sementara.
  • Tidak ada access key yang perlu disimpan, dirotasi, atau bisa bocor.
  • Trust policy harus membatasi sub ke repositori dan cabang spesifik.
  • Pipeline: lint โ†’ analisis statis โ†’ tes โ†’ build image โ†’ migrasi โ†’ deploy.
  • Migrasi dijalankan sebagai task tersendiri, sebelum service diperbarui.

Menyiapkan penyedia OIDC

aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com
{
  "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:organisasi/portal:ref:refs/heads/main"
      }
    }
  }]
}

Baris sub itu adalah seluruh keamanannya. Menulis repo:organisasi/* berarti setiap repositori di organisasimu bisa mengambil role ini โ€” termasuk repositori yang dibuat siapa pun besok. Menulis repo:organisasi/portal:* berarti setiap cabang, termasuk cabang dari pull request pihak luar. Sebutkan repositori dan cabang, atau lebih baik lagi, environment GitHub yang butuh persetujuan manual.

Alur GitHub Actions

name: deploy

on:
  push:
    branches: [main]

permissions:
  id-token: write        # WAJIB untuk OIDC
  contents: read

jobs:
  periksa:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with: { php-version: '8.4', coverage: none }
      - run: composer install --prefer-dist --no-interaction
      - run: ./vendor/bin/pint --test
      - run: ./vendor/bin/phpstan analyse --error-format=github --no-progress
      - run: php artisan test --parallel

  deploy:
    needs: periksa
    runs-on: ubuntu-latest
    environment: production          # butuh persetujuan manual
    steps:
      - uses: actions/checkout@v4

      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/portal-deploy
          aws-region: ap-southeast-1

      - uses: aws-actions/amazon-ecr-login@v2
        id: ecr

      - name: Bangun dan dorong image
        env:
          REPO: ${{ steps.ecr.outputs.registry }}/portal
          SHA: ${{ github.sha }}
        run: |
          docker buildx build --platform linux/amd64 \
            --cache-from type=gha --cache-to type=gha,mode=max \
            -t "$REPO:$SHA" --push .

      - name: Jalankan migrasi
        run: |
          TASK=$(aws ecs run-task \
            --cluster portal --task-definition portal-migrasi \
            --launch-type FARGATE \
            --network-configuration "awsvpcConfiguration={subnets=[$SUBNETS],securityGroups=[$SG]}" \
            --query 'tasks[0].taskArn' --output text)
          aws ecs wait tasks-stopped --cluster portal --tasks "$TASK"
          KODE=$(aws ecs describe-tasks --cluster portal --tasks "$TASK" \
            --query 'tasks[0].containers[0].exitCode' --output text)
          test "$KODE" = "0"

      - name: Perbarui service
        run: |
          aws ecs update-service --cluster portal --service portal-web \
            --task-definition portal-web --force-new-deployment
          aws ecs update-service --cluster portal --service portal-worker \
            --task-definition portal-worker --force-new-deployment

      - name: Tunggu sampai stabil
        run: |
          aws ecs wait services-stable --cluster portal \
            --services portal-web portal-worker

Tiga baris yang paling sering terlewat. Pertama, memeriksa exit code task migrasi โ€” tanpa itu, migrasi yang gagal tetap diikuti deploy, dan kodemu berjalan di atas skema yang salah. Kedua, aws ecs wait services-stable โ€” tanpa itu, pipeline berwarna hijau padahal task baru sedang gagal menyala berulang kali. Ketiga, memperbarui service worker juga; kalau tidak, worker lama memproses job baru dengan kode lama.

Izin untuk role deploy

{
  "Version": "2012-10-17",
  "Statement": [
    { "Effect": "Allow",
      "Action": ["ecr:GetAuthorizationToken"],
      "Resource": "*" },

    { "Effect": "Allow",
      "Action": ["ecr:BatchCheckLayerAvailability", "ecr:PutImage",
                 "ecr:InitiateLayerUpload", "ecr:UploadLayerPart",
                 "ecr:CompleteLayerUpload", "ecr:BatchGetImage"],
      "Resource": "arn:aws:ecr:ap-southeast-1:123456789012:repository/portal" },

    { "Effect": "Allow",
      "Action": ["ecs:UpdateService", "ecs:DescribeServices",
                 "ecs:RunTask", "ecs:DescribeTasks",
                 "ecs:RegisterTaskDefinition", "ecs:DescribeTaskDefinition"],
      "Resource": "*" },

    { "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": ["arn:aws:iam::123456789012:role/portal-execution",
                   "arn:aws:iam::123456789012:role/portal-task"] }
  ]
}

iam:PassRole harus dibatasi ke role yang spesifik. Dengan "Resource": "*", siapa pun yang bisa mengubah pipeline bisa menjalankan task ECS memakai role apa pun di akunmu โ€” termasuk yang punya akses administratif. Ini eskalasi hak akses yang sering lolos dari review karena terlihat seperti detail teknis.

Urutan pipeline yang benar

  1. Lint dan analisis statis โ€” paling cepat, gagalkan lebih dulu.
  2. Tes dengan database yang sama jenisnya dengan produksi.
  3. Build image, beri tag SHA commit.
  4. Pindai kerentanan pada image; gagalkan kalau ada temuan kritis.
  5. Migrasi sebagai task tersendiri; periksa exit code-nya.
  6. Perbarui service โ€” web, worker, dan penjadwal.
  7. Tunggu stabil, lalu jalankan pemeriksaan asap terhadap URL produksi.

Kenapa migrasi sebelum deploy, bukan sesudah. Selama rolling update, kode lama dan kode baru berjalan bersamaan. Migrasi yang dijalankan lebih dulu berarti kode baru selalu menemukan skema yang dibutuhkannya โ€” dan kode lama tetap bekerja, asalkan migrasinya bersifat aditif. Inilah alasan pola expand & contract dari Fase 2 bukan teori: ia adalah syarat supaya urutan ini aman.

Latihan: siapkan penyedia OIDC dan role deploy dengan sub yang dibatasi ke satu repositori dan cabang main. Jalankan pipeline dari cabang lain dan pastikan ia ditolak โ€” itu bukti pembatasannya benar-benar bekerja.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.