#003: Github actionsを用いたAzureリソースデプロイのためのOIDC認証設定手順

※本記事は、個人の意見および個人的活動で得た経験を記したものであり、会社を代表するものではありません

# 1. 実施概要

Github ActionsからAuzreリソースにアクセスするためには、以前はServicePrincipal＋Secretで認証していたそうで、今はセキュリティの観点からFederated Identityを用いたOIDC認証が使用できるようになっている。Github ActionsのIDP - Microsoft Entra間でFederated Identityを用いた信頼関係を設定すると、OIDC認証でSecretを使う必要がないのでよりセキュアになる。

繰り返しになるが、事前にEntra（AzureのIDP）とGithub Actions IDPにて信頼関係を設定する必要がある。信頼関係のあるIDPを事前に設定することで、Entra側はGithub Actions IDPにより発行されたトークンが信頼関係のあるIDPが発行したものかどうかを確認できる。

OIDC認証が完了すると、Github ActionsのワークフローはEntraのService Principalに付与された権限でAzureリソースにアクセスできるようになる。

Service Principalへの権限設定は重要で、どのスコープに対してどのロールを付与するかをしっかり定義する必要がある。

この記事で実施することを簡単に説明する。詳細はMSもしくはGithub の公式ドキュメントを参照。

- Entraにてアプリとそれに紐づくサービスプリンシパルを登録する
- サービスプリンシパルに許可するスコープとロールを割り当てる（今回はRagSystemというリソースグループにContributorを割り当てる）
- フェデレーションID認証情報を登録することで、EntraアプリーGithubリポジトリのActionsワークフロー間で信頼関係を設定する
- Github Actions側にEntraの各ID（Tenant ID, Subscription ID, Service Principal ID）を登録する
- Github ActionsからEntraにアクセスする時にOIDC認証を使用し、Github Actions からid_tokenとaccess_tokenを発行する、その際に登録された各IDをtokensに含める
- Entraは受け取ったtokensからService Principalを特定し、信頼関係のあるIDP（github）にTokensを確認し、問題なければService Principalに付与された権限でのアクセスを許可
- アクセスを許可されたGithub ActionsはService Principalの権限に従ってAzureリソースにアクセス

https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal,linux

https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust?pivots=identity-wif-apps-methods-azcli

# 2. アプリケーションの作成

実行時に指定するパラメータ

- —display-name : 作成するアプリケーション名

実行により生成される情報

- appId (Client ID)
- id (Application Object ID)

```bash
sato@[21:10:32]:~/proj/RagSystem% az ad app create --display-name GithubActions
{
  ...
  "appId": "xxx",
  ...
  "id": "xxx",
  ...
}
```

Azure Portalで作成したアプリケーションの確認場所
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/466853/cebf50de-d8b0-4273-4755-34331ec58049.png)

# 3. Service Principalの作成

実行時に指定するパラメータ

- —id : App ID (Client ID)

実行によりService Principalに関連付けされる情報

- appId (Client ID)
- appOwnerOrganizationId (Tenant ID)

実行により生成される情報

- id (Service Principal Object ID)
- servicePrincipalNames

```bash
sato@[21:11:06]:~/proj/RagSystem% az ad sp create --id xxx
{
  ...
  "appId": "xxx",
  "appOwnerOrganizationId": "xxx",
  ...
  "id": "xxx",
  ...
  "servicePrincipalNames": [
	    "xxx"
  ],
  ...
}
```

Azure Portalで作成したサービスプリンシパルの確認場所は以下。

“Application Type”で”All Application”を選択しないと表示されなかった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/466853/92625aa6-277f-761c-598d-5e7fe2977d43.png)

# 4. デプロイ対象のリソースグループに対して共同作成者ロールをアサイン

Github Actionsから、このService Principalで認証した後、このResource Group に Deploy を行うので、Role の assign を行う。

https://learn.microsoft.com/ja-jp/cli/azure/role/assignment?view=azure-cli-latest#az-role-assignment-create

実行時に指定するParameters

- —role : 付与する Role 名 (contributor：共同作成者)
- —subscription : Subscription ID
- —assignee-object-id : Role を付与する Service Principal ID
- —assignee-principal-type : Role を付与する Security Principal の識別子 (”Service Principal”)
- —scope : Role を適用する範囲（Resource Group ”RagSystem”）

実行時に関連付けされた情報

- principalId (Service Principal ID)
- scope (ロールの範囲) : /subscriptions/$SubscriptionID/resourceGroups/RagSystem
- roleDefinitionID (付与した Contributor ロールのID) : /subscriptions/$SubscriptionID/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c

https://www.azadvertizer.net/azrolesadvertizer/b24988ac-6180-42a0-ab88-20f7382dd24c.html

実行時に生成された情報

- name (ロールアサイン名)

```bash
sato@[21:27:24]:~/proj/RagSystem% az role assignment create --role contributor --subscription xxx --assignee-object-id xxx --assignee-principal-type ServicePrincipal --scope /subscriptions/xxx/resourceGroups/RagSystem
{
  ...
  "name": "xxx",
  "principalId": "xxx",
  ...
  "roleDefinitionId": "/subscriptions/xxx/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c",
  "scope": "/subscriptions/xxx/resourceGroups/RagSystem",
  ...
}
```

Azure Portalで付与したロールの確認場所
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/466853/07fee130-59f3-82ed-e647-bb2ca82603f3.png)

# 5. フェデレーションID資格情報（federated identity credentials）の追加

Entraにアプリケーションに対するフェデレーションID資格情報を登録する。

フェデレーションID資格情報により、外部IDプロバイダー（Github）とアプリケーション（先ほどEntraに登録したアプリケーション）の間に信頼関係を設定する。

https://learn.microsoft.com/en-us/cli/azure/ad/app/federated-credential?view=azure-cli-latest#az-ad-app-federated-credential-create

実行時に指定するパラメータ

- —id : Application Object ID
- —parameters : パラメータを設定したjsonファイル名

パラメータを指定したjsonファイルに含める情報

- name : フェデレーションID資格情報名（例えば“GithubActionsFedID”）
- issuer : トークン発行元IDプロバイダー識別子 (Github Actionsの場合、”https://token.actions.githubusercontent.com/”固定)
- subject : リポジトリ情報（”repo:<GitHubUsername>/<RepoName>:ref:refs/heads/<BranchName>”）
- descriptions : 説明
- audiences[] : 認証トークンを受け取る対象（”api://AzureADTokenExchange”固定)

```bash
sato@[6:57:01]:~/proj/RagSystem/federation-id% az ad app federated-credential create --id xxx --parameters githubactions.json
{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#applications('xxx')/federatedIdentityCredentials/$entity",
  "audiences": [
    "api://AzureADTokenExchange"
  ],
  "description": "This is a federation identity information for RagSystem repositiry of Github actions.",
  "id": "xxx",
  "issuer": "https://token.actions.githubusercontent.com",
  "name": "GithubRagSystem",
  "subject": "repo:masatoshisato/RagSystem:ref:refs/heads/main"
}
```

## 5-1. Microsoft Entra Workload ID (or workload identity) フェデレーション

https://learn.microsoft.com/en-us/entra/workload-id/workload-identities-overview

### 5-1-1. Workload IDの定義

Workload IDという言葉は、簡単にいうとEntraの認証・認可機能を提供するApplication ID、Service Principal ID、Managed IDのことで、アプリケーションやAzureリソースが特定スコープのAzureリソースにアクセスする時に認証・認可を提供する仕組みをより一般的に定義した言葉。

- Application ID : グローバルで一意なアプリケーションオブジェクト
- Service Principal : テナント固有のApplication Instance
- Managed ID : 管理不要のService Principal

いずれもAzureリソースにアクセスが必要なワークロードに付与するID＝Workload ID。

Managed IDはAzureリソース上で実行するワークロードに付与できるID。Application IDとSerivce Principalは外部で実行されるワークロードからAzureリソースにアクセスする場合に利用するID。

これらのIDはAzureリソースのIAMにてそのリソースに対するアクセス権限を付与できる。

### 5-1-2. Workload ID federationの定義

Azure外部で実行されるワークロードで、かつ外部IDPでアプリケーションを認証できる場合、Entraと外部IDPで信頼関係を設定することで、外部ワークロードが安全性の高い方法でAzureリソースにアクセスすることができるようにするための仕組み。

Workload ID Federationを利用すると、外部アプリケーションがAzureリソースにアクセスする際に、シークレット情報（アプリケーション認証用のパスワード）ではなく、OIDCによるトークンを用いてEntraが外部アプリケーション（例えばGithub Actions）を認証できる仕組み。

# 6. これまでの処理をスクリプト化

Github CopilotくんとChatGPTくんのおかげて、勉強し直し状態の私がほぼ１日で作成したスクリプト。ReadmeファイルはChatGPTくんが一発生成。楽すぎて頼り切ってしまう。。

https://github.com/masatoshisato/RagSystem/tree/v1.0.0/infra/fed-id

# 7. Githubで指定リポジトリのGithub Action用シークレットを生成

先ほどのスクリプトで対象リポジトリ（RagSystem）のSettingsで設定できるGithub Actionsのシークレットとして以下３つの値が生成できるので、これらをRag SystemリポジトリのGithub Actionsのシークレットとして登録する。

- AZURE_CLIENT_ID : Application (Client) ID
- AZURE_TENANT_ID : Directory (tenant) ID
- AZURE_SUBSCRIPTION_ID : Subscription ID

![Untitled](https://prod-files-secure.s3.us-west-2.amazonaws.com/35352049-949d-426c-afde-44e4eb718763/370e1709-51b4-4a8e-ab9f-2027e0a1ade4/Untitled.png)

# 8. Github Actionsの “Azure Login” ActionでOIDC認証の実行確認

https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-cli,linux#set-up-azure-login-with-openid-connect-authentication

設定したSecret情報でAzure Login ActionsによるOIDC 認証とAzure 情報の取得が正常に実行できることを確認。

以下のyamlファイルの要約。

- リポジトリにpush要求が実行された時にトリガーされるワークフロー
- このワークフローにはEntraにOIDC認証するためのid_tokenの発行権限と、実行時に参照するリポジトリの参照権限が付与される
- このワークフローはGithub Actionsで提供される最新版のubuntu linuxで実行される
- このワークフローは以下２つのステップで構成される
    - AzureLoginというアクションを利用したEntraへのログイン
        - ログイン時にはGithub Actionsに事前登録したシークレット情報を参照する
    - ログインアカウント情報の出力
        - Azure CLIのaz accountコマンドでログインアカウントのテナントやサブスクリプション情報を出力する

yamlファイルはこちら。

https://github.com/masatoshisato/RagSystem/tree/v1.0.0/.github/workflows