Stopping leaked credentials with Microsoft Purview DLP

Cover - Stopping leaked credentials with Microsoft Purview DLP

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 SQL connection strings and Azure Redis Cache connection string passwords
  • 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
Alerts High severity, every match, to the security team Every match needs triage and likely rotation
Incident report content Excludes original and matched content Stops the alert email becoming a second leak
Mode Turned on immediately Enforced as soon as the policy syncs

Step 1 - Open Data Loss Prevention

Sign in to the Microsoft Purview portal and go to Data Loss Prevention > Policies.

Step 2 - Start a custom policy

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

Preview the changes, then run it:

.\New-CredentialDlpPolicy.ps1 -WhatIf
.\New-CredentialDlpPolicy.ps1

What the script runs

New-DlpCompliancePolicy -Name 'Block Credentials and Secrets' `
    -ExchangeLocation All -SharePointLocation All `
    -OneDriveLocation All -TeamsLocation All -Mode Enable

New-DlpComplianceRule -Name 'Block credentials and secrets' `
    -Policy 'Block Credentials and Secrets' `
    -ContentContainsSensitiveInformation @{
        Name = 'All Credential Types'; minCount = '1'; confidencelevel = 'Medium' } `
    -BlockAccess $true -BlockAccessScope All `
    -GenerateAlert 'secops@contoso.com' `
    -GenerateIncidentReport 'secops@contoso.com' `
    -IncidentReportContent Title, DocumentAuthor, Service, Severity, RulesMatched `
    -ReportSeverityLevel High

Check the policy has synced:

Get-DlpCompliancePolicy -Identity 'Block Credentials and Secrets' |
    Select-Object Name, 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.

Script Runs Purpose
New-CredentialDlpPolicy Once, by an administrator 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.


References

Seรกn O’Farrell | cloud-secure.ai

Microsoft Defender AI agent runtime protection: the series guide (parts 1 to 7)

Microsoft Defender AI agent runtime protection series guide, parts 1 to 7

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 series at a glance

Part Question it answers What you get
Part 1 What is AI agent runtime protection? How Defender detects prompt injection against local AI agents
Part 2 Does it work, and how do I build it? A lab build, validation, and the gaps in the documentation
Part 3 Which agents are running on my endpoints? The Local agents view, licensing and advanced hunting queries
Part 4 How do I stop an agent reaching an MCP server? Custom domain indicators, network protection and a custom detection rule
Part 5 How do I block every new MCP server automatically? An Azure Automation runbook that blocks by default, with scripts on GitHub
Part 6 How do I govern every agent, not just the ones on endpoints? Agent 365 approval, owners and sponsors, and Conditional Access for agents, with a script on GitHub
Part 7 What can DLP protect if nothing has been classified? Purview classification methods, the types of DLP, and a layered design to protect Contoso’s intellectual property

What each part covers

Part 1: the control for agents already on your endpoints

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.

Part 2: building it, proving it, and where the documentation is wrong

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.

Part 3: finding the agents you did not know you had

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.

Part 4: controlling where Claude can connect

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.

Part 5: blocking MCP servers you have not approved

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.

Part 6: moving AI agent governance from Defender into Agent 365

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.

Part 7: data classification first – protecting Contoso’s intellectual property

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.
  • Building a lab or a proof of concept: part 2.
  • 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.


The series

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.

Seรกn O’Farrell | cloud-secure.ai

Microsoft Defender AI agent runtime protection, part 5: blocking MCP servers you have not approved

Part 5 cover - Blocking MCP servers you have not approved

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

How the MCP-ServerAutoBlock runbook works
  1. An agent connects to a remote MCP server. Defender for Endpoint records the connection in DeviceNetworkEvents.
  2. Every hour, the Automation account’s managed identity runs an advanced hunting query through Microsoft Graph runHuntingQuery for domains that AI agent processes reached.
  3. 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.
  4. Every domain left gets a Block domain indicator with alert generation, created through the Defender for Endpoint indicators API.
  5. Network protection enforces the block on every onboarded device, and each blocked connection raises an alert.
  6. 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.

Create an Automation Account - Basics tab

On Advanced, leave System assigned ticked. This managed identity is what the runbook authenticates as, so there are no credentials to store.

Create an Automation Account - Advanced tab with System assigned ticked

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.

Create an Automation Account - Networking tab with Public access selected

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.

Automation account Identity blade with System assigned status On

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
PowerShell output showing ThreatHunting.Read.All and Ti.ReadWrite.All assigned

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.

Create a runbook named MCP-ServerAutoBlock, 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.

Automation account overview with Try Runtime Environment Experience highlighted

Go to Process Automation > Runtime Environments > Create. The system-generated environments stop at 7.2.

Runtime Environments list showing system-generated environments up to PowerShell 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.

Create a Runtime environment named PowerShell-76 with runtime version 7.6

Open the runbook and select Edit > Edit in portal.

Edit menu with Edit in portal highlighted

Set Runtime Environment to PowerShell-76, paste the contents of MCP-ServerAutoBlock.ps1 and select Save. Do not publish yet.

Runbook editor with PowerShell-76 runtime environment selected and Publish highlighted

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:

let agentProcesses = dynamic(['claude.exe', 'copilot.exe', 'code.exe', 'cursor.exe', 'gemini.exe']);
let nodeMarkers = dynamic(['claude', 'copilot', 'gemini']);
DeviceNetworkEvents
| where Timestamp > ago(2h)
| where isnotempty(RemoteUrl)
| where InitiatingProcessFileName in~ (agentProcesses)
    or (InitiatingProcessFileName =~ "node.exe" and InitiatingProcessCommandLine has_any (nodeMarkers))
| where not(InitiatingProcessFileName =~ "claude.exe" and InitiatingProcessCommandLine has "--type=")
| extend HostPart = iff(RemoteUrl has "://", tostring(parse_url(RemoteUrl).Host), tostring(split(RemoteUrl, "/")[0]))
| extend Domain = tolower(trim_end(@"\.", tostring(split(HostPart, ":")[0])))
| where Domain matches regex @"^[a-z0-9-]+(\.[a-z0-9-]+)+$" and not(Domain matches regex @"^[0-9.]+$")
| summarize FirstSeen = min(Timestamp), Connections = count(), Devices = dcount(DeviceId),
            Processes = make_set(InitiatingProcessFileName, 5) by Domain

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.

Test pane output for MCP-ServerAutoBlock v1.3.0 dry run with all pre-flight checks passed

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
  1. Enter the tenant ID or domain when prompted, for example contoso.onmicrosoft.com.
  2. Sign in to Azure.
  3. 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.
  4. 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.

Runbook Schedules blade showing MCP-ServerAutoBlock-Hourly with status On
Schedule properties - recurring every 1 hour, no expiration

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:

claude mcp add --scope user --transport http deepwiki https://mcp.deepwiki.com/mcp
claude mcp list
claude mcp list showing context7 failing with a TLS handshake error and deepwiki connected

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.

Gemini CLI and GitHub Copilot CLI

Both install from npm, so Node.js comes first:

npm install -g @google/gemini-cli
npm install -g @github/copilot
npm installing Gemini CLI and GitHub Copilot CLI

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:

{ "mcpServers": { "gitmcp": { "httpUrl": "https://gitmcp.io/docs" } } }
Gemini CLI /mcp showing gitmcp ready with 5 tools

Copilot CLI signs in with /login, and the server is added from PowerShell:

copilot mcp add --transport http huggingface https://huggingface.co/mcp
copilot mcp list
Copilot CLI signed in, adding the huggingface MCP server and listing servers

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:

DeviceNetworkEvents
| where Timestamp > ago(2h)
| where RemoteUrl has_any ("deepwiki.com", "gitmcp.io", "huggingface.co")
| extend Agent = case(
    InitiatingProcessFileName =~ "claude.exe", "Claude Code",
    InitiatingProcessCommandLine has "gemini", "Gemini CLI",
    InitiatingProcessCommandLine has "copilot", "GitHub Copilot CLI",
    InitiatingProcessFileName)
| summarize FirstSeen = min(Timestamp), Connections = count() by Agent, RemoteUrl, InitiatingProcessFileName
| order by FirstSeen asc
Advanced hunting results showing Claude Code, Gemini CLI and GitHub Copilot CLI connecting to their MCP servers

Three details are worth noticing:

  • 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:

Windows Security notification - This content is blocked by your IT admin - mcp.context7.com

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.

Script Runs Purpose
MCP-ServerAutoBlock Azure Automation, hourly Hunts, compares and blocks new AI agent endpoints
Grant-McpRunbookPermissions Once, by an administrator Grants the managed identity its two API permissions
New-McpRunbookSchedule Once, by an administrator Creates the hourly schedule and links it to the runbook
Invoke-McpAgentTest Lab only, on a test device 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.


The rest of the series

References

Seรกn O’Farrell | cloud-secure.ai

Microsoft Defender AI agent runtime protection, part 4: controlling where Claude can connect

Microsoft Defender AI agent runtime protection, part 4: controlling where Claude can connect

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 doesWhat 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:

Get-MpPreference | Select-Object EnableNetworkProtection
Intune Defender Antivirus policy with Enable Network Protection set to Enabled (block mode)

Then turn on custom network indicators under Settings > Endpoints > General > Optional features. Without it, indicators save but network protection never enforces them.

Custom network indicators switched on in Defender optional features

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.

claude mcp add --transport http --scope user context7 https://mcp.context7.com/mcp
Registering Context7 as a remote HTTP MCP server in Claude Code at user scope

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.

The /mcp command in Claude Code showing Context7 connected

Before blocking anything, confirm the endpoint is reachable from a non-browser process. A JSON reply - even an error - means the connection got through.

Invoke-WebRequest https://mcp.context7.com/mcp -UseBasicParsing
PowerShell reaching Context7 and receiving a JSON reply

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-Date
do {
    try {
        Invoke-WebRequest https://mcp.context7.com/mcp -UseBasicParsing -ErrorAction Stop | Out-Null
        Write-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)" -ForegroundColor Green
            $blocked = $true
        }
    }
    if (-not $blocked) { Start-Sleep -Seconds 600 }
} 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.

Defender Indicators page with the URLs/Domains tab and 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.

Indicator page with mcp.context7.com recognised as indicator type Domain

Action: the possible controls

This page is where the indicator becomes a control.

ActionWhat 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.
Indicator Action page showing Allow, Audit, Warn and Block execution

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.

Indicator alert details: Generate alert, Medium severity, Suspicious activity, recommended actions
Indicator scope set to a specific device group

Save, and the indicator appears in the list with its action, severity and scope.

The saved mcp.context7.com indicator in the URLs/Domains list

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.

Advanced hunting showing Edge blocked by SmartScreen 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.

After a restart, PowerShell fails to reach Context7 with a TLS handshake failure while httpbin.org is 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.

Get-WinEvent -LogName "Microsoft-Windows-Windows Defender/Operational" |
    Where-Object Id -eq 1126 | Select-Object -First 3 | Format-List TimeCreated, Message
Event 1126 showing network protection blocked pwsh.exe from reaching mcp.context7.com

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.

The /mcp command in a fresh Claude Code session showing Context7 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.

Claude reporting a TLS handshake error and suggesting a URL, proxy or firewall fault

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.

Claude reading the Defender event log, identifying the block and declining to work around it

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.

DeviceEvents
| where Timestamp > ago(1d)
| where RemoteUrl has "context7"
| project Timestamp, ActionType, InitiatingProcessFileName, RemoteUrl, AdditionalFields
| order by Timestamp desc
Advanced hunting showing network protection blocks for claude.exe and pwsh.exe

6. The indicatorโ€™s own alert

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.

Alerts queue showing Connection to a custom network indicator alerts
Alert story: Network Filter Lookup Service blocked pwsh.exe from accessing mcp.context7.com

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.

DeviceEvents
| where ActionType == "ExploitGuardNetworkProtectionBlocked"
| where InitiatingProcessFileName in~ ("claude.exe")
| project Timestamp, ReportId, DeviceId, DeviceName,
          InitiatingProcessAccountSid, InitiatingProcessAccountName,
          InitiatingProcessFileName, RemoteUrl, AdditionalFields

Run it in Hunting > Advanced hunting and confirm it returns rows before you build anything on it. Then select Create detection rule.

The detection rule query returning claude.exe blocks, with Create detection rule highlighted

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.

Detection rule frequency options: Continuous (NRT), Custom, Every hour, Every 3 hours, Every 12 hours

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.

Detection rule General page: name, description, query and Every hour with a 4-hour lookback

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.

Detection rule severity Medium, MITRE ATT&CK Command and Control, and recommended actions

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}}.

Dynamic detection rule alert title and description built from query columns

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.

Detection rule entity mapping: Device to DeviceId, User to InitiatingProcessAccountSid, URL to RemoteUrl

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.

Detection rule incident settings with Include in correlation selected

Automated actions: the available controls

These run automatically on the entities in each alert. What is available depends on the columns your query returns.

ActionWhat 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.
Detection rule automated actions: device actions available, file and user actions unavailable

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.

Detection rule scope set to a specific device group
Detection rule review page: every hour, 4-hour lookback, dynamic alert title

8. Taking this to production

  1. Build the list first. Run Part 3 query 7 to find the remote MCP servers already in use, and decide which are sanctioned.
  2. Allow before you block. Create Allow indicators for sanctioned servers, then Audit for the rest, then Block once you trust what Audit tells you.
  3. Deploy the detection rule alongside. Alert-only to begin with; add automated actions once the alerts are clean.
  4. 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.
  5. Expect unblock requests. Agents will tell users how to ask for an Allow indicator. Have an approval route ready.
  6. 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.


The rest of the series

References

Seรกn Oโ€™Farrell | cloud-secure.ai

Entra Internet Access inline DLP: stopping sensitive data before it reaches AI apps

Entra Internet Access inline DLP: Entra Internet Access, Purview inline DLP and Scan with Purview, stopping sensitive data before it reaches AI apps

Scripts on GitHub: the PowerShell scripts, READMEs and test samples used in this post are in cloud-secure-ai/entra-internet-access-inline-dlp.

Summary

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.

Scan with Purview: users and devices connect through dedicated tunnels to Global Secure Access, which sends content to Microsoft Purview and enforces the policy verdict before traffic reaches websites and cloud applications.
  • 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
Graph scopes (read) NetworkAccess.Read.All, Policy.Read.All, Directory.Read.All, Group.Read.All, DeviceManagementConfiguration.Read.All
Tenant state 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 artefacts - screenshot 52

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 1 - Enable Internet Access traffic forwarding - screenshot 3

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.

Step 2 - Create the TLS inspection certificate - screenshot 4

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 2 - Create the TLS inspection certificate - screenshot 5

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 3 - Distribute the root CA to endpoints - screenshot 6
Step 3 - Distribute the root CA to endpoints - screenshot 7

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 firewall
New-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 4 - Apply client prerequisites - screenshot 8
Step 4 - Apply client prerequisites - screenshot 9

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 5 - Create the TLS inspection policy - screenshot 10
Step 5 - Create the TLS inspection policy - screenshot 11
Step 5 - Create the TLS inspection policy - screenshot 12

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 6 - Create the security profile - screenshot 13

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.

Step 7 - Create the content policy - screenshot 14
Step 7 - Create the content policy - screenshot 15

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 8 - Link the security profile via Conditional Access - screenshot 16
Step 8 - Link the security profile via Conditional Access - screenshot 17
Step 8 - Link the security profile via Conditional Access - screenshot 18

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.

Keywords: project reference, project ref, project code, codename, confidential project, Contoso project

Regex: \bCONTOSO-[A-Z]{3}-\d{6}-[A-Z]\b

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.

Confirm the import: Get-DlpSensitiveInformationType -Identity "Contoso Project Reference (Demo)" | Format-List Name, Id, Publisher.

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 9 - Create the Purview sensitive information type (SIT) - screenshot 19

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.

Step 10 - Create the Purview DLP policy - screenshot 20
Step 10 - Create the Purview DLP policy - screenshot 21
Step 10 - Create the Purview DLP policy - screenshot 22
Step 10 - Create the Purview DLP policy - screenshot 23
Step 10 - Create the Purview DLP policy - screenshot 24

The adaptive scopes read the app catalogue from Defender for Cloud Apps; they do not read Defender for Cloud Apps tags (sanctioned, unsanctioned).

Step 10 - Create the Purview DLP policy - screenshot 25
Step 10 - Create the Purview DLP policy - screenshot 26
Step 10 - Create the Purview DLP policy - screenshot 27
Step 10 - Create the Purview DLP policy - screenshot 28
Step 10 - Create the Purview DLP policy - screenshot 30
Step 10 - Create the Purview DLP policy - screenshot 31
Step 10 - Create the Purview DLP policy - screenshot 32
Step 10 - Create the Purview DLP policy - screenshot 33
Step 10 - Create the Purview DLP policy - screenshot 34
Step 10 - Create the Purview DLP policy - screenshot 35

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.

Untrusted inspection certificate - screenshot 36

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.

Resource provider not registered - screenshot 37

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:

  1. 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.
  1. Export the Global Secure Access traffic logs and open the Transactions tab. Per-request detail is not on the Connections tab.
  2. Filter to the application’s host and locate the POST carrying the payload, identified by a non-trivial sentBytes value.
  3. Read httpRequestContentType before reading action. JSON, form-encoded or plain text predicts enforcement; application/connect+proto, grpc or similar binary encodings predict the request passing uninspected.
  4. 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.

1. Entra Internet Access traffic logs - screenshot 38

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.

2. DLP policy alerts - screenshot 39

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") -PageSize 5000 -OutputFormat Csv
$res.ResultData | Out-File .\activity-explorer-ai.csv
3. DSPM for AI (classic) Activity explorer - screenshot 40

Reading the three together

Traffic log / DLP alert / Activity explorer Conclusion
Blocked by content rule / alert raised / DLP rule matched, plane network Working as designed.
Allowed, binary content type / no alert / AI interaction with no sensitive information type Uninspectable transport - the Claude prompt case.
No entry / no alert / no activity Traffic not acquired - client, forwarding profile or QUIC.
Allowed, JSON / no alert / no activity Scan path not working - content rule destinations, Purview adaptive scope, resource provider, or policy not yet distributed (up to 24 hours).
Allowed / alert raised / DLP rule matched, plane browser 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:

Table Carries Caveat
DataSecurityEvents (preview) DlpPolicyMatchInfo, DlpPolicyEnforcementMode, SensitiveInfoTypeInfo, ApplicationNames, TargetUrlDomain 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
| where Timestamp > ago(7d)
| summarize Events = count() by Workload, ActionType, DlpPolicyEnforcementMode
| order by Events desc
// Does anything carry AI app destinations?
DataSecurityEvents
| where Timestamp > ago(7d)
| where isnotempty(TargetUrlDomain) or isnotempty(ApplicationNames)
| project Timestamp, AccountUpn, ApplicationNames, TargetUrlDomain,
ActionType, DlpPolicyEnforcementMode, SensitiveInfoTypeInfo
| order by Timestamp desc

Enforcement and alert queries

// Block actions only. DlpPolicyEnforcementMode: 0 None, 1 Audit, 2 Warn,
// 3 Warn and bypass, 4 Block, 5 Allow
DataSecurityEvents
| where Timestamp > ago(30d)
| where DlpPolicyEnforcementMode == 4
| project Timestamp, AccountUpn, ActionType, ObjectName,
ApplicationNames, TargetUrlDomain, DlpPolicyMatchInfo
| order by Timestamp desc
// Destinations ranked by volume - useful for spotting unsanctioned AI apps in use
DataSecurityEvents
| where Timestamp > ago(30d)
| where isnotempty(TargetUrlDomain)
| summarize Events = count(), Users = dcount(AccountUpn)
by TargetUrlDomain, ActionType
| order by Events desc
// DLP alerts with their evidence
AlertInfo
| where Timestamp > ago(7d)
| where ServiceSource == "Microsoft Data Loss Prevention"
| join kind=inner AlertEvidence on AlertId
| project Timestamp, Title, Severity, EntityType, AccountUpn, RemoteUrl, FileName
| order by Timestamp desc
// Cloud app audit activity for a specific destination
CloudAppEvents
| where Timestamp > ago(7d)
| where AccountDisplayName != ""
| where ApplicationId != 0
| summarize Events = count() by Application, ActionType
| order by Events desc

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 - paste - screenshot 41

Perplexity AI - file upload

Perplexity AI - file upload - screenshot 42

Meta AI - paste

Meta AI - paste - screenshot 43

Meta AI - file upload

Meta AI - file upload - screenshot 44

Grok - paste

Grok - paste - screenshot 45

No response.

Grok - file upload

File processing failed.

Grok - file upload - screenshot 46

Mistral AI - paste

Won’t accept the paste.

Mistral AI - paste - screenshot 47

Mistral AI - file upload

Won’t accept the upload - no error shown.

Mistral AI - file upload - screenshot 48

Claude - paste

Claude - paste - screenshot 49
Claude - paste - screenshot 50

Claude - file upload

Claude - file upload - screenshot 51

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
GSA content policy and its Scan with Purview rule Manual Not exposed in the Graph beta surface
Custom sensitive information type Scripted Import-ContosoSIT.ps1 (Security & Compliance PowerShell)
Purview inline web traffic DLP policy Scripted 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.

  1. 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.
  2. 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.
  3. Prove each AI app your users actually use with the Validation method, and record the result and content type in the coverage register.
  4. Brief the service desk. Blocks appear as the application’s own errors, not as a Purview message.
  5. Tune alerting before widening the groups, so genuine matches are not buried.
  6. Widen in stages, root CA and QUIC policy first, then users.
  7. 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/.

Endpoint Returns
networkAccess/tenantStatus Onboarding state
networkAccess/settings/conditionalAccess Signalling status and options
networkAccess/settings/crossTenantAccess Packet tagging settings
networkAccess/forwardingProfiles?$expand=policies Traffic forwarding profiles and assignments
networkAccess/filteringProfiles?$expand=policies Security profiles and policy links
networkAccess/filteringPolicies?$expand=policyRules Web content filtering policies
networkAccess/cloudFirewallPolicies?$expand=policyRules Cloud firewall policies
networkAccess/tlsInspectionPolicies?$expand=policyRules TLS inspection policies and rules
networkAccess/connectivity/remoteNetworks Remote networks
networkAccess/connectivity/branches Branches

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.

  1. Paste test. Paste the sensitive string into the prompt box and submit. Record the result.
  1. 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:

  1. Confirm it loads and a benign prompt succeeds, ruling out an unrelated block from another control.
  1. Submit the sensitive information type as a typed prompt.
  1. Upload a document containing the same value.
  1. Paste the document’s contents into the prompt box.
  1. 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.

Microsoft Purview

Global Secure Access and Entra Internet Access

Microsoft Graph

Intune

PowerShell

Microsoft 365 message centre


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.

Seรกn O’Farrell | Cloud-Secure.ai