Azure Log Analytics Data Collector API で データを Log Analytics (Azure Sentinel) に格納する with PowerShell

# 本記事の内容

:::note warn
2023年4月時点で、API 経由でのログ取り込みは、Log Ingest API の利用が推奨です。Log Ingest API については別途記事を公開していますので、合わせてご参照ください。
:::


https://qiita.com/YoshiakiOi/items/466c1660ff2811225624

https://qiita.com/YoshiakiOi/items/4f52d6c45686b1de7554

本記事では、[Azure Log Analytics Data Collector API](https://docs.microsoft.com/ja-jp/azure/azure-monitor/platform/data-collector-api) を使って、任意のデータを Azure Log Analytics に取り込むための手順を見ていきたいと思います。以下では特に PowerShell を用いてデータを格納していきます。

# Azure Sentinel への Data Collector API の応用
Azure Sentinel では、データストレージ・検索基盤として Log Analytics を利用しているため、データの格納には Log Analytics の諸々の機能を活用します。Data Collector API は汎用性のある手法ですが、それ以外にもさまざまな手法を利用できます。詳細は、[Azure Sentinel: Creating Custom Connectors](https://techcommunity.microsoft.com/t5/azure-sentinel/azure-sentinel-creating-custom-connectors/ba-p/864060)にまとめられています。

# Data Collector API の仕組み

Log Analytics ワークスペース内のすべてのデータは、特定のレコード型のレコードとして保存されます。 Data Collector API に送信するデータを、JSON 形式の複数のレコードとして設定します。 データを送信すると、要求ペイロード内の各レコードに対応する個別のレコードがリポジトリ内に作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276225/fc848be0-6d4a-8893-9978-44129c11febc.png)

( [Data Collector API の概念](https://docs.microsoft.com/ja-jp/azure/azure-monitor/platform/data-collector-api#concepts) より）

# PowerShell から Data Collector API を利用して、データを格納する

[PowerShell のサンプル](https://docs.microsoft.com/ja-jp/azure/azure-monitor/platform/data-collector-api#powershell-sample)を時間に関する部分だけ少し変更して実行してみます。

```PowerShell

# Replace with your Workspace ID
$CustomerId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"  

# Replace with your Primary Key
$SharedKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# Specify the name of the record type that you'll be creating
$LogType = "MyRecordType"

# You can use an optional field to specify the timestamp from the data. If the time field is not specified, Azure Monitor assumes the time is the message ingestion time
$TimeStampField = "DateValue"
$currentUTCtime = (Get-Date).ToUniversalTime()
$logTime = $currentUTCtime | Get-Date -Format yyyy-MM-ddThh:mm:ss
$logTimeSt = $logTime.ToString()


# Create two records with the same set of properties to create
$json = @"
[{  "StringValue": "MyString1",
    "NumberValue": 42,
    "BooleanValue": true,
    "DateValue": "$logTimeSt",
    "GUIDValue": "9909ED01-A74C-4874-8ABF-D2678E3AE23D"
},
{   "StringValue": "MyString2",
    "NumberValue": 43,
    "BooleanValue": false,
    "DateValue": "$logTimeSt",
    "GUIDValue": "8809ED01-A74C-4874-8ABF-D2678E3AE23D"
}]
"@

# Create the function to create the authorization signature
Function Build-Signature ($customerId, $sharedKey, $date, $contentLength, $method, $contentType, $resource)
{
    $xHeaders = "x-ms-date:" + $date
    $stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource

    $bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash)
    $keyBytes = [Convert]::FromBase64String($sharedKey)

    $sha256 = New-Object System.Security.Cryptography.HMACSHA256
    $sha256.Key = $keyBytes
    $calculatedHash = $sha256.ComputeHash($bytesToHash)
    $encodedHash = [Convert]::ToBase64String($calculatedHash)
    $authorization = 'SharedKey {0}:{1}' -f $customerId,$encodedHash
    return $authorization
}


# Create the function to create and post the request
Function Post-LogAnalyticsData($customerId, $sharedKey, $body, $logType)
{
    $method = "POST"
    $contentType = "application/json"
    $resource = "/api/logs"
    $rfc1123date = [DateTime]::UtcNow.ToString("r")
    $contentLength = $body.Length
    $signature = Build-Signature `
        -customerId $customerId `
        -sharedKey $sharedKey `
        -date $rfc1123date `
        -contentLength $contentLength `
        -method $method `
        -contentType $contentType `
        -resource $resource
    $uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"

    $headers = @{
        "Authorization" = $signature;
        "Log-Type" = $logType;
        "x-ms-date" = $rfc1123date;
        "time-generated-field" = $TimeStampField;
    }

    $response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $body -UseBasicParsing
    return $response.StatusCode

}

# Submit the data to the API endpoint
Post-LogAnalyticsData -customerId $customerId -sharedKey $sharedKey -body ([System.Text.Encoding]::UTF8.GetBytes($json)) -logType $logType
```

# Log Analytics 側で検索してみる

サンプルの検索結果として下記のクエリを実行します。（Project-away は単に表示しない列を指定しているだけなので、本質的には不要です。上記 PowerShell では　$LogType = "MyRecordType" が指定されていましたが、テーブル名はカスタムログである_CLが付き、MyRecordType_CLとなります。また各プロパティ名には_sが付きます。

```
MyRecordType_CL 
| where TimeGenerated > ago(365d) 
| project-away Computer, RawData, _ResourceId
```

結果例：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276225/1f7dd5c7-2b90-6f23-5d17-9053ce15470f.png)

# まとめ

Data Collector API を使い、簡単に PowerShell から Log Analytics にデータを送れることを確認できました。 Data Collector API では C# や Python の SDK も公開しているため、使いなれた言語でデータの送信が可能です。

＊本稿は、個人の見解に基づいた内容であり、所属する会社の公式見解ではありません。また、いかなる保証を与えるものでもありません。正式な情報は、各製品の販売元にご確認ください。