Azure AD と OpenID Connectで連携した GitHub Actions で Azure API Management インスタンスを 生成する

このエントリは2021/12/24現在の情報に基づいています。将来の機能追加や変更に伴い、記載内容からの乖離が発生する可能性があります。

以前以下のようなエントリを書いた。

前回のエントリでは、Service Principalのシークレットを使ってAzure Loginアクションを呼び出していたが、GitHub ActionsがOpenID ConnectでAzure ADと連携できるようになった結果、Service Principalのシークレットを使う必要がなくなった。備忘録として設定を記載しておく。

OpenID Connectとの連携に関する詳細は以下のドキュメントに記載がある。

Configuring OpenID Connect in Azure (Use OpenID Connect within your workflows to authenticate with Azure.)
https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure
GitHub Actions を使用して Azure に接続する / Use GitHub Actions to connect to Azure
https://docs.microsoft.com/azure/developer/github/connect-from-azure

手順

以下の流れで実施する。なお、Azure上の操作はAzure CLI、GitHub ActionsのランナーはGitHub-hostedのLinuxを使っているが、もちろんWindowsでもよい。

  1. GitHub Actionsを表すApplicationをAzure ADに作成する
  2. 1.で作成したApplicationのService Principalを作成する
  3. 2.で作成したService PrincipalにContributor(共同作成者)ロールを割り当てる
  4. 1.で作成したApplicationにフェデレーション資格情報を追加する
  5. 1.で作成したApplicationのApplication ID、Azure ADのテナントID、AzureのSubscription IDを表すシークレットをGitHub Secretに作成する
  6. GitHub Actionsのflowで、OpenID Connect認証を使ってAzure ADログインを構成し、動作確認する
  7. API Managementのプロビジョニングに必要なARMテンプレート(もしくはBicep)とパラメータをGitHubリポジトリに追加する
  8. GitHub Actionsのflowを変更し、プロビジョニングのステップを追加する
  9. 動作確認

1. GitHub Actionsを表すApplicationをAzure ADに作成する

コマンドは以下。

az ad app create --display-name <Application Name>

出力結果は以下(一部省略およびマスキングしている)。appId、objectIdはメモしておく。

{
    "acceptMappedClaims": null,
    "addIns": [],
    "allowGuestsSignIn": null,
    "allowPassthroughUsers": null,
    "appId": "<Application ID>",
    ...,
    "displayName": "<コマンドで指定したApplication Name>",
    ...,
    "objectId": "<ApplicationのObject ID>",
    ...
}

2. 1.で作成したApplicationのService Principalを作成する

コマンドは以下。

az ad sp create --id <1.で作成したApplicationのappId>

出力結果は以下。(一部省略およびマスキングしている)。objectId(このObject IDは上記コマンドで作成したService PrincipalのObject ID)はメモしておく。

{
    "accountEnabled": "True",
    "addIns": [],
    "alternativeNames": [],
    "appDisplayName": "<1.で作成したApplicationのApplication Name>",
    "appId": "<1.で作成したApplicationのappId>",
    "appOwnerTenantId": "<Azure ADのテナントID>",
    ...,
    "displayName": "<1.で作成したApplicationのApplication Name>",
    ...,
    "objectId": "<Service PrincipalのObject ID>",
    "objectType": "ServicePrincipal",
    ...,
    "servicePrincipalNames": [
        "<1.で作成したApplicationのappId>"
    ],
    "servicePrincipalType": "Application",
    ...
}

3. 2.で作成したService PrincipalにSubscriptionスコープでのContributor(共同作成者)ロールを割り当てる

コマンドは以下。

az role assignment create \
  --role contributor \
  --subscription <サブスクリプションID> \
  --assignee-object-id  <2で作成したService PrincipalのObject ID> \
  --assignee-principal-type ServicePrincipal

出力結果は以下(一部省略およびマスキングしている)。

{
    "canDelegate": null,
    ...,
    "principalId": "<2で作成したService PrincipalのObject ID>",
    "principalType": "ServicePrincipal",
    ...,
    "type": "Microsoft.Authorization/roleAssignments"
}

4. 1.で作成したApplicationにフェデレーション資格情報を追加する

コマンドは以下、といいつつ実体はREST APIの呼び出しである。詳細は以下のドキュメントにも記載がある。

GitHub リポジトリを信頼するようにアプリを構成します。 (プレビュー) / Configure an app to trust a GitHub repo (preview)
https://docs.microsoft.com/azure/active-directory/develop/workload-identity-federation-create-trust-github

以下のドキュメントにも記載があるように、subjectの設定が細々しているので注意が必要。以下はrepositoryとbranchの組み合わせの例。表現の都合上改行を入れているが、リクエスト本体のJSONは改行無しで指定する。

az rest --method POST \
--uri 'https://graph.microsoft.com/beta/applications/<1で作成したApplicationのObject ID>/federatedIdentityCredentials' \
--body '{"name":"<フェデレーション資格情報の名前>", "issuer":"https://token.actions.githubusercontent.com",
"subject":"repo:<GitHubのユーザー名>/<リポジトリ名>:refs/heads/<Branch名>",
"description":"<フェデレーション資格情報の補足コメント>",
"audiences":["api://AzureADTokenExchange"]}'

出力結果は以下(一部省略している)。

{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#applications('<1で作成したApplicationのObject ID>')/federatedIdentityCredentials/$entity",
    "audiences": [
        "api://AzureADTokenExchange"
    ],
    "description": "<フェデレーション資格情報の補足コメント>",
    ...,
    "issuer": "https://token.actions.githubusercontent.com",
    "name": "<フェデレーション資格情報の名前>",
    "subject": "<GitHubのユーザー名>/<リポジトリ名>:refs/heads/<Branch名>"
}

これ以後は、GitHub Actionsでの作業に移る。

5. 1.で作成したApplicationのApplication ID、Azure ADのテナントID、AzureのSubscription IDを表すシークレットをGitHub Secretに作成する

GitHub Actionsで使うために、3個のシークレットを作成する。リポジトリレベルでも、環境 (environment)、組織 (organization) レベルでもかまわないが、シークレットのKeyは固定である点に注意。

  • AZURE_CLIENT_ID (1.で作成したApplicationのappId)
  • AZURE_TENANT_ID (Subscriptionが属するAzure ADのtenant ID) 
  • AZURE_SUBSCRIPTION_ID (AzureのSubscription ID)

6. GitHub Actionsのflowで、OpenID Connect認証を使ってAzure ADログインを構成し、動作確認する

以下のドキュメントにあるYAMLを使って試すのがいちばんかんたん。

Azure CLIの場合(Linux):
https://docs.microsoft.com/ja-jp/azure/developer/github/connect-from-azure?tabs=azure-cli%2Clinux#set-up-azure-login-with-openid-connect-authentication
PowerShellの場合(Windows)
https://docs.microsoft.com/ja-jp/azure/developer/github/connect-from-azure?tabs=azure-cli%2Cwindows#set-up-azure-login-with-openid-connect-authentication

Azure CLIに比べて、PowerShellは実行に要する時間が倍ほど違う(CLIは30秒程度、PowerShellは1分程度)ようだが、今回はパフォーマンス確認は目的ではないので深く追わない。

7. API Managementのプロビジョニングに必要なARMテンプレート(もしくはBicep)とパラメータをGitHubリポジトリに追加する

今回は(今回も)ARMテンプレートでプロビジョニングする。利用するARMテンプレートは以下から利用できるもので、Managed Identityを有効化したAPI Managementインスタンスを作成する。

Azure API Management Service – with Managed Identity
https://github.com/Azure/azure-quickstart-templates/tree/master/quickstarts/microsoft.apimanagement/api-management-create-with-msi

ARMテンプレート(azuredeploy.json)とパラメータファイル(azuredeploy.parameters.json)をリポジトリに作成する(2021/12/24現在、Bicepのスクリプトも存在するが、必要であればクローンしておく)。

8. GitHub Actionsのflowを変更し、プロビジョニングのステップを追加する

ARMテンプレートを実行するためのActionは以下を利用する。前エントリでは別のものを使っていたが、今回はパラメータファイルの設定をオーバーライドしたいので、Microsoft純正のものを使う。

Deploy Azure Resource Manager (ARM) Template
https://github.com/marketplace/actions/deploy-azure-resource-manager-arm-template

プロビジョニング先のリソースグループはARMテンプレート内で作成されないので、flow内で作成する必要がある。そのために必要なAzure CLIの実行Actionと、リポジトリのCheckoutのためのActionを利用する。

Azure CLI Action
https://github.com/marketplace/actions/azure-cli-action
Checkout
https://github.com/marketplace/actions/checkout

作成したflowは以下の通り。リソースグループ名をユニークにするために $(date +%s) を使っている。もちろん、API Managementインスタンス名をユニークにするのであれば、ARMテンプレート内で実施することになるが、ARMテンプレートの関数utcNow()を使う場合、Parameterのデフォルト値としてのみ設定できるので注意が必要。

utcNow
https://docs.microsoft.com/azure/azure-resource-manager/templates/template-functions-date#utcnow

name: Run Azure Login with OpenID Connect via Azure CLI
on: [push]
permissions:
  id-token: write
  contents: read
      
jobs: 
  Create-API-Management-Instance-with-GitHub-Actions:
    runs-on: ubuntu-latest
    env:
      ResourceGroup_Base: APIM-rg
      ResourceGroupLocation: "japaneast"
    steps:
    - name: Checkout
      uses: actions/checkout@v2.4.0
    - name: OIDC Login to Azure Public Cloud with Azure CLI
      uses: Azure/login@v1
      with:
        client-id: ${{ secrets.AZURE_CLIENT_ID }}
        tenant-id: ${{ secrets.AZURE_TENANT_ID }}
        subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: Generate unique variable based on timestamp
      run: echo ResourceGroupName=${{ env.ResourceGroup_Base }}-$(date +%s) >> $GITHUB_ENV
    - name: Create Resource Group with Azure CLI
      uses: Azure/CLI@v1
      with:
        inlineScript: |
          az group create -n ${{ env.ResourceGroupName }} -l ${{ env.ResourceGroupLocation }}
          echo "Azure resource group created"
    - name: Create API Management Instance with ARM action
      uses: Azure/arm-deploy@v1
      with:
        template: azuredeploy.json
        resourceGroupName: ${{ env.ResourceGroupName }}
        parameters: azuredeploy.parameters.json publisherName=${{ secrets.PUBLISHER_NAME }} publisherEmail=${{ secrets.PUBLISHER_MAIL }}

パラメータファイル (azuredeploy.parameters.json) は以下のような感じ。Consumption tierを使う場合、skuCountはゼロにしないといけない点は前回のエントリと同じ。

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "sku": {
            "value": "Consumption"
        },
        "publisherEmail": {
            "value": "email@example.com"
        },
        "publisherName": {
            "value": "templateTest"
        },
        "skuCount": {
            "value": 0
        }
    }
}

9. 動作確認

実行すると、リソースグループは接頭辞 (APIM-rg) にランダムな数値文字を追加しているので、ユニークなリソースグループ名を作成できること、そのリソースグループ内にAPI Managementインスタンス (Consumption tier) が作成できることを確認できる。Flowの実行状況は以下の通り。

作成したAPI ManagementインスタンスをAzure Portalで開き、Managed Identityの状況を確認すると、System Managed Identityが有効化されていることを確認できる。

おまけ

このエントリで作成したGitHub Actionsのフローは以下で公開している。

Provision API Management Instance with GitHub Actions
https://github.com/anishi1222/Create-API-Management-Instance-with-GitHub-Actions

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中