はじめに
Azure DevOpsには監査機能が入っており、https://dev.azure.com/{orgnazation}/_settings/audit というURLでアクセスできます。ただし、この監査ログは90日分しか保持されておらず、それ以上の期間保存したい場合、別途APIアクセスでJSONやCSVに抜き出す必要があります。
毎日取得するのも面倒なので、LogicAppsを使って自動化してみましょう。
Token取得
最初にPATを作成します。今回の場合、監査ログだけ取れればいいので、Read Audit Log
の権限だけあれば問題ありません。
Cosmos DB
次はCosmos DBのコンテナーを作ります。Cosmos DBの作り方については解説しません。ここではSQLを使ってサーバーを作っています。
PartitionKeyは何を対象にするかで変えてください。Cosmos DBの課金に結構影響します。今回の場合、ユーザー単位で監査するという仕様でactorUserIdをPartition Keyにしています。
操作単位で監査したいのであれば、actionIdになるでしょうか。ipAddressだとnullにあることがあるようなので、お勧めできません。
Logic Apps
全体フロー
だいたいこんな感じで作りました。細かいステップを解説します。
トリガー
タイマートリガーで毎日一度実行させます。UTCで0時0分(日本時間の朝9時)に一日一度実行します。
(9/3追記)
0:00としても、正確に0:00(UTC)に動くとは限らないようです。1秒前に実行されるとかおきましたので、少なくとも数分後に動かすようにしたほうがいいですね。
開始終了時刻
REST APIに渡す始点、終点の日付部分を作ります。
formatDaterTime(addDays(utcNow(), -1), 'yyyy-MM-dd')
開始時刻をstartTime,終了をendTimeとして作ります。
開始時刻
concat(outputs('日付作成'), 'T00:00:00Z')
終了時刻
concat(outputs('日付作成'), 'T23:59:59Z')
PAT 作成
Azure DevOpsで作ったPATをbase64ToString()で変換します。
HTTP呼び出し
Azure DevOps HTTP呼び出しというコネクターもあるのですが、こちらのほうがわかりやすいので、標準のHTTPコネクターを使います。
REST APIのドキュメント通りですが。
- ヘッダー: Authentication : Basic Base64エンコードしたPAT
- api-version: 5.1-preview.1
- skipAggregation: false
- startTime : outputs('startTime')
- endTime : outputs('endTime')
- 認証:なし
JSONの解析
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" } } }
レコード判定
組織で使うときはたぶんないとは思いますが、日付によっては監査ログがない場合もあります。
empty(body('JSON_の解析'))
これでレコードがある場合のみCosmos DBへの登録処理を呼び出します。
Cosmos DBへの登録
For each制御を使って、レコード単位に登録します。JSONのdecoratedAuditLogEntriesがログエントリーなので、この配列の数だけループさせます。
Logic AppsでデータベースIDやコレクションIDが参照できない場合、Cosmos DBのファイアウォールでAzureデータセンター内からのアクセスを受け入れる
を設定する必要があります。
ドキュメントは現在のアイテム
、Partition Keyはユーザー単位で作ることにするので、actorUserIdを指定します。一番はまったのですが、Partition Keyには明示的にダブルクォーテーションを付けないと、Invalid Partition Keyといわれてエラーになります。
Cosmos DBで作ったPartition Keyと一致させればあとはCosmos DBが勝手に入れてくれます。
実行すると、こんな感じでCosmos DBに格納されます。あとは適度に検索してください。
自動削除
監査ログなので、不要になったら消すということもあるかと思います。コンテナーに明示的にTTLを設定すればいいと思いますが、単位が秒なので、一年なら31536000ですかね。
まとめ
結構苦労しましたが、Logic Apps使ってノンコーディングで監査ログのエクスポートができるようになりました。より高度な監査としては、特定のログが出たらアラートメールを送るようにAzure Functionsをかませてみるとかも面白いと思います。