Azure Database for PostgreSQL フレキシブル サーバーにおける Microsoft Entra ID プリンシパルの監査ログ

データベース監査は、組織のコンプライアンス要件の重要なコンポーネントです。 対象となるアクティビティを監視することで、セキュリティ ベースラインを実現できます。 Azure Database for PostgreSQL フレキシブル サーバーでは、「 Azure Database for PostgreSQL でのログ記録の監査」の説明に従って、pgaudit PG 拡張機能を使用して監査を設定できます。

1 つの課題は、Microsoft Entra ID グループを使用していて、個々のグループ メンバーのアクションを監査する場合に、Microsoft Entra ID 認証と共に監査機能を使用することです。 この課題は、グループ メンバーが個人用アクセス トークンを使用してサインインするが、グループ名をユーザー名として使用するためです。

Kusto クエリ言語 (KQL) は、Azure サービス ログのクエリを可能にする、パイプライン駆動型の読み取り専用の強力なクエリ言語です。 KQL では、大量のデータをすばやく分析するための Azure ログのクエリがサポートされています。 この記事では、KQL を使用して Azure Postgres ログのクエリを実行し、監査ログから Microsoft Entra ID ユーザー情報を抽出します。

[前提条件]

  1. 監査ログの有効化 - Azure Database for PostgreSQL での監査ログ
  2. Azure Postgres ログを Azure Log Analytics に送信できるようにする - ログの構成とアクセス
  3. log_line_prefixパラメーターを調整します。[パラメーター] ブレードで、log_line_prefixを設定して、エスケープuser=%u,db=%d,session=%c,sess_time=%sを同じシーケンスに含め、目的の結果を取得します。
    • 以前は: log_line_prefix = %t-%c-
    • 後: log_line_prefix = %t-%c-user=%u,db=%d,session=%c,sess_time=%s

Kusto クエリ

次の Kusto クエリでは、 AzureDiagnostics を 2 回クエリします。
最初のサブクエリは、文字列Microsoft Entra ID connection authorizedを含むすべての行を検索し、これらのログ行からPrincipalNameと共にSessionIdを抽出します。
2 番目のサブクエリでは、すべての監査ログが検索されます。
最後に、これら 2 つのサブクエリが SessionIdで結合されます。

let lookbackTime = ago(3d);
let opindex = 3;
let startIndex = toscalar(range thirdIndex from opindex to opindex step 1
    | project thirdIndex);
AzureDiagnostics
| where ResourceProvider == 'MICROSOFT.DBFORPOSTGRESQL'
| where TimeGenerated >= lookbackTime
| where Message contains 'Microsoft Entra ID connection authorized'
| extend SessionId = tostring(split(tostring(split(Message, 'session=')[-1]), ',sess_time')[-2])
| extend UPN = iff(Message contains 'UPN',tostring(split(tostring(split(Message, 'UPN=')[-1]), 'oid=')[-2]), '')
| extend appId = iff(Message contains 'appid', tostring(split(tostring(split(Message, 'appid=')[-1]), 'oid=')[-2]), '')
| extend PrincipalName = strcat(UPN, appId)
| project SessionId, PrincipalName
| join kind=leftouter
    (
    AzureDiagnostics
    | where ResourceProvider == 'MICROSOFT.DBFORPOSTGRESQL'
    | where TimeGenerated >= lookbackTime
    | where Message contains 'AUDIT: SESSION'
    | extend RoleName = tostring(split(tostring(split(Message, 'user=')[-1]), ',db')[-2])
    | where RoleName !in ('azuresu', '[unknown]', 'postgres', '')
    | extend SessionId = tostring(split(tostring(split(Message, 'session=')[-1]), ',sess_time')[-2])
    | extend SubMessage = tostring(split(Message, 'SESSION,')[-1])
    | extend splitArray = split(SubMessage, ',')
    | extend SqlQueryP1 = tostring(split(tostring(split(Message, ',,,')[-1]), ',<')[-2])
    | extend SqlQueryP2 = replace_string(tostring(split(SqlQueryP1, ',\"')[-1]), '"', '')
    | extend SqlQueryP3 = tostring(split(Message, ',,,')[1])
    | extend OperationType = tostring(splitArray[startIndex])
    | extend SqlQuery = trim('"', case(OperationType == 'EXECUTE', SqlQueryP2, SqlQueryP1 == '', SqlQueryP3, SqlQueryP1))
    )
    on $left.SessionId == $right.SessionId
| project TimeGenerated, PrincipalName, RoleName, OperationType, SqlQuery

例の結果

結果のテーブルは次のようになります。

タイムジェネレイテッド プリンシパルネーム 役割名 操作タイプ SqlQuery
2025-12-12T16:25:05.104Z user@example.com ExampleGroupName SELECT pg_seclabelsから * を選択します。
2025-12-12T16:25:04.000Z user@example.com user@example.com SELECT pg_seclabelsから * を選択します。

ユーザーがグループ ロールとしてサインインした場合、 PrincipalName 列と RoleName 列には、例の最初の行のように異なる値が表示されます。
PrincipalName値は、サインインしたユーザーを識別します。 RoleName値は、サインイン後にユーザーがアクセスする PostgreSQL のロールを識別します。

PrincipalName は、ユーザー プリンシパルまたはサービス プリンシパルがサインインするかどうかに応じて、ユーザー プリンシパル 名 (UPN) または AppId のいずれかです。