Bot Framework SDK
18 TopicsBot Framework: Build an AI Security Assistant with ease
How to create intelligent Bots with the Bot Framework Intro In an era where cybersecurity threats loom large, the need for vigilant and responsive security measures has never been greater. The Microsoft Bot Framework SDK, with its powerful AI capabilities, offers a new frontier in security management. This blog post will delve into the development of such an AI security assistant, showcasing how to leverage the SDK to interpret security logs, generate KQL queries, and provide real-time security alerts. We’ll explore how to integrate with existing security infrastructure and harness the power of AI to build our own AI Security Assistant. Join us as we explore this exciting intersection of AI and cybersecurity, where intelligent bots stand guard against the ever-evolving landscape of digital threats. Setup Before we start with the Bot Framework SDK, we need to prepare our development environment. This section will guide you through the necessary steps to set up your “canvas” and get started with building your AI-powered writing assistant. Prerequisites: Visual Studio: Ensure you have Visual Studio installed with the .NET desktop development workload. You can download it from the official Microsoft website. Azure Subscription: An active Azure subscription is required to access the Copilot SDK and its related services. If you don’t have one already, you can sign up for a free trial. Bot Framework Emulator: This tool allows you to test your bot locally before deploying it to Azure. Download it from the Bot Framework website. Creating a New Bot Project: Install the Bot Framework SDK: Open Visual Studio and create a new project. Choose the “Echo Bot (Bot Framework v4)” template. This template provides a basic bot structure to get you started quickly. Install the required NuGet Packages: Azure.AI.OpenAI Azure.Core Microsoft.Bot.Builder.Integration.AspNet.Core Configure Your Bot: In the file, you’ll need to configure your bot with the appropriate API keys and endpoints for the Copilot service. You can obtain these credentials from your Azure portal.appsettings.json { "MicrosoftAppType": "xxxxxx", // Leave it empty until publish "MicrosoftAppId": "xxxx", // Leave it empty until publish "MicrosoftAppPassword": "xxxx", // Leave it empty until publish "MicrosoftAppTenantId": "xxxxxx", // Leave it empty until publish "AzureOpenAI": { "ApiKey": "xxxxxxxxxx", "DeploymentName": "gpt-4o", "Endpoint": "https://xxxx.openai.azure.com" }, "AzureSentinel": { // Log Analytics "ClientId": "xxxx", "ClientSecret": "xxxxx", "TenantId": "xxxx", "WorkspaceId": "xxxxx" } } When you open the Echo Bot we need to make changes to our code in order to achieve 3 things: Azure OpenAI Chat Interaction and generic advice KQL Query generation KQL Query execution against a Sentinel Workspace \ Log Analytics The main program is EchoBot.cs (you can rename as needed). using Microsoft.Bot.Builder; using Microsoft.Bot.Schema; using Newtonsoft.Json; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Azure.AI.OpenAI; using Azure; using System.Collections.Generic; using System.Linq; using System; using System.Text.RegularExpressions; namespace SecurityBot.Bots { public class Security : ActivityHandler { private readonly HttpClient _httpClient; private readonly AzureOpenAIClient _azureClient; private readonly string _chatDeployment; private readonly IConfiguration _configuration; private Dictionary<string, int> eventMapping; // Declare eventMapping here public Security(IConfiguration configuration) { _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); _httpClient = new HttpClient(); // Load event mappings from JSON file string eventMappingPath = Path.Combine(AppContext.BaseDirectory, "eventMappings.json"); if (File.Exists(eventMappingPath)) { var json = File.ReadAllText(eventMappingPath); eventMapping = JsonConvert.DeserializeObject<Dictionary<string, int>>(json); } // Azure OpenAI Chat API configuration var endpoint = configuration["AzureOpenAI:Endpoint"]; var apiKey = configuration["AzureOpenAI:ApiKey"]; _chatDeployment = configuration["AzureOpenAI:DeploymentName"]; // Your Chat model deployment name // Initialize the Azure OpenAI client _azureClient = new AzureOpenAIClient(new Uri(endpoint), new AzureKeyCredential(apiKey)); } protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken) { var userInput = turnContext.Activity.Text.ToLower(); // Detect if the user wants to generate a query if (userInput.Contains("generate")) { // If the user says "generate", extract event and date, then generate the query var kqlQuery = await BuildKQLQueryFromInput(userInput, turnContext, cancellationToken); await turnContext.SendActivityAsync(MessageFactory.Text($"Generated KQL Query: {kqlQuery}"), cancellationToken); } else if (userInput.Contains("run")) { // If the user says "run", extract event and date, then run the query var kqlQuery = await BuildKQLQueryFromInput(userInput, turnContext, cancellationToken); var queryResult = await RunKqlQueryAsync(kqlQuery); await turnContext.SendActivityAsync(MessageFactory.Text($"KQL Query: {kqlQuery}\n\nResult: {queryResult}"), cancellationToken); } else { // For other inputs, handle the conversation with Azure OpenAI await GenerateChatResponseAsync(turnContext, userInput, cancellationToken); } } // Generate responses using the Azure OpenAI Chat API without streaming private async Task GenerateChatResponseAsync(ITurnContext<IMessageActivity> turnContext, string userInput, CancellationToken cancellationToken) { var chatClient = _azureClient.GetChatClient(_chatDeployment); // Set up the chat conversation context var chatMessages = new List<ChatMessage> { new SystemChatMessage("You are a cybersecurity assistant responding only to Security related questions. For irrelevant topics answer with 'Irrelevant'"), new UserChatMessage(userInput) }; // Call the Azure OpenAI API to get the complete chat response var chatResponse = await chatClient.CompleteChatAsync(chatMessages); // Access the completion content properly var assistantMessage = chatResponse.Value.Content.FirstOrDefault()?.Text; if (!string.IsNullOrEmpty(assistantMessage)) { // Send the entire response to the user at once await turnContext.SendActivityAsync(MessageFactory.Text(assistantMessage.ToString().Trim()), cancellationToken); } else { await turnContext.SendActivityAsync(MessageFactory.Text("I'm sorry, I couldn't process your request."), cancellationToken); } } // Build a KQL query from the user's input using Text Analytics private async Task<string> BuildKQLQueryFromInput(string input, ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken) { // Start with a base KQL query string kqlQuery = "SecurityEvent | where 1 == 1 "; // Use the eventMapping dictionary to map the user's input to an EventID var matchedEventId = eventMapping.FirstOrDefault(mapping => input.Contains(mapping.Key)).Value; if (matchedEventId != 0) // EventID was found { kqlQuery += $"| where EventID == {matchedEventId} "; } else { // Fallback if no matching EventID is found await turnContext.SendActivityAsync(MessageFactory.Text("Sorry, I couldn't find a matching event ID for your request."), cancellationToken); return null; // Exit early if no valid EventID is found } // Extract the DateRange (e.g., "7 days") and add it to the query var dateRange = ExtractDateRange(input); if (!string.IsNullOrEmpty(dateRange)) { kqlQuery += $"| where TimeGenerated > ago({dateRange}) | project TimeGenerated, Account, Computer, EventID | take 10 "; } return kqlQuery; // Return the constructed KQL query } private string ExtractDateRange(string input) { // Simple extraction logic to detect "7 days", "3 days", etc. var match = Regex.Match(input, @"(\d+)\s+days?"); if (match.Success) { return $"{match.Groups[1].Value}d"; // Return as "7d", "3d", etc. } return null; // Return null if no date range found } // Run KQL query in Azure Sentinel / Log Analytics private async Task<string> RunKqlQueryAsync(string kqlQuery) { var _workspaceId = _configuration["AzureSentinel:WorkspaceId"]; string queryUrl = $"https://api.loganalytics.io/v1/workspaces/{_workspaceId}/query"; var accessToken = await GetAccessTokenAsync(); // Get Azure AD token var requestBody = new { query = kqlQuery }; var jsonContent = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json"); _httpClient.DefaultRequestHeaders.Clear(); _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}"); var response = await _httpClient.PostAsync(queryUrl, jsonContent); var responseBody = await response.Content.ReadAsStringAsync(); return responseBody; // Return the query result } // Get Azure AD token for querying Log Analytics private async Task<string> GetAccessTokenAsync() { var _tenantId = _configuration["AzureSentinel:TenantId"]; var _clientId = _configuration["AzureSentinel:ClientId"]; var _clientSecret = _configuration["AzureSentinel:ClientSecret"]; var url = $"https://login.microsoftonline.com/{_tenantId}/oauth2/v2.0/token"; var body = new Dictionary<string, string> { { "grant_type", "client_credentials" }, { "client_id", _clientId }, { "client_secret", _clientSecret }, { "scope", "https://api.loganalytics.io/.default" } }; var content = new FormUrlEncodedContent(body); var response = await _httpClient.PostAsync(url, content); var responseBody = await response.Content.ReadAsStringAsync(); dynamic result = JsonConvert.DeserializeObject(responseBody); return result.access_token; } } } Event ID Mapping Let’s map most important Event ids to utterances. The Solution can be enhanced with Text Analytics and NLU, but for this workshop we are creating the dictionary. { "failed sign-in": 4625, "successful sign-in": 4624, "account lockout": 4740, "password change": 4723, "account creation": 4720, "logon type": 4624, "registry value was modified": 4657, "user account was changed": 4738, "user account was enabled": 4722, "user account was disabled": 4725, "user account was deleted": 4726, "user account was undeleted": 4743, "user account was locked out": 4767, "user account was unlocked": 4768, "user account was created": 4720, "attempt was made to duplicate a handle to an object": 4690, "indirect access to an object was requested": 4691, "backup of data protection master key was attempted": 4692, "recovery of data protection master key was attempted": 4693, "protection of auditable protected data was attempted": 4694, "unprotection of auditable protected data was attempted": 4695, "a primary token was assigned to process": 4696, "a service was installed in the system": 4697, "a scheduled task was created": 4698, "a scheduled task was deleted": 4699, "a scheduled task was enabled": 4700, "a scheduled task was disabled": 4701, "a scheduled task was updated": 4702, "a token right was adjusted": 4703, "a user right was assigned": 4704, "a user right was removed": 4705, "a new trust was created to a domain": 4706, "a trust to a domain was removed": 4707, "IPsec Services was started": 4709, "IPsec Services was disabled": 4710 } Make all required updates to Program.cs and Startup.cs for the Namespace and the public class. // Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.22.0 using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; namespace SecurityBot { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } } Testing Run the Application and open the Azure Bot Emulator to test the Bot. All you need is to add the localhost URL to the Emulator and make some Chat interactions for example: What is a SOAR ? Using OpenAI Chat Generate a KQL query for failed sign-in logs on the past 3 days Run a KQL query for failed sign-in logs on the past 3 days We have correct executions and KQL against our Sentinel\Log Analytics workspace. Let’s build this Bot on Azure and use it from our Teams Client as our Trusted Security Assistant ! Build on Azure The logic behind a Bot build on Azure is to create an Azure Web App and then the relevant Azure Bot Service. All the steps are published in Microsoft Documentation. You will find the ARM Templates on the Solution Window in Visual Studio 2022: Use the following commands to create your app registration and set its password. On success, these commands generate JSON output. Use thecommand to create an Microsoft Entra ID app registration.az ad app create This command generates an app ID that you’ll use in the next step. az ad app create –display-name “<app-registration-display-name>” –sign-in-audience “AzureADMyOrg” Usefor a single tenant app.AzureADMyOrg Use thecommand to generate a new password for your app registration. az ad app credential resetad app credential reset --id "<appId>" Record values you’ll need in later steps: theapp IDandpasswordfrom the command output. Once you have the App Registration ready and configured deploy the Web App on Azure using the deployment Templates. Create the App Service and the Azure Bot resources for your bot. Both steps use an ARM template and theAzure CLI command to create the resource or resources.az deployment group create Create an App Service resource for your bot. The App service can be within a new or existing App Service Plan.For detailed steps, seeUse Azure CLI to create an App Service. Create an Azure Bot resource for your bot.For detailed steps, seeUse Azure CLI to create or update an Azure Bot. az deployment group create –resource-group <resource-group> –template-file <template-file-path> –parameters “@<parameters-file-path>” Now time to build and Publish the Bot, make sure you have run the Bot resource ARM deployment as we did with the Web App Create the deployment file for the Bot: Switch to your project’s root folder. For C#, the root is the folder that contains the .csproj file. Do a clean rebuild inrelease mode. If you haven’t done so before, runto add required files to the root of your local source code directory. This command generates afile in your bot project folder.az bot prepare-deploy.deployment Within your project’s root folder, create a zip file that contains all files and sub-folders. I suggest after this to run either: Run theaz webapp deploycommandfrom the command line to perform deployment using the Kudu zip push deployment for your app service (web app). Or select the Publish option from the Solution Explorer and Publish using the created Web App. Remember to add the App ID and the relevant details to appsettings.json we saw earlier. In case you need to re test with the Emulator, remove the App Type, the App ID , Password and Tenant ID settings before running the App locally! Upon success make sure the Bot Messaging Endpoint has the Web App URL we created, followed by the /api/messages suffix. In case it is missing add it: Now we must add the correct API Permissions to the App registration in Entra ID. Select the App Registration, go to API Permissions, add permission and select API My Organization uses. Find the Log analytics and add the Application Permissions for Read: This way we are able to run\execute KQL against our Sentinel – Log Analytics Workspace. Bot Channels – Teams Now that our Bot is active and we can Test in “Test in Web Chat”, we can create the Teams Integration. It is really a simple step, where we select the Teams option from the Channels and verify the configuration. Once we enable that, we can get the HTTPS code from the Get Embed option in the Channels Menu, or open he URL Directly when we select the Teams Channel: Before we start using the Bot we must make a significant configuration in Teams Admin Center. Otherwise the Bot will probably show-up but unable to get messages from the Chat. Bot in Teams Finally we are able to use our Security Assistant Bot in Teams, Web or Desktop App. The Bot will provide generic advice from Azure OpenAI Chat model, will generate KQL queries for a number of Events and execute those Queries in Log Analytics and we will see the results in our UI. We can always change the appearance of the results, in this workshop we have minimal presentation for better visibility. The next phase of this Deployment can utilize Language Service where all Event IDs are dynamically recognized through a Text Analytics service. Conclusion In conclusion, this workshop demonstrated the seamless integration of Azure’s powerful AI services and Log Analytics to build a smart, security-focused chatbot. By leveraging tools like Azure OpenAI, Log Analytics, and the Bot Framework, we’ve empowered bots to provide dynamic insights and interact meaningfully with data. Whether it’s querying log events or responding to security inquiries, this solution highlights the potential of AI-driven assistants to elevate security operations. Keep exploring and building with Azure, and unlock new possibilities in automation and intelligence! Architecture:1.8KViews0likes0CommentsHow to get access token for Graph API in Teams bot-based message extension?
I'm developing a Teams bot-based message extension application using the Teams Toolkit in TypeScript. I need to retrieve all the replies for a message in the current channel. According to the documentation, I need to use the Graph API to get the replies. However, to use the Graph API, I need an access token. My questions are: How can I implement OAuth to get the token in a bot-based message extension? Are there any specific permissions or configurations needed in the Azure portal to enable this? Is there an alternative way to get the access token or retrieve the replies without using the Graph API?1.3KViews0likes5CommentsHow to deploy a Bot/Message extn app developed with Teams Toolkit (5.6) to a self-managed server?
I'm developing a Bot/Message Extension App for use in our organization.Using Teams Toolkit, I have created a project and using Typescript for it. Local testing is automatically supported from app registration without much consideration, so it seems very convenient. However, when I try to deploy this, I am a bit confused. Teams Toolkit seems to support deployment according to Azure Bot Service by default.(.dev) I can't use the Bot Service for a number of reasons and should consider running the bot on a self-managed host. First of all, is this possible? In this case, what steps should I take in Teams Toolkit, Azure portal, Teams development portal? If this is not possible with Teams Toolkit, how should I configure the bot's hosting? I have checked, we have to mention the endpoint URL but seems confuse to where it has to be mention.760Views0likes5CommentsAdaptive Card to send message to the end user by command / Bot Framework SDK - JavaScript - node.js
For MS Teams chatbot, by adaptive cards, I want the bot to send text message to the client when the client chooses the respective command. What İs want is opposite of Action.Submit - I want user to receive message when the button is clicked. I try to optimize Action.OpenUrl and Action.Submit but results weren't satisfying.804Views0likes3CommentsHow to send a message to a thread (reply) using botbuilder 4.0 SDK
How to send a message to a thread (reply) using botbuilder 4.0 SDK Scenario:- 1. User A -> Types a message in Teams App (bot) 2. Now Bot has to reply to the same thread that user A has started inTeams App (bot) I am able to send a new message in theTeams App (bot) - Not expected The bot needs to reply to the same thread the user started - Expected If you have any document link or example, please point it out. Thank you.3.5KViews0likes14CommentsUnit Test Teams Bot/Message Extension App created using Teams Toolkit
Hi, I have created a Teams Bot/Message Extension App using Teams Toolkit using VS Code. I want to unit test my bot. How can I do it, as I couldn't find any documentations for it. And also it is necessary to publish it to the Teams Store. Thanks.417Views0likes0CommentsInserting the final response directly into the chat in a Teams Messaging extension
I am working on an action-based messaging extension app for teams. My action can be invoked from a message/compose/commandBox. After submitting the action, currently the adaptive card I am sending is getting inserted into the chat and the user must manually click on send. I want the message to be directly inserted into the chat.Microsoft documentationsays, "If the message extension is invoked from the compose box or directly from a message, your web service can insert the final response directly into the channel or chat. In this case, the Adaptive Card comes from the bot, the bot updates it, and replies to the conversation thread if needed. You must add the bot object to the app manifest using the same ID and defining the appropriate scopes." But I have no idea how to do this. Yes, we will add the bot to manifest, what next? Currently, I am returning my adaptive card using the following code : async handleTeamsMessagingExtensionSubmitAction(context, action) { switch (action.commandId) { case "executeActions": return await executeActions(context, action); default: throw new Error("NotImplemented"); } } async function executeActions(context, action) { const data = action.data; const adaptiveCard = createAdaptiveCard(data); const attachment = { contentType: adaptiveCard.contentType, content: adaptiveCard.content, preview: adaptiveCard, }; return { composeExtension: { type: "result", attachmentLayout: "list", attachments: [attachment], } } }; Could someone please help how can I tweak the codes, to send messages into the chat directly? So that, the user should not have to click on send every time. I don't mind if the message comes from a bot, into the chat.302Views0likes0CommentsHow to send dm even if the user has not installed the app in personal scope.
I am trying to build a bot which will have a few message extension to submit data and then I will make a request to my custom backend. I want to save the data and want to send dm to the user. I am planning to expose a route from my botbuilder restify server (built by teams toolkit) to send a dm to the user (from bot's account) because I could not find a way to send a dm without bot framework. The only problem is when I am trying to send a dm I am getting an error saying "Bot is not installed in user's personal scope", which is true because the app is only installed in a team. How can I tackle this problem? Basically I am building a feedback bot for companies so I want to proactively send dm message to all the employees and admins. What are different ways to do this thing. Feel free to ask about any other information if you need, thanks!Solved2.4KViews0likes13CommentsReply to message is not working as expected
const { MessageFactory, TeamsActivityHandler, BotFrameworkAdapter, TurnContext, teamsGetChannelId, TeamsInfo } = require('botbuilder'); constproperties= { 'membersAdded':[{'id':'28:17a'}],'type':'conversationUpdate','timestamp':'2022-08-11T10:54:30.482Z','id':'f:ce9c82ab7a6feabc','channelId':'msteams','serviceUrl':'https://smba.trafficmanager.net/in/','from':{'id':'29:1YqsjLwgg-0A_V8tL5P3hIw','aadObjectId':'a0858f3e-d57759'},'conversation':{ 'isGroup':true,'conversationType':'channel','tenantId':'3272b84e1','id':'19:PZppMpIo1@thread.tacv2' },'recipient':{'id':'28:17720bfc7ba','name':'bot 1'},'channelData':{ 'team':{'aadGroupId':'9ffd4120579','name':'Ayesha Testing','id':'19:PZp9pIo1@thread.tacv2'},'eventType':'teamMemberAdded','tenant':{'id':'3284e1'},'settings':{'selectedChannel':{'id':'19:PZ1@thread.tacv2'} } } }; constBotConnector=require('botframework-connector'); constadapter=newBotFrameworkAdapter({ appId:'', appPassword:'' }); BotConnector.MicrosoftAppCredentials.trustServiceUrl(properties.serviceUrl); const context = new TurnContext(adapter, properties); const teamsChannelId = '19:PZpplOG9xxoOHhanG9pIo1@thread.tacv2'; console.log(teamsChannelId); const activity = MessageFactory.text('This will be the first message in a new thread'); const [reference] = await TeamsInfo.sendMessageToTeamsChannel(context, activity, teamsChannelId, process.env.MicrosoftAppId); await context.adapter.continueConversationAsync( reference, async turnContext => { await turnContext.sendActivity(MessageFactory.text('This will be the first response to the new thread')); }); Expected: Actual : When I want to send a Reply to the message but it is sending as a new message. And I have to update that reply message that certain times. Can you please look into that? Also Is there a way to batch send or update messages to users in personal chat, otherwise data might be inconsistent for users.Solved2KViews0likes7Comments