|
| 1 | +--- |
| 2 | +title: Pre-send email analysis - Detecting sensitive data and inappropriate content using Azure AI |
| 3 | +titleSuffix: An Azure Communication Services sample |
| 4 | +description: How to detect sensitive data and inappropriate content in email messages before sending, using Azure AI in Azure Communication Services. |
| 5 | +author: MsftPMACS |
| 6 | +manager: darmour |
| 7 | +services: azure-communication-services |
| 8 | + |
| 9 | +ms.author: maniss |
| 10 | +ms.date: 10/30/2024 |
| 11 | +ms.topic: tutorial |
| 12 | +ms.service: azure-communication-services |
| 13 | +ms.subservice: email |
| 14 | +--- |
| 15 | + |
| 16 | +# Pre-send email analysis: Detecting sensitive data and inappropriate content using Azure AI |
| 17 | + |
| 18 | +Azure Communication Services email enables organizations to send high volume messages to their customers using their applications. This tutorial demonstrates how to leverage Azure AI to ensure that your messages accurately reflect your business’s brand and reputation before sending them. Azure AI offers services to analyze your email content for sensitive data and identify inappropriate content. |
| 19 | + |
| 20 | +This tutorial describes how to use Azure AI Text Analytics to check for sensitive data and Azure AI Content Safety to identify inappropriate text content. Use these functions to check your content before sending the email using Azure Communication Services. |
| 21 | + |
| 22 | +## Prerequisites |
| 23 | + |
| 24 | +You need to complete these quickstarts to set up the Azure AI resources: |
| 25 | + |
| 26 | +- [Quickstart: Detect Personally Identifying Information (PII) in text](/azure/ai-services/language-service/personally-identifiable-information/quickstart) |
| 27 | + |
| 28 | +- [Quickstart: Moderate text and images with content safety in Azure AI Studio](/azure/ai-studio/quickstarts/content-safety) |
| 29 | + |
| 30 | +## Prerequisite check |
| 31 | + |
| 32 | +1. In a terminal or command window, run the dotnet command to check that the .NET client library is installed. |
| 33 | + |
| 34 | + `reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP"` |
| 35 | + |
| 36 | +2. View the subdomains associated with your Email Communication Services resource. Sign in to the Azure portal. Locate your Email Communication Services resource. Open the **Provision domains** tab from the left navigation pane. |
| 37 | + |
| 38 | + :::image type="content" source="./media/email-view-subdomains.png" alt-text="Screenshot showing the email subdomains in your Email Communication Services resource in the Azure portal."::: |
| 39 | + |
| 40 | + > [!NOTE] |
| 41 | + > Make sure that the email sub-domain you plan to use for sending email is verified in your email communication resource. For more information, see [Quickstart: How to add custom verified email domains](../quickstarts/email/add-custom-verified-domains.md). |
| 42 | + |
| 43 | +3. View the domains connected to your Azure Communication Services resource. Sign in to the Azure portal. Locate your Azure Communication Services resource. Open the **Email** > **Domains** tab from the left navigation pane. |
| 44 | + |
| 45 | + :::image type="content" source="./media/email-view-connected-domains.png" alt-text="Screenshot showing the email domains connected to your Email Communication Services resource in the Azure portal."::: |
| 46 | + |
| 47 | + > [!NOTE] |
| 48 | + > Verified custom sub-domains must be connected with your Azure Communication Services resource before you use the resource to send emails. For more information, see [Quickstart: How to connect a verified email domain](../quickstarts/email/connect-email-communication-resource.md). |
| 49 | +
|
| 50 | +## Create a new C# application |
| 51 | + |
| 52 | +This section describes how to create a new C# application, install required packages, and create the Main function. |
| 53 | + |
| 54 | +1. In a console window (such as cmd, PowerShell, or Bash), use the `dotnet new` command to create a new console app with the name `EmailPreCheck`. This command creates a simple "Hello World" C# project with a single source file: `Program.cs`. |
| 55 | + |
| 56 | + `dotnet new console -o EmailPreCheck` |
| 57 | + |
| 58 | +2. Change your directory to the newly created `EmailPreCheck` app folder and use the `dotnet build` command to compile your application. |
| 59 | + |
| 60 | + `cd EmailPreCheck` |
| 61 | + |
| 62 | + `dotnet build` |
| 63 | + |
| 64 | +### Install required packages |
| 65 | + |
| 66 | +From the application directory, install the Azure Communication Services Email client and Azure AI libraries for .NET packages using the `dotnet add package` commands. |
| 67 | + |
| 68 | +`dotnet add package Azure.Communication.Email` |
| 69 | + |
| 70 | +`dotnet add package Azure.AI.TextAnalytics` |
| 71 | + |
| 72 | +`dotnet add package Microsoft.Azure.CognitiveServices.ContentModerator` |
| 73 | + |
| 74 | +## Create the Main function |
| 75 | + |
| 76 | +Open `Program.cs` and replace the existing contents with the following code. The *using* directives include the `Azure.Communication.Email` and `Azure.AI namespaces`. The rest of the code outlines the `SendMail` function for your program. |
| 77 | + |
| 78 | +```csharp |
| 79 | +using System; |
| 80 | +using System.Collections.Generic; |
| 81 | +using System.Threading; |
| 82 | +using System.Threading.Tasks; |
| 83 | + |
| 84 | +using Azure; |
| 85 | +using Azure.Communication.Email; |
| 86 | +using Azure.AI.TextAnalytics; |
| 87 | +using Azure.AI.ContentSafety; |
| 88 | +namespace SendEmail |
| 89 | +{ |
| 90 | + internal class Program |
| 91 | + { |
| 92 | + static async Task Main(string[] args) { |
| 93 | + // Authenticate and create the Azure Communication Services email client |
| 94 | +
|
| 95 | + // Set sample content |
| 96 | + |
| 97 | + // Pre-check for sensitive data and inappropriate content |
| 98 | +
|
| 99 | + // Send Email |
| 100 | + } |
| 101 | + } |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +## Add function that checks for sensitive data |
| 106 | + |
| 107 | +Create a new function to analyze the email subject and body for sensitive data such as social security numbers and credit card numbers. |
| 108 | + |
| 109 | +**??? Where does this go? After *Pre-check sensitive data and inappropriate content* ???** |
| 110 | + |
| 111 | +```csharp |
| 112 | +private static async Task<bool> AnalyzeSensitiveData(List<string> documents) |
| 113 | +{ |
| 114 | +// Client authentication goes here |
| 115 | +
|
| 116 | + |
| 117 | +// Function implementation goes here |
| 118 | +
|
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +### Create the Text Analytics client with authentication |
| 123 | + |
| 124 | +Create a new function with a Text Analytics client that also retrieves your connection information. Add the following code into the `AnalyzeSensitiveData` function to retrieve the connection key and endpoint for the resource from environment variables named `LANGUAGE_KEY` and `LANGUAGE_ENDPOINT`. It also creates the new `TextAnalyticsClient` **??? function or variable or ???** . For more information about managing your Text Analytics connection information, see [Quickstart: Detect Personally Identifiable Information \(PII\) > Get your key and endpoint](/azure/ai-services/language-service/personally-identifiable-information/quickstart#get-your-key-and-endpoint). |
| 125 | + |
| 126 | +```csharp |
| 127 | +// This example requires environment variables named "LANGUAGE_KEY" and "LANGUAGE_ENDPOINT" |
| 128 | +string languageKey = Environment.GetEnvironmentVariable("LANGUAGE_KEY"); |
| 129 | +string languageEndpoint = Environment.GetEnvironmentVariable("LANGUAGE_ENDPOINT"); |
| 130 | +var client = new TextAnalyticsClient(new Uri(languageEndpoint), new AzureKeyCredential(languageKey)); |
| 131 | + |
| 132 | +``` |
| 133 | + |
| 134 | +### Check the content for sensitive data |
| 135 | + |
| 136 | +Loop through the content to check for any sensitive data. Start the sensitivity check with a baseline of `false`. If sensitive data is found, return `true`. |
| 137 | + |
| 138 | +Add the following code into the `AnalyzeSensitiveData` function following the line that creates the `TextAnalyticsClient` **???variable???** . |
| 139 | + |
| 140 | +```csharp |
| 141 | +bool senstiveDataDetected = false; // we start with a baseline that of no sensitive data |
| 142 | +var actions = new TextAnalyticsActions |
| 143 | +{ |
| 144 | + RecognizePiiEntitiesActions = new List<RecognizePiiEntitiesAction> { new RecognizePiiEntitiesAction() } |
| 145 | +}; |
| 146 | + |
| 147 | +var operation = await client.StartAnalyzeActionsAsync(documents, actions); |
| 148 | +await operation.WaitForCompletionAsync(); |
| 149 | + |
| 150 | +await foreach (var documentResults in operation.Value) |
| 151 | +{ |
| 152 | + foreach (var actionResult in documentResults.RecognizePiiEntitiesResults) |
| 153 | + { |
| 154 | + if (actionResult.HasError) |
| 155 | + { |
| 156 | + Console.WriteLine($"Error: {actionResult.Error.ErrorCode} - {actionResult.Error.Message}"); |
| 157 | + |
| 158 | + } |
| 159 | + else |
| 160 | + { |
| 161 | + foreach (var document in actionResult.DocumentsResults) |
| 162 | + { |
| 163 | + foreach (var entity in document.Entities) |
| 164 | + { |
| 165 | + if (document.Entities.Count > 0) |
| 166 | + { |
| 167 | + senstiveDataDetected = true; // Sensitive data detected |
| 168 | + } |
| 169 | + |
| 170 | + } |
| 171 | + } |
| 172 | + } |
| 173 | + |
| 174 | + } |
| 175 | +} |
| 176 | +return senstiveDataDetected; |
| 177 | +``` |
| 178 | + |
| 179 | +## Add function that checks for inappropriate content |
| 180 | + |
| 181 | +Create another new function to analyze the email subject and body for inappropriate content such as hate or violence. |
| 182 | + |
| 183 | +```csharp |
| 184 | +static async Task<bool> AnalyzeInappropriateContent(List<string> documents) |
| 185 | +{ |
| 186 | +// Client authentication goes here |
| 187 | +
|
| 188 | +// Function implementation goes here |
| 189 | +} |
| 190 | +``` |
| 191 | + |
| 192 | +### Create the Content Safety client with authentication |
| 193 | + |
| 194 | +Create a new function with a Content Safety client that also retrieves your connection information. Add the following code into the `AnalyzeInappropriateContent` function to retrieve the connection key and endpoint for the resource from environment variables named `CONTENT_LANGUAGE_KEY` and `CONTENT_LANGUAGE_ENDPOINT`. It also creates a new `ContentSafetyClient` **???variable???** . If you're using the same Azure AI instance for Text Analytics, these values remain the same. For more information about managing your Content Safety connection information, see [Quickstart: Detect Personally Identifiable Information (PII) > Create environment variables](/azure/ai-services/language-service/personally-identifiable-information/quickstart#create-environment-variables). |
| 195 | + |
| 196 | +```csharp |
| 197 | +// This example requires environment variables named "CONTENT_LANGUAGE_KEY" and "CONTENT_LANGUAGE_ENDPOINT" |
| 198 | + string contentSafetyLanguageKey = Environment.GetEnvironmentVariable("CONTENT_LANGUAGE_KEY"); |
| 199 | +string contentSafetyEndpoint = Environment.GetEnvironmentVariable("CONTENT_LANGUAGE_ENDPOINT"); |
| 200 | +var client = new ContentSafetyClient(new Uri(contentSafetyEndpoint), new AzureKeyCredential(contentSafetyLanguageKey)); |
| 201 | +``` |
| 202 | + |
| 203 | +### Check for inappropriate content |
| 204 | + |
| 205 | +Loop through the content to check for inappropriate content. Start the inappropriate content detection with a baseline of `false`. If inappropriate content is found, return `true`. |
| 206 | + |
| 207 | +Add the following code into the `AnalyzeInappropriateContent` function after the line that creates the `ContentSafetyClient` **???variable???** . |
| 208 | + |
| 209 | +```csharp |
| 210 | +bool inappropriateTextDetected = false; |
| 211 | +foreach (var document in documents) |
| 212 | +{ |
| 213 | + var options = new AnalyzeTextOptions(document); |
| 214 | + AnalyzeTextResult response = await client.AnalyzeTextAsync(options); |
| 215 | + // Check the response |
| 216 | + if (response != null) |
| 217 | + { |
| 218 | + // Access the results from the response |
| 219 | + foreach (var category in response.CategoriesAnalysis) |
| 220 | + { |
| 221 | + if (category.Severity > 2) // Severity: 0=safe, 2=low, 4=medium, 6=high |
| 222 | + { |
| 223 | + inappropriateTextDetected = true; |
| 224 | + } |
| 225 | + } |
| 226 | + } |
| 227 | + else |
| 228 | + { |
| 229 | + Console.WriteLine("Failed to analyze content."); |
| 230 | + } |
| 231 | +} |
| 232 | +return inappropriateTextDetected; // No inappropriate content detected |
| 233 | +``` |
| 234 | + |
| 235 | +## Update the Main function to run prechecks and send email |
| 236 | + |
| 237 | +Now that you added the two functions for checking for sensitive data and inappropriate content, you can call them before sending email from Azure Communication Services. |
| 238 | + |
| 239 | +### Create and authenticate the email client |
| 240 | + |
| 241 | +You have a few options available for authenticating to an email client. This example fetches your connection string from an environment variable. |
| 242 | + |
| 243 | +Open `Program.cs` in an editor. Add the following code to the body of the Main function to initialize an `EmailClient` with your connection string. This code retrieves the connection string for the resource from an environment variable named `COMMUNICATION_SERVICES_CONNECTION_STRING`. For more information about managing your resource connection string, see [Quickstart: Create and manage Communication Services resources > Store your connection string](../quickstarts/create-communication-resource.md#store-your-connection-string). |
| 244 | + |
| 245 | +```csharp |
| 246 | +// This code demonstrates how to fetch your connection string from an environment variable. |
| 247 | +string connectionString = Environment.GetEnvironmentVariable("COMMUNICATION_SERVICES_CONNECTION_STRING"); |
| 248 | +EmailClient emailClient = new EmailClient(connectionString); |
| 249 | +``` |
| 250 | + |
| 251 | +## Add Sample Content |
| 252 | + |
| 253 | +Add the sample email content into the Main function, following the lines that create the email client. |
| 254 | + |
| 255 | +You need to get the sender email address. For more information about Azure Communication Services email domains, see [Quickstart: How to add Azure Managed Domains to Email Communication Service](../quickstarts/email/add-custom-verified-domains.md). |
| 256 | + |
| 257 | +Modify the recipient email address. **??? Is this enough information ???** |
| 258 | + |
| 259 | +Put both the subject and the message body into a `List<string>` which can be used by the two content checking functions. |
| 260 | + |
| 261 | +```csharp |
| 262 | +//Set sample content |
| 263 | +var sender = "[email protected]"; //obtain the send email from your email resource in the Azure Portal |
| 264 | +var recipient = "[email protected]"; //modify the recipient |
| 265 | +var subject = "Precheck Azure Communication Service Email with Azure AI"; |
| 266 | +var htmlContent = "<html><body><h1>Precheck email test</h1><br/><h4>This email message is sent from Azure Communication Service Email. </h4>"; |
| 267 | +htmlContent += "<p> My SSN is 123-12-1234. My Credit Card Number is: 1234 4321 5678 8765. My address is 1011 Main St, Redmond, WA, 998052 </p>"; |
| 268 | +htmlContent += "<p>A 51-year-old man was found dead in his car. There were blood stains on the dashboard and windscreen."; |
| 269 | +htmlContent += "At autopsy, a deep, oblique, long incised injury was found on the front of the neck. It turns out that he died by suicide.</p>"; |
| 270 | +htmlContent += "</body></html>"; |
| 271 | +List<string> documents = new List<string> { subject, htmlContent }; |
| 272 | +``` |
| 273 | + |
| 274 | +### Pre-check content before sending email |
| 275 | + |
| 276 | +You need to call the two functions to look for violations and use the results to determine whether or not to send the email. Add the following code to the Main function after the sample content. |
| 277 | + |
| 278 | +```csharp |
| 279 | +// Pre-Check content |
| 280 | +bool containsSensitiveData = await AnalyzeSensitiveData(documents); |
| 281 | +bool containsInappropriateContent = await AnalyzeInappropriateContent(documents); |
| 282 | + |
| 283 | +// Send email only if not sensitive data or inappropriate content is detected |
| 284 | +if (containsSensitiveData == false && containsInappropriateContent == false) |
| 285 | +{ |
| 286 | + |
| 287 | + /// Send the email message with WaitUntil.Started |
| 288 | + EmailSendOperation emailSendOperation = await emailClient.SendAsync( |
| 289 | + Azure.WaitUntil.Started, |
| 290 | + sender, |
| 291 | + recipient, |
| 292 | + subject, |
| 293 | + htmlContent); |
| 294 | + |
| 295 | + /// Call UpdateStatus on the email send operation to poll for the status manually |
| 296 | + try |
| 297 | + { |
| 298 | + while (true) |
| 299 | + { |
| 300 | + await emailSendOperation.UpdateStatusAsync(); |
| 301 | + if (emailSendOperation.HasCompleted) |
| 302 | + { |
| 303 | + break; |
| 304 | + } |
| 305 | + await Task.Delay(100); |
| 306 | + } |
| 307 | + |
| 308 | + if (emailSendOperation.HasValue) |
| 309 | + { |
| 310 | + Console.WriteLine($"Email queued for delivery. Status = {emailSendOperation.Value.Status}"); |
| 311 | + } |
| 312 | + } |
| 313 | + catch (RequestFailedException ex) |
| 314 | + { |
| 315 | + Console.WriteLine($"Email send failed with Code = {ex.ErrorCode} and Message = {ex.Message}"); |
| 316 | + } |
| 317 | + |
| 318 | + /// Get the OperationId so that it can be used for tracking the message for troubleshooting |
| 319 | + string operationId = emailSendOperation.Id; |
| 320 | + Console.WriteLine($"Email operation id = {operationId}"); |
| 321 | +} |
| 322 | +else |
| 323 | +{ |
| 324 | + Console.WriteLine("Sensitive data and/or inappropriate content detected, email not sent\n\n"); |
| 325 | +} |
| 326 | +``` |
| 327 | + |
| 328 | +## Next steps |
| 329 | + |
| 330 | +**???Any???** |
| 331 | + |
| 332 | +## Related articles |
| 333 | + |
| 334 | +**???Any???** |
0 commit comments