Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion powershell/Maester.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,10 @@
'Test-MtMdeRetainCleanedMalware', 'Test-MtMdeScheduleScanDay', 'Test-MtMdeScriptScanning',
'Test-MtMdeSignatureBeforeScan', 'Test-MtMdeSignatureUpdateInterval', 'Test-MtMdeSubmitSamplesConsent',
'Test-MtOperationApprovalPolicies',
'Test-MtPimAlertsExists', 'Test-MtPrivPermanentDirectoryRole', 'Test-MtSecurityGroupCreationRestricted',
'Test-MtPimAlertsExists', 'Test-MtPrivPermanentDirectoryRole',
'Test-MtPurviewAiAuditLogIngestion', 'Test-MtPurviewAiDlpPolicy', 'Test-MtPurviewAiInsiderRiskPolicy',
'Test-MtPurviewAiRetentionPolicy', 'Test-MtPurviewAiSensitivityLabelsForFiles',
'Test-MtSecurityGroupCreationRestricted',
'Test-MtServicePrincipalsForAllUsers', 'Test-MtSpExchangeAppAccessPolicy',
'Test-MtTeamsRestrictParticipantGiveRequestControl', 'Test-MtTenantCreationRestricted', 'Test-MtTenantCustomization',
'Test-MtUserAccessAdmin', 'Test-MtVaultSoftDelete', 'Test-MtWindowsDataProcessor',
Expand Down
2 changes: 2 additions & 0 deletions powershell/public/cisa/exchange/Get-MtExo.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
"SharingPolicy" = "Get-SharingPolicy"
"DlpComplianceRule" = "Get-DlpComplianceRule"
"DlpCompliancePolicy" = "Get-DlpCompliancePolicy"
"RetentionCompliancePolicy" = "Get-RetentionCompliancePolicy"
"AppRetentionCompliancePolicy" = "Get-AppRetentionCompliancePolicy"
"MalwareFilterPolicy" = "Get-MalwareFilterPolicy"
"HostedContentFilterPolicy" = "Get-HostedContentFilterPolicy"
"HostedConnectionFilterPolicy" = "Get-HostedConnectionFilterPolicy"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Ensure the Microsoft 365 unified audit log is enabled so Microsoft 365 Copilot, Security Copilot, Copilot Studio and Entra-registered AI app prompts and responses are captured for downstream Purview AI controls.

The unified audit log is the **prerequisite** for the Microsoft Purview Data Security Posture Management (DSPM) for AI activity explorer, the Risky AI usage Insider Risk Management template, eDiscovery searches that include Copilot interactions, and Communication Compliance policies that monitor Copilot prompts/responses.

When `UnifiedAuditLogIngestionEnabled` is `False`:

- AI prompts and responses are **not** captured anywhere in the tenant.
- DSPM for AI activity explorer will be empty.
- Risky AI usage Insider Risk policies cannot generate alerts.
- eDiscovery cannot find Copilot transcripts.
- Communication Compliance for Copilot interactions will not match.

The test passes if `Get-AdminAuditLogConfig` returns `UnifiedAuditLogIngestionEnabled = True`.

#### Remediation action:

1. Connect to Exchange Online PowerShell as a Compliance Administrator or higher: `Connect-ExchangeOnline`.
2. Run: `Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true`.
3. Allow up to 60 minutes for ingestion to begin and confirm by searching the [Microsoft Purview audit search](https://purview.microsoft.com/audit/auditsearch).

#### Related links

- [Microsoft Learn — Turn auditing on or off](https://learn.microsoft.com/en-us/purview/audit-log-enable-disable)
- [Microsoft Learn — Data Security Posture Management for AI](https://learn.microsoft.com/en-us/purview/ai-microsoft-purview)
- [Microsoft Learn — Audit logs for Microsoft 365 Copilot interactions](https://learn.microsoft.com/en-us/purview/audit-copilot)

<!--- Results --->
%TestResult%
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
function Test-MtPurviewAiAuditLogIngestion {
<#
.SYNOPSIS
Ensure the Microsoft 365 unified audit log is enabled so Microsoft 365 Copilot and other AI app interactions are captured.

.DESCRIPTION
Checks the Microsoft 365 unified audit log ingestion state via Get-AdminAuditLogConfig.

Microsoft 365 Copilot, Security Copilot, Copilot in Fabric, Copilot Studio and Entra-registered AI apps all
flow user prompts and responses into the Microsoft Purview unified audit log. The DSPM for AI activity explorer,
Insider Risk Management Risky AI policies, eDiscovery searches for Copilot activity, and Communication Compliance
policies for Copilot interactions all depend on the unified audit log being enabled.

Without unified audit log ingestion, Copilot prompts and responses are NOT captured, leaving organisations with no
forensic record of how generative AI is being used and breaking every downstream Purview AI control.

The test passes if the tenant returns UnifiedAuditLogIngestionEnabled = True.

.EXAMPLE
Test-MtPurviewAiAuditLogIngestion

Returns true if the unified audit log is enabled.

.LINK
https://maester.dev/docs/commands/Test-MtPurviewAiAuditLogIngestion
#>
[CmdletBinding()]
[OutputType([bool])]
param()

Write-Verbose "Test-MtPurviewAiAuditLogIngestion: Checking if the Microsoft 365 unified audit log is enabled."

if (!(Test-MtConnection ExchangeOnline)) {
Add-MtTestResultDetail -SkippedBecause NotConnectedExchange
return $null
}

try {
$config = Get-AdminAuditLogConfig -ErrorAction Stop
$enabled = [bool]$config.UnifiedAuditLogIngestionEnabled

$portalLink = "https://purview.microsoft.com/audit/auditsearch"

if ($enabled) {
$testResultMarkdown = "Well done. Your tenant has [unified audit log ingestion enabled]($portalLink), "
$testResultMarkdown += "so Microsoft 365 Copilot and other AI app prompts/responses are captured for "
$testResultMarkdown += "DSPM for AI, Insider Risk Management, eDiscovery and Communication Compliance."
} else {
$testResultMarkdown = "Your tenant does **not** have [unified audit log ingestion enabled]($portalLink).`n`n"
$testResultMarkdown += "> **Risk:** Without the unified audit log, Microsoft 365 Copilot prompts and responses are not captured. "
$testResultMarkdown += "DSPM for AI activity explorer, the Risky AI usage Insider Risk template, Copilot eDiscovery searches, "
$testResultMarkdown += "and Communication Compliance for Copilot will all be empty or non-functional."
}

Add-MtTestResultDetail -Result $testResultMarkdown
return $enabled
} catch {
if ($_.Exception.Response -and $_.Exception.Response.StatusCode -in @(401, 403)) {
Add-MtTestResultDetail -SkippedBecause NotAuthorized
} else {
Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_
}
return $null
}
}
30 changes: 30 additions & 0 deletions powershell/public/maester/purview/Test-MtPurviewAiDlpPolicy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Ensure a Microsoft Purview Data Loss Prevention (DLP) policy is configured for the **Microsoft 365 Copilot** location, so Copilot is blocked from summarising or surfacing files containing sensitive information types or labelled content the requesting user has access to but should not have AI summarise.

Microsoft 365 Copilot acts on every file, message and meeting that the requesting user can read. Without a DLP policy targeting the Copilot location, Copilot can:

- Summarise files containing **PII, PCI, PHI, secrets or other regulated data**.
- Paraphrase content from labelled documents in chat / Word / PowerPoint generations.
- Expose oversharing risk in OneDrive / SharePoint at AI speed.

A DLP policy on the Copilot location lets you block Copilot interactions involving files that match SITs or have specific sensitivity labels — without disabling Copilot for the user entirely.

The test passes when at least one **enabled, non-simulation** DLP policy targets the Microsoft 365 Copilot location. Detection inspects multiple Purview schema fields (`MicrosoftCopilotLocation`, `Workload`, `Locations`, and `EnforcementPlanes`) so policies created through the older preview surface and through the current Microsoft Purview portal / PowerShell paths are both recognised.

#### Remediation action:

1. Open the [Microsoft Purview portal — Data Loss Prevention — Policies](https://purview.microsoft.com/datalossprevention/policies).
2. Click **+ Create policy** and choose a Custom or template-based DLP policy.
3. Under **Locations**, enable **Microsoft 365 Copilot** (you may also enable Exchange / SharePoint / OneDrive / Devices alongside).
4. Configure rules — for example, match sensitive information types (Credit Card, SSN, customer-specific SITs) or sensitivity labels (Confidential, Highly Confidential).
5. Choose actions — block Copilot from processing the matched file, notify the user, or generate an alert.
6. Set **Mode = Turn the policy on immediately** (avoid leaving it in test/simulation mode for production protection).
7. Verify with: `Get-DlpCompliancePolicy | Where-Object { $_.MicrosoftCopilotLocation -or $_.Workload -match 'Copilot' -or ($_.Locations | Out-String) -match 'Copilot' -or ($_.EnforcementPlanes | Out-String) -match 'Copilot' }`.

#### Related links

- [Microsoft Learn — DLP for Microsoft 365 Copilot](https://learn.microsoft.com/en-us/purview/dlp-microsoft365-copilot-learn-about)
- [Microsoft Learn — Create a DLP policy](https://learn.microsoft.com/en-us/purview/dlp-create-deploy-policy)
- [Microsoft Learn — Microsoft 365 Copilot oversharing assessment](https://learn.microsoft.com/en-us/purview/ai-microsoft-purview)

<!--- Results --->
%TestResult%
117 changes: 117 additions & 0 deletions powershell/public/maester/purview/Test-MtPurviewAiDlpPolicy.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
function Test-MtPurviewAiDlpPolicy {
<#
.SYNOPSIS
Ensure a Microsoft Purview DLP policy is configured for the Microsoft 365 Copilot location.

.DESCRIPTION
Microsoft Purview Data Loss Prevention exposes a **Microsoft 365 Copilot** location that lets administrators
block Copilot from summarising or surfacing files containing sensitive information types (SITs) or labelled content.

Without a DLP policy targeting the Copilot location, Microsoft 365 Copilot can summarise, paraphrase or expose
sensitive content (PII, secrets, financial data, regulated data) from any file the requesting user can already
access — accelerating oversharing risk for AI-assisted workflows.

The test passes if at least one enabled, non-simulation DLP policy targets the Microsoft 365 Copilot location.
Detection is resilient to schema variation: it inspects `MicrosoftCopilotLocation`, `Workload`, `Locations`,
and `EnforcementPlanes` (which can surface Copilot scope as values such as `CopilotExperiences`) so that
policies created through both the older preview surface and the current Microsoft Purview portal /
PowerShell paths are recognised.

.EXAMPLE
Test-MtPurviewAiDlpPolicy

Returns true if a DLP policy is configured for the Microsoft 365 Copilot location.

.LINK
https://maester.dev/docs/commands/Test-MtPurviewAiDlpPolicy
#>
[CmdletBinding()]
[OutputType([bool])]
param()

Write-Verbose "Test-MtPurviewAiDlpPolicy: Checking for DLP policies targeting the Microsoft 365 Copilot location."

if (!(Test-MtConnection ExchangeOnline)) {
Add-MtTestResultDetail -SkippedBecause NotConnectedExchange
return $null
} elseif (!(Test-MtConnection SecurityCompliance)) {
Add-MtTestResultDetail -SkippedBecause NotConnectedSecurityCompliance
return $null
} elseif ($null -eq (Get-MtLicenseInformation -Product ExoDlp)) {
Add-MtTestResultDetail -SkippedBecause NotLicensedExoDlp
return $null
}

try {
$policies = Get-MtExo -Request DlpCompliancePolicy
} catch {
if ($_.Exception.Response -and $_.Exception.Response.StatusCode -in @(401, 403)) {
Add-MtTestResultDetail -SkippedBecause NotAuthorized
} else {
Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_
}
return $null
}

# Match policies that target the Microsoft 365 Copilot location.
# Microsoft Purview surfaces Copilot DLP scope through several schema fields depending on
# how the policy was created (UI vs PowerShell) and the tenant's Purview schema version:
# - MicrosoftCopilotLocation : multivalued; 'All' or specific scopes (legacy / preview shape)
# - Workload : may include 'MicrosoftCopilot' / 'Copilot'
# - Locations : newer schema; can include Copilot entries with Workload/Name 'Copilot*'
# - EnforcementPlanes : newer schema; surfaces values such as 'CopilotExperiences'
# We OR all of these so the test does not false-fail tenants whose Copilot DLP policies are
# represented through the newer schema fields.
$copilotPolicies = @($policies | Where-Object {
($_.MicrosoftCopilotLocation -and ($_.MicrosoftCopilotLocation.DisplayName -or $_.MicrosoftCopilotLocation.Count -gt 0)) -or
($_.Workload -match 'Copilot') -or
($_.Locations -and (@($_.Locations) | Where-Object {
($_ -is [string] -and $_ -match 'Copilot') -or
($_.Workload -match 'Copilot') -or
($_.Name -match 'Copilot') -or
($_.DisplayName -match 'Copilot')
})) -or
($_.EnforcementPlanes -and (
($_.EnforcementPlanes -is [System.Collections.IEnumerable] -and -not ($_.EnforcementPlanes -is [string]) -and (@($_.EnforcementPlanes) | Where-Object { "$_" -match 'Copilot' })) -or
("$($_.EnforcementPlanes)" -match 'Copilot')
))
})

$enabledCopilotPolicies = @($copilotPolicies | Where-Object {
$_.Enabled -and -not $_.IsSimulationPolicy
})

$testResult = $enabledCopilotPolicies.Count -ge 1

$portalLink = "https://purview.microsoft.com/datalossprevention/policies"

if ($testResult) {
$testResultMarkdown = "Well done. Your tenant has at least one enabled [Microsoft Purview DLP policy]($portalLink) "
$testResultMarkdown += "targeting the Microsoft 365 Copilot location.`n`n%TestResult%"
} else {
$testResultMarkdown = "Your tenant does not have an enabled [Microsoft Purview DLP policy]($portalLink) "
$testResultMarkdown += "targeting the Microsoft 365 Copilot location.`n`n"
$testResultMarkdown += "> **Risk:** Microsoft 365 Copilot can summarise, paraphrase or expose files containing sensitive "
$testResultMarkdown += "information types or labelled content from any source the requesting user can access, dramatically "
$testResultMarkdown += "amplifying oversharing risk through AI-assisted workflows.`n`n%TestResult%"
}

$passResult = "✅ Pass"
$failResult = "❌ Fail"
$result = "| Name | Status | Mode | Enabled |`n"
$result += "| --- | --- | --- | --- |`n"
if ($copilotPolicies.Count -eq 0) {
$result += "| _No DLP policies target the Microsoft 365 Copilot location_ | $failResult | - | - |`n"
} else {
foreach ($item in ($copilotPolicies | Sort-Object -Property Name)) {
$itemResult = if ($item.Enabled -and -not $item.IsSimulationPolicy) { $passResult } else { $failResult }
$mode = if ($item.IsSimulationPolicy) { "Simulation" } else { $item.Mode }
$result += "| $($item.Name) | $itemResult | $mode | $($item.Enabled) |`n"
}
}

$testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result

Add-MtTestResultDetail -Result $testResultMarkdown
return $testResult
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Ensure a Microsoft Purview Insider Risk Management policy from the **Risky AI usage** template is enabled so risky Microsoft 365 Copilot and AI-app interactions generate triageable alerts.

The Risky AI usage template detects:

- Prompts attempting to **jailbreak** or bypass system prompts.
- Prompts attempting to **extract or summarise sensitive content** that falls under DLP / SIT signals.
- Generation of **harmful, unethical or otherwise risky** AI responses.
- High-volume / anomalous AI-interaction patterns from individual users.

Without a Risky AI usage policy enabled, these signals are silently lost — Insider Risk reviewers will have no triage queue for AI misuse, and DSPM for AI cannot escalate risky users into Insider Risk Management workflows.

The test requires Microsoft 365 E5 or the Insider Risk Management add-on. The test will skip cleanly on tenants that do not license IRM (cmdlet `Get-InsiderRiskPolicy` is unavailable).

The test passes when at least one Insider Risk policy with an AI-related scenario / template is enabled.

#### Remediation action:

1. Open the [Microsoft Purview portal — Insider Risk Management — Policies](https://purview.microsoft.com/insiderriskmgmt/policies).
2. Click **+ Create policy**.
3. Choose the **Risky AI usage** template (under the AI / Generative AI category).
4. Define users in scope (typically all users or a pilot group), reviewers, and any indicator thresholds.
5. **Enable** the policy and confirm: `Get-InsiderRiskPolicy | Where-Object { $_.InsiderRiskScenario -match 'AI' -and $_.Enabled }`.

> **Tip:** Enable Adaptive Protection so that risky AI users automatically pick up stricter DLP / Conditional Access policies once they cross a risk threshold.

#### Related links

- [Microsoft Learn — Insider Risk Management policies](https://learn.microsoft.com/en-us/purview/insider-risk-management-policies)
- [Microsoft Learn — Detect risky use of AI with Insider Risk Management](https://learn.microsoft.com/en-us/purview/insider-risk-management-policy-templates)
- [Microsoft Learn — Microsoft Purview AI Hub & DSPM for AI](https://learn.microsoft.com/en-us/purview/ai-microsoft-purview)

<!--- Results --->
%TestResult%
Loading
Loading