Microsoft SentinelをIaCで運用する(Azure Bicep + GitHub Actions)

こんにちは!松岡です。

みなさんはMicrosoft Sentinelをどのように運用していますか?
SentinelはGUIが充実しており、ほとんどの操作をポータル上で実行できます。GUIによる操作はわかりやすい一方で、差分の追跡、レビュー、再現性の確保には設計書への反映や手作業に頼らざるを得ず、ミスが生じやすいという側面があります。

今回はAzure BicepとGitHub Actionsを用いて、検知ルールをIaCで管理する方法を検証します。リポジトリにルール定義(Bicep / KQL)を置き、プッシュをトリガーに Lint → Validate → What‑If → Deploy の流れで検知ルールをSentinelに反映します。

<関連記事>
突き抜けた知的好奇心を武器に学びを深め、会社全体へ還元していく(松岡)

全体像

目的

GitHubのリポジトリで検知ルールをコードとして管理し、main へのプッシュをトリガーにGitHub Actionsで Lint / Validate / What‑If / Deployを順に実行して、Microsoft Sentinelへ安全に反映します。

処理フロー

  1. queries/*.kql・detections/*.bicepを編集してプッシュ
  2. GitHub Actionsが起動し、OIDCでAzureにログイン
  3. Lint:Bicepの構文・アナライザーで不整合を検出
  4. Validate:デプロイ前検証(型・参照・権限など)
  5. What‑If:実環境との差分を表示(意図しない変更の検知)
  6. 問題がなければデプロイを実行
  7. Sentinelの検知ルールが更新

リポジトリ構成例

.
├─ infra/
│  ├─ main.bicep
│  └─ modules/
│     └─ scheduledRule.bicep
├─ detections/
│  ├─ signins-mfa-fail.bicep
│  ├─ azureactivity-delete-spike.bicep
│  └─ auditlogs-priv-role-add.bicep
├─ queries/
│  ├─ signins_mfa_fail.kql
│  ├─ azureactivity_delete_spike.kql
│  └─ auditlogs_priv_role_add.kql
├─ env/
│  └─ dev.bicepparam
└─ .github/workflows/
   └─ deploy-sentinel-dev.yml
  • queries/*.kql:KQLクエリ本体
  • detections/*.bicep:クエリを読み込み、検知ルールをインスタンス化
  • infra/modules/*.bicep:再利用可能なモジュール
  • infra/main.bicep:ルートのエントリーポイント(複数ルールを束ねてデプロイ)
  • env/*.bicepparam:環境ごとのパラメーター
  • .github/workflows/*.yml:GitHub Actionsの定義

検知ルールのサンプル

クエリは queries/*.kql に記述し、ルールは detections/*.bicep で読み込みます。

サインインの MFA 失敗が急増

// queries/signins_mfa_fail.kql
SigninLogs
| where ResultType == "500121"
| summarize Count = count() by UserPrincipalName, IPAddress, bin(TimeGenerated, 1h)
| where Count > 5

// detections/signins-mfa-fail.bicep
param workspaceName string

module rule '../infra/modules/scheduledRule.bicep' = {
  name: 'scheduled-signins-mfa-fail'
  params: {
    workspaceName: workspaceName
    ruleStableKey: 'signins-mfa-fail'
    displayName: 'Sign-in MFA failure spike'
    severity: 'Medium'
    query: loadTextContent('../queries/signins_mfa_fail.kql')
    queryFrequency: 'PT1H'
    queryPeriod: 'PT1H'
    triggerOperator: 'GreaterThan'
    triggerThreshold: 0
    tactics: [
      'CredentialAccess'
    ]
    entityMappings: [
      {
        entityType: 'Account'
        fieldMappings: [
          { identifier: 'FullName', columnName: 'UserPrincipalName' }
        ]
      }
      {
        entityType: 'IP'
        fieldMappings: [
          { identifier: 'Address', columnName: 'IPAddress' }
        ]
      }
    ]
  }
}

main.bicepとbicepparamの設定

GitHub Actions の流れ

最初のジョブでOIDCによるAzureログイン、Lint / Validate / What‑Ifを実施して不備や意図しない差分を検出し、続くジョブでcreate(デプロイ / 更新)を行います。

# .github/workflows/deploy-sentinel-dev.yml
name: Deploy Microsoft Sentinel (dev)

on:
  push:
    branches: [ main ]
    paths:
      - 'infra/**'
      - 'detections/**'
      - 'queries/**'
      - 'env/dev.bicepparam'
      - '.github/workflows/deploy-sentinel-dev.yml'
  workflow_dispatch:

permissions:
  id-token: write
  contents: read

env:
  AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
  AZURE_TENANT_ID:       ${{ secrets.AZURE_TENANT_ID }}
  AZURE_CLIENT_ID:       ${{ secrets.AZURE_CLIENT_ID }}
  RESOURCE_GROUP:        rg-iac-sentinel-dev
  DEPLOYMENT_NAME:       sentinel-dev-${{ github.run_number }}

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Azure Login (OIDC)
        uses: azure/login@v2
        with:
          client-id: ${{ env.AZURE_CLIENT_ID }}
          tenant-id: ${{ env.AZURE_TENANT_ID }}
          subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}

      - name: Install Bicep CLI
        run: az bicep install

      - name: Lint (bicep build + analyzers)
        run: az bicep build --file infra/main.bicep

      - name: Validate
        uses: azure/bicep-deploy@v2
        with:
          type: deployment
          operation: validate
          scope: resourceGroup
          subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}
          resource-group-name: ${{ env.RESOURCE_GROUP }}
          template-file: infra/main.bicep
          parameters-file: env/dev.bicepparam

      - name: What-If
        uses: azure/bicep-deploy@v2
        with:
          type: deployment
          operation: whatIf
          scope: resourceGroup
          subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}
          resource-group-name: ${{ env.RESOURCE_GROUP }}
          template-file: infra/main.bicep
          parameters-file: env/dev.bicepparam
          what-if-exclude-change-types: 'Ignore'

  deploy:
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Azure Login (OIDC)
        uses: azure/login@v2
        with:
          client-id: ${{ env.AZURE_CLIENT_ID }}
          tenant-id: ${{ env.AZURE_TENANT_ID }}
          subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}

      - name: Deploy (create/update)
        uses: azure/bicep-deploy@v2
        with:
          type: deployment
          operation: create
          scope: resourceGroup
          subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}
          resource-group-name: ${{ env.RESOURCE_GROUP }}
          name: ${{ env.DEPLOYMENT_NAME }}
          template-file: infra/main.bicep
          parameters-file: env/dev.bicepparam

結果

上記設定で GitHub にプッシュするとフローが実行され、正常に Sentinel 上に分析ルールが反映されます。

今回の検証を通じ、BicepとGitHub ActionsによるIaC化により、変更点をコードとして追跡しやすくし、What‑Ifで差分を事前に確認したうえで安心して反映できることがわかりました。また、実際に検証環境を作成してみて、設定項目が多く、初期整備とレビューの負荷が増えやすいという点も感じました。

GUIと比べると、テンプレートやパイプラインの保守、エラー時の切り戻し、環境ごとのパラメーター管理、権限・スコープ設計など、技術的な運用・保守のハードルは上がります。検知ルールの更新頻度が高くない場合は、IaC導入コストがメリットを上回ることも十分考えられます。

SIEMの検知は継続的に改善・更新されるため、コード化して差分をレビューしながら安全に反映できるメリットは大きいです。一方で、IaCによってコードのメンテナンス負荷が増える側面もあります。

今回は、GitHub ActionsとAzure Bicepを使ってMicrosoft Sentinelの検知ルールをIaC化する方法を紹介しました。