Azure DevOps(に限らないですが)通常のCI/CDではだいたいこんな感じですよね。
一応Service Connectionが使えるパイプラインは管理者が指定できますが、一度許可で指定したらパイプラインの内容がどうあれ、デプロイし放題です。最初にマルウェアチェックのタスクやソース監査のタスクがあっても「めんどくさいからいらないや」とパイプライン編集権限のある人削除してしまえばなんでもデプロイできるようになってしまいます。
これはまずいので、Service Connection(Environmentsでも同じ)を使うときにチェックしたい、という要件が必要になります。
これを実現するための機能がApproval and checks
です。
承認にはいくつかの種類があるのですが、わかりやすいのはBusiness Hours
(使用可能な時間を制限)、Approvals
(承認しないとデプロイできない)です。しかし、承認者がいつも正しくチェックできるとは限りません。そういうチェックは機械にやらせましょうということで、使えるのがRequired template
やEvaluate artifact
です。後者はまた別途解説します。
特にRequired template
は特定のテンプレートが含まれていないとService ConnectionとEnvironmentsを使わせないという設定なので、極めて有効です。しかし、この機能ドキュメントも試しただけではわかりづらい仕様があります*1。
まず、テンプレートのおさらいです。呼び出されるテンプレートをPipelinesレポジトリのapprvaldemo/credscan.ymlに配置します。
steps: - task: CredScan@2 displayName: 'Check Cred scan' inputs: toolMajorVersion: 'V2'
わかりやすくするために引数などは使わないテンプレートです。そして、そのテンプレートを使用する呼び出し側。resources
句でGitレポジトリをリソースとして参照して、templateという名前でPipelinesレポジトリを参照可能とします。
resources: repositories: - repository: templates type: git name: SessionDemo/Pipelines stages: - stage: Deploy jobs: - deployment: Deployment displayName: 'Deploy' pool: vmImage: 'windows-latest' strategy: runOnce: deploy: steps: - template: apprvaldemo/credscan.yml@templates
最後の-template
でテンプレートとして用意されている外部レポジトリのYAMLファイルを参照しています。この例では非常に単純なタスクですが、一連のデプロイやビルド方法、特定のコンプライアンス関係のタスクをまとめて使用するのが一般的かと思います。
じゃあ、このテンプレートをApprovalに設定すればいいのか?こういう形にしたいということで、このテンプレートをApproval and checks
に指定したとします。
これで良さそうに見えるので実行してみると…。
ビルドが失敗します。チェックが失敗したとかではなくて、そもそもApproval and checks
にRequired template
を指定するときはこのようにYAMLパイプラインの一部として存在するテンプレートの指定は認められていません。評価の順番としてはこうなるそうです。
- テンプレートを展開
- 式を評価
- stageを評価して依存関係をチェック
- すべてのリソースに関して承認をチェック (以下略)
で、この「テンプレートを展開」ですが、何もしなくても展開ではなくて、extends
句を使った展開でなくてはなりません。実際にはこうなります。
呼び出される側のテンプレートはこの方法で、template.yml
という名前でGitレポジトリのルートフォルダーに保存します。
parameters: - name: buildConfiguration type: string default: Release steps: - task: DownloadBuildArtifacts@1 inputs: buildType: 'specific' project: '0a9af6d9-(略)' pipeline: '238' specificBuildWithTriggering: true buildVersionToDownload: 'latestFromBranch' allowPartiallySucceededBuilds: true branchName: 'refs/heads/main' downloadType: 'single' artifactName: 'approvaldemo' downloadPath: '$(System.ArtifactsDirectory)' - task: AzureWebApp@1 inputs: azureSubscription: '(Service connection)' appType: 'webApp' appName: '(appname)' package: '$(System.ArtifactsDirectory)/**/*.zip' deploymentMethod: 'auto'
- project: Azure DevOps内のプロジェクトのGUID。REST APIでも使います。
- pipeline: パイプラインのIDを指定します。こちらは連番ですね。
GUIではこのように指定します。
Download Build Artifacts
タスクの指定例です。GUIではプロジェクト名とパイプライン名が名前で選択できるので、楽です。そして、呼び出すパイプラインはこのように指定します。
pool: vmImage: ubuntu-latest variables: buildConfiguration: 'Release' extends: template: templatefile.yml parameters: buildConfiguration: $(buildConfiguration)
extends
でテンプレートを指定します。これでこのパイプライン内で指定したテンプレートが完全に展開されます。Approval and checks
への指定はこうなります。
Service Connectionですが、Environmentでも同じです。
項目 | 説明 |
---|---|
repositry | プロジェクト名/レポジトリ の形式で指定します |
ref | Gitのブランチ名です。ref/heads/ブランチ名 です。デフォルトをmainにしている人は気を付けてください |
Path to required YAML template | YAMLテンプレートファイル名を指定します。私はサブフォルダー指定したら動きませんでした |
Approval and checks
を保存すると、指定したテンプレートを使わない限り、Service Connectionへのデプロイに失敗します。
先ほどのエラーはApproval and checks
の書式間違いですが、こちらはちゃんとApproval and checks
のポリシー違反であることが分かります。
注意事項(今後変わるかもしれない)
templateに指定するテンプレートのYAMLはGitのルートフォルダに置かれていなくてはなりませんでした。サブフォルダに置くと「Gitのコミットにない」といわれました。もしかしたらバグかもしれない。
パイプラインやapproval and checks
を編集すると、挙動がおかしいように見えるので、聞いてみます…。
一応シナリオとしては書いたようにセキュリティのチェックをするためのに用意されています。テンプレート内で独自のフローを使いたければ、引数に渡して、stepsとして展開する、という方法が紹介されています…が、自分が試したときはresources
で外部レポジトリのフォルダー使うと、Queueに入ろうとして、エラーになったのだけどなぁ🤔。
*1:最初に試したときはこうじゃなかったはずなんだけどなぁ…