PowerShell Setup: Control 1.24 - Defender AI Security Posture Management (AI-SPM)
Last Updated: January 2026 Modules Required: Az.Security, Az.ResourceGraph
Prerequisites
Install-Module -Name Az.Security -Force -Scope CurrentUser
Install-Module -Name Az.ResourceGraph -Force -Scope CurrentUser
Automated Scripts
Check AI-SPM Status
<#
.SYNOPSIS
Checks if AI-SPM is enabled for subscriptions
.EXAMPLE
.\Get-AISPMStatus.ps1
#>
Write-Host "=== Check AI-SPM Status ===" -ForegroundColor Cyan
Connect-AzAccount
$subscriptions = Get-AzSubscription
foreach ($sub in $subscriptions) {
Set-AzContext -SubscriptionId $sub.Id
Write-Host "`nSubscription: $($sub.Name)" -ForegroundColor Yellow
# Check Defender CSPM status
$pricing = Get-AzSecurityPricing -Name "CloudPosture" -ErrorAction SilentlyContinue
if ($pricing) {
Write-Host " Defender CSPM: $($pricing.PricingTier)" -ForegroundColor Green
# Check extensions
$extensions = $pricing.Extension
$aiSpm = $extensions | Where-Object { $_.Name -eq "AIThreatProtection" }
if ($aiSpm) {
Write-Host " AI-SPM (AIThreatProtection): Enabled" -ForegroundColor Green
} else {
Write-Host " AI-SPM (AIThreatProtection): Not found" -ForegroundColor Yellow
}
Write-Host " Extensions enabled: $($extensions.Name -join ', ')"
} else {
Write-Host " Defender CSPM: Not enabled" -ForegroundColor Red
}
}
Inventory AI Workloads
<#
.SYNOPSIS
Inventories AI workloads across subscriptions using Resource Graph
.EXAMPLE
.\Get-AIWorkloadInventory.ps1 -OutputPath ".\AIWorkloads.csv"
#>
param(
[string]$OutputPath = ".\AIWorkloads.csv"
)
Write-Host "=== AI Workload Inventory ===" -ForegroundColor Cyan
Connect-AzAccount
# Query for AI-related resources
$query = @"
Resources
| where type in~ (
'microsoft.cognitiveservices/accounts',
'microsoft.machinelearningservices/workspaces',
'microsoft.search/searchservices'
)
| project
name,
type,
resourceGroup,
subscriptionId,
location,
sku = properties.sku.name,
kind = kind
| order by type, name
"@
$results = Search-AzGraph -Query $query
Write-Host "AI workloads found: $($results.Count)" -ForegroundColor Green
if ($results.Count -gt 0) {
$results | Export-Csv -Path $OutputPath -NoTypeInformation
Write-Host "Inventory exported to: $OutputPath" -ForegroundColor Green
# Summary by type
$results | Group-Object -Property type | ForEach-Object {
Write-Host " $($_.Name): $($_.Count)"
}
}
Export Attack Paths
<#
.SYNOPSIS
Exports attack paths from Defender for Cloud
.EXAMPLE
.\Export-AttackPaths.ps1 -OutputPath ".\AttackPaths.csv"
#>
param(
[string]$OutputPath = ".\AttackPaths.csv",
[string]$SubscriptionId
)
Write-Host "=== Export Attack Paths ===" -ForegroundColor Cyan
Connect-AzAccount
if ($SubscriptionId) {
Set-AzContext -SubscriptionId $SubscriptionId
}
# Get attack paths via REST API (PowerShell module doesn't have direct cmdlet)
$token = (Get-AzAccessToken -ResourceUrl "https://management.azure.com").Token
$headers = @{
"Authorization" = "Bearer $token"
"Content-Type" = "application/json"
}
$subscriptionId = (Get-AzContext).Subscription.Id
$uri = "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Security/attackPaths?api-version=2024-01-01"
try {
$response = Invoke-RestMethod -Uri $uri -Method Get -Headers $headers
$attackPaths = $response.value
Write-Host "Attack paths found: $($attackPaths.Count)" -ForegroundColor Green
# Filter for AI-related paths
$aiPaths = $attackPaths | Where-Object {
$_.properties.displayName -match "AI|ML|cognitive|openai" -or
$_.properties.description -match "AI|ML|cognitive|openai"
}
Write-Host "AI-related attack paths: $($aiPaths.Count)" -ForegroundColor Yellow
if ($aiPaths.Count -gt 0) {
$export = $aiPaths | ForEach-Object {
[PSCustomObject]@{
Name = $_.name
DisplayName = $_.properties.displayName
Description = $_.properties.description
RiskLevel = $_.properties.riskLevel
EntryPointCount = $_.properties.entryPointEntityInfos.Count
}
}
$export | Export-Csv -Path $OutputPath -NoTypeInformation
Write-Host "Attack paths exported to: $OutputPath" -ForegroundColor Green
}
}
catch {
Write-Host "Error retrieving attack paths: $($_.Exception.Message)" -ForegroundColor Red
}
Validation Script
<#
.SYNOPSIS
Validates Control 1.24 - Defender AI-SPM configuration
.EXAMPLE
.\Validate-Control-1.24.ps1
#>
Write-Host "=== Control 1.24 Validation ===" -ForegroundColor Cyan
Connect-AzAccount
$results = @()
# Check 1: Defender CSPM enabled
Write-Host "`n[Check 1] Defender CSPM Status" -ForegroundColor Cyan
$pricing = Get-AzSecurityPricing -Name "CloudPosture" -ErrorAction SilentlyContinue
if ($pricing -and $pricing.PricingTier -eq "Standard") {
Write-Host " [PASS] Defender CSPM enabled" -ForegroundColor Green
$results += [PSCustomObject]@{Check="Defender CSPM"; Status="PASS"; Details="Enabled"}
} else {
Write-Host " [FAIL] Defender CSPM not enabled" -ForegroundColor Red
$results += [PSCustomObject]@{Check="Defender CSPM"; Status="FAIL"; Details="Not enabled"}
}
# Check 2: AI workloads discovered
Write-Host "`n[Check 2] AI Workload Discovery" -ForegroundColor Cyan
$query = "Resources | where type in~ ('microsoft.cognitiveservices/accounts', 'microsoft.machinelearningservices/workspaces') | count"
$count = (Search-AzGraph -Query $query).Count
if ($count -gt 0) {
Write-Host " [PASS] AI workloads discovered: $count" -ForegroundColor Green
$results += [PSCustomObject]@{Check="AI Discovery"; Status="PASS"; Details="$count workloads"}
} else {
Write-Host " [INFO] No AI workloads found (may be expected)" -ForegroundColor Yellow
$results += [PSCustomObject]@{Check="AI Discovery"; Status="INFO"; Details="No workloads"}
}
# Check 3: Security recommendations
Write-Host "`n[Check 3] AI Security Recommendations" -ForegroundColor Cyan
$recommendations = Get-AzSecurityTask | Where-Object {
$_.Name -match "AI|cognitive|machine learning|openai"
}
Write-Host " AI-related recommendations: $($recommendations.Count)"
$results += [PSCustomObject]@{Check="Recommendations"; Status="INFO"; Details="$($recommendations.Count) found"}
# Summary
Write-Host "`n=== Validation Summary ===" -ForegroundColor Cyan
$results | Format-Table -AutoSize
Write-Host "`n=== Validation Complete ===" -ForegroundColor Cyan
Complete Configuration Script
<#
.SYNOPSIS
Configures Control 1.24 - Defender AI Security Posture Management
.DESCRIPTION
This script validates AI-SPM configuration, inventories AI workloads,
and exports security posture data for compliance evidence.
.PARAMETER ExportPath
Path for exports (default: current directory)
.EXAMPLE
.\Configure-Control-1.24.ps1 -ExportPath "C:\Evidence"
.NOTES
Last Updated: January 2026
Related Control: Control 1.24 - Defender AI Security Posture Management
#>
param(
[string]$ExportPath = "."
)
try {
# Connect to Azure
Write-Host "Connecting to Azure..." -ForegroundColor Cyan
Connect-AzAccount
Write-Host "Configuring Control 1.24: Defender AI Security Posture Management" -ForegroundColor Cyan
# Step 1: Check Defender CSPM
Write-Host "`n[Step 1] Checking Defender CSPM status..." -ForegroundColor Yellow
$pricing = Get-AzSecurityPricing -Name "CloudPosture" -ErrorAction SilentlyContinue
if ($pricing) {
Write-Host " Defender CSPM: $($pricing.PricingTier)" -ForegroundColor Green
} else {
Write-Host " [WARNING] Defender CSPM not configured" -ForegroundColor Yellow
Write-Host " Enable in Azure Portal: Defender for Cloud > Environment settings > Defender CSPM"
}
# Step 2: Inventory AI workloads
Write-Host "`n[Step 2] Inventorying AI workloads..." -ForegroundColor Yellow
$query = @"
Resources
| where type in~ (
'microsoft.cognitiveservices/accounts',
'microsoft.machinelearningservices/workspaces'
)
| project name, type, resourceGroup, location
"@
$aiWorkloads = Search-AzGraph -Query $query
Write-Host " AI workloads found: $($aiWorkloads.Count)" -ForegroundColor Green
if ($aiWorkloads.Count -gt 0) {
$workloadFile = Join-Path $ExportPath "AIWorkloads-$(Get-Date -Format 'yyyyMMdd').csv"
$aiWorkloads | Export-Csv -Path $workloadFile -NoTypeInformation
Write-Host " Exported to: $workloadFile" -ForegroundColor Green
}
# Step 3: Export security recommendations
Write-Host "`n[Step 3] Exporting AI security recommendations..." -ForegroundColor Yellow
$tasks = Get-AzSecurityTask
$aiTasks = $tasks | Where-Object {
$_.RecommendationType -match "AI|cognitive|machine|openai" -or
$_.ResourceId -match "cognitive|machinelearning|openai"
}
Write-Host " AI recommendations: $($aiTasks.Count)"
if ($aiTasks.Count -gt 0) {
$taskFile = Join-Path $ExportPath "AIRecommendations-$(Get-Date -Format 'yyyyMMdd').csv"
$aiTasks | Select-Object Name, RecommendationType, State, ResourceId |
Export-Csv -Path $taskFile -NoTypeInformation
Write-Host " Exported to: $taskFile" -ForegroundColor Green
}
# Step 4: Summary report
Write-Host "`n[Step 4] Generating summary..." -ForegroundColor Yellow
$summary = [PSCustomObject]@{
Timestamp = Get-Date
DefenderCSPM = $pricing.PricingTier
AIWorkloads = $aiWorkloads.Count
SecurityRecommendations = $aiTasks.Count
SubscriptionId = (Get-AzContext).Subscription.Id
}
$summaryFile = Join-Path $ExportPath "AISPM-Summary-$(Get-Date -Format 'yyyyMMdd').json"
$summary | ConvertTo-Json | Out-File -FilePath $summaryFile
Write-Host " Summary saved to: $summaryFile" -ForegroundColor Green
Write-Host "`n[PASS] Control 1.24 configuration completed successfully" -ForegroundColor Green
}
catch {
Write-Host "[FAIL] Error: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "[INFO] Stack trace: $($_.ScriptStackTrace)" -ForegroundColor Yellow
exit 1
}
finally {
# Cleanup connections
if (Get-AzContext) {
Disconnect-AzAccount -ErrorAction SilentlyContinue | Out-Null
Write-Host "`nDisconnected from Azure" -ForegroundColor Gray
}
}
Back to Control 1.24 | Portal Walkthrough | Verification Testing | Troubleshooting