Azure DevOps で本番環境へのデプロイを管理する|承認・自動確認・切り戻しの組み立て方

Azure Pipelines の承認は、承認を付けた「環境」を使うステージでしか求められません。環境を使わずに同じサービス接続でデプロイするパイプラインには、サービス接続の側にチェックが無ければ、承認はかかりません。

承認の設定はパイプラインの YAML の外にあり、YAML を書き換えても外せません。ただし、それが意味を持つのは、本番へのデプロイが必ずその環境を通る場合に限られます。承認を付けただけで安心せず、本番へ届く経路のほうを塞いでおく必要があります。

本番のデプロイの前に承認を挟む、プルリクエストでレビューを必須にする。ここまでは Azure DevOps を使う方の多くが押さえている基本だと思います。弊社はお客様専用の環境を Azure 上に構築し、検証環境と本番環境の2面を Azure Pipelines でデプロイする運用をしています。この記事では、本番環境へのデプロイを「承認」「自動確認」「切り戻し」の3つで組み立てる方法を、実際に組んで運用した経験から解説します。承認では付ける場所と見落としやすい細部を、自動確認では何が返れば正常とするかを、切り戻しではデプロイ直前の復元点と訓練の進め方を扱います。

株式会社ロクシアシステムズは、福岡と東京を拠点に、全国のお客様へ Microsoft 365・Azure の導入支援と、AIの業務活用(AIプライベート)の支援を提供しているIT企業です。

本番へのデプロイを、承認・自動確認・切り戻しの3つで組み立てる

弊社が Azure DevOps で組んでいる本番へのデプロイの流れは、次のとおりです。変更はプルリクエストのレビューを経てマージされ、まず検証環境へ自動でデプロイされます。本番へのステージは承認を待って止まり、承認されると復元点を退避してからデプロイし、最後に自動で確認します。問題があれば、決めておいた手順で切り戻します。

柱決めることこれが無いと起きること
承認今、本番に出してよいか。誰が判断するか業務の繁忙時や告知の前に、変更が本番に出る
自動確認デプロイ後に何が返れば正常とするか本番が壊れた状態を、デプロイ成功として通してしまう
切り戻し問題が出たときに、何をどこまで戻すか障害の最中に戻し方を考えることになる

この3つを支える前提として、本番を操作できるサービス接続をどう守るかがあります。本番への入口が承認を通らずに使えてしまうと、3つの柱が働きません。以下、承認から順に解説します。

承認:プルリクエストのレビューがあれば、承認は要らないのでは?

本番へのデプロイに承認を挟むと聞いて、多くの方が最初に思うのはこの疑問です。本番用のブランチにブランチポリシーを設定し、レビュー担当者の承認を必須にしていれば、本番に出る変更はすべて誰かが見ているはずです。

弊社は、この2つを別のものとして両方置いています。理由は、決めていることが違うからです。

プルリクエストのレビューデプロイの承認
決めること何を変えるか(変更の中身が正しいか)いつ本番に出すか(今出してよいか)
設定する場所リポジトリのブランチポリシーパイプラインの環境(承認とチェック)
判断する人コードを読める人本番の稼働に責任を持つ人
効く範囲そのブランチへのマージその環境を使うステージの実行

変更の中身が正しくても、お客様の業務が集中する時間帯や、利用者への告知の前に本番へ出すべきではない場面があります。また、パイプラインはプルリクエストを経ずに手動で実行することもできます。レビューは変更の中身を守り、承認は本番へ出すタイミングを守る、と分けて考えています。

ブランチポリシーの側にも、確かめておきたい設定があります。Microsoft Learn によると、「Allow requestors to approve their own changes」を有効にしない限り、プルリクエストの作成者が自分で承認しても、必要なレビュー担当者の人数には数えられません。さらに「Prohibit the most recent pusher from approving their own changes」で、最後に変更を押し込んだ人の承認を数えないようにもできます。一方で、ポリシーを迂回する権限を持つ人は、保護されたブランチに直接書き込めます。この権限を誰が持っているかも、あわせて確認しておきます。

承認:環境に付けるだけでは、承認を通らない経路が残る

YAML のパイプラインで本番のデプロイに承認を挟むには、Azure DevOps の「環境」(Environments)を作り、その環境に承認のチェックを付けます。パイプラインの側では、デプロイのジョブでその環境を指定します。Azure Pipelines は各ステージの実行前に一時停止し、そのステージが使うリソースに付いたチェックがすべて満たされるまで待ちます。

YAML — 本番の環境を指定したデプロイのジョブ
- stage: Production
  jobs:
  - deployment: DeployApp
    environment: production        # この環境に付けた承認が求められる
    strategy:
      runOnce:
        deploy:
          steps:
          - task: AzureFunctionApp@2
            inputs:
              connectedServiceNameARM: sc-production   # 本番へのサービス接続
              appType: functionAppLinux
              appName: <アプリ名>
              package: <パッケージのパス>

Learn は、承認などのチェックは YAML ファイルの中に定義されないため、YAML を編集する人がステージ開始前のチェックを変えることはできない、と説明しています。承認を外すには、環境の設定を変える権限が要ります。

ただし、チェックが評価されるのは、ステージがそのリソースを使うときだけです。環境を指定しない通常のジョブで、同じサービス接続を使って本番にデプロイすれば、環境に付けた承認は一度も求められません。サービス接続の利用を許可されたパイプラインの YAML を編集できる人であれば、承認を通らずに本番へ届く経路を作れる、ということです。

YAML — 環境を指定しないジョブには、環境の承認がかからない
- stage: Production
  jobs:
  - job: DeployDirect               # 通常のジョブ。環境を指定していない
    steps:
    - task: AzureFunctionApp@2
      inputs:
        connectedServiceNameARM: sc-production   # 同じサービス接続なら本番に届く

Learn は、チェックを付けられるリソースとして、環境のほかにサービス接続・リポジトリ・変数グループ・セキュアファイル・エージェントプールを挙げています。本番へ届く経路は、最終的には本番へのサービス接続を通ります。弊社は、承認を環境だけでなくサービス接続の側でも効かせる前提で設計しています。使える手段は次のとおりです。

手段内容防げること
サービス接続に承認を付ける本番へのサービス接続に、環境と同じ承認のチェックを付ける環境を指定しないジョブからのデプロイ
ブランチの制御サービス接続を使える実行を、指定したブランチ(refs/heads/main の形で書く)からのものに限る。ブランチ保護が有効であることも条件にできる作業用のブランチからの本番デプロイ
必須のテンプレート決められたテンプレートを継承しないパイプラインを失敗させる承認や確認の手順を省いたパイプライン
パイプラインの権限を絞るサービス接続の「Open access」を有効にせず、使えるパイプラインを個別に許可する新しく作ったパイプラインからの利用

なお、チェックを定義したリソースの管理者の権限を持つ人は、チェックを迂回できます。Learn によると、迂回した場合は誰が迂回したかがチェックの画面に表示されます。管理者の権限を誰が持つかも、承認の設計の一部です。

承認:設定で見落としやすい細部

承認のチェック自体は、承認者を選ぶだけで設定できます。ただし既定の動きには、運用を始めてから気付くものがいくつかあります。

グループを承認者にすると、1人の承認で進む

承認者にはユーザーだけでなくグループも指定できます。Learn によると、グループを承認者にした場合、グループの中の1人が承認すれば実行は先に進みます。「このグループの全員が見る」という意味にはなりません。複数の立場の人の承認が必要なら、承認者を個別に並べ、必要な承認の数を指定します。

自分が始めた実行を、自分で承認できるか

承認のチェックには、承認者が自分の実行を承認することを許すか制限するかの設定があります。デプロイを始めた人と承認する人を分けたい場合は、ここを制限にしておきます。人数の少ないチームでは、制限すると承認できる人がいなくなることもあるため、誰がデプロイを始め、誰が承認するかを先に決めてから設定します。

期限までに承認されないと、ステージは「スキップ」になる

承認には期限を設定できます。Learn によると、YAML のパイプラインでは、期限までに承認されなかったステージは「スキップ」として扱われます。却下や失敗とは表示が違うため、デプロイされなかったことを見落とさないようにします。期限切れのステージは再実行できます。なお、従来型のリリースパイプラインの承認は、期限切れで「却下」になります。同じ「承認」でも挙動が違う点に注意が要ります。

承認できる人の一覧は、承認待ちが始まった時点で固定される

Learn は、承認できるユーザーの一覧は、承認とチェックの実行が始まった時点で固定されると説明しています。承認待ちになってから承認者を追加しても、その実行には反映されません。担当者の交代や休暇に備えて承認者を足す場合は、次の実行から効くものとして扱います。

排他ロックの既定では、待っている古い実行が取り消される

同じ環境へのデプロイが同時に走らないように、排他ロックのチェックを付けることがあります。ロックの動き(lockBehavior)を指定しない場合の既定は runLatest で、最新の実行だけが進み、待っていた古い実行は取り消されます。ロックを待っている間に新しい実行が来ると、古い実行は取り消され、その変更は新しい実行にまとめて含まれて本番に出ることになります。1つずつ順にデプロイしたい場合は sequential を指定します。

承認:作業する人が代わりに承認しない

Azure DevOps では、環境へのデプロイの履歴として、どのパイプラインのどの実行がデプロイしたか、どのコミットや作業項目が新しく本番に入ったかを確認できます。承認の結果は、実行ごとのチェックの画面や API で確認できます。

弊社は、この承認を「本番に出すことを、責任を持つ人が判断した」ことの記録として扱っています。そのため、デプロイの作業をする者は、承認者の代わりに承認しないことを決めています。急いでいるときほど、作業者が承認者の権限を借りて進めたくなりますが、そうすると承認の記録は残っても、判断した人が誰かという意味が失われます。お客様に本番の変更を説明するときに、誰がいつ判断したかを示せることを大切にしています。

本番への入口:サービス接続にシークレットを置かない

パイプラインが Azure のリソースを操作するには、Azure Resource Manager のサービス接続が要ります。従来は、アプリ登録のクライアントシークレットや証明書をサービス接続に保存する形が一般的でした。この場合、シークレットの有効期限が切れるとデプロイが止まり、漏えいした場合は本番を操作できる資格情報が外に出ることになります。

ワークロード ID フェデレーション(workload identity federation)を使うと、サービス接続にシークレットを保存せずに済みます。パイプラインの実行ごとに Azure DevOps が発行するトークンを、Microsoft Entra ID が信頼して短時間のアクセストークンに交換する仕組みです。Azure Resource Manager のサービス接続での利用は、2024年2月に一般提供になりました。弊社がお客様専用の環境に新しく作るサービス接続は、原則としてこの方式にしています。

既存のシークレット方式のサービス接続は、ワークロード ID フェデレーションに変換できます。Learn によると、変換後7日以内であれば元のシークレット方式に戻せます。ただし変換の機能が使えるのは、Azure DevOps が自動で作成したサービス接続で、かつ1つのプロジェクトだけで使っている場合です。手作業で作成した接続は、新しく作り直すことになります。

また、Microsoft Entra ID で認証するパイプラインのタスクのほとんどは対応していますが、一部に対応していないタスクや、新しい版への更新が必要なタスクがあります。Marketplace の拡張機能のタスクは、提供元に確認が要ります。切り替える前に、パイプラインで使っているタスクを一覧にして確かめておきます。

自動確認:何が返れば正常かを、項目ごとに先に決める

デプロイが終わったら、パイプラインの中で本番が期待どおりに動いているかを自動で確かめます。ここで大切なのは、「何が返れば正常か」を項目ごとに先に決めておくことです。弊社は、次の2つの失敗を経験してから、確認する項目を見直しました。

ツールの終了結果を、デプロイの成否として扱わない

アプリの前段にアクセス制限をかけている環境では、デプロイのツールがデプロイ後に行う疎通確認が制限に弾かれ、ツールは失敗として終わることがあります。弊社では、ツールが失敗を返したのに、アプリは新しいコードで正常に動いていたことがありました。逆に、ツールは成功を返したのに、関数が1つも読み込まれていなかったこともあります。デプロイの成否は、ツールの終了結果ではなく、下の表の確認で判定しています。Azure Functions での経験はAzure Functions Flex Consumption のデプロイと運用で解説しています。

「302 が返った」で終わらせない

App Service 認証(Easy Auth)を有効にしたアプリは、サインインしていない要求に 302 を返し、サインインの画面へ転送します。弊社は当初、「302 が返ること」を正常の条件にしていました。ところが、設定の変更でサインイン後の戻り先がアプリ本体のホスト名に変わり、利用者がサインインできない状態になったときも、302 は返り続けていました。この状態を自動確認は正常と判定していました。

302 の応答には転送先(Location)が含まれ、その中にサインイン後の戻り先が入っています。いまは、戻り先が公開しているホスト名を指しているかまで確かめています。また、サインインしていない要求を転送する設定では、存在しない URL でも Easy Auth が手前で 302 を返すため、302 はその URL にアプリが存在する証明にもなりません。Front Door と組み合わせたときの戻り先の設定は、Azure Front Door の背後のアプリを守るで解説しています。

弊社の構成で確かめている項目は次のとおりです。何を確かめるべきかは構成によって変わるため、例としてご覧ください。

確認する項目正常の条件見つけられること
関数の一覧期待する数の関数が登録されているツールは成功なのに中身が空
公開している入口トップが 200、認証が要る URL が 302入口の経路や証明書の不備
302 の転送先サインイン後の戻り先が公開しているホスト名を指すサインインできない状態
アプリ本体への直接の要求アクセス制限で拒否されるアクセス制限が外れた状態
重要なアプリ設定安全側の値になっている(例:認可が判定できないときは拒否する)設定が既定値や緩い値に戻った状態
監視への接続Application Insights にテレメトリが届く障害時にログが残らない状態

切り戻し:デプロイの直前に、復元点を退避する

自動確認で問題が見つかったとき、戻す先が分からなければ切り戻せません。承認は「出してよいか」を人が判断する仕組みで、出した後に元へ戻す仕組みではありません。弊社は切り戻しの準備として、本番のステージの中で、承認の後・デプロイの前に、本番のアプリの状態を退避する手順を自動で走らせています。

退避するのは、アプリ設定・認証の設定・アクセス制限・リソース本体(マネージド ID を含む)です。コードはリポジトリに履歴が残りますが、これらの設定は手作業で変わることがあり、Azure の変更の履歴からも前の値をたどれるとは限りません。インフラをコードで管理するときに設定が消える仕組みと、変更の履歴でたどれる範囲は、Azureの構成をBicepで管理するときの落とし穴で解説しています。

YAML — デプロイの前に状態を退避し、パイプラインの成果物として残す
steps:
- task: AzureCLI@2
  displayName: 復元点を退避する
  inputs:
    azureSubscription: sc-production
    scriptLocation: scriptPath
    scriptPath: tools/snapshot-app-state.ps1   # 設定を JSON で書き出すスクリプト
- task: PublishPipelineArtifact@1
  inputs:
    targetPath: snapshot
    artifact: pre-deploy-snapshot

ここで注意が要るのは、パイプラインの成果物の寿命です。Learn によると、パイプラインの実行が保持期間を過ぎて削除されると、その実行の成果物も一緒に削除されます。複数ステージの YAML パイプラインの保持期間はプロジェクトの設定でしか決められず、環境ごとには変えられません。本番のデプロイの実行は、保持リース(retention lease)で保持期間を延ばすか、復元点を別の保管場所に写しておきます。

また、アプリ設定の退避には、接続文字列やキーの値がそのまま含まれることがあります。成果物はパイプラインを閲覧できる人がダウンロードできるため、秘密情報そのものは Key Vault に置き、アプリ設定からは Key Vault の参照で読む形にしておくと、退避した内容に値が載らなくなります。

切り戻し:手順を決めて、訓練しておく

本番で問題が見つかったときに、その場で戻し方を考えるのは危険です。弊社は、本番の運用を始める前に、検証環境で切り戻しを実際に一度通しています。

コードの切り戻しには、Azure Repos の完了したプルリクエストにある「Revert」を使います。Learn によると、Revert を選ぶと、そのプルリクエストの変更を打ち消す1つのコミットを持つ新しいブランチが作られ、そこから新しいプルリクエストを作ってマージすると切り戻しが完了します。打ち消すプルリクエストも通常の変更と同じくブランチポリシーのレビューを通り、マージされればパイプラインが動き、本番のステージでは承認が求められます。緊急時でも承認の記録が途切れないのが、この方法を選ぶ理由です。

ただし、この方法で戻るのはリポジトリで管理しているものだけです。訓練してみると、戻るものと戻らないものの境目がはっきりします。

対象Revert で戻るか戻す方法
アプリのコード戻る打ち消すプルリクエストをマージし、パイプラインで再デプロイする
インフラのコードで管理している設定コードは戻る再適用が必要。適用前の確認は通常の変更と同じ手順で行う
手作業で変えたアプリ設定・認証の設定戻らない退避した復元点と比べて、手で戻す
データの変更(データベースの項目の追加など)戻らない変更の前に、戻せる形で変更するかを設計しておく

検証環境で訓練する意味は、もう1つあります。検証環境を本番と同じ構成で作っていても、コードで複製できないものがあります。弊社では、App Service 認証に使うアプリ登録の API のアクセス許可が検証環境にだけ無く、実際にサインインを試すまで誰も気付かなかったことがあります。アプリ登録は Azure のリソースではないため、Bicep などのテンプレートでは複製されません。訓練で実際に操作してみることで、こうした差も見つかります。

この記事の要点

承認

  • プルリクエストのレビューは変更の中身を、承認は本番に出すタイミングを決める。両方置く
  • 環境の承認は、その環境を使うステージにしか効かない。本番へのサービス接続にも承認やブランチの制御を付ける
  • グループの承認は1人で進む、期限切れはスキップ、承認者の一覧は承認待ちの開始時に固定、排他ロックの既定は古い実行を取り消す
  • 作業する人が代わりに承認しない

本番への入口

  • サービス接続はワークロード ID フェデレーションにしてシークレットを置かない

自動確認

  • ツールの終了結果や 302 だけで判定せず、何が返れば正常かを項目ごとに決める

切り戻し

  • 承認の後・デプロイの前に復元点を退避し、成果物の保持期間に注意する
  • Revert で承認の記録を保ったまま戻し、戻らないものを訓練で把握しておく

弊社の支援

弊社は、お客様専用のAI基盤やアプリを Azure 上に構築する形で、AIの業務活用を支援しています。構築した環境は、検証環境と本番環境の2面を同じ構成で持ち、Azure Pipelines で検証環境へ自動でデプロイし、本番へは承認を経てデプロイする形で運用しています。

手作業でデプロイしてきた既存の環境についても、リポジトリへの移行から、検証環境の複製、承認とデプロイ後の確認を組み込んだパイプラインの構築、切り戻しの訓練まで、段階的に進める支援を行っています。インフラの払い出しや変更管理の考え方はAzureのインフラ払い出しをセルフサービス化するもあわせてご覧ください。

よくあるご質問

こちらをクリックして「よくある質問」を表示

ブランチポリシーでレビューを必須にしていれば、デプロイの承認は不要ですか?

弊社は両方置くことをお勧めしています。レビューは変更の中身が正しいかを、承認は今本番に出してよいかを判断するもので、決めていることが違います。また、パイプラインはプルリクエストを経ずに手動で実行することもできます。

環境に承認を付ければ、本番へのデプロイは必ず承認を通りますか?

承認が求められるのは、その環境を使うステージだけです。環境を指定しないジョブで同じサービス接続を使えば、承認は求められません。本番へのサービス接続にも承認やブランチの制御を付け、使えるパイプラインを個別に許可しておくことをお勧めします。

承認されないまま期限が過ぎると、どうなりますか?

YAML のパイプラインでは、そのステージはスキップとして扱われ、デプロイは行われません。却下とは表示が違うため、デプロイされていないことを見落とさないようにします。期限切れのステージは再実行できます。従来型のリリースパイプラインでは、期限切れは却下として扱われます。

デプロイのツールが成功を返せば、デプロイは成功したと考えてよいですか?

それだけでは判断できません。弊社では、ツールが失敗を返したのに正常に動いていたことも、成功を返したのに関数が1つも読み込まれていなかったこともあります。関数の一覧、公開している入口の応答、302 の転送先、アクセス制限、重要なアプリ設定など、何が返れば正常かを項目ごとに決めて確かめることをお勧めします。

デプロイ前に退避した復元点は、いつまで残りますか?

パイプラインの成果物として残した場合は、その実行が保持期間を過ぎて削除されるときに一緒に削除されます。本番のデプロイの実行は保持リースで保持期間を延ばすか、復元点を別の保管場所に写しておくことをお勧めします。

切り戻しでは、アプリの設定も元に戻りますか?

プルリクエストの Revert で戻るのは、リポジトリで管理しているものだけです。手作業で変えたアプリ設定や認証の設定、データの変更は戻りません。これらは、デプロイ前に退避した復元点と比べて戻すか、戻せる形で変更するよう設計しておきます。

お客様専用の Azure 環境を、承認を経てから本番に出す形で運用したい、手作業のデプロイをパイプラインに置き換えたい、というご相談を承っています。現在の構成と運用体制をお聞かせいただければ、承認者の決め方から、デプロイ後の確認、切り戻しの手順まで、進め方をご提案します。お問い合わせよりお気軽にご相談ください。

関連サービス・関連コラム

まずは無料トライアルで、効果をご確認ください

「AIプライベート」は短期・低コストで試せます。帳票入力(AI-OCR)・電話対応(AI-Voice)・計画づくり(AI-Enhance)の自動化から、クラウド(Azure・Microsoft 365)・DXのご相談まで、お気軽にどうぞ。