GitHub Actions で OpenID Connect を使って Microsoft Graph PowerShell SDK に接続する

GitHub Actions から Microsoft Graph PowerShell SDK を使用する際のアプリ認証に OpenID Connect を用いる方法を紹介します。

Azure CLI や Azure Az PowerShell モジュール を使用する場合はすでに [Azure Login](https://github.com/marketplace/actions/azure-login) という公式 Action が用意されていて簡単に OpenID Connect によるログインができるのですが、 Microsoft Graph API にはそういったものが無いためいささか面倒です。

## アプリケーション登録

1. Azure Portal にサインインし、[Azure Active Directory] – [アプリの登録] – [新規登録]
1. アプリの名前を決めて [登録]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177762/2ddc89b5-b968-9e53-a820-57d8f062ee9d.png)
1. [アプリケーション (クライアント) ID] と [ディレクトリ (テナント) ID] の値を控えておく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177762/d4b57b49-b7c9-e43d-9044-d65f9f9ca7bd.png)
1. [API のアクセス許可] – [+アクセス許可の追加] - [Microsoft Graph] – [アプリケーションの許可] と進み、アプリに適切な権限を追加する
※ デフォルトで`User.Read`の委任アクセス許可が付与されていますがこれは消してしまって構いません
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177762/1abcc301-9c22-c478-0029-7b9cf5869cae.png)
1. [\<テナント名\>に管理者の同意を与えます] をクリックする

## フェデレーション資格情報の登録
1. 登録したアプリの [証明書とシークレット] - [フェデレーション資格情報] - [資格情報の追加]
1. 必要な情報を入力して [追加]
  フェデレーション資格情報のシナリオ： [Azure リースをデプロイする GitHub Actions] を選択
  組織： アプリを呼び出す GitHub 組織名 or ユーザ名
  リポジトリ： アプリを呼び出す GitHub レポジトリ名
  エンティティ型： いくつか選択肢があるので適切なものを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177762/9a488b37-b7c3-9b16-2892-c5b5be85e39a.png)

この画面で設定した [組織] 、[リポジトリ]、[エンティティ型] は呼び出し元の GitHub Actions 状態と正確に一致しなければなりません。例えばエンティティ型に [ブランチ] を選択しブランチ名に [main] を設定しているのに、呼び出し元の GitHub Actions のブランチ名が [master] だったりすると認証が通りません。

## GitHub シークレットの設定
1. GitHub レポジトリの [Settings] - [Security] - [Secrets] - [Actions] から Secret を2つ登録
  `AZURE_CLIENT_ID`： Azure 側アプリの [アプリケーション (クライアント) ID] の値を設定
  `AZURE_TENANT_ID`： Azure 側アプリの [ディレクトリ (テナント) ID] の値を設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177762/4ac2c969-31d3-a42e-739b-5b805d08a5b5.png)

## GitHub Actions Workflow
ここまで準備ができれば以下のような Workflow で Microsoft Graph PowerShell SDK が使えるようになります。

```yaml
# .github/workflows/graphapi.yml
name: Connect Microsoft Graph API

on:
  workflow_dispatch:
  push:
    branches: ["main"]

permissions:
  id-token: write
  contents: read

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
    - name: Aquire Access Token
      id: oidc
      shell: pwsh
      run: |
        $federated_token = curl -s -H "Authorization: bearer $env:ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$env:ACTIONS_ID_TOKEN_REQUEST_URL&audience=api://AzureADTokenExchange" `
        | ConvertFrom-Json | Select-Object -ExpandProperty Value
        $access_token = curl -s -X POST "https://login.microsoftonline.com/$env:AZURE_TENANT_ID/oauth2/v2.0/token" `
          -F client_id=$env:AZURE_CLIENT_ID `
          -F grant_type=client_credentials `
          -F scope=https://graph.microsoft.com/.default `
          -F client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer `
          -F client_assertion=$federated_token `
        | ConvertFrom-Json | Select-Object -ExpandProperty access_token
        echo "::add-mask::$access_token"
        echo "::set-output name=GRAPH_TOKEN::$access_token"
      env:
        AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
        AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}

    - name: Connect Microsoft Graph PowerShell
      shell: pwsh
      run: |
        Connect-MgGraph -AccessToken '${{ steps.oidc.outputs.GRAPH_TOKEN }}'
          #Something to do
          Get-MgUser -All
        Disconnect-MgGraph

```

流れとしては`curl`を使って認証用のエンドポイントにPOSTリクエストを投げることでアクセストークンを取得し、アクセストークンを`Connect-MgGraph`に渡すことで Microsoft Graph API に接続しています。

アクセストークン取得処理のシェルは個人的な好みで PowerShell (`pwsh`) を使用していますが、Bash 等でもほぼ同じ書き方で書けるかと思います。

アクセストークンの有効期限はデフォルトでは1時間(3600秒)に設定されているようです。
ほとんどの場合は十分だと思いますが、長時間かかるような処理を実行する場合は適切なタイミングでトークンの再取得が必要になりますので注意してください。