はじめに
Azure DevOpsには監査機能が入っており、https://dev.azure.com/{orgnazation}/_settings/audit というURLでアクセスできます。ただし、この監査ログは90日分しか保持されておらず、それ以上の期間保存したい場合、別途APIアクセスでJSONやCSVに抜き出す必要があります。
docs.microsoft.com
毎日取得するのも面倒なので、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コネクターを使います。
docs.microsoft.com
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データセンター内からのアクセスを受け入れる
を設定する必要があります。
docs.microsoft.com
ドキュメントは現在のアイテム
、Partition Keyはユーザー単位で作ることにするので、actorUserIdを指定します。一番はまったのですが、Partition Keyには明示的にダブルクォーテーションを付けないと、Invalid Partition Keyといわれてエラーになります。
Cosmos DBで作ったPartition Keyと一致させればあとはCosmos DBが勝手に入れてくれます。
実行すると、こんな感じでCosmos DBに格納されます。あとは適度に検索してください。
自動削除
監査ログなので、不要になったら消すということもあるかと思います。コンテナーに明示的にTTLを設定すればいいと思いますが、単位が秒なので、一年なら31536000ですかね。
docs.microsoft.com
まとめ
結構苦労しましたが、Logic Apps使ってノンコーディングで監査ログのエクスポートができるようになりました。より高度な監査としては、特定のログが出たらアラートメールを送るようにAzure Functionsをかませてみるとかも面白いと思います。