This is part 6 of the series “Create Azure Resource Manager Bot“.
Azure Resource Manager is the deployment and management service for Azure. It provides a management layer that enables you to create, update, and delete resources in your Azure account. You use management features, like access control, locks, and tags, to secure and organize your resources after deployment.
We will use the Bot to manage our Azure resources. In this post we will be implementing two functionalities – Get all resource group details and Create a resource group.
Output
Pre-requisites
- Azure Subscription
- Visual Studio
- Bot Framework v4 templates
- Bot Emulator
- Service Principal in Azure – To know how to create a service principal, go through my post on Creating Service Principal using PowerShell.
- LUIS Prediction and Authoring resource
Visual Studio, Bot Emulator, and Templates links are available in Download page.
Creating a LUIS App
Go to the LUIS portal – https://luis.ai and click on My Apps. Select your Azure subscription name and Authoring resource. Authoring resource can be created in Azure -> Create Resource -> Language Understanding.
Click on New App for conversation and select Import as JSON. Below JSON I have exported from the app used in this project. Save this JSON and import it as a new app.
{
"luis_schema_version": "7.0.0",
"intents": [
{
"name": "Cancel",
"features": []
},
{
"name": "CreateResourceGroupIntent",
"features": []
},
{
"name": "GetAllResourceGroupsIntent",
"features": []
},
{
"name": "None",
"features": []
}
],
"entities": [],
"hierarchicals": [],
"composites": [],
"closedLists": [],
"prebuiltEntities": [],
"utterances": [
{
"text": "all resource group",
"intent": "GetAllResourceGroupsIntent",
"entities": []
},
{
"text": "all resource group details",
"intent": "GetAllResourceGroupsIntent",
"entities": []
},
{
"text": "bye",
"intent": "Cancel",
"entities": []
},
{
"text": "cancel",
"intent": "Cancel",
"entities": []
},
{
"text": "close",
"intent": "Cancel",
"entities": []
},
{
"text": "close it",
"intent": "Cancel",
"entities": []
},
{
"text": "create azure resource group",
"intent": "CreateResourceGroupIntent",
"entities": []
},
{
"text": "create new resource group",
"intent": "CreateResourceGroupIntent",
"entities": []
},
{
"text": "create new rg",
"intent": "CreateResourceGroupIntent",
"entities": []
},
{
"text": "create resource group",
"intent": "CreateResourceGroupIntent",
"entities": []
},
{
"text": "create rg",
"intent": "CreateResourceGroupIntent",
"entities": []
},
{
"text": "get me out of here",
"intent": "Cancel",
"entities": []
},
{
"text": "get me the details of all resource groups",
"intent": "GetAllResourceGroupsIntent",
"entities": []
},
{
"text": "logout",
"intent": "Cancel",
"entities": []
},
{
"text": "new resource group",
"intent": "CreateResourceGroupIntent",
"entities": []
},
{
"text": "new rg",
"intent": "CreateResourceGroupIntent",
"entities": []
},
{
"text": "quit",
"intent": "Cancel",
"entities": []
},
{
"text": "show me all resource groups",
"intent": "GetAllResourceGroupsIntent",
"entities": []
},
{
"text": "shutdown",
"intent": "Cancel",
"entities": []
},
{
"text": "what all resource groups there in my subscription",
"intent": "GetAllResourceGroupsIntent",
"entities": []
}
],
"versionId": "0.1",
"name": "AzureResourceManager",
"desc": "",
"culture": "en-us",
"tokenizerVersion": "1.0.0",
"patternAnyEntities": [],
"regex_entities": [],
"phraselists": [],
"regex_features": [],
"patterns": [],
"settings": []
}
Open the app and click on Train and then Publish in Production Slot. Click on Manage -> Settings and make a note of App Id.

Click on Azure Resources -> Add Prediction Resource. If you have not created the Prediction resource in Azure, you can create it here. Make a note of the Primary Key and End Point URL.

Creating a Core Bot project in Visual Studio
Kindly watch the below video or refer to the post to create a Core Bot project using the templates. Will be continuing from the created core bot project. I have used the Core Bot template with .NET Core 3.1 version.
Editing the Project Structure
Remove the following files and folder which are not required for our requirement –
- BookingDetails.cs
- FlightBookingRecognizer.cs
- BookingDialog.cs
- CancelAndHelpDialog.cs
- DateResolverDialog.cs
- CognitiveModels Folder
Remove the following lines of code from Startup.cs
// Register LUIS recognizer
services.AddSingleton<FlightBookingRecognizer>();
// Register the BookingDialog.
services.AddSingleton<BookingDialog>();
Remove all the method content from all methods in MainDialog.cs. Also, remove the imports which are showing error. Ignore the error for methods where return statements are missing. Below is what the MainDialog class will look like after the above removal.
public class MainDialog : ComponentDialog
{
protected readonly ILogger Logger;
// Dependency injection uses this constructor to instantiate MainDialog
public MainDialog(ILogger<MainDialog> logger)
: base(nameof(MainDialog))
{
Logger = logger;
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
IntroStepAsync,
ActStepAsync,
FinalStepAsync,
}));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> IntroStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
}
private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
}
}
Adding new class files and folders
I will be using the namespace “CoreBot” fort his project.
- Create a new class files “User.cs”, “AzureDetails.cs”, and “LUISHelper.cs” under CoreBot namespace.
- Create a new folder “Utilities” under the CoreBot namespace.
- Create a new class files “AzureResourceManagerREST.cs” and “AzureResourcesData.cs” under CoreBot.Utilities namespace.
- Create a new folder “ResourceGroup” under CoreBot.Dialogs namespace.
- Create a new class file “CreateResourceGroupDialog.cs” under CoreBot.Dialogs.Operations namespace.
- Create a new class file “InitialDialog.cs” under CoreBot.Dialogs namespace.
We have now created the structure we will use for this demo. The end structure will look like this –

Creating the Azure Resource Manager Bot
Open the file welcomeCard.json under CoreBot.Cards namespace and replace the content with the following to create a new Adaptive card that will show up when the user joins the conversation:
{
"type": "AdaptiveCard",
"body": [
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Azure Resource Manager Bot",
"size": "ExtraLarge",
"wrap": true,
"weight": "Bolder",
"color": "Dark"
},
{
"type": "TextBlock",
"text": "Welcome to **Azure Resource Manager**. Manage your Azure resources right from here without going to Azure portal or writing PowerShell scripts.",
"wrap": true
}
],
"style": "good",
"bleed": true
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.2",
"actions": [
{
"type": "Action.ShowCard",
"title": "Help",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "To use bot services, say something like\n* \"Show me all resource groups\"\n* \"Create a new resource group\"\n* \"Get details about your subscription\"\n\nTo Logout, say something like\n* \"cancel\"\n* \"logout\"\n* \"quit\"\n\nIt is always a good practice to logout after using the services.",
"wrap": true,
"color": "Dark"
}
],
"style": "good"
}
]
}
},
{
"type": "Action.OpenUrl",
"title": "Go to Azure Portal",
"url": "https://portal.azure.com/"
}
]
}
If you want to learn creating attractive adaptive cards for your bot, refer to my post on “Create Adaptive Cards“. Open appsettings.json and add the configuration details.
{
"LuisAPIHostName": "westus.api.cognitive.microsoft.com",
"LuisAPIKey": "<LUIS Primary Key>",
"LuisAppId": "<LUIS App ID>",
"MicrosoftAppId": "<Microsoft App ID>",
"MicrosoftAppPassword": "<Microsoft App Password>",
"ScmType": "None"
}
For testing the bot locally, MicrosoftAppId and Password are not required. Enter the other details which we have copied from the LUIS portal.
Replace the code in MainDialog.cs with the below code.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// Generated with Bot Builder V4 SDK Template for Visual Studio CoreBot v4.9.2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
using Microsoft.Recognizers.Text.DataTypes.TimexExpression;
using Microsoft.Extensions.Configuration;
using CoreBot.Utilities;
using CoreBot.Dialogs.ResourceGroup;
namespace CoreBot.Dialogs
{
public class MainDialog : ComponentDialog
{
protected readonly IConfiguration Configuration;
protected readonly ILogger Logger;
private readonly IStatePropertyAccessor<User> _userAccessor;
// Dependency injection uses this constructor to instantiate MainDialog
public MainDialog(IConfiguration configuration, ILogger<MainDialog> logger, UserState userState)
: base(nameof(MainDialog))
{
Configuration = configuration;
Logger = logger;
_userAccessor = userState.CreateProperty<User>("User");
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new InitialDialog(userState));
AddDialog(new CreateResourceGroupDialog(userState));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
IntroStepAsync,
ActStepAsync,
LUISStepAsync,
FinalStepAsync,
}));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> IntroStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(Configuration["LuisAppId"]) || string.IsNullOrEmpty(Configuration["LuisAPIKey"]) || string.IsNullOrEmpty(Configuration["LuisAPIHostName"]))
{
await stepContext.Context.SendActivityAsync(
MessageFactory.Text("NOTE: LUIS is not configured. To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and 'LuisAPIHostName' to the appsettings.json file."), cancellationToken);
return await stepContext.NextAsync(null, cancellationToken);
}
else
{
if (AzureDetails.AccessToken == null)
{
var messageText = stepContext.Options?.ToString() ?? "Before starting, fill the following form to connect you to Azure.";
var infoMessage = MessageFactory.Text(messageText, messageText);
await stepContext.Context.SendActivityAsync(infoMessage, cancellationToken);
return await stepContext.BeginDialogAsync(nameof(InitialDialog), new User(), cancellationToken);
}
else
{
return await stepContext.NextAsync(stepContext, cancellationToken);
}
}
}
private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var messageText = stepContext.Options?.ToString() ?? "What can I help you with today?";
var promptMessage = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput);
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
}
private async Task<DialogTurnResult> LUISStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
await LuisHelper.ExecuteLuisQuery(Configuration, Logger, stepContext.Context, cancellationToken);
var intentValue = (string)stepContext.Result;
switch (AzureDetails.Intent)
{
case "GetAllResourceGroupsIntent":
AzureResourceManagerREST.getAllResourceGroupDetails().GetAwaiter().GetResult();
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Here is the details of all your resource groups:"));
await stepContext.Context.SendActivityAsync(MessageFactory.Text(AzureDetails.Response));
break;
case "Cancel":
AzureDetails.AccessToken = null;
AzureDetails.SubscriptionID = null;
AzureDetails.TenantID = null;
AzureDetails.ClientID = null;
AzureDetails.ClientSecret = null;
await stepContext.Context.SendActivityAsync(MessageFactory.Text("You are now successfully disconnected from Azure services. Your Azure details has been cleared."));
break;
case "CreateResourceGroupIntent":
return await stepContext.BeginDialogAsync(nameof(CreateResourceGroupDialog), new User(), cancellationToken);
default:
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Kindly rephrase your query."));
break;
}
return await stepContext.NextAsync(null, cancellationToken);
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (stepContext.Result != null)
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Thank you for using Azure Resource Manager Bot."), cancellationToken);
}
else
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Thank you."), cancellationToken);
}
var promptMessage = "What else can I do for you?";
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage, cancellationToken);
}
}
}
Add the below code in InitialDialog.cs
using CoreBot.Utilities;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace CoreBot.Dialogs
{
public class InitialDialog : ComponentDialog
{
private readonly IStatePropertyAccessor<User> _userAccessor;
public InitialDialog(UserState userState) : base(nameof(InitialDialog))
{
_userAccessor = userState.CreateProperty<User>("User");
var waterfallSteps = new WaterfallStep[]
{
SubscriptionIdStepAsync,
TenantIdStepAsync,
ClientIdStepAsync,
ClientSecretStepAsync,
ConfirmStepAsync,
SummaryStepAsync,
};
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new NumberPrompt<int>(nameof(NumberPrompt<int>)));
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> SubscriptionIdStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Please provide your Azure Subscription ID.")
}, cancellationToken);
}
private async Task<DialogTurnResult> TenantIdStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["Subscription ID"] = (string)stepContext.Result;
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Please provide Tenant ID.")
}, cancellationToken);
}
private async Task<DialogTurnResult> ClientIdStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["Tenant ID"] = (string)stepContext.Result;
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Please provide Client ID.")
}, cancellationToken);
}
private async Task<DialogTurnResult> ClientSecretStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["Client ID"] = (string)stepContext.Result;
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Please provide Client Secret.")
}, cancellationToken);
}
private async Task<DialogTurnResult> ConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["Client Secret"] = (string)stepContext.Result;
return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Would you like to Confirm?")
}, cancellationToken);
}
private async Task<DialogTurnResult> SummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if ((bool)stepContext.Result)
{
AzureDetails.SubscriptionID = (string)stepContext.Values["Subscription ID"];
AzureDetails.TenantID = (string)stepContext.Values["Tenant ID"];
AzureDetails.ClientID = (string)stepContext.Values["Client ID"];
AzureDetails.ClientSecret = (string)stepContext.Values["Client Secret"];
var msg = "Here are the details you provided:\nSubscription ID: " + AzureDetails.SubscriptionID + ", Tenant ID: " + AzureDetails.TenantID + ", ClientID: " + AzureDetails.ClientID + ", Client Secret: ****************";
await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg));
AzureResourceManagerREST.GetAuthorizationToken();
if (AzureDetails.AccessToken == null)
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Request failed. Access token not generated. Retry again."));
} else
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Thank you, Request is Confirmed. You are now successfully connected to Azure Services."));
}
return await stepContext.EndDialogAsync(null, cancellationToken);
}
else
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Request Not Confirmed."));
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}
}
}
}
Add the below code in CreateResourceGroupDialog.cs
using AdaptiveCards;
using CoreBot.Utilities;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Choices;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace CoreBot.Dialogs.ResourceGroup
{
public class CreateResourceGroupDialog : ComponentDialog
{
private readonly IStatePropertyAccessor<User> _userAccessor;
public CreateResourceGroupDialog(UserState userState) : base(nameof(CreateResourceGroupDialog))
{
_userAccessor = userState.CreateProperty<User>("User");
var waterfallSteps = new WaterfallStep[]
{
ResourceGroupNameStepAsync,
LocationStepAsync,
ConfirmStepAsync,
SummaryStepAsync,
};
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new NumberPrompt<int>(nameof(NumberPrompt<int>)));
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> ResourceGroupNameStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Please provide Resource group name.")
}, cancellationToken);
}
private async Task<DialogTurnResult> LocationStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["Resource Group Name"] = (string)stepContext.Result;
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Please select a Location."));
// Create card
var card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 0))
{
// Use LINQ to turn the choices into submit actions
Actions = AzureResourcesData.resourceGroupLocationList.Select(choice => new AdaptiveSubmitAction
{
Title = choice,
Data = choice, // This will be a string
}).ToList<AdaptiveAction>(),
};
// Prompt
return await stepContext.PromptAsync(nameof(ChoicePrompt), new PromptOptions
{
Prompt = (Activity)MessageFactory.Attachment(new Attachment
{
ContentType = AdaptiveCard.ContentType,
// Convert the AdaptiveCard to a JObject
Content = JObject.FromObject(card),
}),
Choices = ChoiceFactory.ToChoices(AzureResourcesData.resourceGroupLocationList),
// Don't render the choices outside the card
Style = ListStyle.None,
},
cancellationToken);
}
private async Task<DialogTurnResult> ConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["Location"] = ((FoundChoice)stepContext.Result).Value;
return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Would you like to Confirm?")
}, cancellationToken);
}
private async Task<DialogTurnResult> SummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if ((bool)stepContext.Result)
{
AzureDetails.ResourceGroupName = (string)stepContext.Values["Resource Group Name"];
AzureDetails.Location = (string)stepContext.Values["Location"];
AzureResourceManagerREST.createResourceGroup().GetAwaiter().GetResult();
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Here is the response from Azure."));
await stepContext.Context.SendActivityAsync(MessageFactory.Text(AzureDetails.Response));
return await stepContext.EndDialogAsync(null, cancellationToken);
}
else
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Request Not Confirmed."));
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}
}
}
}
Install the nuget package “AdaptiveCards”.

Add below code in AzureResourceManagerREST.cs
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace CoreBot.Utilities
{
public static class AzureResourceManagerREST
{
public static void GetAuthorizationToken()
{
ClientCredential cc = new ClientCredential(AzureDetails.ClientID, AzureDetails.ClientSecret);
var context = new AuthenticationContext("https://login.microsoftonline.com/" + AzureDetails.TenantID);
var result = context.AcquireTokenAsync("https://management.azure.com/", cc);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
AzureDetails.AccessToken = result.Result.AccessToken;
}
public static async Task getAllResourceGroupDetails()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("https://management.azure.com/subscriptions/" + AzureDetails.SubscriptionID + "/resourcegroups?api-version=2019-10-01");
client.DefaultRequestHeaders.Accept.Clear();
/*client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));*/ //ACCEPT header
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + AzureDetails.AccessToken);
//var requestUri = $"knowledgebases/{knowledgebaseId}/generateAnswer";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, client.BaseAddress);
//var body = $"{{\"question\": \"{"symptoms"}\", \"top\": \"{1}\"}}";
//var content = new StringContent(body, Encoding.UTF8, "application/json");
//request.Content = content;
var response = await MakeRequestAsync(request, client);
AzureDetails.Response = response;
//Console.WriteLine(response);
}
public static async Task createResourceGroup()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("https://management.azure.com/subscriptions/" + AzureDetails.SubscriptionID + "/resourcegroups/" + AzureDetails.ResourceGroupName + "?api-version=2019-10-01");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + AzureDetails.AccessToken);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put, client.BaseAddress);
var body = $"{{\"location\": \"{AzureDetails.Location}\"}}";
var content = new StringContent(body, Encoding.UTF8, "application/json");
request.Content = content;
var response = await MakeRequestAsync(request, client);
AzureDetails.Response = response;
}
public static async Task<string> MakeRequestAsync(HttpRequestMessage getRequest, HttpClient client)
{
var response = await client.SendAsync(getRequest).ConfigureAwait(false);
var responseString = string.Empty;
try
{
response.EnsureSuccessStatusCode();
responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
catch (HttpRequestException)
{
// empty responseString
}
return responseString;
}
}
}
Add below code in AzureResourcesData.cs. I have added limited locations. You can add all the locations supported by Azure.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CoreBot.Utilities
{
public class AzureResourcesData
{
public static List<string> resourceGroupLocationList = new List<string>{ "East Asia", "Southeast Asia", "Central US", "East US", "East US 2", "West US", "North Central US", "South Central US", "North Europe", "West Europe", "Japan West", "Japan East" };
public static List<string> resourceGroupLocationCodesList = new List<string> { "eastasia", "southeastasia", "centralus", "eastus", "eastus2", "westus", "northcentralus", "southcentralus", "northeurope", "westeurope", "japanwest", "japaneast" };
}
}
Add following properties in AzureDetails.cs and make the class static.
public static string SubscriptionID { get; set; }
public static string ClientID { get; set; }
public static string TenantID { get; set; }
public static string ClientSecret { get; set; }
public static string AccessToken { get; set; }
public static string Intent { get; set; }
public static string Entity { get; set; }
public static string Response { get; set; }
public static string ResourceGroupName { get; set; }
public static string Location { get; set; }
Add below code in LUISHelper.cs
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// Generated with Bot Builder V4 SDK Template for Visual Studio CoreBot v4.3.0
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.AI.Luis;
using Microsoft.Bot.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using CoreBot;
namespace CoreBot
{
public static class LuisHelper
{
//private static object services;
public static async Task<string> ExecuteLuisQuery(IConfiguration configuration, ILogger logger, ITurnContext turnContext, CancellationToken cancellationToken)
{
try
{
// Create the LUIS settings from configuration.
var luisApplication = new LuisApplication(
configuration["LuisAppId"],
configuration["LuisAPIKey"],
"https://" + configuration["LuisAPIHostName"]
);
var recognizerOptions = new LuisRecognizerOptionsV3(luisApplication)
{
PredictionOptions = new Microsoft.Bot.Builder.AI.LuisV3.LuisPredictionOptions
{
IncludeInstanceData = true,
}
};
var recognizer = new LuisRecognizer(recognizerOptions);
// The actual call to LUIS
var recognizerResult = await recognizer.RecognizeAsync(turnContext, cancellationToken);
var (intent, score) = recognizerResult.GetTopScoringIntent();
AzureDetails.Intent = intent;
}
catch (Exception e)
{
logger.LogWarning($"LUIS Exception: {e.Message} Check your LUIS configuration.");
}
return AzureDetails.Intent;
}
}
}
Leave the User class empty and add the following services in Startup.cs.
services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
services.AddSingleton<InitialDialog>();
Import the below libraries in Startup.cs.
using Microsoft.Bot.Builder.BotFramework;
using Microsoft.Bot.Connector.Authentication;
Run the project using Cntrl+F5. Open your bot emulator and give the bot URL http://localhost:3978/api/messages/ to connect. Check the port number on the webpage opened on running the project.
Below is the welcome message you will get when a user joins the conversation.

The Bot asks for the Azure Subscription Key, Client ID, Tenant ID, and Client Secret. These details you will be getting when you have created the Azure Principal. Check the Pre-requisites to refer to the article.
If you provide all the details correctly to the Bot, it will get the access token for you to manage all the resources.
Once you are done using the Azure services from the bot, you can tell the bot to logout or cancel or quit. The bot will delete all your information along with the Access token. To start again, you will have to provide the details again.
Get the source code from GitHub.