Integrating Microsoft Agent Framework with Microsoft Purview Services for Data Governance for AI Agents

As more organizations adopt AI-driven setups and applications the need for governance and operational security across these services has been more than important than ever before. You definitely wouldn't want your AI agents to have open access across your systems without any form of governance or compliances regulating them.
This is where Microsoft Purview provides a powerful platform for seamlessly securing and governing your data. Microsoft Purview helps reduce complexity and improve governance related to mitigating risks in the era of AI.
Microsoft Agent Framework supports seamless integration with Microsoft Purview to help implement governance and compliance for your AI agents.
If you aren't much aware of Microsoft Purview and its capabilities, I would highly recommend to get familiar with it . A good starting point can be Microsoft official documentation on Microsoft Purview here.
Prerequisites
You will require Azure subscription with a M365 E5 license. This is a must. Any other license for example E3 or Business premium or Standard license unfortunately will not work.
Also required is Microsoft 365 pay-as-you-go option enabled.
A thorough understanding of Microsoft Entra and OIDC authentication through MSAL.NET and how they integrate in the overall Microsoft ecosystem. I have extensively used them across all of my blogs on Microsoft fabric and Microsoft Azure. I would like to highlight a couple of them to make understand on how to integrate them. You can find them here and here .Though the articles are specific for with Microsoft fabric and Azure , they will help you to get a general understanding of the concepts.
Some conceptual understanding of Microsoft Purview like policy framework and how these policies are used to govern organizational data.
Below is a conceptual flow of MSAL.NET which is generic OAuth 2.0 and OIDC client.
SetUp
The very first step is to setup a Service Principal in Microsoft Entra that has the following Microsoft Graph permissions :
ProtectionScopes.Compute.All
ContentActivity.Write
Content.Process.All
Purview.DelegatedAccess
ProtectionScopes.Compute.All
I created a one named Purview_Monitoring
Next , the following prerequisites has to be configured in Purview.
The "Configuration" names are little different in the new Purview UI
Microsoft Purview Audit >>
Its possible to turn it ON in two ways
Through the Purview portal or PowerShell
Purview Portal >>
Click "Start recording user and admin activity" to enable the Audit option.
PowerShell >>
At the very first , to use PowerShell for Purview you will have to use the following command
Connect-IPPSSession
to connect to Security & Compliance
Use PowerShell command line and not PowerShell ISE
Connect-IPPSSession will prompt you to authenticate your credentials
Once authenticated, load to the ExchangeOnlineManagement cmdlet. If not already installed, the cmdlet will auto install it for you.
Import-Module ExchangeOnlineManagement
Next, connect to Exchange Online PowerShell through your credentials. In my case it is sachin.nandanwar@azureguru.net
Connect-ExchangeOnline -UserPrincipalName sachin.nandanwar@azureguru.net
and then run the following command to enable Audit logs option.
Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true
It might take about 60 minutes for the Audit logs to be enabled
To check the status, under Solutions click the DSPM for AI (Classic) option
The above option is not available under new DSPM page
DSPM for AI >>
In the next step , enable the KYD Policy.
There is no option to set this up through the UI. You will have to set it up through PowerShell.
$locations = "[{`"Workload`":`"Applications`",`"Location`":`"$myEntraAppId`",`"LocationDisplayName`":`"$myEntraAppName`",`"LocationSource`":`"Entra`",`"LocationType`":`"Individual`",`"Inclusions`":[{`"Type`":`"Tenant`",`"Identity`":`"All`"}]}]"
New-FeatureConfiguration `
-FeatureScenario KnowYourData `
-Name "Secure interactions from enterprise apps (preview)" `
-Mode Enable `
-ScenarioConfig '{"Activities":["UploadText","DownloadText"],"EnforcementPlanes":["Application"],"SensitiveTypeIds":["All"],"IsIngestionEnabled":true}' `
-Locations $locations
I have blurred the the $myEntraAppId value. This is the ClientID of the service principal that was set earlier. Note that"Secure interactions from enterprise apps (preview)" is a policy.
Through the above PowerShell script, we are tying up the service principal with the policy.
Once executed ,under DSPM for AI >>Policies option you should be able to see the created policy.
Communication Compliance >>
Under Solutions, click Communication Compliance
Select the "Detect unethical interactions for agents" option
There is an issue while setting up this policy. Its not possible to set it up for a specific Service Principal.
Check the screenshot below , where I tried to search for the service principal Purview_Monitoring that was configured earlier.
Its un available in the Select users option.
while the Entra users are visible
Anyways, setting it up for All users doesn't impact the flow.
The status is "Activating". It will take sometime for the status to change to "Ready".
Insider Risk Management >>
Under Insider Risk Management option select Quick Policy
and select "Risky AI Usage" policy
This is a standard general policy and is not tied to a specific user/s .
This is the last step and this basically completes all the underlying prerequisites.
Use Case
Now that all the prerequisites are in place, let's look through a use case in which we create a Microsoft Purview policy to block a specific prompt.
Prompts such as "Remember the credit card number..." are typically blocked by the AI model itself and there is no operational need for Microsoft Purview to validate and block the prompt. So the challenge is to create a policy that blocks a specific prompt only when it originates from our set up i.e. through an agent that runs the service principal.
As we saw earlier, the Microsoft Purview UI does not currently provide a way to scope policies to an individual Microsoft Entra service principal under which an Agent executes. This is where PowerShell comes to the rescue.
Lets create a policy that blocks a text say "oranges".
To get started, Run the following commands through PowerShell command line and NOT PowerShell ISE.
Also ensure that you are already connected through Connect-IPPSSession
Connect-IPPSSession
Import-Module ExchangeOnlineManagement
$myEntraAppId = "Service Principal ClientId"
$myEntraAppName = "Service Principal Name"
$locations = "[{`"Workload`":`"Applications`",`"Location`":`"$myEntraAppId`",`"LocationDisplayName`":`"$myEntraAppName`",`"LocationSource`":`"Entra`",`"LocationType`":`"Individual`",`"Inclusions`":[{`"Type`":`"Tenant`",`"Identity`":`"All`"}]}]"
New-DlpCompliancePolicy -Name "Test Oranges DLP" -Mode Enable -Locations $locations -EnforcementPlanes @("Application")
It takes some time for policy status to change from "Sync In Progress" to "Ready".
In the next step create a "Sensitive info types".
There are a lot of predefined "Sensitive info types" but we will create a custom one for our use case.
Name the sensitivity info type as "My Oranges Test SIT".
Next, create a pattern
and select "Add primary element" and under it select "Regular expression" option.
Add ID and keyword "oranges". The text "oranges" will be checked
If required, there is an option to use inbuilt regular expression by clicking the "Choose from existing regular expressions" in the screen above.
In the next screen, keep the Confidence level to High for highest accuracy.
Click Next
You can double check the sensitivity type through the following PowerShell command.
Get-DlpSensitiveInformationType 'My Oranges Test SIT'
In the next step, we will create a rule that is tied with the policy and leverages the sensitivity type that we created.
New-DlpComplianceRule -Name "Test Oranges Rule" -Policy "Test Oranges DLP" -ContentContainsSensitiveInformation @{Name = "My Oranges Test SIT"} -GenerateAlert $true -GenerateIncidentReport @("siteadmin") -NotifyUser @("user@email.com") -RestrictAccess @(@{setting="UploadText";value="Block"})
That's all the settings required in Purview.
At this point, all the prerequisites, policies and sensitivity types in Purview are configured.
Code
In a console application install the following packages
dotnet add package Azure;
dotnet add package Azure.AI.OpenAI;
dotnet add package Microsoft.Agents.AI;
dotnet add package Microsoft.Agents.AI.Purview;
dotnet add package Microsoft.Extensions.AI;
dotnet add package Microsoft.Extensions.Configuration;
dotnet add package Microsoft.Extensions.DependencyInjection;
After the above artifacts are in place, add the following code to read the settings from appsettings.json in Program.cs of the project.
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false)
.Build();
Read the credentials and register a Chatclient
var credential = new AzureKeyCredential(configuration["AppSettings:ApiKey"]);
ServiceCollection servicecollection = new();
builder.Services.AddKeyedChatClient(
"ChatClient",
(
sp =>
new AzureOpenAIClient(new Uri(configuration["AppSettings:EndPoint"]), credential)
.GetChatClient(configuration["AppSettings:Chat_DeploymentName"])
.AsIChatClient()
)
);
In the next step, register the following AIAgent in the hosted DI container.
PurviewAgent >>
servicecollection.AddSingleton<ChatClientAgent>(sp =>
{
Func<ChatClientAgentOptions> func = () =>
{
return new ChatClientAgentOptions
{
ChatOptions = new ChatOptions
{
Instructions ="You are a secure assistant.",
},
Name = "PurviewAgent",
Id = "1"
};
};
return new ChatClientAgent(sp.GetKeyedService<IChatClient>("ChatClient"), options: func());
});
Build service and fetch agent through ServiceProvider as ChatClientAgent.
ServiceProvider serviceProvider = servicecollection.BuildServiceProvider();
var agent = serviceProvider.GetServices<ChatClientAgent>();
List<ChatClientAgents> chatclientagent = new(agent);
This is the most crucial part of the code. Here is where we leverage the Purview library
AIAgent agent_ = chatClientAgents[0].AsBuilder().WithPurview(await .esult(), new PurviewSettings("Purview_Monitoring")
{
BlockedPromptMessage = "<<< BLOCKED BY PURVIEW PROMPT >>>",
BlockedResponseMessage = "<<< BLOCKED BY PURVIEW RESPONSE >>>"
}).Build();
We will have to pass TokenCredentials for the service principal authentication
To implement it, I have used the authentication method where the bearer token generated by MSAL is converted to TokenCredentials.
I have a blog on this topic where I used it for Fabric OneLake authentication for DataLakeServiceClient .
First we will have to create a Credentials class.
AccessTokenCredential.cs >>
using Azure.Core;
using Azure.Identity;
using System.IdentityModel.Tokens.Jwt;
public class AccessTokenCredential : ClientSecretCredential
{
public AccessTokenCredential(string accessToken)
{
AccessToken = accessToken;
}
private string AccessToken;
public AccessToken FetchAccessToken()
{
JwtSecurityToken token = new JwtSecurityToken(AccessToken);
return new AccessToken(AccessToken, token.ValidTo);
}
public override ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
return new ValueTask<AccessToken>(FetchAccessToken());
}
public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
JwtSecurityToken token = new JwtSecurityToken(AccessToken);
return new AccessToken(AccessToken, token.ValidTo);
}
}
and then create authentication class that returns the TokenCredentials
Authentication.cs >>
using Azure.Core;
using Microsoft.Identity.Client;
using System.IdentityModel.Tokens.Jwt;
namespace Security
{
internal class Authentication
{
public static string clientId = "Service Principal Client Id";
private static string[] scopes = ["https://graph.microsoft.com/.default"];
private static string Authority = "https://login.microsoftonline.com/organizations";
private static string RedirectURI = "http://localhost";
public async static Task<TokenCredential> ReturnAuthenticationResult()
{
string AccessToken;
PublicClientApplicationBuilder PublicClientAppBuilder =
PublicClientApplicationBuilder.Create(clientId)
.WithAuthority(Authority)
.WithCacheOptions(CacheOptions.EnableSharedCacheOptions)
.WithRedirectUri(RedirectURI);
IPublicClientApplication PublicClientApplication = PublicClientAppBuilder.Build();
var accounts = await PublicClientApplication.GetAccountsAsync();
AuthenticationResult result;
try
{
result = await PublicClientApplication.AcquireTokenSilent(scopes, accounts.First())
.ExecuteAsync()
.ConfigureAwait(false);
}
catch
{
result = await PublicClientApplication.AcquireTokenInteractive(scopes)
.ExecuteAsync()
.ConfigureAwait(false);
}
JwtSecurityToken token = new JwtSecurityToken(result.AccessToken);
AccessTokenCredential tokenCredential = new AccessTokenCredential(result.AccessToken);
return tokenCredential;
}
}
}
Next, we send a prompt to the model asking about oranges
AgentResponse agentResponse = await agent_.RunAsync("What are oranges ?");
Console.WriteLine(agentResponse);
And the purview would block the response
If the prompt is about grapes there will be a relevant response
AgentResponse agentResponse = await agent_.RunAsync("What are grapes ?");
Console.WriteLine(agentResponse);
Execution >>
Conclusion >>
To wrap it up , a good AI ecosystem success shouldn't be based only by what an AI agent can do, but also by what it should be allowed to do. Microsoft Purview helps bridge that gap by bringing governance directly into the AI application lifecycle.
Microsoft Purview and its seamless integration with the Microsoft Agent Framework, provides a powerful foundation for building AI agents that are not only intelligent but also secure, compliant, and enterprise-ready. Governance shouldn't be an afterthought it should be part of the architecture from day one.
Thanks for reading !!



