Azure DevOps Workload identity federationの設定方法

Azure

Azure DevOpsからAzureへ、アプリやリソースをデプロイする際の認証方法は複数用意されています。その中で、昨年パブリックプレビューであった、「Workload identity federation」が2024年2月にGAしました。

今回は、この新しい認証についての設定や動作確認を試していきたいと思います。今回紹介する内容については、以下のブログを参考にしています。詳細はそちらをご確認ください。

Workload identity federation

wif(workload identity federation)の一番のメリットは、シークレットの管理が不要になることにあります。従来からシークレットの管理が不要な認証方法は存在しましたが、設定が煩雑でした。そして、それ以外の認証方法ではシークレットを利用します。そのため、シークレット管理が運用上必要となります。

しかし、wifではシークレットの管理が不要になるため、セキュリティ態勢の向上運用負荷の軽減効果が期待できます。wifの仕組みなどの詳細は、冒頭に記載したブログをご覧ください。

それでは、実際にwifの設定と動作確認を行っていきましょう。

前提

Entra IDでのアプリの登録やロールの割り当て(RBAC)などの概念や操作方法について、ある程度経験がある前提で記載しています。

また、terraformやDevOpsでのパイプライン設定についても、ある程度の経験がある前提で記載していますので、一部説明を省略しているところがあります。

wifを使ってDevOpsからAzureへの認証設定を実施した後は、terraformを使ってリソースグループのデプロイを試します。

自動作成

自動と手動による2つの作成方法があります。自動作成では、DevOps上で設定するだけで、Entra IDへのアプリの登録やフェデレーション資格情報の作成などを裏で実施してくれます。そのため、設定が容易であり、基本的にこの方法での設定がおすすめです。

DevOpsでのサービス接続の作成

Azure DevOpsの対象のプロジェクトの設定から、「サービス接続」を選択します。その後、右上の新規作成(New service connection)をクリックします。

接続の種類では、「Azure Resource Manager」を選択します。

今回は自動作成を試すのため、一番上の「Workload Identity federation (automatic)」を選択します。

Azureへの認証が求められたら、ご自身のアカウントでログインします。その後、接続したいサブスクリプションやリソースグループを選択し、サービス接続名を設定したら保存します。

設定は以上になります。簡単ですね。

パイプラインの設定

「前提」で述べた通り、terraformによるAzureリソースの作成をパイプラインを通して試します。terraformのコードはリソースグループを作成するだけのシンプルなものです。以下にパイプライン設定を記載します。拡張機能は「Azure Pipelines Terraform Tasks」を利用しています。各プロパティなど詳細はリンク先をご確認ください。

trigger:
  branches:
    include:
      - master

pool:
  vmImage: windows-2022

variables:
  - group: "terraform-var"

stages:
  - stage: Build
    jobs:
      - job: terraformJob
        steps:
          - task: TerraformInstaller@1
            inputs:
              terraformVersion: $(TerraformVersion)

          - task: TerraformCLI@1
            inputs:
              command: "init"
              backendType: "azurerm"
              backendServiceArm: $(ServiceConnectionName)
              backendAzureRmResourceGroupName: $(ResourceGroup)
              backendAzureRmStorageAccountName: $(StorageAccountName)
              backendAzureRmContainerName: $(StorageContainerName)
              backendAzureRmKey: $(TfStateName)
              workingDirectory: "$(System.DefaultWorkingDirectory)"
              allowTelemetryCollection: false
            displayName: terraform init

          - task: TerraformCLI@1
            inputs:
              command: "apply"
              environmentServiceName: $(ServiceConnectionName)
              workingDirectory: "$(System.DefaultWorkingDirectory)"
            displayName: terraform apply

9行目で変数を定義していますが、変数はDevOpsの「ライブラリ」という機能を使用しています。ライブラリで、「terraform-var」という変数グループを作成し、そのグループの中に、今回使用している「ServiceConnectionName」などの変数を定義しています。DevOpsの「パイプライン > ライブラリ」から作成できます。作成した変数グループを10行目で指定しています。ライブラリを使用しない場合は、必要な変数をすべてパイプライン設定ファイルに記載します。しかし、ライブラリを利用すれば、パイプライン設定ファイル上での定義は1行で済み、定義した変数をその後の処理で自由に使用することができます。変数の変更をする際も、ライブラリで定義した値を変更すればよく、設定ファイルを更新する必要はありません。

なお、タスクは「TerraformCLI@1」を指定しています。もともとバージョン0を指定していましたが、「Terraform backend initialization for AzureRM only support service principal authorization」というエラーが発生してしまいました。

ロールの付与

サービス接続を作成したとき、AzureのEntra IDには「アプリ」が作成されます。このアプリの資格情報を利用してデプロイを行いますが、現時点ではロールが割り当たっていないため認可エラーになります。そこで、Azureへのデプロイに必要なロールを付与します。今回はお試しということで権限の範囲は広いですが、「共同作成者」を割り当てます。

なお、権限不足で失敗する場合はパイプライン上で以下のようなエラーが発生します。その際は権限を見直してみてください。

Status=403 Code="AuthorizationFailed" Message="The client 'ad5xxx' with object id 'ad5xxx' does not have authorization to perform action

Entra IDの「アプリの登録 > すべてのアプリケーション」から、サービス接続作成時に同時に作成されたアプリが確認できます。「[DevOps組織名]-[プロジェクト名]-[AzureサブスクリプションID]」という名前で作成されていると思います。そのアプリをクリックしたら、概要の基本情報ページに、「ローカルディレクトリでのマネージドアプリケーション」という項目があるので、それをクリックします。これにロールを付与します。エラーメッセージが出ていれば、メッセージ内のidと同じオブジェクトIDを持っていることが確認できると思います。

ロールを付与するスコープはサブスクリプションとします。そのため、「サブスクリプション > アクセス制御(IAM) > ロールの割り当ての追加」の順にクリックします。ロールは共同作成者を選び、メンバーに上記のアプリを指定します。以下の画像はロールが閲覧者となっていますが、共同作成者に読み替えてください。オブジェクトIDがEntra IDから確認したIDと同一であることを確認して割り当てます。

パイプラインの実行

以上で、デプロイのための準備が整いました。それでは、パイプラインを実行してみます。

問題なくapplyが成功し、リソースグループが作成されました。

手動作成

何かしらの制限で自動作成ができないこともあると思います。そこで、手動でも作成してみましょう。

サービス作成1

途中までは自動と同じ手順で、サービス接続を作成します。認証方法まで来たら、「Workload Identity federation (manual)」」を選択します。その後、サービス接続名を適当に入力して次へをクリックします。

そうすると、以下のように「issuer」と「Subject identifer」が表示されます。これをアプリ作成時に使用します。

アプリの登録とフェデレーション資格情報の作成

今回は、自動作成時とは異なり、アプリやフェデレーション資格情報も手動で作成す必要があります。まずは、Entra IDの「アプリの登録」からアプリを作成します。名前だけ入力して、そのほかはデフォルトのまま登録します。

作成後、「証明書とシークレット > フェデレーション資格情報」の順にクリックして、資格情報を追加します。

資格情報の追加画面で、先ほどサービス接続作成中に表示された値を入力します。フェデレーション資格情報のシナリオ」は「その他の発行者」を選択します。そして、発行者」に「Issuerを、サブジェクト識別子」に「Subject identiferの値を入力します。最後に名前を適当に付けて完了です。

その後は、作成したアプリに対して「共同作成者」をサブスクリプションをスコープに割り当てます。手順は自動作成と同じですので省略します。サービス接続を作成する前にロールを割り当てないと、接続確認時にエラーになります。

サービス接続作成2

「サービス作成1」の続きです。下にスクロールするとサブスクリプション情報等を入力する画面が表示されますので、デプロイ対象のAzure情報を入力します。

Service Principal Id」が何を入力すべきかわかりづらいと思います。ここは、「アプリの登録とフェデレーション資格情報の作成」で作成したアプリの「アプリケーション(クライアント)ID」を指定します。オブジェクトIDではありません。

デプロイ

以上で手動作成手順は完了です。あとは、自動作成時と同じくパイプラインを実行してみてください。問題なくデプロイが成功するはずです。

最後に

シークレットだと有効期限を最長2年までしか設定できません。そのため定期的に更新する運用作業が発生していました。また万が一シークレットが外部に漏れてしまうと、重大なセキュリティインシデントに繋がりかねません。

今回新たにGAになった「Workload identity federation」を利用することで、これらの問題を解決できます。

コメント