1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Azureテナント開設時のログ保管設定をPowerShellで自動化する

1
Last updated at Posted at 2026-08-31

はじめに

Azureのテナントを開設したとき、最初にやっておくべきことの1つがログの保管設定です。
しかし、Azure Portal から手動で設定しようとすると設定箇所が複数に分散しており、手順書通りに進めても抜け漏れが発生しやすいという問題があります。

本記事では、Azure Cloud Shell で PowerShell スクリプトを実行するだけで、以下をまとめて設定する方法を紹介します。

  • ログ保管先のストレージアカウント作成
  • Azureアクティビティログの転送設定
  • Entra ID(旧Azure AD)の監査ログ・サインインログの転送設定
  • ログの90日後自動削除設定

スクリプト1本で完結するため、新しいテナントを開設するたびに同じ手順で確実に設定できます。

対象読者

  • Azureテナントの管理者
  • Entra ID(旧Azure AD)の管理者
  • Azure Cloud Shell の基本操作はできる

前提条件・必要な権限

実行前に以下の権限を持つアカウントで Cloud Shell にサインインしてください。

権限 用途
Azureサブスクリプションの Owner リソースグループ・ストレージの作成、アクティビティログ設定
Entra ID の グローバル管理者 Entra ID 診断設定(監査ログ・サインインログ)の構成

リソースプロバイダーについて
本スクリプトは Microsoft.Storage および Microsoft.Insights リソースプロバイダーが必要です。新規テナントではこれらが未登録の場合があり、未登録のままストレージアカウントや診断設定を作成すると NotFound エラーが発生します。スクリプト内で自動的に登録状態を確認し、未登録の場合は登録を行いますが、登録にはサブスクリプション Owner 権限が必要です。

設定内容の概要

項目 設定値
リソースグループ名 rg-log-archive
ストレージアカウント名 自動生成(サブスクリプションIDから一意に決定)
ストレージの冗長性 Standard LRS(ローカル冗長ストレージ)
ネットワークアクセス 拒否(Bypass: AzureServices で診断設定からの書き込みのみ許可)
ログ保持期間 90日(ライフサイクル管理で自動削除)
アクティビティログのカテゴリ Administrative / Security / ServiceHealth / Alert / Recommendation / Policy / Autoscale / ResourceHealth
Entra IDログのカテゴリ AuditLogs / SignInLogs

スクリプトの概要

スクリプトは6つのステップで構成されています。

Step 1 - リソースプロバイダー登録
ストレージアカウントの作成に必要な Microsoft.Storage と、診断設定に必要な Microsoft.Insights のリソースプロバイダーが登録されているか確認し、未登録の場合は登録して完了を待機します。新規テナントではこれらが未登録のことがあり、登録せずに後続ステップを実行すると NotFound エラーが発生するため、最初に確認を行います。

Step 2 - リソースグループ作成
ログ保管用のリソースグループ rg-log-archive を作成します。

Step 3 - ストレージアカウント作成
ログの保管先となるストレージアカウントを作成します。名前はサブスクリプション ID をもとに SHA256 ハッシュで自動生成するため、手動での命名は不要です。同じサブスクリプションで実行する限り常に同じ名前が生成されるため、冪等性(何度実行しても同じ結果になる性質)が保たれます。パブリックアクセスを拒否しつつ、Azure サービスからのアクセスのみを Bypass で許可しています。

Step 4 - ライフサイクル管理設定
ストレージに保管された Blob を90日後に自動削除するルールを設定します。

Step 5 - アクティビティログ診断設定
サブスクリプションのアクティビティログをストレージアカウントに転送する診断設定を構成します。

Step 6 - Entra ID 診断設定
Entra ID の監査ログ(AuditLogs)とサインインログ(SignInLogs)をストレージアカウントに転送する診断設定を構成します。Cloud Shell の認証情報をそのまま利用するため、追加のサインインは不要です。

なお、スクリプトはリソースが既存の場合はスキップして続行するため、何度実行しても安全です。

実行手順

1. 操作対象のサブスクリプションとテナントを確認する

複数のサブスクリプションやテナントを管理している場合、意図しない環境に設定が行われないよう、スクリプト実行前に必ず操作対象を確認してください。

現在の接続先は以下のコマンドで確認できます。

Get-AzContext | Select-Object Name, Subscription, Tenant

対象のサブスクリプションと異なる場合は、以下のコマンドで切り替えてください。

# 利用可能なサブスクリプションの一覧表示
Get-AzSubscription

# 対象サブスクリプションへの切り替え
Set-AzContext -SubscriptionId "切り替え先のサブスクリプションID"

切り替え後、再度 Get-AzContext で対象のサブスクリプションになっていることを確認してからスクリプトを実行してください。

2. スクリプトファイルをCloud Shellにアップロードする

Azure Portal を開き、上部の Cloud Shell アイコンをクリックします。Cloud Shell が開いたら、[PowerShell] に切り替えます。
ファイルのアップロード手順は Azure Cloud Shell の新しいユーザー インターフェイスを使用する方法 | Microsoft Learnの[ファイルの管理] メニューセクションをご参照ください。

3. スクリプトを実行する

./setup-log-archive.ps1

実行すると以下のように進捗が表示されます。

=== セットアップ開始 ===
リソースグループ : rg-log-archive
ストレージ名     : lgxxxxxxxxxxxxxxxxxxxx
場所             : japaneast

[1/6] リソースプロバイダーを確認中...
      Microsoft.Storage: 登録済み
      Microsoft.Insights: 登録済み
[2/6] リソースグループを作成中...
      作成完了: rg-log-archive
[3/6] ストレージアカウントを作成中...
      作成完了: lgxxxxxxxxxxxxxxxxxxxx
[4/6] ライフサイクル管理を設定中(90日後削除)...
      設定完了
[5/6] Activity Log 診断設定を構成中...
      設定完了
[6/6] Entra ID 診断設定を構成中...
      Cloud Shell の認証情報を使用します。
      設定完了

=== セットアップ完了 ===

4. ログのアーカイブを確認する

スクリプト実行後、ストレージアカウントにコンテナが作成されるまで1時間程度かかります。1時間以上経過してから、以下の手順で確認してください。

ストレージアカウントのネットワーク設定を Deny にしているため、Cloud Shell からコンテナを確認するには一時的にアクセスを許可する必要があります。

# 変数の設定
$subId = (Get-AzContext).Subscription.Id
$hash  = [System.BitConverter]::ToString(
           [System.Security.Cryptography.SHA256]::Create().ComputeHash(
             [System.Text.Encoding]::UTF8.GetBytes("${subId}rg-log-archive")
           )
         ).Replace("-","").ToLower().Substring(0,18)
$stName = "lg$hash"

# 一時的にネットワークアクセスを許可
Update-AzStorageAccountNetworkRuleSet `
  -ResourceGroupName "rg-log-archive" `
  -Name $stName `
  -DefaultAction Allow
Start-Sleep -Seconds 10

# コンテナ一覧を確認
$ctx = (Get-AzStorageAccount -ResourceGroupName "rg-log-archive" -Name $stName).Context
Get-AzStorageContainer -Context $ctx | Select-Object Name, LastModified

# 確認後、ネットワークアクセスを元に戻す
Update-AzStorageAccountNetworkRuleSet `
  -ResourceGroupName "rg-log-archive" `
  -Name $stName `
  -DefaultAction Deny `
  -Bypass AzureServices

正常にアーカイブされている場合、以下のようなコンテナが作成されています。

コンテナ名 内容
insights-activity-logs Azure アクティビティログ
insights-logs-auditlogs Entra ID 監査ログ
insights-logs-signinlogs Entra ID サインインログ

注意: insights-logs-auditlogsinsights-logs-signinlogs は、それぞれ監査操作やサインインが発生して初めてコンテナが作成されます。スクリプト実行直後は insights-activity-logs のみ存在する場合がありますが、正常な動作です。

トラブルシューティング

1時間以上経過してもコンテナが作成されない場合

スクリプト実行時にエラーが発生して途中で失敗した場合(例: リソースプロバイダー未登録による NotFound エラー)、診断設定は作成されるが内部的に壊れた状態になることがあります。この場合、設定値を確認しても正常に見えますが、Azure Monitor がログをストレージに配信しません。

対処として、診断設定を一度削除して再作成します。以下のコマンドをまとめて実行してください。

# ---- 変数の設定 ----
$subId = (Get-AzContext).Subscription.Id
$subScope = "/subscriptions/$subId"
$hash  = [System.BitConverter]::ToString(
           [System.Security.Cryptography.SHA256]::Create().ComputeHash(
             [System.Text.Encoding]::UTF8.GetBytes("${subId}rg-log-archive")
           )
         ).Replace("-","").ToLower().Substring(0,18)
$stName = "lg$hash"
$storageAccountId = (Get-AzStorageAccount -ResourceGroupName "rg-log-archive" -Name $stName).Id

# ---- 1. リソースプロバイダーの確認 ----
# 未登録の場合は登録してから再作成してください
Get-AzResourceProvider -ProviderNamespace Microsoft.Insights |
  Select-Object -First 1 RegistrationState
Get-AzResourceProvider -ProviderNamespace Microsoft.Storage |
  Select-Object -First 1 RegistrationState

# ---- 2. 既存の診断設定を削除 ----
Remove-AzDiagnosticSetting -ResourceId $subScope -Name "activitylog-to-storage" -ErrorAction SilentlyContinue
Invoke-AzRestMethod -Method DELETE `
  -Path "/providers/microsoft.aadiam/diagnosticSettings/entra-to-storage-min?api-version=2017-04-01"
Write-Host "削除完了。60秒待機..."
Start-Sleep -Seconds 60

# ---- 3. Activity Log 診断設定を再作成 ----
$categories = @(
    "Administrative","Security","ServiceHealth","Alert",
    "Recommendation","Policy","Autoscale","ResourceHealth"
)
$logSettings = $categories | ForEach-Object {
    New-AzDiagnosticSettingLogSettingsObject -Category $_ -Enabled $true
}
New-AzDiagnosticSetting `
    -Name "activitylog-to-storage" `
    -ResourceId $subScope `
    -StorageAccountId $storageAccountId `
    -Log $logSettings | Out-Null
Write-Host "Activity Log 診断設定: 作成完了"

# ---- 4. Entra ID 診断設定を再作成 ----
$body = @{
    properties = @{
        storageAccountId = $storageAccountId
        logs = @(
            @{ category = "AuditLogs";  enabled = $true; retentionPolicy = @{ enabled = $false; days = 0 } }
            @{ category = "SignInLogs"; enabled = $true; retentionPolicy = @{ enabled = $false; days = 0 } }
        )
    }
} | ConvertTo-Json -Depth 5
$result = Invoke-AzRestMethod `
    -Method PUT `
    -Path "/providers/microsoft.aadiam/diagnosticSettings/entra-to-storage-min?api-version=2017-04-01" `
    -Payload $body
Write-Host "Entra ID 診断設定: HTTP $($result.StatusCode)"

Write-Host ""
Write-Host "再作成完了。1時間後にコンテナを確認してください。"

再作成後、1時間程度待ってから「4. ログのアーカイブを確認する」の手順でコンテナの存在を確認してください。

スクリプト全文(setup-log-archive.ps1)

# ============================================================
# ログ保管環境セットアップスクリプト
# 対象: Azure Cloud Shell (PowerShell)
# 必要権限: サブスクリプション Owner + Entra ID のグローバル管理者
# ============================================================

# ---- パラメータ(必要に応じて変更) ----------------------
$rgName           = "rg-log-archive"
$location         = "japaneast"
$retentionDays    = 90
$diagNameActivity = "activitylog-to-storage"
$diagNameEntra    = "entra-to-storage-min"
# ----------------------------------------------------------

# ストレージアカウント名を自動生成(小文字英数字、24文字以内)
$subId = (Get-AzContext).Subscription.Id
$hash  = [System.BitConverter]::ToString(
           [System.Security.Cryptography.SHA256]::Create().ComputeHash(
             [System.Text.Encoding]::UTF8.GetBytes("$subId$rgName")
           )
         ).Replace("-","").ToLower().Substring(0,18)
$storageAccountName = "lg$hash"

Write-Host ""
Write-Host "=== セットアップ開始 ===" -ForegroundColor Cyan
Write-Host "リソースグループ : $rgName"
Write-Host "ストレージ名     : $storageAccountName"
Write-Host "場所             : $location"
Write-Host ""

# ============================================================
# Step 1: リソースプロバイダー登録
# ============================================================
Write-Host "[1/6] リソースプロバイダーを確認中..." -ForegroundColor Yellow

$requiredProviders = @("Microsoft.Storage", "Microsoft.Insights")
foreach ($providerNamespace in $requiredProviders) {
    $provider = Get-AzResourceProvider -ProviderNamespace $providerNamespace
    if ($provider.RegistrationState -eq "Registered") {
        Write-Host "      ${providerNamespace}: 登録済み" -ForegroundColor Gray
    } else {
        Write-Host "      ${providerNamespace}: 未登録 → 登録中..." -ForegroundColor Yellow
        Register-AzResourceProvider -ProviderNamespace $providerNamespace | Out-Null
        do {
            Start-Sleep -Seconds 10
            $provider = Get-AzResourceProvider -ProviderNamespace $providerNamespace
        } while ($provider.RegistrationState -ne "Registered")
        Write-Host "      ${providerNamespace}: 登録完了" -ForegroundColor Green
    }
}

# ============================================================
# Step 2: リソースグループ作成
# ============================================================
Write-Host "[2/6] リソースグループを作成中..." -ForegroundColor Yellow

$rg = Get-AzResourceGroup -Name $rgName -ErrorAction SilentlyContinue
if ($rg) {
    Write-Host "      既存のリソースグループを使用: $rgName" -ForegroundColor Gray
} else {
    New-AzResourceGroup -Name $rgName -Location $location | Out-Null
    Start-Sleep -Seconds 5   # ARM 伝播待ち
    Write-Host "      作成完了: $rgName" -ForegroundColor Green
}

# ============================================================
# Step 3: ストレージアカウント作成
# ============================================================
Write-Host "[3/6] ストレージアカウントを作成中..." -ForegroundColor Yellow

$st = Get-AzStorageAccount -ResourceGroupName $rgName -Name $storageAccountName -ErrorAction SilentlyContinue
if ($st) {
    Write-Host "      既存のストレージアカウントを使用: $storageAccountName" -ForegroundColor Gray
} else {
    $st = New-AzStorageAccount `
        -ResourceGroupName $rgName `
        -Name $storageAccountName `
        -Location $location `
        -SkuName Standard_LRS `
        -Kind StorageV2 `
        -AllowBlobPublicAccess $false `
        -MinimumTlsVersion TLS1_2 `
        -ErrorAction Stop

    Update-AzStorageAccountNetworkRuleSet `
        -ResourceGroupName $rgName `
        -Name $storageAccountName `
        -DefaultAction Deny `
        -Bypass AzureServices `
        -ErrorAction Stop | Out-Null

    Write-Host "      作成完了: $storageAccountName" -ForegroundColor Green
}

$storageAccountId = $st.Id
if ([string]::IsNullOrEmpty($storageAccountId)) {
    Write-Host "      エラー: storageAccountId を取得できませんでした。スクリプトを中断します。" -ForegroundColor Red
    exit 1
}

# ============================================================
# Step 4: ライフサイクル管理(90日削除)
# ============================================================
Write-Host "[4/6] ライフサイクル管理を設定中(${retentionDays}日後削除)..." -ForegroundColor Yellow

$action = Add-AzStorageAccountManagementPolicyAction `
    -BaseBlobAction Delete `
    -daysAfterCreationGreaterThan $retentionDays

$filter = New-AzStorageAccountManagementPolicyFilter -BlobType blockBlob

$rule = New-AzStorageAccountManagementPolicyRule `
    -Name "delete-after-retention" `
    -Action $action `
    -Filter $filter

Set-AzStorageAccountManagementPolicy `
    -ResourceGroupName $rgName `
    -StorageAccountName $storageAccountName `
    -Rule $rule | Out-Null

Write-Host "      設定完了" -ForegroundColor Green

# ============================================================
# Step 5: Activity Log 診断設定
# ============================================================
Write-Host "[5/6] Activity Log 診断設定を構成中..." -ForegroundColor Yellow

$categories = @(
    "Administrative","Security","ServiceHealth","Alert",
    "Recommendation","Policy","Autoscale","ResourceHealth"
)

$logSettings = $categories | ForEach-Object {
    New-AzDiagnosticSettingLogSettingsObject -Category $_ -Enabled $true
}

$subScope = "/subscriptions/$subId"

$existing = Get-AzDiagnosticSetting -ResourceId $subScope -Name $diagNameActivity -ErrorAction SilentlyContinue
if ($existing) {
    Remove-AzDiagnosticSetting -ResourceId $subScope -Name $diagNameActivity | Out-Null
}

New-AzDiagnosticSetting `
    -Name $diagNameActivity `
    -ResourceId $subScope `
    -StorageAccountId $storageAccountId `
    -Log $logSettings | Out-Null

Write-Host "      設定完了" -ForegroundColor Green

# ============================================================
# Step 6: Entra ID 診断設定(Entra ID のグローバル管理者権限が必要)
# ============================================================
Write-Host "[6/6] Entra ID 診断設定を構成中..." -ForegroundColor Yellow
Write-Host "      Cloud Shell の認証情報を使用します。" -ForegroundColor Gray

$body = @{
    properties = @{
        storageAccountId = $storageAccountId
        logs = @(
            @{ category = "AuditLogs";  enabled = $true; retentionPolicy = @{ enabled = $false; days = 0 } }
            @{ category = "SignInLogs"; enabled = $true; retentionPolicy = @{ enabled = $false; days = 0 } }
        )
    }
} | ConvertTo-Json -Depth 5

try {
    $result = Invoke-AzRestMethod `
        -Method PUT `
        -Path "/providers/microsoft.aadiam/diagnosticSettings/$($diagNameEntra)?api-version=2017-04-01" `
        -Payload $body
    if ($result.StatusCode -ge 200 -and $result.StatusCode -lt 300) {
        Write-Host "      設定完了" -ForegroundColor Green
    } else {
        Write-Host "      エラー (HTTP $($result.StatusCode)): $($result.Content)" -ForegroundColor Red
        exit 1
    }
} catch {
    Write-Host "      エラー: $_" -ForegroundColor Red
    exit 1
}

# ============================================================
# 完了サマリー
# ============================================================
Write-Host ""
Write-Host "=== セットアップ完了 ===" -ForegroundColor Cyan
Write-Host "ストレージアカウントID : $storageAccountId"
Write-Host "Activity Log 診断設定  : $diagNameActivity"
Write-Host "Entra 診断設定         : $diagNameEntra"
Write-Host ""

注意事項・補足

リソースプロバイダーについて

新規テナントでは Microsoft.StorageMicrosoft.Insights などのリソースプロバイダーが未登録の場合があります。未登録のままストレージアカウントや診断設定を作成しようとすると NotFound エラーが発生します。本スクリプトでは Step 1 で自動的に登録状態を確認・登録しますが、手動で確認する場合は以下のコマンドを使用してください。

# 登録状態の確認
Get-AzResourceProvider -ProviderNamespace Microsoft.Storage | Select-Object RegistrationState
Get-AzResourceProvider -ProviderNamespace Microsoft.Insights | Select-Object RegistrationState

# 未登録の場合の手動登録
Register-AzResourceProvider -ProviderNamespace Microsoft.Storage
Register-AzResourceProvider -ProviderNamespace Microsoft.Insights

実行環境について

本スクリプトは Azure Cloud Shell(PowerShell モード)での実行を前提としています。ローカルの PowerShell 環境でも大部分は動作しますが、Entra ID 診断設定(Step 6)は認証の仕組みが異なるため、別途対応が必要になる場合があります。

WARNING メッセージについて

実行時に Upcoming breaking changes in the cmdlet という WARNING が表示されることがありますが、これは将来の Azure PowerShell バージョンアップの予告であり、現時点の動作には影響ありません。

Entra ID の既存診断設定が残っている場合について

同一カテゴリ(AuditLogs / SignInLogs)に同一のストレージアカウントを複数の診断設定で重複して指定することはできません。スクリプト実行時に HTTP 409 Conflict エラーが発生した場合は、以下のコマンドで既存の診断設定を確認・削除してから再実行してください。

# Entra ID 診断設定の全件確認
$result = Invoke-AzRestMethod -Method GET `
    -Path "/providers/microsoft.aadiam/diagnosticSettings?api-version=2017-04-01"
($result.Content | ConvertFrom-Json).value | Select-Object name

# 不要な診断設定の削除(設定名は上記で確認した名前に置き換える)
Invoke-AzRestMethod `
    -Method DELETE `
    -Path "/providers/microsoft.aadiam/diagnosticSettings/設定名?api-version=2017-04-01"

アクティビティログ診断設定の更新について

Step 5 では既存の診断設定を一度削除して再作成しています。削除から再作成が完了するまでの数秒間、ログ転送が中断されます。初期セットアップ時の実行を想定しているため通常は問題ありませんが、稼働中の環境での再実行時はご留意ください。

Entra ID の retentionPolicy について

retentionPolicyenabled: false は診断設定側の保持期間管理を無効にしています。保持期間は Step 4 のストレージライフサイクルポリシーで一元管理しており、これは Microsoft が推奨する方式です。

Entra ID の追加ログカテゴリについて

本スクリプトは最低限の構成として AuditLogsSignInLogs を設定しています。Entra ID P1 / P2 ライセンスをお持ちの場合は、NonInteractiveUserSignInLogs(非対話型サインイン)や ServicePrincipalSignInLogs(サービスプリンシパルのサインイン)なども収集対象に加えることを検討してください。

おわりに

テナント開設時のログ設定は、後から「やっておけばよかった」となりやすい設定の1つです。本スクリプトを活用することで、手順の抜け漏れなく確実にログ保管環境を構築できます。

参考情報

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?