Skip to main content

Command Palette

Search for a command to run...

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

Updated
10 min readView as Markdown
Integrating Microsoft Agent Framework with Microsoft Purview Services for Data Governance for AI Agents
S
From Synapse Analytics, Power BI, Spark, Microsoft Fabric,ASP.NET Core and recently Agentic AI on .NET I try to explore, learn and share all aspects of Microsoft Data Stack in this blog.

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 .

https://www.azureguru.net/customize-clientsecretcredential-class-for-onelake-authentication-in-microsoft-fabric

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 !!