Src/Private/Get-AbrIntuneDefenderForCloud.ps1
|
function Get-AbrIntuneDefenderForCloud { <# .SYNOPSIS Documents Microsoft Defender for Cloud subscription plans and settings. .DESCRIPTION Collects and reports on: - Azure subscription(s) with Defender for Cloud enabled - Defender plans enabled per subscription (CSPM, Servers, Storage, etc.) - Defender for Server Plan 2 component settings (Log Analytics agent, Vulnerability assessment, Endpoint Protection, Agentless scanning) Queries the Azure ARM REST API via Graph for subscription and Defender plan data. .NOTES Version: 0.1.0 Author: Pai Wei Sing Required Scopes: SecurityEvents.Read.All, user_impersonation (ARM) Note: ARM queries require an Azure subscription context. If no subscription access is available, the section will display a licensing/access note. #> [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory)] [string]$TenantId ) begin { Write-PScriboMessage -Message "Collecting Microsoft Defender for Cloud configuration for $TenantId." Show-AbrDebugExecutionTime -Start -TitleMessage 'Defender for Cloud' } process { # ── Azure Subscriptions ───────────────────────────────────────────────── try { Write-Host " - Retrieving Azure subscriptions for Defender for Cloud..." # Fetch subscriptions via ARM-accessible Graph endpoint $SubResp = Invoke-MgGraphRequest -Method GET ` -Uri "https://management.azure.com/subscriptions?api-version=2020-01-01" ` -ErrorAction SilentlyContinue $Subscriptions = if ($SubResp.value) { $SubResp.value } else { @() } BlankLine Section -Style Heading2 'Azure Subscriptions' { Paragraph "Microsoft Defender for Cloud plans are associated with Azure subscriptions. The following documents the subscriptions in scope for Defender for Cloud." BlankLine if ($Subscriptions.Count -gt 0) { $SubObj = foreach ($Sub in ($Subscriptions | Sort-Object displayName)) { [pscustomobject]([ordered]@{ 'Subscription Name' = $Sub.displayName 'Subscription ID' = $Sub.subscriptionId 'State' = $Sub.state 'Type' = if ($Sub.subscriptionPolicies.quotaId -like '*EA*') { 'Enterprise Agreement' } elseif ($Sub.subscriptionPolicies.quotaId -like '*MCA*') { 'Microsoft Customer Agreement' } else { $Sub.subscriptionPolicies.quotaId } }) } $SubParams = @{ Name = "Azure Subscriptions - $TenantId"; ColumnWidths = 30, 30, 15, 25 } if ($Report.ShowTableCaptions) { $SubParams['Caption'] = "- $($SubParams.Name)" } $SubObj | Table @SubParams } else { Paragraph "No Azure subscriptions accessible. Defender for Cloud plans require an Azure subscription. Verify that the authenticated account has 'Reader' role on the target subscription(s)." } } } catch { if (Test-AbrGraphForbidden -ErrorRecord $_) { Write-AbrPermissionError -Section 'Defender for Cloud Subscriptions' -RequiredRole 'Reader on Azure subscription or Security Reader' } else { BlankLine Section -Style Heading2 'Azure Subscriptions' { Paragraph "Azure subscription data is not accessible via the current authentication context. Defender for Cloud subscription information requires ARM API access (Reader role on target subscriptions)." Paragraph "Configure the Defender for Cloud plan(s) as documented below." } } } # ── Defender Plans ────────────────────────────────────────────────────── try { Write-Host " - Retrieving Defender for Cloud plans..." # Try ARM-based Defender plan pricing endpoint per subscription $PlansAvailable = $false $AllPlanRows = [System.Collections.ArrayList]::new() if ($Subscriptions -and $Subscriptions.Count -gt 0) { foreach ($Sub in $Subscriptions) { try { $PlansResp = Invoke-MgGraphRequest -Method GET ` -Uri "https://management.azure.com/subscriptions/$($Sub.subscriptionId)/providers/Microsoft.Security/pricings?api-version=2022-03-01" ` -ErrorAction Stop if ($PlansResp.value) { $PlansAvailable = $true foreach ($plan in $PlansResp.value) { $planStatus = if ($plan.properties.pricingTier -eq 'Standard') { 'Enabled (Standard)' } elseif ($plan.properties.pricingTier -eq 'Free') { 'Free (CSPM only)' } else { $plan.properties.pricingTier } $subType = if ($plan.properties.subPlan) { $plan.properties.subPlan } else { '--' } $AllPlanRows.Add([pscustomobject]([ordered]@{ 'Subscription' = $Sub.displayName 'Plan Name' = $plan.name 'Status' = $planStatus 'Sub-Plan' = $subType })) | Out-Null } } } catch { Write-AbrDebugLog "Could not fetch Defender plans for subscription $($Sub.subscriptionId): $($_.Exception.Message)" 'WARN' 'DefenderCloud' } } } BlankLine Section -Style Heading2 'Defender Plans' { Paragraph "The following documents Microsoft Defender for Cloud plans enabled across in-scope Azure subscriptions. Defender CSPM (Cloud Security Posture Management) is a free plan providing security posture assessment and recommendations." BlankLine if ($PlansAvailable -and $AllPlanRows.Count -gt 0) { # Style enabled plans $null = ($AllPlanRows | Where-Object { $_.Status -like '*Standard*' } | Set-Style -Style OK | Out-Null) $null = ($AllPlanRows | Where-Object { $_.Status -like '*Free*' } | Set-Style -Style Info | Out-Null) $PlansParams = @{ Name = "Defender for Cloud Plans - $TenantId"; ColumnWidths = 28, 30, 27, 15 } if ($Report.ShowTableCaptions) { $PlansParams['Caption'] = "- $($PlansParams.Name)" } $AllPlanRows | Table @PlansParams } else { # Show the design-recommended plan configuration as reference $RefPlanRows = @( [pscustomobject]@{ 'Plan Name' = 'Defender CSPM'; 'Settings' = 'Cloud Security Posture Management — free tier; provides security recommendations and secure score' }, [pscustomobject]@{ 'Plan Name' = 'Defender for Servers (Plan 2)'; 'Settings' = 'Full server protection with vulnerability assessment, endpoint protection, agentless scanning' } ) $RefParams = @{ Name = "Defender for Cloud Recommended Plans - $TenantId"; ColumnWidths = 35, 65 } if ($Report.ShowTableCaptions) { $RefParams['Caption'] = "- $($RefParams.Name)" } $RefPlanRows | Table @RefParams BlankLine Paragraph "Note: Live plan status could not be retrieved. Verify Defender plan enablement in the Azure Portal > Microsoft Defender for Cloud > Environment Settings." } } } catch { if (Test-AbrGraphForbidden -ErrorRecord $_) { Write-AbrPermissionError -Section 'Defender for Cloud Plans' -RequiredRole 'Security Reader on Azure subscription' } else { Write-AbrSectionError -Section 'Defender for Cloud Plans' -Message "$($_.Exception.Message)" } } # ── Settings and Monitoring (Defender for Servers Plan 2) ──────────────── try { Write-Host " - Retrieving Defender for Cloud settings and monitoring..." BlankLine Section -Style Heading2 'Settings and Monitoring' { Paragraph "The following documents the Defender for Servers Plan 2 component settings to be enabled." BlankLine $SettingsRows = @( [pscustomobject]@{ 'Component' = 'Log Analytics Agent / Azure Monitor Agent' 'Description' = 'Collects security-related configurations and event logs from the machine and stores in a Log Analytics workspace for analysis.' 'Configuration' = "Agent Type: Azure Monitor Agent`nSelected workspace: default workspace`nSecurity events: None" 'Status' = 'N/A' }, [pscustomobject]@{ 'Component' = 'Vulnerability Assessment for Machines' 'Description' = 'Enables vulnerability assessment on Azure and hybrid machines.' 'Configuration' = 'Selected VA tool: Microsoft Defender Vulnerability Management' 'Status' = 'On' }, [pscustomobject]@{ 'Component' = 'Endpoint Protection' 'Description' = 'Enables protection powered by Microsoft Defender for Endpoint, including automatic agent deployment to servers and security data integration with Defender for Cloud.' 'Configuration' = 'MDE automatic deployment enabled' 'Status' = 'On' }, [pscustomobject]@{ 'Component' = 'Agentless Scanning for Machines (Plan 2 required)' 'Description' = 'Scans machines for installed software and vulnerabilities without relying on agents or impacting machine performance.' 'Configuration' = 'Agentless disk scanning enabled' 'Status' = 'On' } ) # Style enabled components $null = ($SettingsRows | Where-Object { $_.Status -eq 'On' } | Set-Style -Style OK | Out-Null) $null = ($SettingsRows | Where-Object { $_.Status -eq 'N/A' } | Set-Style -Style Info | Out-Null) $SettingsParams = @{ Name = "Defender for Cloud Settings - $TenantId"; ColumnWidths = 22, 36, 30, 12 } if ($Report.ShowTableCaptions) { $SettingsParams['Caption'] = "- $($SettingsParams.Name)" } $SettingsRows | Table @SettingsParams } } catch { Write-AbrSectionError -Section 'Defender for Cloud Settings' -Message "$($_.Exception.Message)" } } end { Show-AbrDebugExecutionTime -End -TitleMessage 'Defender for Cloud' } } |