Azure Pipelinesでジョブの実行に承認などを挟み一時的に止める


要約
--------

https://docs.microsoft.com/en-us/azure/devops/pipelines/process/approvals?view=azure-devops&tabs=check-pass

- Azure Pipelinesの「Environments」を作成(例: production)
- Environmentsに「Approvals and checks」を設定
- YAML定義でジョブで`environments: Environments名`を設定


Environmentsは、どのVM or Kubernetesに対してどのようにデプロイするか、とかできますが、今回はそれは使いません。（Azure WebAppsやAzure Functions等で行うため）

背景
--------

デプロイの自動化をしても、いきなり反映されると困るときがあります。

体裁としての承認フローが求められる時もありますが、今回はデプロイ前に色々作業したくなるときがあるので止めたいときがあります。

- ステージングの動作を見て問題なかったら同じ成果物を本番に反映したい
    - 本番とステージングでブランチで分けて管理する方法もあるが、ビルドが別々になり異なる成果物になってしまう
- DBのスキーマ変更を先んじてやっておかないと新しいアプリが動かない。スキーマ変更をデプロイフローに入れてもいいが、負荷状況をみてやらないといけないことや、そもそもエージェントからDBに繋がせることを許すのは厳しいので、手作業で反映したいとか。（これみんなどうしてるか気になる。）

なので、確認や作業を済ませたりして「ヨシッ！」とやってからデプロイをしたい、という感じです。



Azure Pipelinesでは、前から「Releases」という項目があり、それで同様なことができることは知っていました（クラシックデプロイと呼ばれています）
が、最近はReleasesに当たる作業もYAMLで書いて管理するのがナウなやり方のようです。
設定自体はYAMLだけに収まらないものの、できそうなのでやってみました。

手順
--------

### Environmentsの作成

Environmentsから適当に作ります。
Nameは設定に使うので、対象に合わせたわかりやすい名前にしましょう。（staging, production, qa, testなどなど）
Resourceは今回はNoneにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/b12c9ece-d6e5-e92d-7499-2b28ded2033e.png)

### 作成したEnvironmentsに「Approvals and checks」を設定

作成したEnvironmentsを開き、「Approvals and checks」を設定していきます。最初は何もありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/b4cccec1-de88-3545-820c-11b8409582fb.png)

「Approvals and checks」には色々ありますが、今回はApplobals(承認)を使います。これは承認の作業をするまで一時停止状態になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/de1793ae-2b8b-c247-d5cc-b7c68b913a68.png)

Aproversには承認してくれる人をいれます。開発メンバーや意思決定者などがいいでしょう。
Instructions to approversは、承認するときの画面に表示されます。何か確認してほしいこととかを書いておきましょう。
（productionへのデプロイなら、stagingに上がっている内容を見て問題ないか判断してもらうとか？）
後は自作自演を許すか、有効期限をつける等です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/0ab2394a-a4ad-b45f-fe6a-b9fc85123a75.png)

Environmentsの作成と設定は以上です。

### YAMLを書く

通常のジョブ(job)の代わりにデプロイメントジョブ(deployment)で設定します。
この時、承認を要求するジョブはステージを分けたほうが良いかと思います。（後述）

以下はビルドジョブとデプロイジョブを2つのステージに分けた例です。


``` yaml
# ...省略

stages:
  - stage: Build_And_Test
    jobs:
      - job: Build_And_Test_Job
        # ...省略
  - stage: Deploy
    dependsOn: Build_And_Test
    # デプロイはmasterブランチのみ
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) 
    jobs:
      - deployment: deployment 
        environment: staging # ここをEnvironmentsの設定に合わせる
        strategy:
          runOnce:
            deploy:
              steps:
                - checkout: none
                - download: none # ref: https://docs.microsoft.com/ja-jp/azure/devops/pipelines/process/resources
                - task: DownloadBuildArtifacts@1
                  inputs:
                    downloadPath: $(System.DefaultWorkingDirectory)
                    artifactName: BuildOutputs
                  displayName: "Download Build Artifacts"
```

このYAMLを反映します。

### 実行

トリガーや手動実行などでパイプラインを実行します。

初回はEnvironmentsのアクセス許可が必要です。パイプラインを見に行き、許可してあげます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/43ee8743-dea2-4143-cb6b-b864502adc9a.png)

Permitを押して許可してあげます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/ec06bd4d-e70c-feac-be65-e0bbea811e8f.png)


上手く流れると、DeployのタイミングでWaitingになります。承認待ちが必要だからですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/94288fd8-41fd-88c9-f7e9-a7b92e048db0.png)

承認者にはこんな感じのメールも来ます。Approvalsで設定した文言が含まれないのは残念ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/b456fdf2-63e9-0bfa-eff3-a6c3a4a29865.png)

メール中の「Review Approval」のリンクを押すと、Pipelinesのページが出てくるので、問題なければApprove(承認)してあげます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/b52e310e-4957-6342-a608-408f9ed2d81f.png)

承認するとジョブが動き始めます。良かったですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/e6ab26b0-0675-5733-445c-d64157b11660.png)


その他
----------------


### ステージの最初にapprovalを求められる？

environmentsの設定はジョブ単位で出来ますが、試した感じでは、ステージ単位で見ているような気がします。（dependsOnのせい？）

というのも、最初は1つのステージでまとめて、job-1でビルドとテストを行い、job-2にデプロイという流れを考えていましたが、job-2の前に承認を入れようとしましたが、job-1を実行する前に承認を要求されました。(それならstageに設定するオプションにしてほしい)

YAMLの設定は以下でした。1ステージに1ジョブ1デプロイメントジョブを書いています。

``` yaml
stages:
  - stage: Build_And_Test
    jobs:
      - job: Build_Functions
        # ...省略
      - deployment: deployment # デプロイメントジョブを同ステージに並べている
        dependsOn: Build_Functions
        condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
        environment: staging # ここをEnvironmentsの設定に合わせる
        strategy:
          # 省略

```

このYAMLで実行し、最初に承認を要求されたのでRejectした時の画面ですが、deploymentジョブに依存しているBuild_Functionsジョブが実行されていません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/19c50a7a-eb17-7279-1229-fff6d3087bba.png)

Build_Functionsジョブが実行された後のdeploymentジョブで承認されることを期待していましたが・・・

この辺りの挙動はドキュメントに書いてありそうですが、ぱっとすぐ見つけられなかったので「とりあえずステージを分けましょう」という気持ちになりました。