Azure DevOps で Pipeline の手動承認を通知する方法


自分的には Teams への通知をしたかった。ということで Teams にアプリがありました。
追加したいチャンネルに Azure Pipelines のアプリを追加します。

追加すると、以下のようなメッセージが表示されます。

あとは、以下のようなチャットを打ち込むと

@Azure Pipelines subscribe https://dev.azure.com/組織名/プロジェクト名/_build?definitionId=PipelineのID

こんな感じで、パラパラと表示されます。

カード内の Approve ボタンでポータルまでいかなくても承認可能です。便利。

このアプリのドキュメントはこちらになります。
https://docs.microsoft.com/en-us/azure/devops/pipelines/integrations/microsoft-teams?view=azure-devops

本来書こうと思った記事

上の通り作らなくて通知する方法ありました。元々は Azure Functions でやるぜ!ということを書こうと思ったのですが、必要なくなりました。でも勿体ないので残しておきます。
メール通知は、無さそうに見えるのでメール通知をしたかったら作らないといけないと思ったけど、Teams の通知をメールでも送るようにすればいいですね。

本文

何か今時点でないんですよね。
昔のクラシックパイプラインと呼ばれるものには、あったのですが YAML Pipeline には無さそう。(あったらごめんなさい)

かといって定期的に Azure DevOps のページ見るか?と言われると絶対見ないので、何らかの方法での通知手段は欲しいです。Azure DevOps の手動承認の設定は Environments か Service connection に設定できて、これに影響を与える Pipeline が実行されるときに承認が必要になるという流れなんですが、その設定画面を見てみると Azure Functions とか Invoke REST API とかありますね。

つまり、チェック用の Webhook みたいなものなので、ここに通知処理入れれば出来そう?ということでやってみました。今回は Azure Functions で行ってみようと思います。
Azure Functions を選ぶと以下のような設定画面になります。

ふむふむ、Pipeline の変数をヘッダーやら Body やらに埋め込んで関数を呼んでくれそうですね。使える変数の一覧は下のドキュメントにあります。

Use predefined variables

そして、対象の Pipeline の状態をチェックするための URL は変数をいくつか組み合わせると以下のような感じで作れます。

$(System.CollectionUri)/$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)

ということで、 Invoke Azure Function の Headers の設定に以下のような定義を追加して

{
"Content-Type":"application/json", 
"PlanUrl": "$(system.CollectionUri)", 
"BuildId": "$(Build.BuildId)",
"TeamProject": "$(System.TeamProject)"
}

受け側の Azure Functions で URL を組み立てれば OK ですね。Azure Functions はプログラム書けるので、あとは SendGrid 使ってメールなり、適当なチャットの IncomingWebhook とかでメッセージ投げつけてやればいい感じです。

自分は Microsoft Teams を使ってるので、Teams の IncomingWebhook を構成して URL をゲットしました。

IncomingWebhook を作りたいチャンネルのメニュー(右端の…)からコネクタを選ぶと追加できます。

ゲットした URL に

{ "text": "message" }

のような JSON を送るとチャンネルにメッセージが投下されます。ということで、適当に関数を定義して…

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Net.Http;
using System.Text;

namespace DevOpsFunction
{
    public static class Function1
    {
        public static HttpClient Client { get; } = new HttpClient();

        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            var uri = new Uri($"{req.Headers["PlanUrl"]}/{req.Headers["TeamProject"]}/_build/results?buildId={req.Headers["BuildId"]}");
            await Client.PostAsync(
                "https://outlook.office.com/webhook/xxxxxx-xxxx-xxxx-xxxxx@xxxxxx-xxxxxx-xxxxx-xxxxx/IncomingWebhook/xxxxxxxxxxxxx/xxxxxx-xxxxx-xxxx-xxxx", // IncomingWebhook の URL
                new StringContent($"{{ \"text\": \"承認待ちのビルドがあります。 <a href='{uri}'>開く</a>\"}}", Encoding.UTF8, "application/json"));
            return new OkResult();
        }
    }
}

この Service connection か Environments の Approvals and checks で、この関数を先ほどのヘッダーの設定をして呼ぶように Azure DevOps 側で設定します。そして、忘れずに人による Approvals も一緒に追加しておきましょう。

そうすると、承認が必要なビルドが走ると Teams に以下のようなメッセージが表示されます。

「開く」となっているリンクをクリックすると該当のビルドのページが開くので、あとは以下の Review ボタンから Aproove なり Reject が出来ます。

問題点は、承認を保留にしたまま放置してると別のビルドが走ったりしたタイミングで再度通知がくることですね…。
これは、ちょっと過去に送ったものは送らないような作りこみがいりそうです…。

まとめ

教訓:先に、既に作られたものがないか探せ。