kkamegawa's weblog

Visual Studio,TFS,ALM,VSTS,DevOps関係のことについていろいろと書いていきます。Google Analyticsで解析を行っています

My Work Itemsのソートをカスタマイズしたい

というご意見がありましたので、ちょっと調べてみました。My Work Itemそのもののカスタマイズはできないのですが、ワンアクション増えるもののQueriesをいじればできます。

f:id:kkamegawa:20190918050832p:plain

適当なプロジェクトのQueriesからこんな感じでクエリを作ります。Assigned Toを@Meにして、StateをNot In Closed, Removed, Done にします。Activeなものだけでいいなら、Stateを= Activeとかにします。

デフォルトでQuery across projectsにチェックが入っているので、自分に権限のあるプロジェクトを横断検索してくれます。

保存するときにFavoriteの★をチェックしておきます。

f:id:kkamegawa:20190918050843p:plain

… をクリックして、Column optionsを出します。ここでソートしたい列を選択します。今回はTarget Dateなので、Target Dateで降順ソート(↓)しました。OK押して保存。

f:id:kkamegawa:20190918050837p:plain

Favoritesに入れているので、My Work Itemsの二つ隣のFavoritesに入っていますね。毎日使うものならMove upで上にもっていけばいいでしょう。

f:id:kkamegawa:20190918050847p:plain

実行するとこんな感じでTarget Dateでソートした結果が得られました。Azure CLIでも同じことはできます。

Favoritesから対象のQueryを選ぶワンアクション増えるものの、希望のことはこれでできるんじゃないかなと思います。

後、ダッシュボード編集権限とチームクエリに保存する権限があれば、Query Resultsウィジェット使ってダッシュボードに貼り付けるというのもいいと思います。

Azure DevOps 2019/9/3の更新

また間が開いてしまいましたが、Sprint 157リリースノートの翻訳を行いました。オリジナルはこちらから読んでください。

docs.microsoft.com

今回は親子関係のあるアイテムの追跡、ファイルブロックポリシーのサポートはexeをコミットするなっつっても入れる人防止できるので、便利です。

GitHubでは昔からあり、結構希望されていた、fixedと入れるとwork itemクローズしてくれる機能のサポートはいいですね。もちろん現在英語のfixのみのようです。

VS CodeからPipelinesのYAMLファイルを作成できるようになったので、リポジトリクローンして作成できるのはいいですね。

App Servicesでプレビュースワップのサポート(スロットがそのプレビューだった気がするんですが…)あたりが多くの人が使える便利機能でしょうか。

Ruby 2.3系のEOLにともない、Microsoft Hosted エージェントからRuby 2.3が削除されたそうです。使っている人は気を付けてください。

ではまた三週間後。

Translate to Japanese to Azure DevOps release note ...

Logic AppsでAzure DevOpsの監査ログをCosmos DBへ保存する

はじめに

Azure DevOpsには監査機能が入っており、https://dev.azure.com/{orgnazation}/_settings/audit というURLでアクセスできます。ただし、この監査ログは90日分しか保持されておらず、それ以上の期間保存したい場合、別途APIアクセスでJSONやCSVに抜き出す必要があります。

docs.microsoft.com

毎日取得するのも面倒なので、LogicAppsを使って自動化してみましょう。

Token取得

f:id:kkamegawa:20190901092840p:plain

最初にPATを作成します。今回の場合、監査ログだけ取れればいいので、Read Audit Logの権限だけあれば問題ありません。

Cosmos DB

f:id:kkamegawa:20190901092847p:plain

次はCosmos DBのコンテナーを作ります。Cosmos DBの作り方については解説しません。ここではSQLを使ってサーバーを作っています。

PartitionKeyは何を対象にするかで変えてください。Cosmos DBの課金に結構影響します。今回の場合、ユーザー単位で監査するという仕様でactorUserIdをPartition Keyにしています。

操作単位で監査したいのであれば、actionIdになるでしょうか。ipAddressだとnullにあることがあるようなので、お勧めできません。

Logic Apps

全体フロー

f:id:kkamegawa:20190901092851p:plain

だいたいこんな感じで作りました。細かいステップを解説します。

トリガー

f:id:kkamegawa:20190901092855p:plain

タイマートリガーで毎日一度実行させます。UTCで0時0分(日本時間の朝9時)に一日一度実行します。

(9/3追記)
0:00としても、正確に0:00(UTC)に動くとは限らないようです。1秒前に実行されるとかおきましたので、少なくとも数分後に動かすようにしたほうがいいですね。

開始終了時刻

f:id:kkamegawa:20190901092800p:plain

REST APIに渡す始点、終点の日付部分を作ります。

formatDaterTime(addDays(utcNow(), -1), 'yyyy-MM-dd')

f:id:kkamegawa:20190901092804p:plain

開始時刻をstartTime,終了をendTimeとして作ります。

開始時刻

concat(outputs('日付作成'), 'T00:00:00Z')

終了時刻

concat(outputs('日付作成'), 'T23:59:59Z')

PAT 作成

f:id:kkamegawa:20190901092837p:plain

Azure DevOpsで作ったPATをbase64ToString()で変換します。

HTTP呼び出し

f:id:kkamegawa:20190901092809p:plain

Azure DevOps HTTP呼び出しというコネクターもあるのですが、こちらのほうがわかりやすいので、標準のHTTPコネクターを使います。

docs.microsoft.com

REST APIのドキュメント通りですが。

  • ヘッダー: Authentication : Basic Base64エンコードしたPAT
  • api-version: 5.1-preview.1
  • skipAggregation: false
  • startTime : outputs('startTime')
  • endTime : outputs('endTime')
  • 認証:なし

JSONの解析

f:id:kkamegawa:20190901092814p:plain

HTTPの出力結果を使うためのJSON Schemaを定義します。APIのページにあるサンプルレスポンスを使ってサンプルペイロードからJSON Schemaを作ればいいです。

(9/5追記)
と思っていたのですが、ipAddressがnullになることがある場合の失敗と、Azure DevOps自身が定期的に実行するジョブの監査ログでsummryがnullになることが多かったので、こんな感じにしました。

{
  "type": "object",
  "properties": {
    "continuationToken": {
      "type": "string"
    },
    "decoratedAuditLogEntries": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "actionId": {
            "type": "string"
          },
          "activityId": {
            "type": "string"
          },
          "actorCUID": {
            "type": "string"
          },
          "actorDisplayName": {
            "type": "string"
          },
          "actorImageUrl": {},
          "actorUserId": {
            "type": "string"
          },
          "area": {
            "type": "string"
          },
          "authenticationMechanism": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "categoryDisplayName": {
            "type": "string"
          },
          "correlationId": {
            "type": "string"
          },
          "data": {
            "type": "object"
          },
          "details": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "ipAddress": {},
          "projectId": {
            "type": "string"
          },
          "projectName": {},
          "scopeDisplayName": {
            "type": "string"
          },
          "scopeId": {
            "type": "string"
          },
          "scopeType": {
            "type": "string"
          },
          "timestamp": {
            "type": "string"
          },
          "userAgent": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "correlationId",
          "activityId",
          "actorCUID",
          "actorUserId",
          "authenticationMechanism",
          "timestamp",
          "scopeType",
          "scopeDisplayName",
          "scopeId",
          "projectId",
          "projectName",
          "ipAddress",
          "userAgent",
          "actionId",
          "data",
          "details",
          "area",
          "category",
          "categoryDisplayName",
          "actorDisplayName",
          "actorImageUrl"
        ]
      }
    },
    "hasMore": {
      "type": "boolean"
    }
  }
}

レコード判定

f:id:kkamegawa:20190901092818p:plain

組織で使うときはたぶんないとは思いますが、日付によっては監査ログがない場合もあります。

empty(body('JSON_の解析'))

これでレコードがある場合のみCosmos DBへの登録処理を呼び出します。

Cosmos DBへの登録

f:id:kkamegawa:20190901092824p:plain

For each制御を使って、レコード単位に登録します。JSONのdecoratedAuditLogEntriesがログエントリーなので、この配列の数だけループさせます。

Logic AppsでデータベースIDやコレクションIDが参照できない場合、Cosmos DBのファイアウォールでAzureデータセンター内からのアクセスを受け入れるを設定する必要があります。

docs.microsoft.com

ドキュメントは現在のアイテム、Partition Keyはユーザー単位で作ることにするので、actorUserIdを指定します。一番はまったのですが、Partition Keyには明示的にダブルクォーテーションを付けないと、Invalid Partition Keyといわれてエラーになります。

Cosmos DBで作ったPartition Keyと一致させればあとはCosmos DBが勝手に入れてくれます。

f:id:kkamegawa:20190901092832p:plain

実行すると、こんな感じでCosmos DBに格納されます。あとは適度に検索してください。

自動削除

監査ログなので、不要になったら消すということもあるかと思います。コンテナーに明示的にTTLを設定すればいいと思いますが、単位が秒なので、一年なら31536000ですかね。

docs.microsoft.com

まとめ

結構苦労しましたが、Logic Apps使ってノンコーディングで監査ログのエクスポートができるようになりました。より高度な監査としては、特定のログが出たらアラートメールを送るようにAzure Functionsをかませてみるとかも面白いと思います。

Azure DevOps CLIでWork Itemの一覧をざっくり処理する

知り合いから、「Azure BoardsのWork Itemをざっくり見たい。ブラウザー以外で」という相談をたまに受けます。

なぜブラウザー以外かといえば、ブラウザーはたいていテストに使ってるからなんですね。やむを得ない事情で閉じないといけないとか、常時起動できるかといえばそうでもない。

Azure CLIの拡張機能でAzure DevOpsが提供されています。ネットワークの問題がなければ(GitHub Releaseからのダウンロードなので、止めている組織もあります)、これで入ります。

az extension add --name azure-devops

az devops loginでPATを使ったログインになります。

Azure Boardsの特定のIDを中身を取りたい場合こうします。

az boards work-item show --id xxx

これでWork Itemの結果が取得できますが、REST APIと同じJSONで返却されます。概要だけ見たいときはtable形式で。

az boards work-item show --id xxx --output table

ID    Type    Title                         Assigned To           State
----  ------  ----------------------------  --------------------  -------
614   Task    ワークスペースのところを書く     hoo@exsample.com      Active

これではいちいちID覚えてないといけないので、クエリで自分(@Me)にアサインされているタスクを検索します。

az boards query --wiql
  "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.AssignedTo] = @Me AND [System.State] = 'Active'"

WIQLの構文はこちらを見てください。

docs.microsoft.com

JSON形式で返ってくるので、--output tableでもうちょっと見やすくなります。

ID    Title
----  ----------------------------------------------
23    サブメニューまでログイン
92    TFS本サンプルのテスト
107   TFSUGの華麗なデモ

これでIDとTitleが取れるので、az board show --id 23 --open で対象のアイテムがブラウザーで開かれます。プレビューですが、az interactiveでインタラクティブなコマンドが使えます。ヒストリーも強力なので便利そうです。

az interactiveを使う場合、プレビューですが、Windows Terminalsを使うほうがいいでしょう。cmd.exeよりもコンソール出力の応答がいいようです。