1. はじめに
GitHub Actions から Azure にデプロイする際、これまでは az ad sp create-for-rbac --sdk-auth で生成した長寿命の JSON シークレットを AZURE_CREDENTIALS として GitHub Secrets に保存する方法が広く使われてきました。しかし Azure CLI のリファレンス上でもこの --sdk-auth(--json-auth のエイリアス)は 非推奨(Deprecated) と明記されており、代わりに OIDC (OpenID Connect) による Workload Identity Federation への移行が公式に推奨されています。
本記事では、GitHub Actions の id-token: write 権限と Microsoft Entra ID の federated credential を組み合わせ、長寿命シークレットを一切保存せずに Azure へログインする 手順を、実際のセットアップ手順に沿って一般化して解説します。GitHub Actions のワークフロー設計そのものについては Reusable Workflows 実践ガイド でも扱っているので、あわせて参照してください。
2. OIDC 認証の仕組み
2.1 なぜ長寿命シークレットをやめるのか
GitHub Secrets に保存した長寿命のクラウド認証情報は、漏洩・失効忘れ・ローテーション運用の負荷といったリスクを常に抱えます。Microsoft Entra ID の公式ドキュメントも「ワークロード ID フェデレーションは、資格情報の手動管理負荷とシークレット漏洩・証明書失効リスクの両方を排除する」と説明しており、OIDC 方式へ切り替えることで次のメリットが得られます[^2]。
- クラウド側にシークレットを複製する必要がない
- クラウド側の IAM(Entra ID / Azure RBAC)でアクセスを一元管理できる
- トークンが自動的に短命失効するため、ローテーション運用が不要になる
2.2 OIDC トークン交換の流れ
permissions: id-token: write は「JWT を要求・使用する権限」のみを付与するものであり、他のリソースへの書き込み権限を直接与えるものではありません[^1]。実際の認証フローは次のようになります。
- Azure 側で事前に federated credential(
issuer/subject/audience)を設定しておく - ジョブ実行のたびに GitHub の OIDC プロバイダーが短命 JWT を発行する
- ワークフロー内の
azure/loginなどのアクションが JWT を提示してトークン交換を要求する - Entra ID がクレームを検証し、一致すれば短命のアクセストークンを返す
- 発行されたアクセストークンで Azure Resource Manager や コンテナレジストリの API を呼び出す
2.3 主要クレームと federated credential の 3 条件一致
OIDC トークンの主要クレームは次のとおりです[^3]。
| クレーム | 内容 |
|---|---|
iss |
発行者。GitHub Actions では固定値 https://token.actions.githubusercontent.com |
sub |
サブジェクト。リポジトリ・環境・ブランチなどを表す文字列 |
aud |
オーディエンス。Azure では既定で api://AzureADTokenExchange |
| その他 | repository / repository_id / environment / ref / job_workflow_ref / run_id など |
Entra ID 側は、登録した federated credential の issuer / subject / audience が提示された JWT のクレームと 完全一致 するかどうかだけを検証します。この仕組みは Kubernetes・GCP・AWS・SPIFFE/SPIRE などでも採用されている「workload identity federation」の一種です[^4]。
3. 移行の背景 — 旧方式からの脱却
az ad sp create-for-rbac --sdk-auth(--json-auth のエイリアス)は Azure CLI のリファレンス上で明示的に Deprecated と表示され、マネージド ID が利用できる場合はそちらを優先検討するよう案内されています[^5]。
| 観点 | 旧方式(AZURE_CREDENTIALS / --sdk-auth) |
OIDC(Workload Identity Federation) |
|---|---|---|
| GitHub Secrets への保存内容 | クライアントシークレットを含む長寿命 JSON | クライアント ID・テナント ID・サブスクリプション ID のみ(シークレットなし) |
| 有効期限 | 手動更新するまで有効(失念リスクあり) | JWT・アクセストークンとも短命で自動失効 |
| 漏洩時の影響 | 有効期限内は不正利用され続ける | トークンが短命なため影響範囲が限定的 |
| Azure CLI 上の扱い | Deprecated | 公式推奨 |
4. セットアップ手順
4.1 必要な GitHub Secrets
リポジトリの Settings → Secrets and variables → Actions に以下を設定します。長寿命シークレットは含まれません。
| Secret 名 | 用途 |
|---|---|
AZURE_CLIENT_ID |
OIDC 連携先の Entra ID アプリケーション(サービスプリンシパル)ID |
AZURE_TENANT_ID |
Entra テナント ID |
AZURE_SUBSCRIPTION_ID |
デプロイ先サブスクリプション ID |
AZURE_WEBAPP_NAME |
デプロイ対象の Web App 名(サービス固有の値) |
AZURE_DEPLOY_SLOT_NAME |
デプロイ対象の slot 名(例: staging) |
AZURE_RESOURCE_GROUP |
デプロイ先リソースグループ |
AZURE_ACR_NAME |
イメージの push 先となる Azure Container Registry 名 |
廃止対象: 旧
AZURE_CREDENTIALS(az ad sp create-for-rbac --sdk-authで生成した JSON)を使っている場合は、移行後に Settings → Secrets and variables → Actions から削除してください。
4.2 Entra ID アプリケーションとサービスプリンシパルの作成
APP_NAME="<your-repo>-github-actions"
SUBSCRIPTION_ID="<your-subscription-id>"
RESOURCE_GROUP="<your-resource-group>"
ACR_NAME="<your-acr-name>"
# シークレットなしでアプリ + サービスプリンシパルを作成
az ad app create --display-name "$APP_NAME"
APP_ID=$(az ad app list --display-name "$APP_NAME" --query "[0].appId" -o tsv)
az ad sp create --id "$APP_ID"
SP_OBJECT_ID=$(az ad sp show --id "$APP_ID" --query id -o tsv)
取得した値は GitHub Secrets に次のように対応させます。
APP_ID→AZURE_CLIENT_IDaz account show --query tenantId -o tsv→AZURE_TENANT_ID- サブスクリプション ID →
AZURE_SUBSCRIPTION_ID
4.3 ロールの付与(最小権限の考え方)
az ad sp create-for-rbac と異なり、az ad app create / az ad sp create は既定でいかなるロールも割り当てません。必要なロールを スコープを絞って 個別に付与します。
# デプロイ先リソースグループ単位で Contributor を付与する例
az role assignment create --assignee "$APP_ID" \
--role "Contributor" \
--scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP"
# コンテナレジストリへの push 権限のみを付与する例
az role assignment create --assignee "$APP_ID" \
--role "AcrPush" \
--scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.ContainerRegistry/registries/$ACR_NAME"
最小権限のポイント: サブスクリプション全体ではなく、対象リソースグループ・対象リソースまでスコープを絞ります。
Contributorより狭い権限で足りる場合はWebsite Contributorのようなサービス固有ロールへの置き換えを検討してください。
4.4 GitHub Environments の設定
federated credential の subject に environment:staging / environment:production を指定する構成を採る場合、Entra ID 側に登録するより前に GitHub 側の Environment を作成しておく必要があります。Settings → Environments で staging と production を作成します。
Environment には次の保護ルールを追加設定することを強く推奨します。
- 対象ブランチ・タグの制限
- 必須レビュアー(承認者)
- wait timer(デプロイまでの待機時間)
4.5 federated credential の登録
GitHub Environment 単位で subject を発行する構成が代表的です(例: staging と production)。4.4 で作成した Environment 名と subject の値を一致させます。
fic-staging.json:
{
"name": "<your-repo>-staging",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:<your-org>/<your-repo>:environment:staging",
"audiences": ["api://AzureADTokenExchange"]
}
fic-production.json:
{
"name": "<your-repo>-production",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:<your-org>/<your-repo>:environment:production",
"audiences": ["api://AzureADTokenExchange"]
}
登録コマンド:
az ad app federated-credential create --id "$APP_ID" --parameters fic-staging.json
az ad app federated-credential create --id "$APP_ID" --parameters fic-production.json
注意: federated credential の管理(作成・更新・削除)には Application Administrator / Cloud Application Administrator / Global Administrator / Hybrid Identity Administrator のいずれかのロールが必要です。1 アプリあたり最大 20 個まで登録でき、
issuer/subjectはそれぞれ最大 600 文字です。
4.6 subject 形式のバリエーション
federated credential の subject は用途に応じて 4 つの形式から選びます[^3]。
| シナリオ | subject 書式 | 適用ケース |
|---|---|---|
| Environment 指定 | repo:<owner>/<repo>:environment:<env-name> |
job に environment: を紐付けた場合。ブランチ・タグに関わらず同一 subject になり、多数のブランチ・タグからデプロイする構成で有用 |
| ブランチ指定 | repo:<owner>/<repo>:ref:refs/heads/<branch> |
environment を使わない job で特定ブランチに限定したい場合 |
| タグ指定 | repo:<owner>/<repo>:ref:refs/tags/<tag> |
リリースタグの push を起点とするデプロイに限定したい場合 |
| Pull Request | repo:<owner>/<repo>:pull_request |
pull_request イベントでのみ、job が environment を参照していない場合に成立 |
セキュリティ上の注意点:
- GitHub 公式は「未信頼のリポジトリがアクセストークンを要求できないよう、最低 1 つの条件(subject など)を必ず定義しなければならない」と明記しています[^3]。
pull_requestをトリガーとするワークフローに、Azure デプロイのような強い権限を紐付けることは推奨されません。fork 由来の PR でも同一リポジトリ名で subject が成立し得るためです。- Entra ID 側は「
subjectの値が一致していない場合、作成時にはエラーにならず、トークン交換のタイミングで初めてサイレントに失敗する」と警告しています[^2]。登録直後は必ず実際のワークフロー実行で疎通確認してください。 - ワイルドカードは非対応です。
buildのようにビルド・push を行う job が environment を指定していない場合は、その job にも environment を付与するか、ブランチ subject(repo:<owner>/<repo>:ref:refs/heads/<branch>)を別途登録する必要があります。
4.7 ワークフロー側の permissions 設定
permissions: id-token: write は OIDC を必要とする job のみ に付与し、lint / test のような CI job には付与しません。
jobs:
ci:
# lint / test のみ。OIDC は不要なため id-token は付与しない
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run lint
- run: npm test
build-and-deploy:
needs: ci
if: github.ref == 'refs/heads/main'
environment: staging
permissions:
contents: read
id-token: write # OIDC トークン取得に必須
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Azure login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Build and push image
run: |
az acr build --registry "${{ secrets.AZURE_ACR_NAME }}" \
--image "<your-image>:${{ github.sha }}" .
- name: Deploy to staging slot
run: |
az webapp deployment slot swap \
--resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \
--name "${{ secrets.AZURE_WEBAPP_NAME }}" \
--slot "${{ secrets.AZURE_DEPLOY_SLOT_NAME }}" \
--target-slot production
5. トラブルシューティング
| 症状 | 確認ポイント |
|---|---|
AADSTS70021: No matching federated identity record found for presented assertion.[^9] |
federated credential の subject と GitHub 側(repo:<owner>/<repo>:environment:... 等)が完全一致するか確認する。federated credential 登録直後はテナント内へのレプリケーションが完了するまで数分かかることがあり、その間は同じエラーが一時的に発生し得るため、少し時間を置いてから再試行することも有効[^9] |
AuthorizationFailed(コンテナレジストリ push / Web App デプロイ時) |
OIDC トークン交換自体は成功しているが、Azure RBAC 側で対象リソースへの権限が不足している。ロール割当のスコープ・ロール種別を確認する |
id-token: write が効いていない |
該当 job の permissions: に id-token: write が明示されているか確認する |
| federated credential 登録時にエラーが出ないのに認証が通らない | Entra ID は subject 不一致を登録時にエラーとして返さない。ワークフロー実行時のログで初めて失敗が顕在化するため、登録後は必ず実行して疎通確認する |
6. 2025〜2026 年の最新動向
6.1 Immutable subject claims(2026 年 7 月 15 日〜)
従来の GitHub 既定の sub は organization 名・repository 名のみを使用していたため、リポジトリを削除して同名で再作成すると、別の所有者が同じ subject を取得できてしまう 問題がありました。2026 年 7 月 15 日以降に作成・リネーム・移管されたリポジトリでは、owner ID と repository ID を含む immutable な sub 形式(例: repo:<owner>@<owner-id>/<repo>@<repo-id>:ref:refs/heads/main)が既定になります[^3]。既存リポジトリは明示的なオプトインまで従来形式を維持しますが、AWS・GCP・Azure・HashiCorp Vault いずれの信頼ポリシーもこの新形式への更新が将来的に必要になり得ます。
次の表は、旧形式と immutable 形式の違いをまとめたものです。
| 項目 | 旧形式(〜2026/7/14 作成分は継続) | immutable 形式(2026/7/15〜新規作成分) |
|---|---|---|
sub の構成要素 |
organization 名 + repository 名のみ | owner ID + repository ID を含む |
sub の例 |
repo:<owner>/<repo>:ref:refs/heads/main |
repo:<owner>@<owner-id>/<repo>@<repo-id>:ref:refs/heads/main |
| リポジトリ削除 → 同名で再作成した場合のリスク | 別の所有者が同じ subject を再取得できてしまう | owner ID / repository ID が変わるため再取得は成立しない |
| 適用対象 | 既存リポジトリ(明示的なオプトインまで維持) | 2026 年 7 月 15 日以降に作成・リネーム・移管されたリポジトリ |
| 信頼ポリシー側の対応要否 | 変更不要 | Azure / AWS / GCP / HashiCorp Vault 側の subject 設定更新が将来必要になり得る |
6.2 その他のアップデート
| 機能 | 概要 |
|---|---|
| カスタムプロパティの OIDC クレーム化 | repo_property_* プレフィックスで、リポジトリのカスタムプロパティを OIDC クレームに含め、属性ベースのアクセス制御(ABAC)ポリシーを組めるようになっている |
sub claim のカスタマイズ |
REST API の include_claim_keys で sub のフォーマット自体を再定義できる |
| Dependabot の OIDC 対応 | AWS CodeArtifact / Azure DevOps Artifacts / JFrog Artifactory 向けのプライベートレジストリ認証で OIDC が利用可能になっている |
| 2026 年のセキュリティロードマップ | Workflow Execution Protections(2026 年 6 月 18 日 GA)、Actions Data Stream、GitHub-hosted ランナー向け egress ファイアウォール(技術プレビュー)などが進行中[^6] |
7. 他クラウドとの比較
Azure 以外の主要クラウドも同様の OIDC 連携をサポートしています[^7] [^8]。
| 項目 | Azure | AWS | GCP |
|---|---|---|---|
| 信頼設定の実体 | Entra ID アプリ/マネージド ID の federated credential | IAM OIDC Identity Provider + IAM ロールの trust policy | Workload Identity Pool + Provider |
| 既定 audience | api://AzureADTokenExchange |
sts.amazonaws.com |
プロバイダー設定に依存 |
| subject 条件の指定方法 | subject フィールドに直接文字列を指定 |
IAM trust policy の Condition.StringEquals |
attribute mapping で assertion.sub を評価する条件式 |
| カスタムクレーム対応 | 標準の aud / sub のみ |
非対応 | 対応(job_workflow_ref 等をネイティブ参照可能) |
| immutable subject claim 対応 | 対応(2026/7/15〜新形式) | 対応 | 対応 |
8. まとめ
GitHub Actions から Azure への OIDC 連携は、長寿命シークレットを保存せずにデプロイを自動化できる、現時点で最も推奨される認証方式です。既に AZURE_CREDENTIALS を使っている場合は、次の順序で移行を進めるとスムーズです。
- Entra ID アプリケーション・サービスプリンシパルを新規作成する
- 最小権限でロールを付与する
- federated credential を Environment 単位で登録する
- ワークフローの OIDC 対応 job にのみ
id-token: writeを付与する - 疎通確認後、旧
AZURE_CREDENTIALSを GitHub Secrets から削除する
2026 年 7 月 15 日以降は immutable subject claims のデフォルト化も控えているため、リポジトリの作成・移管を予定している場合は subject 形式の変更点も併せて確認しておくことをおすすめします。ワークフロー全体の再利用性を高めたい場合は、Reusable Workflows 実践ガイド と組み合わせて OIDC ログイン部分を共通化するのも有効です。
9. 参考リンク
9.1 脚注
[^1]: Configuring OpenID Connect in Azure — GitHub Docs
[^2]: Workload identity federation — Microsoft Entra ID
[^3]: About security hardening with OpenID Connect — GitHub Docs
[^4]: Security hardening your deployments — OpenID Connect concepts
[^5]: az ad sp — Azure CLI reference
[^6]: GitHub Actions security roadmap discussion
[^7]: Configuring OpenID Connect in Amazon Web Services — GitHub Docs
[^8]: Configuring OpenID Connect in Google Cloud Platform — GitHub Docs
[^9]: Workload identity federation for app considerations — Microsoft Entra Workload ID(「Time for federated credential changes to propagate」節に AADSTS70021: No matching federated identity record found for presented assertion. の文言が明記されている)
9.2 参考リンク一覧
- Configuring OpenID Connect in Azure — GitHub Docs
- Workload identity federation — Microsoft Entra ID
- About security hardening with OpenID Connect — GitHub Docs
- Security hardening your deployments — OpenID Connect concepts
- az ad sp — Azure CLI reference
- GitHub Actions security roadmap discussion
- Configuring OpenID Connect in Amazon Web Services — GitHub Docs
- Configuring OpenID Connect in Google Cloud Platform — GitHub Docs
- Workload identity federation for app considerations — Microsoft Entra Workload ID
この記事の執筆にあたり、AI の支援を受けています。






