In this post:why every licensed Microsoft 365 tenant should block credentials with Purview DLP, what a single leaked secret can lead to, a step-by-step build in the Purview portal, and a PowerShell script that deploys the same policy in one run.
Credentials end up in places they were never meant to be. A connection string pasted into a Teams chat to unblock a colleague. An API key in a config file uploaded to a project site. A service account password emailed to a contractor. Each copy outlives the conversation that created it - it is indexed, searchable, synced to devices and retained for years.
Secrets are different from most sensitive data. A leaked customer record exposes information; a leaked credential hands over access. Most secrets - storage keys, SAS tokens, client secrets, personal access tokens - are accepted on their own. There is no MFA prompt, no Conditional Access check and often no sign-in log to give the game away.
This policy belongs in every licensed tenant
There is no reason for any organisation whose Microsoft 365 subscription includes credential scanning sensitive information types not to have this policy configured and turned on. It costs nothing extra, takes minutes to deploy and protects every user in every Microsoft 365 workload. The risks in the table below make the case on their own.
Key risks of leaked credentials
Credential leaked
What an attacker gets
Business impact
Severity
User login credentials and passwords
Direct sign-in as the user, and often to other services where the password is reused
Account takeover and further phishing from a trusted mailbox
โ Critical
Azure storage account key or SAS token
Read, write and delete access to the storage account data, with no MFA or Conditional Access check
Data theft, tampering, and deletion of data and backups held in storage
โ Critical
Azure SQL connection string
Direct access to the database
Bulk exfiltration of customer and business data
โ Critical
Client secret or API key
The ability to act as the application, with every permission it has been granted
Tenant-wide data access where the app holds broad Microsoft Graph permissions
โ Critical
X.509 private key or management certificate
The ability to impersonate a service or authenticate as an app
Persistent access that survives user password resets
โ Critical
GitHub personal access token
Source code, pipelines and the secrets stored in them
Supply chain compromise
โ High
Slack, Google and other third-party tokens
Access to partner and SaaS platforms
Exposure that spreads beyond Microsoft 365
โ High
Any of the above
A foothold for privilege escalation and lateral movement
Ransomware, and GDPR notification to the Data Protection Commission within 72 hours where personal data is put at risk
โ Critical
Scenario: a phished mailbox
Picture a common incident. A finance user falls for a phishing email, an attacker steals their session token and gets into their mailbox. The first thing many attackers do with mailbox access is search it - for terms such as password, AccountKey= or connection string.
Now picture what they find: an email from eight months ago in which a developer sent an Azure Storage connection string to unblock a data upload. That single line contains the storage account name and its access key.
The key works on its own. No MFA, no Conditional Access, no sign-in risk policy stands between the attacker and the data.
The blast radius is the whole storage account. Every container, file share, queue and table in it can be read, altered or deleted.
It stays valid until someone rotates it. Resetting the phished user’s password and revoking their sessions does nothing to the key.
It may go unnoticed. Key-based access does not appear in Entra ID sign-in logs, and storage diagnostic logging is often not enabled.
A mailbox compromise that should have been contained to one user becomes a data breach. With this policy enforced, that email is blocked at the moment it is sent, so the key never lands in the mailbox for an attacker to find.
Existing secrets are still there. DLP stops new credentials being shared. It does not remove the ones already sitting in mailboxes, sites and chats - use the Data Security Posture Agent or a content search to find them, then rotate every one.
Why DLP is the right control
Secret scanning in code repositories and threat detection in Defender both matter, but neither stops a person sharing a secret in email or Teams. A Purview DLP policy acts at the moment of sharing, across the Microsoft 365 workloads where most collaboration happens, and applies whether the leak is careless or deliberate.
Blocking is not revocation. If the policy fires, the credential has already been typed or pasted somewhere. Treat every alert as a prompt to rotate the secret, not just as a blocked message.
Credential protection in 2026
Microsoft’s credential scanning sensitive information types (SITs) have been in Purview since 2022 (MC402123, roadmap 88941), with All Credential Types as the bundled detection. What has changed in 2026 is how much further Microsoft is taking credential detection:
Credential scanning in the Data Security Posture Agent (MC1259828). LLM-powered detection of exposed credentials, including Entra ID credentials, private keys and API tokens, across selected data locations, with risk scores and AI-generated insights. Public preview began in late March 2026; general availability is rolling out from early October to early November 2026.
SIT testing in DLP (MC1235742, February 2026). Upload a file of up to 2.5 MB and test it against one SIT or all of them before building a policy - a quick way to confirm your credential samples are detected.
The two work together. The posture agent finds credentials already sitting in your data estate; the DLP policy in this post stops new ones being shared. Roadmap dates move and features roll out in waves, so confirm availability in your own tenant.
The All Credential Types SIT
All Credential Types is a bundled SIT. Rather than matching a single pattern, it fires when any of its underlying credential SITs matches, using checksums or keyword and pattern combinations. Detections include:
Azure storage account access keys and shared access signatures
Azure Service Bus, Event Grid, SignalR and Function keys
Client secrets and API keys
Amazon S3 client secret access keys
GitHub personal access tokens, Google API keys and Slack access tokens
HTTP authorisation headers
X.509 certificate private keys
User login credentials and general passwords (English keywords only)
Prerequisites
Requirement
Detail
Licensing
Microsoft 365 E5, E5 Compliance or E5 Information Protection & Governance - required for credential scanning SITs
Purview role group
Compliance Administrator, Compliance Data Administrator, Information Protection Admin or Security Administrator - or a custom role group with DLP Compliance Management for least privilege
Alert recipients
Users, groups or mail contacts that already exist in the tenant. External addresses need a mail contact first
PowerShell (script only)
Windows PowerShell 5.1 or PowerShell 7, with ExchangeOnlineManagement 3.2.0 or later
Policy design
Setting
Value
Why
Policy name
Block Credentials and Secrets
Clear purpose in alerts and reports
Locations
Exchange, SharePoint, OneDrive, Teams
Where credentials are most often shared
Scope
All users (full directory)
Secrets leak from every team, not just IT
Condition
All Credential Types, Medium confidence, 1+ instances
Broad coverage with a sensible false-positive balance
Action
Block everyone
A secret shared internally is still a leaked secret
Select Create policy, then Enterprise applications & devices. Under Categories choose Custom, select Custom policy and then Next.
Step 3 - Name the policy
Name the policy Block Credentials and Secrets, add a description and select Next. On Assign admin units, leave Full directory and select Next.
Step 4 - Choose the locations
Make sure Exchange email, SharePoint sites, OneDrive accounts and Teams chat and channel messages are ticked. Untick Devices, Instances and On-premises repositories - the portal ticks these by default, and they need extra configuration this policy does not cover.
Step 5 - Add the credential condition
On Define policy settings, choose Create or customize advanced DLP rules and select Next. Select Create rule and name it Block credentials and secrets.
Under Conditions, choose Add condition > Content contains > Add > Sensitive info types, tick All Credential Types and select Add. Leave Medium confidence and an instance count of 1 to Any.
Step 6 - Block everyone
Under Actions, choose Add an action > Restrict access or encrypt the content in Microsoft 365 locations. The default is Block only people outside your organization - change it to Block everyone.
Step 7 - Configure alerts
Under Incident reports, set the severity to High, keep Send an alert to admins when a rule match occurs on, and use Add or remove users to add your security team. If you turn on email incident reports, leave out the matched and original content so the alert never carries the secret.
Step 8 - Turn the policy on
Select Save, then Next. On Policy mode, choose Turn the policy on immediately - the default is simulation mode. Select Next, review the summary and select Submit.
Allow up to an hour for the policy to sync to all four locations before testing.
Automating it with PowerShell
The companion script New-CredentialDlpPolicy.ps1 builds the same policy and rule in one run. It lists the required privileges and licensing before it starts, prompts for the tenant ID, admin UPN and alert recipients, and then:
Confirms the tenant ID exists, and that you signed in to that tenant, before changing anything
Checks your account holds DLP management permissions
Creates the policy and rule idempotently, with -WhatIf support and a confirmation before enforcing
Keeps the credential itself out of incident report emails
New-DlpCompliancePolicy-Name'Block Credentials and Secrets'`-ExchangeLocationAll-SharePointLocationAll`-OneDriveLocationAll-TeamsLocationAll-ModeEnableNew-DlpComplianceRule-Name'Block credentials and secrets'`-Policy'Block Credentials and Secrets'`-ContentContainsSensitiveInformation@{Name='All Credential Types';minCount='1';confidencelevel='Medium'}`-BlockAccess$true-BlockAccessScopeAll`-GenerateAlert'secops@contoso.com'`-GenerateIncidentReport'secops@contoso.com'`-IncidentReportContentTitle,DocumentAuthor,Service,Severity,RulesMatched`-ReportSeverityLevelHigh
Check the policy has synced:
Get-DlpCompliancePolicy-Identity'Block Credentials and Secrets'|Select-ObjectName,Mode,DistributionStatus
Testing and tuning
Use synthetic credentials only. A fake GitHub token or an Azure storage connection string with a made-up key is enough to trigger the rule. Never test with a live secret.
Validate detection first. Use the Test option under Sensitive info types to confirm your samples match before you send them.
Watch the signals. Check Data loss prevention > Alerts and Activity explorer after each test.
Tune General Password if it is noisy. It relies on English password keywords and is the most likely source of false positives. Raise the confidence level or handle it in a separate rule.
Rotate, then investigate. Every true positive means a secret has been exposed. Rotate it, then find out how it got there.
The script
The script is in the GitHub repository with its README. It contains no tenant-specific IDs.
Creates the Block Credentials and Secrets policy and rule, with tenant, permission and licensing checks
Wrapping up
A leaked credential is not a data protection problem waiting to happen - it is access already handed over. If your licensing includes credential scanning SITs, this policy should be on. It takes minutes in the portal or one run of the script, it covers every user across Exchange, SharePoint, OneDrive and Teams, and it turns the next pasted connection string from a breach into a blocked message and an alert.
Series guide:seven parts on securing the AI agents in your organisation, from Microsoft Defender for Endpoint on the device, to governance in Microsoft Agent 365, to classifying the data they can reach with Microsoft Purview: what each part covers, and where to start.
AI agents such as Claude Code, GitHub Copilot and Gemini CLI are already on managed endpoints, running with the full rights of the signed-in user and connecting to MCP servers that nobody has reviewed. This series works through what Microsoft Defender for Endpoint can do about that today: detecting prompt injection, finding the agents, and controlling and automatically blocking the servers they talk to. It then moves up a level to Microsoft Agent 365, where you decide which agents exist, who owns them and what they can reach, and finally to Microsoft Purview, where classification decides what data is allowed through once a destination is approved.
Every part is built and tested in a lab or dev tenant, with screenshots of what actually happened, including where the product or the documentation did not behave as expected.
The starting point: what AI agent runtime protection is, which agents it covers and how it detects prompt injection against agents running locally on your endpoints. If you are new to the topic, start here.
The lab build and the validation work behind it. It shows the protection working end to end, and records where the published documentation did not match what the tenant actually served, so you do not lose the same time I did.
Visibility before control. The Local agents view in the Defender portal, why discovery is activity based rather than installation based, the licensing split between the inventory and the risk assessment, and a set of advanced hunting queries that go well beyond what the portal shows.
Runtime protection does not decide which MCP servers an agent may reach. Part 4 closes that gap for a known server: a Defender for Endpoint custom domain indicator, enforced by network protection, blocks Claude Code from an unsanctioned remote MCP server. It covers why the indicator must be the bare domain, why Edge enforces it before network protection does, and a custom detection rule to alert on the blocks.
An indicator only blocks the domains you list. Part 5 automates it: an Azure Automation runbook with a managed identity runs every hour, finds every new remote endpoint an AI agent has used, and blocks it with a Defender indicator and an alert. Approved servers are permitted with an Allow indicator. It is tested with Claude Code, GitHub Copilot CLI and Gemini CLI (an agent runtime protection does not support), and the scripts are on GitHub.
Parts 1 to 5 act on the device, once an agent is already running. Part 6 moves up to governance in Microsoft Agent 365, where every agent in the tenant is inventoried and approved before people can use it. A dev tenant with 318 agents had only 84 visible to Entra, so most were out of reach of Conditional Access. The post works through four gates: who can build, Agent 365 approval, owners, sponsors and access packages, and Conditional Access and ID Protection for agents. It also covers the Workload ID Premium gap that E7 does not include, and a companion script on GitHub that reviews the tenant and creates the policies in report-only mode.
Parts 1 to 6 decide where data is allowed to go. Part 7 answers the question that has to come first: what is the data? Once a destination is approved, whether an MCP server, Copilot or an AI app, only a control that recognises the content can stop the wrong data reaching it. The post covers every Purview classification method – sensitivity labels, custom SITs, exact data match, document fingerprinting and trainable classifiers – and the types of DLP that act on them, from Endpoint DLP to inline web traffic and the Copilot location. It walks through how proximity and confidence levels keep a custom SIT from flooding the alert queue, where GSA prompt injection protection fits and where it does not, finding IP already at rest with eDiscovery, and the Triage Agent in DLP. It is the foundation the other six parts stand on.
Where to start
New to the topic: read part 1, then part 3 to see what is already running in your estate.
Need to control MCP servers now:part 4 for a single server, then part 5 to block every new one automatically.
Need to know which agents exist and who approved them:part 6.
Need to stop sensitive data reaching agents and AI apps you have approved:part 7, the foundation every other control relies on.
More to come
The series is not finished. Microsoft is moving quickly on AI agent security in the Defender portal, and the next parts will follow the new capabilities as they arrive, including the new Sentinel workbook and automation features in the Defender portal. Each one is built and tested in a lab before it is written up.
Follow along on cloud-secure.ai or connect with me on LinkedIn to hear when the next part is published.
Disclaimer: The views in this series are my own. They do not represent my employer, or Microsoft or any other vendor whose products are discussed here.
Series:Part 1 introduced AI agent runtime protection in Microsoft Defender for Endpoint. Part 2 covered the lab build and validation. Part 3 found the agents running on your endpoints. Part 4 blocked an unsanctioned MCP server with a Defender indicator. Part 5 automates it, so every new MCP server is blocked until you approve it.
In part 4 a Defender for Endpoint custom domain indicator, enforced by network protection, stopped Claude Code reaching an unsanctioned remote MCP server. It worked, but it only blocks the domains you list. A developer who adds a new MCP server on a different domain connects to it without anyone noticing.
This part closes that gap with automation. An Azure Automation runbook runs every hour, finds every new remote endpoint an AI agent has used, and blocks it with a Defender indicator and an alert. Approved servers are permitted with an Allow indicator. There is no Sentinel dependency, no app registration and no secret to rotate – just a managed identity, two API permissions and three PowerShell scripts.
The same approach covers any agent, not just the ten or so that runtime protection supports. This post tests it with Claude Code, GitHub Copilot CLI and Gemini CLI, which runtime protection does not support.
Why MCP servers need controlling
A remote MCP server is a third-party service that an AI agent sends data to and takes instructions from. Developers add them in minutes, usually without security, procurement or data protection review – and each one becomes a new processor of your organisation’s data.
Key risks of sending corporate data to unapproved MCP servers
Risk
What could happen
Risk level
Data exfiltration
Prompts, documents, credentials, customer information, source code or other sensitive data could be transmitted to the MCP operator.
โ Critical
Loss of data governance
Once information is sent outside approved Microsoft 365 and Azure controls, your organisation may lose control over where it is stored, processed or subsequently transferred.
โ Critical
Unknown data retention
The MCP provider may log requests, responses, prompts or tool parameters indefinitely, or use different retention policies from your organisation.
โ High
Credential and token exposure
OAuth tokens, API keys, connection strings or other authentication material could inadvertently be exposed to a malicious or poorly designed MCP server.
โ Critical
Excessive permissions
An MCP server may be granted access to files, databases, Git repositories, email, APIs or cloud services beyond what the user actually needs.
โ Critical
Malicious tool execution
A compromised or malicious MCP server could expose tools designed to manipulate an agent into performing unintended actions.
โ Critical
Prompt and tool injection
Content returned by an MCP server can influence an AI agent into invoking additional tools or disclosing information it would not normally provide.
โ High
Supply-chain compromise
A legitimate MCP server, its dependencies or its hosting environment could subsequently become compromised.
โ High
Data residency
Data may be processed or logged in regions that do not meet organisational, customer or regulatory residency requirements.
โ High
Compliance exposure
Sending personal, regulated or customer data to an unapproved service could breach GDPR, contractual obligations or internal security policies.
โ High
Lack of auditability
Security teams may have little or no visibility of what users sent to the MCP server or what the server subsequently did with it.
โ High
Shadow AI and shadow IT
Users can introduce new third-party processors and integrations without security, procurement or data protection review.
โ High
Controlling which MCP servers agents can reach addresses these risks at the source, and brings two further benefits:
Visibility. Every block alert is an inventory entry. You learn which servers your DevOps teams actually use, rather than guessing.
Any agent. Runtime protection covers a fixed list of agents. Network indicators apply to every process on every onboarded device, so new and unsupported agents are covered too.
How it works
An agent connects to a remote MCP server. Defender for Endpoint records the connection in DeviceNetworkEvents.
Every hour, the Automation account’s managed identity runs an advanced hunting query through Microsoft Graph runHuntingQuery for domains that AI agent processes reached.
The runbook drops the agents’ own service domains (a never-block list), any domain with an Allow indicator and any domain that already has an indicator.
Every domain left gets a Block domain indicator with alert generation, created through the Defender for Endpoint indicators API.
Network protection enforces the block on every onboarded device, and each blocked connection raises an alert.
An administrator approves trusted servers by changing the indicator’s action to Allow. Later runs report them as sanctioned and leave them alone.
The default is deny: anything new is blocked until someone approves it. An IndicatorAction parameter switches the runbook to Audit for a tuning period.
What advanced hunting showed first
Before building anything, I ran the runbook’s hunting logic over 30 days of lab data. It returned 63 domains contacted by agent processes. Only one was a remote MCP server reached directly from the device – mcp.context7.com, already blocked in part 4. The rest taught three lessons that shaped the runbook:
claude.exe is two products. Claude Code, the CLI, logs with a plain claude.exe command line. The Claude desktop app is an Electron app, and its traffic comes from helper processes with --type=utility on the command line – hCaptcha, Datadog, Sentry and claudeusercontent.com. The runbook excludes those helper processes, so it assesses Claude Code only. The desktop app’s remote connectors only ever appeared as mcp-proxy.anthropic.com, so the MCP server’s own domain is not visible to the device.
VS Code hides the extension. All GitHub Copilot extension traffic leaves through VS Code’s shared network service process. Hunting can attribute a connection to code.exe, never to Copilot, so the whole editor is treated as the agent and its own service domains go on the never-block list.
Telemetry is noise. Agent and editor telemetry endpoints (datadoghq.com, sentry.io, exp-tas.com) would otherwise be blocked on the first run. They are on the never-block list.
With those changes, replaying the 30 days of data through the runbook produced no new blocks – the right baseline before adding new MCP servers.
Prerequisites
Requirement
Detail
Defender for Endpoint
Devices onboarded, network protection in block mode and custom network indicators enabled (part 4)
Azure subscription
To host the Automation account
Entra role
Global Administrator or Privileged Role Administrator, to grant the API permissions
Azure role
Automation Contributor or Contributor on the Automation account
PowerShell 7
With the Microsoft Graph and Az modules, on the administrator’s workstation
Step 1 – Create the Automation account
In the Azure portal, search for Automation Accounts and select Create. On Basics, create a resource group such as rg-mcp-automation, name the account aa-mcp-indicators and pick your region.
On Advanced, leave System assigned ticked. This managed identity is what the runbook authenticates as, so there are no credentials to store.
On Networking, choose Public access. The setting only controls inbound access to the account’s own endpoints, such as webhooks and Hybrid Runbook Workers. The runbook’s outbound calls to Graph and the Defender API work either way. Choose private access if your policy requires Private Link on every PaaS resource.
Select Review + Create > Create.
Step 2 – Confirm the managed identity
Open the account and go to Account Settings > Identity > System assigned. Status should be On. The runbook never needs the object ID – Azure Automation provides the identity’s token endpoint inside every job.
Step 3 – Grant the API permissions
The managed identity needs two application permissions:
API
Permission
Used for
Microsoft Graph
ThreatHunting.Read.All
Advanced hunting through runHuntingQuery
WindowsDefenderATP
Ti.ReadWrite.All
Reading and creating indicators. The .All permission lets the runbook see indicators created by people as well as its own.
Hunting goes through Microsoft Graph because Microsoft is retiring the Defender for Endpoint and Defender XDR advanced hunting APIs on 1 February 2027 (MC1220762). Indicators stay on the Defender for Endpoint API.
App roles cannot be assigned to a managed identity in the portal, so the first script does it. Run it from PowerShell 7 and enter the tenant and the Automation account name when prompted:
.\Grant-McpRunbookPermissions.ps1
The script finds the identity by name and contains no tenant-specific IDs. It is safe to re-run – roles already assigned are skipped. New role assignments can take 15-30 minutes to appear in the identity’s tokens.
Step 4 – Create the runbook
Go to Process Automation > Runbooks > Create. Name it MCP-ServerAutoBlock with runbook type PowerShell.
The classic runbook experience offers PowerShell 7.2 at most. Support for PowerShell 7.1 and 7.2 in Azure Automation has ended, so the runbook should move to a newer runtime. Newer versions are only available through Runtime environments. On the account Overview, select Try Runtime Environment Experience.
Go to Process Automation > Runtime Environments > Create. The system-generated environments stop at 7.2.
Name the new environment PowerShell-76, language PowerShell, runtime version 7.6, and leave the default packages. The runbook needs no extra modules. In my tenant 7.6 reported itself as a preview (7.6.0-preview.5). Use 7.4 where it is offered and you need a generally available runtime – the runbook runs unchanged on 7.2, 7.4 and 7.6.
Open the runbook and select Edit > Edit in portal.
Set Runtime Environment to PowerShell-76, paste the contents of MCP-ServerAutoBlock.ps1 and select Save. Do not publish yet.
What the runbook runs
The core of the runbook is a single advanced hunting query. The agent process list and node.exe markers come from Automation variables, with lab-tested defaults:
Agents that run under node.exe, such as Gemini CLI, are matched on their command line. The runbook then compares the results with the never-block list and the tenant’s existing domain indicators before creating anything.
Parameter
Default
Purpose
DryRun
False
Report what would be created without creating anything
LookbackHours
2
Overlaps the hourly schedule so nothing is missed
MaxNewIndicators
20
Safety cap per run
IndicatorAction
Block
Audit logs and alerts only, for tuning
Step 5 – Dry run in the test pane
Select Test pane, set dryRun to True and select Start. The pre-flight checks decode each token and confirm both app roles are present before the runbook does anything else.
With no new agent activity in the last two hours, there is nothing to block – and nothing was created. When new MCP servers appear, they show as Would create (Block) in a dry run.
Step 6 – Publish and schedule
Select Publish > Yes. Scheduled runs always use the published version. Then run the third script, which needs no parameters:
.\New-McpRunbookSchedule.ps1
Enter the tenant ID or domain when prompted, for example contoso.onmicrosoft.com.
Sign in to Azure.
If your account can see more than one subscription, type the number shown next to the subscription that holds the Automation account – for example 1 or 10 – and press Enter.
If more than one Automation account has the runbook, type the number of the right one.
The script creates an hourly schedule starting at the next full hour and links it to the runbook with DryRun set to False, LookbackHours to 2 and IndicatorAction to Block. One hour is the most frequent a schedule can run in Azure Automation.
Permitting an approved MCP server
Approval lives in Defender, next to the blocks. In the Defender portal, go to Settings > Endpoints > Indicators > URLs/Domains:
Already blocked – open the AI agent endpoint (auto, Block): โฆ indicator, change Action to Allow and save.
Approving in advance – select Add item, enter the domain, set Action to Allow and record the owner or change reference in the title.
Every later run reports the domain as Sanctioned (Allow). Two rules apply:
Change the action, never delete the indicator. If a Block indicator is deleted, the next run sees the domain again and blocks it again.
Use the bare domain. As part 4 showed, network protection only sees the domain name for non-browser processes, so a URL-style indicator never matches.
Network protection can take time to pick up indicator changes on a device – in part 4 the device needed a restart. Plan approvals with that delay in mind.
Defender for Cloud Apps is not the right place for this list. It sanctions apps from its catalogue, and most MCP endpoints are not in the catalogue, so they are never discovered as apps and cannot be sanctioned there.
Seeing it work with three agents
To prove the “any agent” claim, I gave three agents a new remote MCP server each, on domains with no indicator. Only Claude Code is on runtime protection’s supported list – Gemini CLI is not supported at all.
Agent
MCP server
Process Defender sees
Claude Code
https://mcp.deepwiki.com/mcp
claude.exe
Gemini CLI
https://gitmcp.io/docs
node.exe (command line contains gemini)
GitHub Copilot CLI
https://huggingface.co/mcp
copilot.exe
Claude Code
Add the server for the user, so it is available in every folder:
The list shows both sides of the control in one screen. Context7 fails with EPROTO: The TLS handshake failed – that is network protection enforcing the part 4 indicator. DeepWiki, which nobody has reviewed, connects.
A note on TLS inspection. In my lab the installs first failed: the Node.js download returned 403 and npm reported SELF_SIGNED_CERT_IN_CHAIN. Both were caused by Entra Internet Access (Global Secure Access) web filtering and TLS inspection on the same device. If you run Global Secure Access, add exclusions for nodejs.org and registry.npmjs.org, or set NODE_USE_SYSTEM_CA=1 so Node.js trusts the Windows certificate store. For this test I paused the client, so the only control in the path was Defender for Endpoint.
Gemini CLI signs in with a Gemini API key from Google AI Studio – its Sign in with Google option is no longer offered for individual accounts. Add GitMCP to %USERPROFILE%\.gemini\settings.json:
Note the built-in github-mcp-server. Copilot CLI ships with GitHub’s own MCP server already configured, which matters for the shared-domain point below.
What Defender records
A small lab helper, Invoke-McpAgentTest.ps1, runs each agent non-interactively with a prompt that uses its MCP tool. Each agent connects to its MCP servers as it starts, so the connection is logged even when the tool call itself fails. Minutes later, all three appear in advanced hunting:
Gemini CLI is node.exe. Only the command line identifies it, which is why the runbook matches node.exe agents on command-line markers.
Copilot CLI is copilot.exe, even when installed from npm – the package runs a native binary.
Each server appears twice, once as a bare domain and once as an https:// URL. The runbook reduces both to the bare domain, so each server gets one indicator. The pwsh.exe rows are the test helper’s own reachability check – PowerShell is not in the agent list, so the runbook ignores them.
What the user sees when a server is blocked
When an agent reaches a blocked domain, network protection drops the connection and Windows shows a notification. This is Context7, blocked by the part 4 indicator, being reached when Claude Code started:
Claude Code connects to every configured MCP server at start-up, so the block – and its alert in the Defender portal – happened before anyone asked it a question. DeepWiki, GitMCP and Hugging Face are now in advanced hunting and match none of the never-block entries, so they are exactly what the runbook’s next hourly run picks up: each gets a Block indicator and an alert, and from then on each agent sees this same notification until an administrator approves the server.
GitHub Copilot and shared domains
GitHub hosts its remote MCP server at api.githubcopilot.com/mcp/, and Copilot CLI includes it as a built-in server. Network protection cannot see the /mcp/ path, so a block on api.githubcopilot.com affects everything on that host. In the lab, Copilot Free traffic from VS Code used api.individual.githubcopilot.com for the Copilot service itself, a different host – but Business and Enterprise plans use other hosts, so do not assume the same split.
For that reason githubcopilot.com is on the never-block list, and the runbook does not block GitHub’s own MCP server. Treat it as approved by default. If you want to block it, test with a manual Block indicator on api.githubcopilot.com first and confirm Copilot itself still works on your plan.
Limits to know
Detect, then block. A brand-new server is reachable until it appears in advanced hunting and the next run blocks it – typically within one to two hours. A true pre-emptive allow-list belongs at the network edge, in a proxy or egress firewall.
Domain only. The runbook cannot tell an MCP server from any other site an agent reaches. A site Claude Code fetches during a web search can be blocked too. Keep the never-block list tuned, review the alerts, and start on Audit if that risk is too high.
Shared hosts. Where an MCP server shares a host with the agent’s own service, blocking one blocks both.
Electron hosts. VS Code traffic is attributed to code.exe, not to the extension, and the Claude desktop app’s remote connectors are not visible on the device.
Indicator alerts. As part 4 found, the indicator’s own alert ignores the severity set on it. Use the part 4 custom detection rule for tailored alerting.
The scripts
The scripts are in the Defender-mcp-server-autoblock repository on GitHub, each with its own README. None contains tenant-specific IDs.
Makes Claude Code, Gemini CLI and Copilot CLI call their MCP servers, before and after the block
Wrapping up
Part 4 showed that one indicator can stop an agent reaching one MCP server. Part 5 turns that into a standing control: every new remote endpoint an AI agent uses is found within the hour, blocked by default and raised as an alert, and approval is a single change in the Defender portal. It works for any agent you can name, with nothing more than a managed identity, two API permissions and a scheduled runbook.
Series:Part 1 introduced AI agent runtime protection in Microsoft Defender for Endpoint. Part 2 covered the lab build and validation. Part 3 found the agents running on your endpoints. Part 4 closes a gap the first three exposed: controlling where Claude can connect.
Microsoftโs AI agent runtime protection supports around ten local agents. Defender discovers more than fifty. And none of that decides which remote MCP servers an agent is allowed to reach. This post closes that gap for Claude, with a control most organisations already own - and because the control works at network level rather than inside the agent, the same technique applies to any agent a developer installs.
Every remote MCP server an agent uses is an outbound connection from a developer laptop to a service nobody reviewed. Part 3 query 7 lists them. A Defender for Endpoint custom URL/domain indicator, enforced by network protection, blocks that connection at network level. It does not care which process is asking - Claude here, GitHub Copilot in a later part. A custom detection rule, running every hour, then raises the alert you want and can respond automatically.
At a glance
What it does
What it does not do
Blocks any process, including any AI agent, from reaching an unsanctioned remote MCP server
Inspect prompts or stop prompt injection - that remains runtime protectionโs job
Records the block on the device, in advanced hunting and in the incident queue
Control local (stdio) MCP servers, which make no network connection
Raises a meaningful alert every hour, with automated response actions, through a custom detection rule
Separate an MCP server from another service on the same domain - it blocks at domain level
What I learned the hard way
Test in Edge and you prove nothing. Edge enforced the indicator hours before network protection did. Every agent kept connecting until the device restarted.
Use the bare domain. For HTTPS from anything other than Edge, network protection only sees the domain. A URL with a scheme or path matches in Edge and never for an agent.
The agent will not tell the user. Claude reported a TLS handshake error and blamed the URL, a proxy or a firewall.
The indicatorโs alert follows its own rules. It ignored the severity and category I set - which is why the detection rule matters.
The control depends only on stable, generally available Defender for Endpoint features: indicators, network protection and the DeviceEvents table. The moving parts are delivery timing, alert behaviour, and the AgentsInfo and exposure graph schemas, which differ from their published documentation and change during preview. The detection logic in this post avoids those tables for that reason.
1. Prerequisites
Two settings decide whether this works at all. Microsoft Edge enforces indicators through SmartScreen, but every other process - including every AI agent - relies on network protection. If network protection is off or in audit mode, your indicator will block the browser and nothing else.
Set network protection to block mode. I used the Defender Antivirus policy in Intune; on the device, EnableNetworkProtection should return 1:
Then turn on custom network indicators under Settings > Endpoints > General > Optional features. Without it, indicators save but network protection never enforces them.
2. The test: an agent with a remote MCP server
I used Claude Code because it was already in the lab, but the agent is incidental - the indicator does not care which process makes the connection. For the MCP server I chose Context7, a public documentation server on its own dedicated domain, so blocking it breaks nothing else.
Inside Claude Code, /mcp confirms the server is connected. Both of its tools are marked open-world: they reach outside the machine, which is exactly the traffic an indicator can act on.
Before blocking anything, confirm the endpoint is reachable from a non-browser process. A JSON reply - even an error - means the connection got through.
Keep this test to hand. Once the indicator is in place (section 3), the loop below repeats it every 10 minutes and stops the moment the connection is blocked - a server reply of any kind, even an error, counts as not blocked yet. It also reports how long the block took to arrive.
$start=Get-Datedo{try{Invoke-WebRequesthttps://mcp.context7.com/mcp -UseBasicParsing -ErrorAction Stop | Out-NullWrite-Host"$(Get-Date -Format HH:mm) - reached server, not blocked yet"$blocked=$false}catch{if($_.Exception.Response){Write-Host"$(Get-Date -Format HH:mm) - server replied ($($_.Exception.Response.StatusCode)), not blocked yet"$blocked=$false}else{Write-Host"$(Get-Date -Format HH:mm) - BLOCKED: $($_.Exception.Message)"-ForegroundColorGreen$blocked=$true}}if(-not$blocked){Start-Sleep-Seconds600}}until($blocked)Write-Host"Time to block: $([math]::Round(((Get-Date) - $start).TotalMinutes)) minutes"
Do not expect it to be quick. Microsoft documents up to 48 hours for a new URL or domain indicator to reach a device, with most taking effect in under two. In my lab, Edge enforced the indicator within about four hours, but network protection did not block PowerShell or Claude until I restarted the device, roughly twelve hours after creating it. Section 4 covers why. Leave the window open, and keep the device awake so the loop is not interrupted.
3. Creating the custom indicator
Go to Settings > Endpoints > Rules > Indicators, open the URLs/Domains tab and select Add item.
Indicator
Enter the bare domain: mcp.context7.com. For HTTPS from anything other than Edge, network protection only sees the domain name in the TLS handshake - never the scheme or the path. Enter a full URL and it will match in Edge and silently fail for every agent. The portal confirms the type as Domain, which is what you want to see.
Action: the possible controls
This page is where the indicator becomes a control.
Action
What it does
Allow
Never block the domain, overriding other policies. This is how you sanction an MCP server.
Audit
Allow the connection and record it. The right starting point in production.
Warn
Show a warning the user can bypass. Designed for browsers; for agents with no browser interface, Block is the predictable choice.
Block execution
Stop the connection. Used in this test.
Alert details and scope
Tick Generate alert so the SOC sees the block, and scope the indicator to a device group - start with the group built from the Part 3 discovery queries. Section 6 shows what Defender actually did with these alert settings.
Save, and the indicator appears in the list with its action, severity and scope.
4. Proving the block - and the trap
This is the section I would have wanted before starting. A few hours after creating the indicator, I browsed to the domain in Edge and got the โblocked by your organizationโ page. Advanced hunting agreed: a SmartScreen warning with Experience CustomBlockList.
It looked finished. It was not. At the same moment, PowerShell, curl and Chrome on the same device were still reaching the server. Edge checks indicators against the cloud on every visit; network protection works from a list downloaded to the device, and that list had not picked up the new indicator. Seven hours later it still had not.
A restart fixed it. Straight after, PowerShell failed with a TLS handshake error - which is what a network protection block looks like - while httpbin.org, a control indicator added minutes earlier, was still reachable.
The device event log confirms it was Defender and not a coincidence. Event 1126 is a network protection block, and it names the process and the destination.
The lesson: if an indicator works in Edge and not for an agent, restart the device before you start troubleshooting. And never use a browser to prove an indicator protects anything else.
5. Blocking the agent
With the block active, I started a fresh Claude Code session. The MCP server now shows as failed.
Asking Claude to use the tool produced the most telling screenshot of the test. Claude reported a TLS handshake error and suggested a wrong URL, a proxy or a TLS-inspecting firewall. Nothing told the user the connection had been blocked by policy. A developer would reasonably spend time fixing their MCP configuration, or raise a ticket about a broken tool. Brief your service desk on what this error means before you deploy.
It also respected the instruction not to fall back to web search, so it did not simply route around the block. When I asked it to check the Defender event log, Claude identified the block itself, declined to work around it, and told the user exactly where in the portal to request an Allow indicator. Helpful - and a preview of the precise, well-informed unblock requests your team will start receiving.
Advanced hunting shows the agent and PowerShell side by side, with ResponseCategory set to CustomBlockList - proof the block came from the indicator rather than web content filtering or Defender for Cloud Apps.
The block raised an alert, correlated into an incident, with the device, the user and the blocking process as evidence. The alert story shows the detail an analyst needs.
What it did not do was honour the indicatorโs alert settings. It arrived as โConnection to a custom network indicatorโ, severity Informational, category Command and Control, with Microsoftโs generic recommended actions. Neither the Medium severity, the Suspicious activity category nor my recommended actions appeared. If your SOC triages by severity, these alerts will sit at the bottom of the queue. That is the job of the next section.
7. Running it every hour: the custom detection rule
The indicator blocks; the detection rule decides what happens next. A custom detection rule runs an advanced hunting query on a schedule, raises an alert with the title and severity you choose, and can take automated response actions. It also catches blocks from any source - your indicators, Defender for Cloud Apps or web content filtering - as long as the blocked process is an agent.
The query
A detection rule needs Timestamp, ReportId and an impacted asset such as DeviceId in its results. This query returns all three from a single, generally available table. Add the process names of the agents in your estate to the list.
Run it in Hunting > Advanced hunting and confirm it returns rows before you build anything on it. Then select Create detection rule.
General: name and frequency
Give the rule a name your analysts will recognise, then choose the frequency. The options are Continuous (NRT), every hour, every 3, 12 or 24 hours, or a custom interval. Defender recommends NRT for this query because it reads a single table with no joins.
I chose every hour. It gives a predictable cadence for a policy-violation alert, and the 4-hour lookback means a missed run does not lose events. Use NRT if you intend to attach a disruptive action and want it to fire within minutes.
Further down the same page, set the severity - Medium for a policy block - tick the Command and Control tactic, and add recommended actions for the analyst. Unlike the indicator, the rule honours all three.
Alert settings
This is where the rule beats the indicatorโs alert. The title and description accept column values in double braces, so every alert names the agent, the device and the endpoint: AI agent {{InitiatingProcessFileName}} blocked from {{RemoteUrl}}.
Entity mapping was filled in automatically from the query: the device from DeviceId, the user from the account SID, and the URL as related evidence. These decide how alerts correlate into incidents.
Check the mapping before moving on. If the Device entity points at the wrong column, the alert has no impacted device - and any automated action has nothing to run against.
Incident settings
Leave Include in correlation selected. The ruleโs alerts then join the same incident as the indicatorโs alert for that device and user, so analysts see the whole story in one place.
Automated actions: the available controls
These run automatically on the entities in each alert. What is available depends on the columns your query returns.
Action
What it does
Isolate device
Cuts the device off the network except for Defender. The strongest containment, and the most disruptive.
Collect investigation package
Gathers forensic data from the device without interrupting the user. A sensible default for this rule.
Run antivirus scan
Starts a scan on the device.
Restrict app execution
Allows only Microsoft-signed software to run. That stops a third-party agent completely - and almost everything else the developer uses.
Files: Allow/Block, Quarantine
Unavailable here, because the query returns no file hash.
Users: Mark user as compromised
Unavailable with the SID mapping used here.
There is no action to block a URL. That is why the indicator does the blocking and the rule handles the response. Start with Collect investigation package, and keep isolation or app restriction for rules you trust completely.
Scope, review and create
Scope the rule to the same device group as the indicator, then review. On creation the rule queries the last 30 days once, so expect alerts for historic blocks straight away.
8. Taking this to production
Build the list first. Run Part 3 query 7 to find the remote MCP servers already in use, and decide which are sanctioned.
Allow before you block. Create Allow indicators for sanctioned servers, then Audit for the rest, then Block once you trust what Audit tells you.
Deploy the detection rule alongside. Alert-only to begin with; add automated actions once the alerts are clean.
Brief the service desk. A blocked agent reports a TLS or connection error, not a policy block. Give them the error and the event ID.
Expect unblock requests. Agents will tell users how to ask for an Allow indicator. Have an approval route ready.
Allow for delay. Changes to indicators can take hours to reach network protection, in both directions. An agent can stay blocked after you approve it.
Closing
Runtime protection inspects what an agent reads, for the agents Microsoft supports. Discovery tells you which agents exist. Neither decides where an agent is allowed to connect. A custom indicator does - for Claude in this test, and for any agent that connects over the network - and an hourly detection rule turns each block into an alert with a response attached.
Part 4 is manual by design: someone has to spot a new MCP server and create the indicator. In the next part, an Azure Automation runbook finds new MCP endpoints every hour and creates the indicators for you - with no Sentinel dependency. After that, I run the same control against GitHub Copilot.
People paste customer lists, contract wording and project codes into ChatGPT and other AI apps every day, often from browsers IT does not control. This post shows how to stop that at the network, before the data leaves: Microsoft Entra Internet Access routes browser traffic through Microsoft’s cloud and decrypts it, then hands the content to Microsoft Purview, which blocks anything matching a sensitive information type. It works in any browser and needs no browser extension.
This post records a working deployment, tested in October 2026. File uploads were blocked on all six applications tested, and typed or pasted prompts on five of six - the exception is Claude, which sends prompts in a binary format Purview can’t read (see Coverage findings).
Every step is shown with screenshots so it can be followed and checked in the portal, and the repeatable parts are scripted - see Scripts. If you are new to Global Secure Access, read the key terms below first.
Key terms
Global Secure Access (GSA) - Microsoft’s Security Service Edge (SSE): a cloud service that device traffic is routed through so it can be inspected and controlled. Entra Internet Access is the part that handles internet and SaaS traffic.
Global Secure Access client - the agent on the device that sends its traffic to Global Secure Access.
TLS inspection - decrypting HTTPS traffic inside Global Secure Access so its content can be read. Devices must trust the inspection certificate, or every inspected site shows a certificate error.
Microsoft Purview DLP - data loss prevention policies that detect sensitive data and decide whether to allow or block it.
Sensitive information type (SIT) - the pattern Purview looks for, such as a regular expression plus supporting keywords.
Scan with Purview - the Global Secure Access content policy action that hands decrypted content to Purview for a verdict.
Adaptive app scope - a Purview-managed group of applications, such as All unmanaged AI apps, built from the Defender for Cloud Apps catalogue.
Enforcement plane - where a DLP policy acts: the browser plane (inside Edge for Business) or the network plane (Global Secure Access, used here).
QUIC - the UDP-based protocol behind HTTP/3. Global Secure Access can’t inspect it, so it must be turned off in browsers.
Purpose and scope
This post sets out how to deploy Microsoft Purview inline DLP over Microsoft Entra Internet Access, and how to verify that it actually protects a given generative AI application. Specifically, it covers the Network and non-Microsoft secure browsers option on the DLP policy’s Choose where to enforce the policy page, with Edge for Business left off (see Architecture).
It is written for engineers and architects deploying the control in their own tenant or in customer tenants. It assumes working knowledge of Entra ID, Conditional Access, Intune and Purview; the key terms above cover the rest.
The scope is the network enforcement plane: sensitive data leaving the organisation through unmanaged cloud and generative AI applications, inspected at the network layer rather than in the browser. Prompt injection protection, which inspects content going to the model rather than data leaving the organisation, is a separate Global Secure Access feature with its own prompt policies and is out of scope.
Coverage is not uniform across applications. The Coverage findings section sets out tested results, and Known limitations and residual risk sets out what remains; both should be read before the control is presented to a customer as complete.
Architecture
In the Purview DLP policy wizard, the Choose where to enforce the policy page offers two options. This post is about the second one.
Edge for Business - inline protection built into Microsoft Edge for Business on managed work devices, for a published list of supported apps. It is also a pay-as-you-go capability, and to be effective Edge must be the default browser with third-party browsers blocked; otherwise a user can simply switch browser.
Network and non-Microsoft secure browsers - inspection in the network path through Global Secure Access, using Scan with Purview. It works in Chrome, Firefox and any other browser, as well as Edge, and covers any application in the Defender for Cloud Apps catalogue.
This post configures Network and non-Microsoft secure browsers on its own, with Edge for Business left off. It is aimed primarily at browsers other than Edge: organisations that can’t or won’t standardise on Edge and block every other browser still get the same protection, whichever browser people use.
Global Secure Access acquires the traffic, terminates TLS, and passes the decrypted payload to Purview for classification. Purview returns a verdict and Global Secure Access enforces it. Because enforcement happens at the network layer, a block is returned as an HTTP 403 and the application renders whatever error it chooses.
How Scan with Purview works
Preview. At the time of writing (October 2026), Scan with Purview - the Purview DLP integration with Entra Internet Access - is still in preview. Microsoft’s message centre post MC1181769 schedules general availability to roll out between the end of September and the end of October 2026, so confirm the status in the tenant before presenting the control to a customer as generally available.
Scan with Purview is the link between the two services. Global Secure Access carries and decrypts the traffic; Purview decides whether the content may leave. Neither does the other’s job: Global Secure Access doesn’t classify content, and Purview can’t see network traffic unless Global Secure Access hands it over.
Acquire. The Global Secure Access client sends the device’s traffic through a dedicated tunnel to Global Secure Access, Microsoft’s Security Service Edge. Conditional Access decides which security profile applies to the user, and continuous access evaluation (CAE) keeps that decision current during the session.
Decrypt. TLS inspection terminates the connection so the request body - a prompt or an uploaded file - can be read. Without TLS inspection there is nothing to scan.
Hand over. A content policy rule with the action Scan with Purview passes the decrypted content to Purview. With Use destinations and activities defined in Purview selected, the Purview adaptive app scope decides which applications are in scope.
Classify and decide. Purview checks the content against the sensitive information types in the inline web traffic DLP policy for that user, application and activity, and returns a verdict - allow, audit or block.
Enforce. Global Secure Access applies the verdict. An allowed request continues through the outbound tunnel to the website or cloud application; a blocked one gets an HTTP 403, and the application shows its own error.
Record. The request appears in the Global Secure Access traffic logs, and a match appears in Purview Activity explorer and as a DLP alert - the three logs used in Validation.
What Purview can read. Text sent as JSON, plain text, HTML or XML, and supported file formats, up to 3 MB for both text and files through Scan with Purview. Content Purview can’t parse, such as a prompt sent as binary Protocol Buffers, passes without a verdict. That is why Claude’s file uploads are blocked but its typed and pasted prompts are not (see Coverage findings).
Fail-open. If Purview can’t complete an inspection - the content is too large, for example - the traffic is allowed. These transactions can be found in the traffic logs using the PurviewStatus field.
Requirement specific to this path. The Azure subscription behind Scan with Purview must have the Microsoft.Purview resource provider registered. Otherwise traffic reaches Purview and no verdict comes back (see Common failure modes).
Prerequisites
Licensing requires one of two combinations per user: Microsoft 365 E7, or Purview E5 (or equivalent) plus Entra Internet Access. Purview pay-as-you-go billing must also be set up before Step 10: the DLP policy wizard only offers the Network enforcement option once it is. Microsoft’s setup guide is linked under Microsoft documentation.
Requirement
Detail
Licensing
M365 E7, or Purview E5 + Entra Internet Access
Roles
Global Secure Access Administrator; Conditional Access Administrator; Intune Administrator; a Purview role able to create DLP policies
Global Secure Access onboarded; Internet Access traffic forwarding available
Endpoints
Windows 11, Intune-managed, Global Secure Access client installed
Constraint 1 - B2B guest users are out of scope
Microsoft Purview network and browser data security policies do not apply to B2B guest users. A guest signed in with an external identity is not evaluated, regardless of the device they are using or the policy scope.
This matters more than it first appears. Many organisations onboard contractors, consultants and partner staff as guests precisely so they can be managed centrally. Those users are frequently the ones handling material a customer would most want protected, and they fall outside this control entirely.
Three practical responses:
Where external staff handle sensitive material regularly, issue member accounts rather than guest accounts. This is the only option that brings them into scope.
Where guest access must remain, do not rely on DLP for that population. Use web content filtering to block the generative AI category outright for guests, which is an access control rather than a content control and does apply.
State the exclusion explicitly in any coverage statement given to a customer. A claim that sensitive data is blocked from AI applications is incorrect for the guest population, and discovering that during an incident is considerably worse than disclosing it during design.
Constraint 2 - group membership and assignment surfaces
The solution assigns objects across three surfaces, and they do not use the same kind of group:
Object
Assigned to
Group type
Intune trusted certificate profile
Devices
Device group
Intune QUIC settings catalog policy
Devices
Device group
Conditional Access policy applying the security profile
Users
User group
Traffic forwarding profile
Users or devices
Either, depending on configuration
A device missing from the device group receives neither the root CA nor the QUIC policy. A user missing from the user group is never scoped into the security profile. Both produce the same end-user symptom - nothing is blocked - and neither surfaces as an error anywhere in the console.
Reading Intune assignment status correctly. The check-in status on a configuration profile reports four counts: Succeeded, Error, Conflict and Not applicable. Those counts describe devices that were targeted. A profile targeting devices that cannot apply it reports a non-zero Not applicable. A profile targeting nothing at all reports:
Succeeded: 0
Error: 0
Conflict: 0
Not applicable: 0
All four at zero is not a healthy profile with nothing to do. It means the profile has no targets, either because no assignment exists or because the assigned group is empty. There is no warning, no amber state and no message; the page looks the same as a profile that has simply not been picked up yet.
Verify membership directly rather than inferring it from assignment reporting. On the endpoint, confirm the certificate is present in the Local Computer trusted root store and that chrome://policy or edge://policy shows QuicAllowed applied as a machine policy. Those two checks confirm delivery in a way the Intune console does not.
Building a test lab
If you are proving the control in a lab before production, three prerequisites are easy to overlook, and each produces a failure that looks like a policy problem rather than a setup problem.
Lab build
Component
Requirement
Test endpoint
Windows 11 VM, Entra-joined and Intune-enrolled, member of the group used for all assignments
Azure VM
Microsoft Entra ID login extension installed. The test account needs the Virtual Machine User Login or Virtual Machine Administrator Login role. Entra Internet Access policies are identity-based, so the test server must be signed in with an Entra ID identity.
Test identity
An Entra account licensed for Entra Internet Access. Sign in to the Global Secure Access client with that identity - an unlicensed account leaves the client showing connected while the traffic is never acquired
Azure subscription
The subscription backing Scan with Purview must have the Microsoft.Purview resource provider registered. Subscription > Settings > Resource providers
Sensitive information type
A custom SIT with a distinctive pattern unlikely to occur naturally, so matches are unambiguous
Test artefacts
Prepare two items carrying the same sensitive information type:
A short text string for pasting into prompt boxes.
A Word document containing that string in its body, kept under 3 MB. Place the value in the body rather than only the filename or document properties, and put a keyword (for example “project reference”) next to it. Repeating the value raises the instance count, not the confidence level - without a keyword it only matches at Medium.
Test matrix
Run each row in Edge and Chrome, signed in to the application, and capture the traffic log evidence for each.
Test
Expected observation
Paste the string into the prompt
Blocked where the application submits JSON; passes where it submits a binary transport
Upload the document
Blocked - multipart/form-data is parseable
Paste the document’s contents into the prompt
Same result as the paste test, not the upload test
The third row is the one worth running explicitly. It is the natural user response to a refused upload, and on an application with a binary transport it succeeds where the upload failed.
Isolating the result
Disable or remove any Edge for Business DLP policy before testing the network plane, and confirm in Activity Explorer that the enforcement plane reads network. With both planes active, a browser-plane block is easily attributed to the network policy under test.
Other controls in the lab - Defender for Cloud Apps policies, Defender network protection - can block an application before the request reaches Global Secure Access. Confirm the application loads and a benign prompt succeeds before introducing the sensitive information type.
Implementation sequence
The order matters. Enabling TLS inspection before the root CA is trusted breaks every inspected site for users in scope.
Automate it: steps 3 and 4 (Deploy-GsaEndpointPrereqs.ps1), step 9 (Import-ContosoSIT.ps1) and step 10 (New-PurviewInlineDlpPolicy.ps1) can be run with the scripts in the GitHub repository. Each script has its own README. The steps below show the same configuration manually so it can be checked in the portal.
Before you start
Have these in place before Step 1. Each one, if missed, shows up later as “nothing is blocked” with no error to explain why.
Licences assigned to the pilot users (see Prerequisites).
Purview pay-as-you-go billing set up, linked to an Azure subscription in the same tenant. Only a Global Administrator can do this; see Microsoft documentation.
Microsoft.Purview resource provider registered on that subscription: Azure portal > Subscriptions > your subscription > Settings > Resource providers. Without it, traffic reaches Purview and no verdict comes back.
Two pilot groups: a device group for the Intune profiles and a user group for Conditional Access, with the same people and devices in both (see Constraint 2).
Test devices: Windows 11, Entra joined or hybrid joined, Intune-enrolled, with the Global Secure Access client installed.
Roles: Global Secure Access Administrator, Conditional Access Administrator, Intune Administrator, and a Purview role that can create DLP policies.
The scripts, if you are using them, downloaded from the GitHub repository.
Step 1 - Enable Internet Access traffic forwarding
Global Secure Access > Connect > Traffic forwarding: enable the Internet access profile and assign it to the pilot users or groups. Deploy the Global Secure Access client to the test devices and confirm it shows Connected against the correct tenant. In the client’s Advanced diagnostics > Forwarding profile, Internet Access rules should appear; this can take up to 15 minutes.
Step 2 - Create the TLS inspection certificate
Global Secure Access > Secure > TLS inspection policies > TLS inspection settings > Create certificate > Microsoft-managed. Microsoft generates a tenant-specific root CA and retains the private key. Alternatively, generate a CSR and sign it with the organisation’s own PKI.
Why this step stays manual. Microsoft generates a unique certificate authority every time a certificate is created. The root downloads as M-TLSi-<id>-root.cer (for example M-TLSi-h1i4m-root.cer), and the <id> changes with each new certificate, including a rotation. It can’t be copied from another tenant or recreated from a template, and the published Graph API only covers the own-PKI (CSR) flow. Create it here, then use … > Download root certificate.
In the portal the certificate always appears as Microsoft-managed TLS inspection CA, with its ID underneath (for example M-TLSi-h1i4m), type Managed and status Disabled. Note the ID and the Root CA expiration date, and leave the certificate disabled until Step 3 is complete.
Microsoft-managed certificates aren’t exposed through Microsoft Graph. Tested in October 2026, /beta/networkAccess/tls/externalCertificateAuthorityCertificates returned an empty list while the certificate was Active - that endpoint only holds own-PKI (CSR) certificates. So Deploy-GsaEndpointPrereqs.ps1 can’t look the certificate up by its display name. It works from the downloaded root instead (-CertificateId h1i4m picks M-TLSi-h1i4m-root.cer), confirms it is a Microsoft-managed root (subject CN=Microsoft Entra TLS Inspection Root CA, O=Microsoft) and prints the ID, thumbprint and expiry to check against the portal.
Step 3 - Distribute the root CA to endpoints
Download the root certificate and deploy it with an Intune trusted certificate profile. Destination store must be Computer certificate store - Root. Assign it to the device group, then confirm on a test device before proceeding.
Automated:Deploy-GsaEndpointPrereqs.ps1 creates the trusted certificate profile from the downloaded M-TLSi-<id>-root.cer (Computer certificate store - Root) and assigns it to the device group. It warns if the group has no devices. See Deploy-GsaEndpointPrereqs-README.md.
Step 4 - Apply client prerequisites
Global Secure Access can’t inspect QUIC (HTTP/3) or DNS over HTTPS, so turn both off in the browsers. In an Intune settings catalog policy, for both Microsoft Edge and Google Chrome, set Allow QUIC protocol to Disabled and Control the mode of DNS-over-HTTPS to Disabled, and assign it to the device group. Optionally, also block outbound UDP 443 at the Windows firewall:
# Optional: block QUIC (UDP 443) at the Windows firewallNew-NetFirewallRule -DisplayName "Block QUIC" -Direction Outbound -Action Block -Protocol UDP -RemotePort 443
Automated:Deploy-GsaEndpointPrereqs.ps1 creates the QUIC part of this policy (QuicAllowed set to Disabled for Edge and Chrome) and assigns it to the device group. The DNS-over-HTTPS setting and the optional firewall rule remain manual. See Deploy-GsaEndpointPrereqs-README.md.
Step 5 - Create the TLS inspection policy
Global Secure Access > Secure > TLS inspection policies > Create policy. Name it, set Action to Inspect, then select Next, Next and Submit. With Inspect as the default, all traffic is decrypted unless it matches a bypass rule. Microsoft adds two bypass rules automatically - a system rule for destinations that are incompatible with TLS inspection, and a recommended bypass list for sensitive categories - so keep both.
Now enable the certificate. Once Step 3 is confirmed on a test device, go to the TLS inspection settings tab, select the ellipsis on the certificate and choose Enable. Its status changes to Active within a few minutes. Enabling it before devices trust the root breaks every inspected site.
Step 6 - Create the security profile
Global Secure Access > Secure > Security profiles > Create profile. Name it, select Next, then Link a policy > Existing TLS inspection policies, choose the policy from Step 5, select Add, Next and Create a profile. Link any web content filtering or threat intelligence policies the profile also needs, and set its priority. The content policy is linked in Step 7.
Step 7 - Create the content policy
Global Secure Access > Secure > Content policies > Create Policy. Name it, select Next, then Add a new rule with the action Scan with Purview and select Use destinations and activities defined in Purview for this rule. Choosing Specify destinations for this rule instead means the rule’s own destination list governs and the Purview adaptive scope is ignored.
Leave Session type blank. It is an optional filter: left unset, the rule applies to all traffic, whereas Agent limits it to AI agent traffic only.
Then link the content policy to the security profile: Security profiles > your profile > Link a policy > Existing Content policy, select it and keep the default state.
Step 8 - Link the security profile via Conditional Access
Entra admin centre > Entra ID > Conditional Access > Create new policy. Assign it to the pilot user group. Under Target resources, select All internet resources with Global Secure Access. Under Session, select Use Global Secure Access security profile and choose the profile from Step 6. Set Enable policy to On and create it. The profile can take up to an hour to take effect.
Step 9 - Create the Purview sensitive information type (SIT)
Run Import-ContosoSIT.ps1 (see Import-ContosoSIT-README.md), or create the type by hand in the Purview portal under Information Protection > Classifiers > Sensitive info types, using the values below. The script only creates the new type and never changes an existing one. For production, replace the demo pattern with your organisation’s own identifiers.
Format:CONTOSO-AAA-999999-A, for example CONTOSO-FIN-204815-K. It matches at High confidence when a keyword is within 300 characters, and at Medium for the value on its own.
Allow time: a new type can take a few minutes to appear in the portal, and policy changes up to 24 hours to reach enforcement.
Step 10 - Create the Purview DLP policy
Purview portal > Data loss prevention > Policies > Create policy > Inline web traffic, then Custom > Custom policy. Add the required adaptive app scopes, including All unmanaged AI apps. Enable Network and non-Microsoft secure browsers and leave Edge for Business off unless browser-plane coverage is also intended. Choose Create or customize advanced DLP rules and add the rule below.
Settings used in this lab (as in the screenshots below): data source All unmanaged AI apps, all users; Network and non-Microsoft secure browsers on, Edge for Business off; rule Scan with Purview Inline - content contains the custom sensitive information type at High confidence, instance count 1 to Any; all four activities; action Block; email to the user who performed the activity; admin alert on every match, severity Low. At High, the test content needs a keyword near the value (the test strings use “project reference”); use Medium to match the value on its own.
Automated:New-PurviewInlineDlpPolicy.ps1 creates this policy and rule. Pay-as-you-go billing must be set up first, as the Network option only appears once it is. See New-PurviewInlineDlpPolicy-README.md.
The adaptive scopes read the app catalogue from Defender for Cloud Apps; they do not read Defender for Cloud Apps tags (sanctioned, unsanctioned).
Common failure modes
Each of the following presents to the user as the same symptom - the policy did not block - and each has a different cause. Work through them in order; the first three account for most deployment failures.
Group membership
The single most common cause. If the device is not a member of the group used for Intune assignment, neither the root CA nor the QUIC policy reaches it, and the Conditional Access policy may not apply to the user.
The tell is an Intune profile reporting 0 succeeded, 0 error, 0 conflict, 0 not applicable. Zero across all four is not a healthy profile with nothing to do - it means no targets. Verify membership before investigating anything else.
Untrusted inspection certificate
If TLS inspection is enabled before the root CA is trusted on the endpoint, every inspected site fails with ERR_CERT_AUTHORITY_INVALID. On sites using HSTS the browser offers no bypass at all, so the user cannot proceed even if they wanted to.
Confirm the certificate is present in the Local Computer trusted root store, not Current User. Install it manually on one device to prove the chain resolves, then fix the Intune delivery rather than leaving the manual install in place.
Certificate path length constraint
Where the organisation’s own PKI signs the inspection certificate, a root enforcing pathLenConstraint = 1 prevents the Global Secure Access intermediate from validating beneath it. The symptom is ERR_CERT_INVALID rather than ERR_CERT_AUTHORITY_INVALID, on every inspected site, with the root correctly installed.
QUIC bypass
Traffic negotiating HTTP/3 leaves the tunnel entirely rather than falling back to TCP. There is no traffic log entry at all, which is indistinguishable from a policy that is not working. Confirm QuicAllowed is applied at chrome://policy and edge://policy before concluding anything from an empty log.
Content rule destination scope
If the content rule uses Specify destinations for this rule, only those destinations are offered to Purview for scanning. The Purview adaptive app scope is then irrelevant, and the result is one application blocking while others pass, with a policy that appears correctly configured.
Resource provider not registered
If the Azure subscription backing Scan with Purview does not have Microsoft.Purview registered, the scan path does not function. Traffic reaches Purview and no verdict returns.
Validation
Coverage must be verified per application. A policy that is correctly scoped can still fail to protect an application, and nothing in the console indicates this.
The method:
Submit the sensitive information type into the application and note the behaviour. Errors are generic: applications render their own failure text, not a policy message.
Export the Global Secure Access traffic logs and open the Transactions tab. Per-request detail is not on the Connections tab.
Filter to the application’s host and locate the POST carrying the payload, identified by a non-trivial sentBytes value.
Read httpRequestContentTypebefore reading action. JSON, form-encoded or plain text predicts enforcement; application/connect+proto, grpc or similar binary encodings predict the request passing uninspected.
Confirm against action and policyRuleName, then cross-check Purview DLP alerts for a matching entry at location ExtendedApplications.
Predicting the outcome from the content type before reading the action distinguishes a demonstrated mechanism from a coincidence.
To confirm which enforcement plane acted, filter Purview Activity Explorer on enforcement plane: network indicates Entra Internet Access, browser indicates Edge for Business. If both planes are configured, the block observed may not come from the policy under test.
A diagnostic distinction worth internalising: an application that reports no response and produces no traffic log entry was never inspected, which is not the same as a policy that evaluated and allowed. The traffic log separates the two.
Validation evidence - the three logs
For every test, export three logs for the same test window and read them together. Each answers a different question, and only the three together show whether a request was acquired, inspected and classified. Any one of them on its own can mislead, and the exports are the evidence behind each validation result.
Log
Question it answers
Where
Entra Internet Access traffic logs (Transactions)
Did Global Secure Access see the request, how was it encoded, and what did it do with it?
Entra admin centre > Global Secure Access > Monitor > Traffic logs > Transactions
DLP policy alerts
Did a DLP rule match, and which policy, rule and sensitive information type?
Defender portal > Incidents & alerts > Alerts (Microsoft’s recommended location for DLP alerts), or Purview > Data loss prevention > Alerts
DSPM for AI (classic) Activity explorer
Did Purview record the AI interaction and the classification, and on which enforcement plane?
Purview > DSPM for AI (classic) > Activity explorer
Run one test at a time and note its start and end time. Exports from the three sources are not necessarily in the same time zone - Activity explorer exports are in UTC - so convert before lining entries up.
1. Entra Internet Access traffic logs
Set the time range to the test window and filter on the application’s destination FQDN, for example chatgpt.com.
Use the Transactions tab. The Connections tab summarises connections and has no per-request detail.
Show the destination FQDN, URL, HTTP method, httpRequestContentType, sent bytes, action and policy rule name columns, then export the filtered view.
Find the POST carrying the payload - the one with a non-trivial sent bytes value - and read the content type before the action.
What the log shows
What it means
No entry for the application
The request was never acquired. Check the Global Secure Access client, the traffic forwarding profile assignment and the QUIC policy.
Allowed, binary content type (e.g. application/connect+proto)
The request was acquired but Purview cannot parse the body. A transport limitation, not a policy fault.
Allowed, JSON or form-encoded, no content policy rule
The request was readable but not sent for scanning, or Purview returned no verdict. Check the content rule destinations, the Purview adaptive scope and the Microsoft.Purview resource provider.
Blocked by the content policy rule
Network-plane DLP enforced the block.
Blocked by a different rule (web filtering, threat intelligence)
Another control blocked the request before DLP - the test proves nothing about DLP.
The same data is available through Microsoft Graph (GET /beta/networkAccess/logs/traffic, filtered on createdDateTime and destinationFQDN), but the Graph schema has no request content type field, so the portal export is still needed for that column.
2. DLP policy alerts
In the Defender portal alert queue, filter the service source to Microsoft Data Loss Prevention and the time range to the test window, then export the list.
Open each alert and confirm the policy, rule, sensitive information type (Contoso Project Reference (Demo)), confidence level, user and destination.
Network-plane matches should show the location ExtendedApplications.
A block in the traffic log with no alert usually means alerting is turned off on the rule or the alert hasn’t arrived yet - activity can take up to 30 minutes to appear. An alert with no matching block points to a rule in audit mode, or to the browser plane. The AlertInfo / AlertEvidence query in Hunting from Defender XDR returns the same alerts with their evidence.
3. DSPM for AI (classic) Activity explorer
Filter on the test window, the test user and the activity (AI interaction and DLP rule matched), then export to CSV.
Check the enforcement plane (network rather than browser), the application, the sensitive information type detected and the action taken.
Activity explorer holds 30 days of data.
The same data can be exported from Security & Compliance PowerShell:
# Activity explorer export for the test window (UTC). Page through with -PageCookie if LastPage is false.Connect-IPPSSession$res=Export-ActivityExplorerData-StartTime(Get-Date'2026-10-17T09:00')-EndTime(Get-Date'2026-10-17T18:00')`-Filter1@("Activity","AIAppInteraction")-PageSize5000-OutputFormatCsv$res.ResultData|Out-File.\activity-explorer-ai.csv
An Edge for Business policy acted, not the network policy under test.
Blocked by content rule / no alert / no activity yet
Wait 30 minutes and export again before drawing a conclusion.
Keep the three exports together, named with the application and test date, alongside the screenshots for that run. They are the evidence behind each entry in the coverage register.
Hunting from Defender XDR
The Global Secure Access traffic logs are not available in Defender advanced hunting. They live in the Entra admin centre, or are exported through Entra diagnostic settings to Log Analytics, an Event Hub or Microsoft Sentinel. The per-request detail used for the method in the Validation section - content type, action, policy rule name - is only reachable there.
The Purview side is reachable from the Defender portal without a Log Analytics workspace. Three tables are relevant:
Populated by Insider Risk Management; returns nothing unless the organisation has opted in to share insider risk alerts with Defender
CloudAppEvents
Audit activity across locations
The table Microsoft’s DLP investigation guidance points at
AlertInfo / AlertEvidence
DLP alerts and their entities
Alert-level only, not per-activity
Whether network-plane events populate these tables is not stated in Microsoft’s documentation. Confirm by querying a known test window rather than assuming coverage.
Discovery queries
Run these first against a window in which blocks are known to have fired. An empty result from DataSecurityEvents usually indicates the Insider Risk Management opt-in rather than a policy problem.
// What data security activity exists, and under which workload?DataSecurityEvents|whereTimestamp>ago(7d)|summarizeEvents=count()byWorkload,ActionType,DlpPolicyEnforcementMode|orderbyEventsdesc
// Does anything carry AI app destinations?DataSecurityEvents|whereTimestamp>ago(7d)|whereisnotempty(TargetUrlDomain)orisnotempty(ApplicationNames)|projectTimestamp,AccountUpn,ApplicationNames,TargetUrlDomain,ActionType,DlpPolicyEnforcementMode,SensitiveInfoTypeInfo|orderbyTimestampdesc
// Destinations ranked by volume - useful for spotting unsanctioned AI apps in useDataSecurityEvents|whereTimestamp>ago(30d)|whereisnotempty(TargetUrlDomain)|summarizeEvents=count(),Users=dcount(AccountUpn)byTargetUrlDomain,ActionType|orderbyEventsdesc
// DLP alerts with their evidenceAlertInfo|whereTimestamp>ago(7d)|whereServiceSource=="Microsoft Data Loss Prevention"|joinkind=innerAlertEvidenceonAlertId|projectTimestamp,Title,Severity,EntityType,AccountUpn,RemoteUrl,FileName|orderbyTimestampdesc
// Cloud app audit activity for a specific destinationCloudAppEvents|whereTimestamp>ago(7d)|whereAccountDisplayName!=""|whereApplicationId!=0|summarizeEvents=count()byApplication,ActionType|orderbyEventsdesc
Column names and permitted values should be confirmed against the tenant before these are used operationally; the schema is in preview and subject to change.
Coverage findings
File uploads were blocked on all six applications tested. Typed and pasted prompts were blocked on five of six - every application except Claude. The variable is the wire format the application uses, not the policy scope.
Tested 17-18 October 2026 against a single Purview DLP policy scoped to all unmanaged AI apps, using one custom sensitive information type, in Edge and Chrome on one managed device. DLP rule settings: High confidence, instance count 1 to Any, action Block.
Application
Prompt transport
Prompt
File upload
ChatGPT
application/json to /backend-api/f/conversation
Blocked
Blocked
Perplexity
application/json to /rest/sse/perplexity_ask
Blocked
Blocked
Claude
application/connect+proto to /claudeai-rpc/โฆ
Allowed
Blocked
Meta AI
Not recorded
Blocked
Blocked
Grok
Not recorded
Blocked (no response)
Blocked
Mistral AI
Not recorded
Blocked (not accepted)
Blocked (not accepted)
Results for Meta AI, Grok and Mistral AI are from the application’s behaviour in the screenshots under Test evidence. Confirm each against the traffic logs and record the request content type, as the validation method requires.
Purview parses JSON, plain text, HTML and XML. ChatGPT and Perplexity submit prompts as JSON, so the payload is classified and the sensitive information type matches. Claude submits prompts as Protocol Buffers over Connect RPC. That payload is binary, Purview cannot read it, and the request passes without evaluation.
Claude is the controlling case, because it demonstrates the mechanism twice within one application: the same policy blocks a Word document containing the sensitive information type, because multipart/form-data can be reassembled and inspected, while permitting the same content typed into the prompt box.
Two secondary observations from the same traffic:
Perplexity echoed the sensitive information type into its own analytics telemetry, which was blocked separately. Sensitive data reaching third-party telemetry is a path rarely considered in scoping.
Claude produced block events on GET requests retrieving conversation history, where Purview matched text received from the application. Enforcement fired on the response direction while the request direction passed.
Test evidence
Screenshots from the paste and file upload tests, in Edge and Chrome with the custom sensitive information type. A blocked request shows the application’s own error, or no response at all, rather than a Purview message - see Operational considerations.
Perplexity AI - paste
Won’t accept the prompt.
Perplexity AI - file upload
Meta AI - paste
Meta AI - file upload
Grok - paste
No response.
Grok - file upload
File processing failed.
Mistral AI - paste
Won’t accept the paste.
Mistral AI - file upload
Won’t accept the upload - no error shown.
Claude - paste
Claude - file upload
Known limitations and residual risk
Binary prompt transports are not inspected. Any application submitting prompts as Protocol Buffers, gRPC-web or a similar encoding passes without evaluation, regardless of policy scope. There is no console indication that an application falls into this category.
Blocking file upload does not close the prompt path. Where an upload is refused, a user can open the document and paste its contents into the prompt box. On an application with a parseable transport this is still caught. On one with a binary transport it is not: the paste travels the same uninspected path as any typed prompt, whatever its length or origin. This is the most likely real-world bypass, and it requires no technical knowledge - it is the obvious next thing a user tries.
WebSocket support varies. All integrations classify HTTP and HTTPS. WebSocket support differs by solution, so streaming interfaces may fall outside inspection.
Size limits. Purview evaluates up to 4 MB of text and files up to 3 MB. Larger items produce a limit outcome rather than a policy decision. Through Scan with Purview the limit is 3 MB for both text and files, and content that can’t be inspected is allowed (fail-open) - check the PurviewStatus field in the traffic logs.
Protocol constraints. QUIC is unsupported and bypasses the tunnel entirely rather than failing closed - traffic that negotiates HTTP/3 is never seen. TLS inspection does not support HTTP/2 negotiation; sites requiring it will not load.
Scope exclusions. Policies do not apply to B2B guest users.
Certificate chain constraint. If the signing root enforces a path length constraint of 1, the Global Secure Access intermediate cannot be validated beneath it and clients receive certificate errors on every inspected site.
The defensible position for a customer is that file-based exfiltration is covered broadly, prompt-based exfiltration is covered for applications using parseable transports, and the set of covered applications must be established by testing rather than assumed from the category scope.
Operational considerations
User experience differs by plane. The browser plane renders a branded Purview interstitial inside the page. The network plane cannot inject interface elements - it returns an HTTP 403, and the application displays whatever its own error handling produces. In practice this means generic failures: an upload error, a session error, or raw block-page HTML rendered inside the chat window. Service desks should be briefed, because these do not look like policy messages.
Alert volume needs tuning before go-live. Pilot testing by a single user generated 29 low-severity alerts across two days. At production scale, untuned alerting will bury genuine signals.
Re-verification is ongoing, not one-off. An application using JSON today may adopt a binary transport in a future release, silently removing prompt-path coverage. Re-run the validation method after significant vendor changes and on a periodic schedule.
Endpoint prerequisites are the common failure point. Certificate distribution and QUIC policy both depend on correct group membership. Verify on a representative device rather than trusting assignment reporting.
Automation boundaries. Most of the tenant configuration is reproducible through Microsoft Graph beta: traffic forwarding profiles, security profiles and their policy links, TLS inspection policies and rules, Conditional Access, and the Intune profiles. The table shows what stays manual and what the scripts cover:
Element
Status
Reason
TLS inspection root CA
Manual
Generated per tenant; cannot be copied between tenants
New-PurviewInlineDlpPolicy.ps1 (Security & Compliance PowerShell, -EnforcementPlanes Network). Pay-as-you-go billing must be set up first
Intune QUIC policy and root CA profile
Scripted
Deploy-GsaEndpointPrereqs.ps1. Creating and downloading the Microsoft-managed certificate stays manual
The scripts therefore cover the repeatable tenant plumbing; the certificate and the content policy stay manual.
Scripts
The scripts, READMEs and test samples are in the Cloud-Secure.ai GitHub repository. Each script has a README covering prerequisites, parameters, the order of operations and troubleshooting. Import-ContosoSIT.ps1 and New-PurviewInlineDlpPolicy.ps1 were tested end to end in a live tenant; Deploy-GsaEndpointPrereqs.ps1 was tested with -WhatIf only, so run it with -WhatIf first.
Script
What it does
Further information
Deploy-GsaEndpointPrereqs.ps1
Creates the Intune QUIC block policy for Edge and Chrome and the trusted certificate profile for the TLS inspection root CA. Creating and downloading the Microsoft-managed certificate stays manual (Step 2).
Deploy-GsaEndpointPrereqs-README.md
Import-ContosoSIT.ps1
Creates the Contoso Project Reference (Demo) sensitive information type. Create only - never changes an existing type.
Import-ContosoSIT-README.md
New-PurviewInlineDlpPolicy.ps1
Creates the inline web traffic DLP policy (All unmanaged AI apps, Network) and its block rule, as in Step 10. Create only.
New-PurviewInlineDlpPolicy-README.md
Rolling out to production
The lab proves the mechanism; production needs a staged rollout. This order keeps the risk low and works the same for one tenant or many.
Pilot with IT first. Keep the device and user groups small, and confirm the root CA and QUIC policy on every pilot device before adding its user to the Conditional Access policy.
Start the DLP policy in simulation mode. Choose simulation as the policy mode (or -Mode TestWithNotifications in the script), review matches in Activity explorer and the alert queue for a week, then turn it on.
Prove each AI app your users actually use with the Validation method, and record the result and content type in the coverage register.
Brief the service desk. Blocks appear as the application’s own errors, not as a Purview message.
Tune alerting before widening the groups, so genuine matches are not buried.
Widen in stages, root CA and QUIC policy first, then users.
Know the rollback. Turning off the Conditional Access policy removes the security profile, so TLS inspection and Scan with Purview stop for those users. Setting the DLP policy back to simulation stops blocking but keeps the visibility.
Key takeaways
File uploads were blocked on all six AI applications tested. Prompt blocking depends on how each application sends data: JSON is inspected, binary formats such as Claude’s prompts pass.
Order matters: the root CA must be on devices before TLS inspection is switched on.
Group membership is the most common failure, and Intune does not flag an empty group.
Coverage has to be proven per application, with the traffic log content type as evidence, and re-tested when vendors change their apps.
B2B guest users are out of scope.
Scan with Purview is in preview at the time of writing; check its status before presenting it as generally available.
Reference
Supporting detail for the implementation: the Microsoft Graph beta endpoints and limits confirmed in testing, the coverage register for the generative AI applications tested, and the Microsoft documentation referred to throughout.
Microsoft Graph beta endpoints and limits
Confirmed Graph beta endpoints
Verified against a live tenant. All are under https://graph.microsoft.com/beta/.
networkAccess/settings returns 404. networkAccess/contentPolicies and networkAccess/tls return 400 under every spelling tested; neither the content policy nor the TLS inspection certificate settings appear to be exposed.
networkAccess/tls/externalCertificateAuthorityCertificates returns 200 with an empty list while a Microsoft-managed certificate is Active. It only holds own-PKI (CSR) certificates, so Microsoft-managed certificates can’t be read or created through Graph.
Inline evaluation limits
Item
Limit
Text (upload and download)
4 MB
Files (upload and download)
3 MB
Policy distribution
Up to 24 hours
Activity and audit visibility
Up to 30 minutes
Supported activities
Four activities are available in both collection and DLP policies: text sent to or shared with a cloud or AI app; text received from a cloud or AI app; files uploaded to or shared with a cloud or AI app; files downloaded from a cloud or AI app. Actions are limited to audit only and block.
Generative AI application coverage register
This register records the six applications tested: ChatGPT, Perplexity, Claude, Meta AI, Grok and Mistral AI. Test any other application before claiming coverage for it. Do not record results from assumption - the point of the validation method is that transport behaviour cannot be inferred from the vendor, the category scope, or from how a similar product behaves.
The Edge list column records whether Microsoft documents the application as a supported unmanaged cloud app for browser-plane DLP in Edge for Business. It is a documented fact, not a test result, and it says nothing about network-plane coverage.
Application
Edge list
Prompt - content type
Prompt - result
Upload - content type
Upload - result
ChatGPT (consumer)
Yes
application/json
Blocked
multipart/form-data
Blocked
Perplexity AI
Yes
application/json
Blocked
multipart/form-data
Blocked
Claude
No
application/connect+proto
Allowed
multipart/form-data
Blocked
Grok (xAI)
Yes
Blocked (no response)
Blocked
Meta AI
Yes
Blocked
Blocked
Mistral AI
Blocked (not accepted)
Blocked (not accepted)
Running each test
Two tests per application, both in Edge and Chrome, signed in, with the same sensitive information type.
Paste test. Paste the sensitive string into the prompt box and submit. Record the result.
Upload test. Attach a document containing the same value, under 3 MB, with the value in the document body. Record the result.
After each pair, open the traffic logs Transactions tab, filter to the application host, and read httpRequestContentType on the request carrying the payload. Record that value whether the test blocked or passed - the transport is the finding, and without it a later regression cannot be explained.
Use Blocked, Allowed or Inconclusive in the result columns. Inconclusive covers the case where the application failed for its own reasons, no traffic log entry appeared, or another control intervened; re-run rather than recording a pass.
Pasting the contents of a document into the prompt box is not recorded separately. It travels the same path as any typed prompt, so its outcome always matches the paste test rather than the upload test. That equivalence is the point worth demonstrating to a customer, not a third data point.
Keep the date of each test and the supporting screenshots alongside this table. Vendors change encodings without notice, and a result without a date cannot be trusted six months later.
Microsoft notes that some applications, naming ChatGPT, Runway and Meta AI, intermittently send content in encoded form or through dynamically generated endpoints, which can affect policy enforcement. Treat that as a reason to test repeatedly rather than once.
Why Claude behaves differently
Claude is not inherently exempt from the policy. The adaptive scope covers it, Purview evaluates its traffic, and file uploads are blocked. The difference is confined to the prompt submission path.
The mechanism is the encoding of the request body:
ChatGPT posts the prompt to /backend-api/f/conversation as application/json. Purview parses JSON, reads the prompt text, matches the sensitive information type, and the request is blocked.
Perplexity posts to /rest/sse/perplexity_ask as application/json, with the same outcome.
Claude posts to /claudeai-rpc/โฆ as application/connect+proto - Protocol Buffers carried over Connect RPC. The body is a binary serialisation with no readable text, so classification finds nothing to match and the request is allowed.
The content types a Purview content rule can inspect are JSON, plain text, HTML and XML for text, plus the listed file formats. A protobuf body falls outside all of them. This is a parsing limitation, not a policy misconfiguration, and nothing in the Purview or Global Secure Access interface indicates that an application falls into this category.
The same application demonstrates both behaviours, which is why it is the useful test case: a Word document carrying the identical sensitive information type is blocked, because multipart/form-data can be reassembled and the document parsed. One policy, one sensitive information type, one application, two outcomes decided solely by wire format.
Testing further applications
For each application added to the register:
Confirm it loads and a benign prompt succeeds, ruling out an unrelated block from another control.
Submit the sensitive information type as a typed prompt.
Upload a document containing the same value.
Paste the document’s contents into the prompt box.
Read httpRequestContentType on the request carrying the payload from the traffic logs Transactions tab, and record it alongside the action.
Record the transport even where the result is a block. When a vendor changes encoding in a later release, the recorded baseline is what makes the regression visible.
Microsoft documentation
Microsoft documentation and announcements referred to in this post. Several features are in preview and the pages change often - check the date on each page.
Disclaimer:Scan with Purview is in preview at the time of writing, so verify current availability in your own tenant before planning a deployment. Everything in this post was tested in a dedicated lab tenant in October 2026. The test scenarios are entirely my own and are no reflection on the generative AI providers named. The views in this post are my own and are accurate at the time of writing. Microsoft’s protections and the generative AI vendors’ own protections may change.