【Azure】Github ActionからApp Serviceデプロイ時にOIDC認証を使用するための７ステップ


## はじめに

Visual Studio のアップデートにより Azure App Service デプロイ時のセキュリティが強化されて基本認証がデフォルトで無効になりました。

https://learn.microsoft.com/ja-jp/visualstudio/releases/2022/release-notes

ただ 1 点問題があり、環境の問題かそのままの状態では認証がうまくいかなったです。そのため以前は基本認証に戻す方法をとっていました。

https://qiita.com/KM_20005/items/6e43f39e356832d5dcc5

認証を通すことができるようになったため、この記事では OIDC 認証を使用して Github Actions から Azure にデプロイする方法を紹介します。

## 前提

- Azure のアカウントを保有している
- Azure App Service にアプリのリソースを作成済み
- Github アカウントを保有している
- Shell 上から Azure の操作ができる(`az login`を正常に実行できる)環境がある

## 手順表

操作の手順は基本的に公式チュートリアルに沿って実施します。

https://learn.microsoft.com/ja-jp/azure/azure-sql/database/connect-github-actions-sql-db?view=azuresql&tabs=openid

1. デプロイ対象のリソースの AppID を取得
2. サービスプリンシパルを作成
3. 新しいロールの割り当て
4. フェデレーション ID 用の Json をローカルに準備
5. フェデレーション ID の資格情報を作成
6. Github にシークレット登録
7. yml ファイルの修正

順番に説明します。

また ID などが複雑であるため、出力された Json をテキストエディタに張り付けて作業完了まで見直せるようにするのがおすすめです。

## 1. デプロイ対象のリソース AppID を取得

2 のサービスプリンシパル作成で使用するためデプロイしたいアプリの AppID を取得します。下記コマンドを実行してください。

```shell
az ad app list
```

大量の json ファイルが出力されてかなりビビりますが落ち着いて 1 度メモ帳に張り付けましょう。

そして確認すべき項目は 2 つだけです。

- `displayName` : Azure App Service に作成したリソース名
- `appId` : 取得したい AppID

出力された JSON から`displayName`でデプロイ対象のアプリを検索して`appId`を確認してください。

## 2. サービスプリンシパルの作成

下記コマンドを実行してサービスプリンシパルを作成します。`$appId`は先ほど確認した`appID`に置き換えてください。

```shell
az ad sp create --id $appId
```

上記のコマンドを実行すると生成されたサービスプリンシパルの結果が出力されます。
結果のうち`Id`と`appOwnerOrganizationId`は以降の操作で使用するためメモしておいてください。

:::note warn
`Id`は 1.で確認した`appId`とは別の値です。
:::

## 3. 新しいロールの割り当て

下記コマンドを実行してロールを割り当てます。置き換えが必要なのは 3 種類です。

- `$assigneeObjectId`は 2.で確認した`Id`に置き換えてください。
- `$subscriptionId`はサービスが存在するサブスクリプションの ID に置き換えてください。
- `$resourceGroupName`はアプリが存在するリソースグループ名で置き換えてください。

```shell
az role assignment create --role contributor --subscription $subscriptionId --assignee-object-id  $assigneeObjectId --assignee-principal-type ServicePrincipal --scope /subscriptions/$subscriptionId/resourceGroups/$resourceGroupName
```

## 4. フェデレーション ID 用の Json を準備

資格情報の登録の際に Json ファイルをローカルに作成し、その中に値を記載したのちにそれを読み込む必要があります。

Shell を実行しているパスに`credential.json`ファイルを追加して中身を書き換えます。

- `<CREDENTIAL-NAME>`に任意の名称を記載してください。
- `octo-org/octo-repo`を`リポジトリが存在するGithubユーザー名/デプロイ対象のリポジトリ名`に書き換えてください。

```json
{
  "name": "<CREDENTIAL-NAME>",
  "issuer": "https://token.actions.githubusercontent.com",
  "subject": "repo:octo-org/octo-repo:ref:refs/heads/master",
  "description": "Testing",
  "audiences": ["api://AzureADTokenExchange"]
}
```

Github 上で下記のようになっているリポジトリの場合は

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783169/92df6718-b11e-9366-eb15-49228b671646.png)

このような Json になります。

```json
{
  "name": "DeployTest-OIDC",
  "issuer": "https://token.actions.githubusercontent.com",
  "subject": "repo:KM5075/BizCardKeeper:ref:refs/heads/master",
  "description": "Testing",
  "audiences": ["api://AzureADTokenExchange"]
}
```

:::note warn
上記の設定は master ブランチからの認証を承認するためのフェデレーションを生成しています。Azure にログインが必要なのは基本的にデプロイのタイミングであるため master ブランチで問題ないと思います。

ただし OIDC 認証が正しく設定できているかの確認をするために別のブランチで認証する場合には`subject`の`ref:`以下を都度修正してください。
:::

## 5. フェデレーション ID の資格情報を作成

資格用の Json が準備出来たら下記コマンドを実行します。

```shell
az ad app list
```

:::note info
最初の手順 1.で実行したのと同じコマンドです。その時の出力がさかのぼれる場合は不要です。
:::

そして確認すべき項目は 2 つだけです。

- `displayName` : Azure App Service に作成したリソース名
- `Id` : 取得したい ID(appId ではないので注意。識別のためこちらは`objectId`と呼ぶ)

ここまで出来たら下記コマンドを実行します。`<APPLICATION-OBJECT-ID>`は`objectId`で置き換えてください。

```shell
az ad app federated-credential create --id <APPLICATION-OBJECT-ID> --parameters credential.json
```

## 6. Github にシークレット登録

設定はすべて完了したためあとは必要な情報を Github シークレットに登録していきます。

| シークレット          | 内容                                   |
| --------------------- | -------------------------------------- |
| AZURE_CLIENT_ID       | 1.で出力された`appId`                  |
| AZURE_TENANT_ID       | 2.で出力された`appOwnerOrganizationId` |
| AZURE_SUBSCRIPTION_ID | 使用するサブスクリプション ID          |

## 7. yml ファイルの修正

yml ファイルに以下の 2 点を確認します。

1. 権限の付与(permissions)の追加
2. ログイン処理で使用しているパラメーターの確認(Visual Studio から出力したら不要？)

```yml
jobs:
  build:
    #省略

  deploy:
    runs-on: windows-latest
    needs: build
    # 権限の付与の追加
    permissions:
      contents: read
      id-token: write
    steps:
      # ログイン処理の追加
      - name: Azure Login
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - name: Download artifact from build job
        uses: actions/download-artifact@v4
        with:
          name: webapp
          path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
      - name: Deploy to Azure WebApp
        uses: azure/webapps-deploy@v2
        with:
          app-name: ${{ env.AZURE_WEBAPP_NAME }}
          package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
```

## 実行

上記をすべて行った状態でフローを実行すると Azure へのログインおよびデプロイが可能になります。

## おわりに

改めて記事を書いてみるとかなり複雑だという印象を受けました。複数の画面を見に行くとわかりづらくなる上に、画面のレイアウトがそこそこの頻度で変わるためコマンドラインをメインで説明しましたが、それでも複雑になりました。。。

特に複数のところにある ID を使用する必要があるというのが理解を難しくしている印象です。

この記事が皆様のコーディングライフの助けになれば幸いです。

## 参考

https://learn.microsoft.com/ja-jp/visualstudio/releases/2022/release-notes

https://learn.microsoft.com/ja-jp/azure/azure-sql/database/connect-github-actions-sql-db?view=azuresql&tabs=openid

https://qiita.com/KM_20005/items/6e43f39e356832d5dcc5