Defender for Cloud 推奨事項を分析するための Azure Resource Graph クエリ

2025/02/10に公開

はじめに

Defender for Cloud の推奨事項は Azure Resource Graph から取得可能です。Azure ポータルである程度分析はできますが、個別に詳細な情報を確認したい場合やブックで可視化したい場合などは Azure Resource Graph でクエリを実行する必要があります。このとき活用できそうなサンプルをいくつか書いておきます。

ベース KQL クエリ

個別にカスタマイズするよりもクエリの処理は重くなりますが、今回はベースとなるクエリを準備して、様々な用途で加工しやすくします。

securityresources
| where type =~ "microsoft.security/assessments"  // Defender for Cloud の推奨事項 (assessments) を取得
| extend assessmentType = (tostring(properties.metadata.assessmentType))  // 推奨事項の種類を取得
| extend assessmentTypeSkimmed = case(  // 推奨事項の種類を分類
                    tostring(properties.metadata.assessmentType) == "BuiltIn", "BuiltIn",
                    tostring(properties.metadata.assessmentType) == "BuiltInPolicy", "BuiltIn",
                    tostring(properties.metadata.assessmentType) == "CustomPolicy", "Custom",
                    tostring(properties.metadata.assessmentType) == "CustomerManaged", "Custom",
                    tostring(properties.metadata.assessmentType) == "ManualCustomPolicy", "Custom",
                    tostring(properties.metadata.assessmentType) == "ManualBuiltInPolicy", "BuiltIn",
                    dynamic(null)
                    )
| extend assessmentId = tolower(id)  // 推奨事項の一意識別子を小文字で取得
| extend assessmentKey = name  // 推奨事項のキーを取得
| extend source = tostring(properties.resourceDetails.Source)  // 推奨事項対象リソースのソースを取得  (例: "Azure", "AWS", "GCP")
| extend resourceId = tostring(properties.resourceDetails.ResourceId)  // 推奨事項対象リソースのIDを取得
| extend displayName = tostring(properties.displayName)  // 推奨事項の表示名を取得
| extend statusCode = tostring(properties.status.code)  // 推奨事項のステータスコードを取得 (例: "Healthy", "Unhealthy")
| extend severity = tostring(properties.metadata.severity)  // 推奨事項の重大度を取得
| extend severityLevel = (  // 重大度を数値化 (High = 3, Medium = 2, Low = 1)
                case(severity =~ "High", 3,
                  severity =~ "Medium", 2,
                  severity =~ "Low", 1,
                  0))
| extend riskLevelText = tostring(properties.risk.level)  // リスクレベルを文字列で取得
| extend riskLevel = (  // リスクレベルを数値化 (Critical = 4, High = 3, Medium = 2, Low = 1)
                case(riskLevelText =~ "Critical", 4,
                  riskLevelText =~ "High", 3,
                  riskLevelText =~ "Medium", 2,
                  riskLevelText =~ "Low", 1,
                  0))
| extend riskFactors = iff(isnull(properties.risk.riskFactors), dynamic([]), properties.risk.riskFactors)  // リスク要因を取得 (null の場合は空のリスト)
| extend statusCause = tostring(properties.status.cause)  // ステータス変更の原因を取得 (例: "Exempt" = 免除)
| extend isExempt = iff(statusCause == "Exempt", tobool(1), tobool(0))  // 免除されているかどうかをブール値で取得
| extend firstEvaluationDate = tostring(todatetime(properties.status.firstEvaluationDate))  // 最初の推奨事項日を取得
| extend statusChangeDate = tostring(todatetime(properties.status.statusChangeDate))  // ステータス変更日を取得
| extend url  = strcat("https://", tostring(properties.links.azurePortal))  // Azure ポータルへのリンクを作成
| project tenantId, subscriptionId, resourceGroup, resourceId, source, displayName, statusCode, severity, severityLevel, riskLevelText, riskLevel, riskFactors, isExempt, statusCause, statusChangeDate, assessmentType, assessmentTypeSkimmed, assessmentKey, url  // 必要な列を出力

以下のようなイメージで取得できます。

推奨事項をリスク別で集計

Defender CSPM を有効化している場合に利用できるリスク別推奨事項 (個別の推奨事項の重要度に加えてリソース固有のリスクを加味して推奨事項のリスクを決定) を集計します。

<ベース クエリ>
| where statusCode == "Unhealthy"
| summarize Critical = countif(riskLevel == "4"),
            High = countif(riskLevel == "3"),
            Medium = countif(riskLevel == "2"),
            Low = countif(riskLevel == "1") 
  by subscriptionId, source
| project subscriptionId, source, Critical, High, Medium, Low
| order by subscriptionId asc, source

推奨事項ごとに集計

各推奨事項で該当のリソース数を集計します。

<ベース クエリ>
| where statusCode == "Unhealthy"
| summarize resourceCount = count() by subscriptionId, source, displayName, severity, severityLevel
| project subscriptionId, source, displayName, severity, severityLevel, resourceCount
| order by subscriptionId asc, severityLevel desc

特定サブスクリプションで緊急/高のリスク別推奨事項をリストアップ

特定のサブスクリプションにおける緊急/高のリスク別推奨事項をリストアップします。

<ベース クエリ>
| extend resourceName = tostring(split(resourceId, "/")[-1])  // リソース名を取得
| where subscriptionId == "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
| where riskLevel >=3
| project resourceGroup, resourceName, source, displayName, severity, severityLevel, riskLevelText,riskLevel, url
| order by riskLevel desc, severityLevel desc
| project-away riskLevel, severityLevel

特定の推奨事項のリソースをリストアップし経過日数とリンクを表示

特定の推奨事項に合致するリソースをリストアップし、検出からの経過日数と Azur ポータルへのリンクを表示します。

<ベース クエリ>
| where statusCode == "Unhealthy"
| where displayName == "TLS should be updated to the latest version for web apps" 
| extend elapsedDays = round((now() - todatetime(statusChangeDate)) / 1d, 0)
| project resourceId, displayName, severity, riskLevelText, riskLevel, riskFactors, elapsedDays, url
| order by riskLevel desc

適用除外を集計

推奨事項として検出されない適用除外対象となっているリソースを集計します。

<ベース クエリ>
| where isExempt == 1
| summarize count() by subscriptionId, source, displayName

Microsoft (有志)

Discussion