gokigenmaruのブログ

40から始めるクラウドエンジニア

Azure Automationで外部ログをAPIで取得しBlobに格納する

今回はAzureの外にあるログ取得~Blob格納までをAutomationで自動化する話です。

概要

今回の対象はAzure内ではなく外部製品のログは監査系のログとなります。
製品の仕様として監査ログを14日間取得されますが、それ以降は破棄されてしまうため、破棄される前にどこかに格納しておく必要がありました。
今回はAzure Blobストレージに格納し長期保管をすることが目的です。

要件

要件を簡単にまとめると以下の通りとなります。

  • 1時間おきにログを取得する
  • 個別のTOKENを使用する
  • Internet経由でログをgetする
  • 取得してくるデータはJSON形式
  • ログのデータ構造はdata配列内にログが複数存在する形
  • Pagination設定がある
  • ログはBlobストレージに格納する

Automationの作成イメージ

  • 1時間おきにログを取得する

これはAzure AutomationとAutomationのスケジュール機能で実装します。
Runbookの種類はPowershellにしました。

  • 個別のTOKENを使用する

Tokenはシークレット情報になりますので、コード内にべた書きや他人の目に触れるようなところには置きたくありません。
なので、TokenはAzure KeyVault(キーコンテナー)に格納し、コード内でKeyVaultから取得するようにします。

  • Internet経由でログをgetする

LinuxとかであればcurlですがPowershellなのでInvoke-RestMethodで実装します。
learn.microsoft.com

  • 取得してくるデータはJSON形式

PowershellJSONをいじるのには向いていない気がします…。

  • ログのデータ構造はdata配列内にログが複数存在する形

ちょっと苦戦しましたが、今回は何とかなりました。
というのも、将来的にDataExplorerなどでログ検索をすることを考慮した場合、data配列の下にログが入り1行ですべて表されるのはクエリ検索するにはちょっと不向きなので、data配列をなくして1つのログを1行で表せるようにしました。
元のデータは以下の感じでした。

{
    "data": [
        {
            "xxx": "aaa",
            "yyy": "bbb",
            "zzz": "ccc"
        },
        {
            "xxx": "aaa",
            "yyy": "bbb",
            "zzz": "ccc"
        }
    ],
    "pagination": {
        "current_page": xx,
        "next_page": xx
    }
}
  • Pagination設定がある

Paginationの情報は今回はJSONのデータの最後にPaginationの配列が入る形でした。

  • データはBlobストレージに格納する

Automationと同じサブスクリプション内のBlobに格納しています。
フォルダはAzureの診断ログを取るときと同じような構成となる「autid/y=yyyy/m=MM/d=dd/h=HH/yyyymmdd.json」としました。

Automation

コードは以下の通りです。

try
{
    "Logging in to Azure..."
    Connect-AzAccount -Identity
}
catch {
    Write-Error -Message $_.Exception
    throw $_.Exception
}

# Token取得
$secret = Get-AzKeyVaultSecret -VaultName "KeyVault名" -Name "シークレット名" AsPlainText

# URI生成
$headerParam = @{"Authorization" = "Bearer " + $secret}
$date = (Get-date).ToUniversalTime().AddHours(-1).ToString("yyyy-MM-dd-HH")

# 一時使用ファイル作成
New-Item -Path . -Name "data.json"

# ログ取得
$UserResponse = 1
while ($UserResponse -ne $null){
    # Getするページ設定
    $url = "https://example.com/api/audit?since=" + $date + ":00:00+00:00&page%5Bnumber%5D=" + $UserResponse
    Invoke-RestMethod -Headers $headerParam -Uri $url -Method Get -OutFile "audit.json"
    # 次ページがあるかどうかチェック
    $apidata = Get-Content -Path audit.json -Raw | ConvertFrom-Json
    $UserResponse = $apidata.pagination.next_page
    # Jsonデータ加工
    $apidata.data | ForEach-Object{
        $auditlog = $_
        $auditlog | ConvertTo-Json -Compress | Add-Content -Path "data.json" -Force
    }
}

# Blob格納
$StorageHT = @{
    ResourceGroupName = "リソースグループ名"
    Name              = "Storageアカウント名"
}
$StorageAccount = Get-AzStorageAccount @StorageHT
$Context = $StorageAccount.Context
$date2 = (Get-date).ToUniversalTime().AddHours(-1).ToString("yyyy")
$date3 = (Get-date).ToUniversalTime().AddHours(-1).ToString("MM")
$date4 = (Get-date).ToUniversalTime().AddHours(-1).ToString("dd")
$date5 = (Get-date).ToUniversalTime().AddHours(-1).ToString("yyyyMMddhh")

$ContainerName = "Blobのコンテナ名"
$Blob = "audit/y=$date2/m=$date3/d=$date4/h=$date1/$date5.json"

$Blob1HT = @{
    File             = 'data.json'
    Container        = $ContainerName
    Blob             = $Blob
    Context          = $Context
    StandardBlobTier = 'Hot'
}
Set-AzStorageBlobContent @Blob1HT

# テンポラリーファイル削除
Remove-item ./audit.json
Remove-item ./data.json


取得したログをそのまま出力するのであれば「Jsonデータ加工」のロジックは不要です。

スケジュール設定

スケジュールはAutomationのスケジュール設定で実施しました。
繰り返しは定期的、間隔は1時間にしています。

おわり

定期的に実行することは手動ではなくなるべく簡単に自動化したいところ。
今回はAzureメインでよそのシステムのログを定期的に取得しデータを少し加工してBlobコンテナーに格納することをAzure Automationで実現しました。
こんな感じでどんどん自動化していけると仕事の手間が減っていいですね。
VM建てたりKubernetesのcronjobも考えましたが、思ってたよりPowershellでいろいろと出来て、Automationで実現出来たのでよかったです。
もう少しうまくPowershellで書けるといいのですが…。いけてないところは直してくれると嬉しいです。