From 1185e10090a4d74f04175f1b614d0c879cac8674 Mon Sep 17 00:00:00 2001 From: Jason Bouska Date: Wed, 13 Mar 2024 15:17:56 -0400 Subject: [PATCH] Update Priority Queue Cloud Pattern Update to .NET 8.0 Updated README --- .../PriorityQueueConsumerHigh.csproj | 22 +-- .../PriorityQueueConsumerHighFn.cs | 19 +- .../PriorityQueueConsumerHigh/Program.cs | 7 + .../local.settings.template.json | 8 + .../PriorityQueueConsumerLow.csproj | 23 +-- .../PriorityQueueConsumerLowFn.cs | 19 +- .../PriorityQueueConsumerLow/Program.cs | 7 + .../local.settings.template.json | 8 + .../PriorityQueueSender.csproj | 25 ++- .../PriorityQueueSenderFn.cs | 21 +- priority-queue/PriorityQueueSender/Program.cs | 26 +++ .../local.settings.template.json | 8 + priority-queue/Readme.md | 179 ++++++++++-------- 13 files changed, 238 insertions(+), 134 deletions(-) create mode 100644 priority-queue/PriorityQueueConsumerHigh/Program.cs create mode 100644 priority-queue/PriorityQueueConsumerHigh/local.settings.template.json create mode 100644 priority-queue/PriorityQueueConsumerLow/Program.cs create mode 100644 priority-queue/PriorityQueueConsumerLow/local.settings.template.json create mode 100644 priority-queue/PriorityQueueSender/Program.cs create mode 100644 priority-queue/PriorityQueueSender/local.settings.template.json diff --git a/priority-queue/PriorityQueueConsumerHigh/PriorityQueueConsumerHigh.csproj b/priority-queue/PriorityQueueConsumerHigh/PriorityQueueConsumerHigh.csproj index 1ac631dd..5dd483eb 100644 --- a/priority-queue/PriorityQueueConsumerHigh/PriorityQueueConsumerHigh.csproj +++ b/priority-queue/PriorityQueueConsumerHigh/PriorityQueueConsumerHigh.csproj @@ -1,22 +1,20 @@ - net6.0 + net8.0 v4 + Exe + enabled - - true - - - - - - - PreserveNewest - PreserveNewest Never - + + + + + + + \ No newline at end of file diff --git a/priority-queue/PriorityQueueConsumerHigh/PriorityQueueConsumerHighFn.cs b/priority-queue/PriorityQueueConsumerHigh/PriorityQueueConsumerHighFn.cs index 5ce7ad67..9cfe92f2 100644 --- a/priority-queue/PriorityQueueConsumerHigh/PriorityQueueConsumerHighFn.cs +++ b/priority-queue/PriorityQueueConsumerHigh/PriorityQueueConsumerHighFn.cs @@ -1,14 +1,21 @@ -using Microsoft.Azure.WebJobs; using Microsoft.Extensions.Logging; +using Microsoft.Azure.Functions.Worker; namespace PriorityQueueConsumerHigh { - public static class PriorityQueueConsumerHighFn + public class PriorityQueueConsumerHighFn { - [FunctionName("HighPriorityQueueConsumerFunction")] - public static void Run([ServiceBusTrigger("messages", "highPriority", Connection = "ServiceBusConnection")]string highPriorityMessage, ILogger log) + private readonly ILogger _logger; + + public PriorityQueueConsumerHighFn(ILogger logger) + { + _logger = logger; + } + + [Function("HighPriorityQueueConsumerFunction")] + public void Run([ServiceBusTrigger("messages", "highPriority", Connection = "ServiceBusConnectionString")] string highPriorityMessage) { - log.LogInformation($"C# ServiceBus topic trigger function processed message: {highPriorityMessage}"); + _logger.LogInformation($"C# ServiceBus topic trigger function processed message: {highPriorityMessage}"); } } -} +} \ No newline at end of file diff --git a/priority-queue/PriorityQueueConsumerHigh/Program.cs b/priority-queue/PriorityQueueConsumerHigh/Program.cs new file mode 100644 index 00000000..cd97ae1f --- /dev/null +++ b/priority-queue/PriorityQueueConsumerHigh/Program.cs @@ -0,0 +1,7 @@ +using Microsoft.Extensions.Hosting; + +var host = new HostBuilder() + .ConfigureFunctionsWorkerDefaults() + .Build(); + +host.Run(); \ No newline at end of file diff --git a/priority-queue/PriorityQueueConsumerHigh/local.settings.template.json b/priority-queue/PriorityQueueConsumerHigh/local.settings.template.json new file mode 100644 index 00000000..078ee8ad --- /dev/null +++ b/priority-queue/PriorityQueueConsumerHigh/local.settings.template.json @@ -0,0 +1,8 @@ +{ + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", + "ServiceBusConnectionString": "SERVICE_BUS_CONNECTION_STRING" + } +} \ No newline at end of file diff --git a/priority-queue/PriorityQueueConsumerLow/PriorityQueueConsumerLow.csproj b/priority-queue/PriorityQueueConsumerLow/PriorityQueueConsumerLow.csproj index 6ce569f0..5dd483eb 100644 --- a/priority-queue/PriorityQueueConsumerLow/PriorityQueueConsumerLow.csproj +++ b/priority-queue/PriorityQueueConsumerLow/PriorityQueueConsumerLow.csproj @@ -1,23 +1,20 @@ - net6.0 + net8.0 v4 - 736bb6a2-68b4-463b-a8fb-3a90cba7cd4f + Exe + enabled - - true - - - - - - - PreserveNewest - PreserveNewest Never - + + + + + + + \ No newline at end of file diff --git a/priority-queue/PriorityQueueConsumerLow/PriorityQueueConsumerLowFn.cs b/priority-queue/PriorityQueueConsumerLow/PriorityQueueConsumerLowFn.cs index 36c1c593..0a652603 100644 --- a/priority-queue/PriorityQueueConsumerLow/PriorityQueueConsumerLowFn.cs +++ b/priority-queue/PriorityQueueConsumerLow/PriorityQueueConsumerLowFn.cs @@ -1,14 +1,21 @@ -using Microsoft.Azure.WebJobs; using Microsoft.Extensions.Logging; +using Microsoft.Azure.Functions.Worker; namespace PriorityQueueConsumerLow { - public static class PriorityQueueConsumerLowFn + public class PriorityQueueConsumerLowFn { - [FunctionName("LowPriorityQueueConsumerFunction")] - public static void Run([ServiceBusTrigger("messages", "lowPriority", Connection = "ServiceBusConnection")]string lowPriorityMessage, ILogger log) + private readonly ILogger _logger; + + public PriorityQueueConsumerLowFn(ILogger logger) + { + _logger = logger; + } + + [Function("LowPriorityQueueConsumerFunction")] + public void Run([ServiceBusTrigger("messages", "lowPriority", Connection = "ServiceBusConnectionString")] string lowPriorityMessage) { - log.LogInformation($"C# ServiceBus topic trigger function processed message: {lowPriorityMessage}"); + _logger.LogInformation($"C# ServiceBus topic trigger function processed message: {lowPriorityMessage}"); } } -} +} \ No newline at end of file diff --git a/priority-queue/PriorityQueueConsumerLow/Program.cs b/priority-queue/PriorityQueueConsumerLow/Program.cs new file mode 100644 index 00000000..cd97ae1f --- /dev/null +++ b/priority-queue/PriorityQueueConsumerLow/Program.cs @@ -0,0 +1,7 @@ +using Microsoft.Extensions.Hosting; + +var host = new HostBuilder() + .ConfigureFunctionsWorkerDefaults() + .Build(); + +host.Run(); \ No newline at end of file diff --git a/priority-queue/PriorityQueueConsumerLow/local.settings.template.json b/priority-queue/PriorityQueueConsumerLow/local.settings.template.json new file mode 100644 index 00000000..078ee8ad --- /dev/null +++ b/priority-queue/PriorityQueueConsumerLow/local.settings.template.json @@ -0,0 +1,8 @@ +{ + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", + "ServiceBusConnectionString": "SERVICE_BUS_CONNECTION_STRING" + } +} \ No newline at end of file diff --git a/priority-queue/PriorityQueueSender/PriorityQueueSender.csproj b/priority-queue/PriorityQueueSender/PriorityQueueSender.csproj index 1ac631dd..e0d4619e 100644 --- a/priority-queue/PriorityQueueSender/PriorityQueueSender.csproj +++ b/priority-queue/PriorityQueueSender/PriorityQueueSender.csproj @@ -1,22 +1,21 @@ - + - net6.0 + net8.0 v4 + Exe + enabled - - true - - - - - - - PreserveNewest - PreserveNewest Never - + + + + + + + + \ No newline at end of file diff --git a/priority-queue/PriorityQueueSender/PriorityQueueSenderFn.cs b/priority-queue/PriorityQueueSender/PriorityQueueSenderFn.cs index 9429b6d6..405c710e 100644 --- a/priority-queue/PriorityQueueSender/PriorityQueueSenderFn.cs +++ b/priority-queue/PriorityQueueSender/PriorityQueueSenderFn.cs @@ -1,31 +1,34 @@ using System; using System.Threading.Tasks; using Azure.Messaging.ServiceBus; -using Microsoft.Azure.WebJobs; +using Microsoft.Extensions.Logging; +using Microsoft.Azure.Functions.Worker; namespace PriorityQueueSender { - public static class PriorityQueueSenderFn + public class PriorityQueueSenderFn(ILogger logger, ServiceBusClient client) { - [FunctionName("PriorityQueueSenderFunction")] - public static async Task Run( - [TimerTrigger("0,30 * * * * *")] TimerInfo myTimer, - [ServiceBus("messages", Connection = "ServiceBusConnection")] IAsyncCollector collector ) + private readonly ILogger _logger = logger; + private readonly ServiceBusClient _client = client; + + [Function("PriorityQueueSenderFunction")] + public async Task Run([TimerTrigger("0,30 * * * * *")] TimerInfo myTimer) { + var sender = _client.CreateSender("messages"); for (int i = 0; i < 10; i++) { var messageId = Guid.NewGuid().ToString(); var lpMessage = new ServiceBusMessage() { MessageId = messageId }; lpMessage.ApplicationProperties["Priority"] = Priority.Low; lpMessage.Body = BinaryData.FromString($"Low priority message with Id: {messageId}"); - await collector.AddAsync(lpMessage); + await sender.SendMessageAsync(lpMessage); messageId = Guid.NewGuid().ToString(); var hpMessage = new ServiceBusMessage() { MessageId = messageId }; hpMessage.ApplicationProperties["Priority"] = Priority.High; hpMessage.Body = BinaryData.FromString($"High priority message with Id: {messageId}"); - await collector.AddAsync(hpMessage); + await sender.SendMessageAsync(hpMessage); } } } -} +} \ No newline at end of file diff --git a/priority-queue/PriorityQueueSender/Program.cs b/priority-queue/PriorityQueueSender/Program.cs new file mode 100644 index 00000000..c393da75 --- /dev/null +++ b/priority-queue/PriorityQueueSender/Program.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Azure.Messaging.ServiceBus; + +var host = new HostBuilder() + .ConfigureFunctionsWorkerDefaults() + .ConfigureAppConfiguration((hostingContext, config) => + { + config.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true); + }) + .ConfigureServices(services => + { + var configuration = services.BuildServiceProvider().GetRequiredService(); + + services.AddSingleton(configuration); + + services.AddSingleton(sp => + { + var connectionString = configuration.GetValue("ServiceBusConnectionString"); + return new ServiceBusClient(connectionString); + }); + }) + .Build(); + +host.Run(); \ No newline at end of file diff --git a/priority-queue/PriorityQueueSender/local.settings.template.json b/priority-queue/PriorityQueueSender/local.settings.template.json new file mode 100644 index 00000000..078ee8ad --- /dev/null +++ b/priority-queue/PriorityQueueSender/local.settings.template.json @@ -0,0 +1,8 @@ +{ + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", + "ServiceBusConnectionString": "SERVICE_BUS_CONNECTION_STRING" + } +} \ No newline at end of file diff --git a/priority-queue/Readme.md b/priority-queue/Readme.md index 01e0ef99..8181a550 100644 --- a/priority-queue/Readme.md +++ b/priority-queue/Readme.md @@ -1,100 +1,129 @@ -# Priority Queue Pattern +# Priority Queue pattern example -This document describes the Priority Queue Pattern example from the guide [Cloud Design Patterns](http://aka.ms/Cloud-Design-Patterns). +This directory contains an example of the [Priority Queue pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/priority-queue). -## System Requirements +This example demonstrates how priority queues can be implemented by using Service Bus topics and subscriptions. A time-triggered Azure Function is responsible for sending messages to a topic, each with a priority assigned. The receiving Azure Functions read messages from subscriptions that have the corresponding priority. In this example, the _PriorityQueueConsumerHigh_ Azure function can scale out to 200 instances, while the _PriorityQueueConsumerLow_ function runs only with one instance. This example simulates high priority messages being read from the queue more urgently than low priority messages. -* Microsoft .NET 6 -* Microsoft Visual Studio 2019 or later version -* Azure Functions Core Tools version 4x +This example also demonstrates operational aspects of applications running on Azure. Monitoring tools need to be used in order to understand how the sample works. Azure Functions in the solution **must** be configured to use the diagnostics mechanism. If not, you will not see the trace information generated by the example. -## Before you start +## :rocket: Deployment guide -Ensure that you have installed all of the software prerequisites. +Install the prerequisites and follow the steps to deploy and run an example of the Priority Queue pattern. -The example demonstrates operational aspects of applications running in Windows Azure. Therefore, you will need to use the monitoring tools in order to understand how the code sample works. You **must** ensure that the Azure Functions in the solution are configured to use the diagnostics mechanism. If not, you will not see the trace information generated by the example. +### Prerequisites -## About the Example +- Permission to create a new resource group and resources in an [Azure subscription](https://azure.com/free). +- [Git](https://git-scm.com/downloads) +- [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) +- [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) +- [Microsoft Visual Studio 2022](https://visualstudio.microsoft.com/vs/) or later version +- [Azure Functions Core Tools v4.x](https://learn.microsoft.com/azure/azure-functions/functions-run-local#install-the-azure-functions-core-tools) -This example shows how you can implement priority queues by using Service Bus Topics and Subscriptions. A timer triggered Azure Function is responsible for sending messages to a topic. It assigns a priority to each message. The receiving Azure Functions read messages from subscriptions that have the corresponding priority. In the example, the PriorityQueueConsumerHigh Azure function can scale out to 200 instances, whereas the PriorityQueueConsumerLow worker runs only with one instance. This simulates high priority messages being read from the queue more urgently than low priority messages. -## Running the Example +### Steps -You can either run this example locally from Visual Studio or you can run it by deploying it to Azure. +1. Clone the repository -* From the Azure Portal, provision an Azure Service Bus Namespace. -* Once the Service Bus Namespace is created, add a new topic to it, name it "messages" (leave all the properties default). -* Once the topic is created, add a new subscription to it, name it "highPriority", set "10" as Max delivery count. -* Add a new filter of type "SqlFilter" to the subscription, name it "priorityFilter". -* Set the expression __Priority = 'highpriority'__ in the filter body. -* Click on "Save changes". -* Add another subscription to the topic, name it "lowPriority", set "10" as Max delivery count. -* Add a new filter of type "SqlFilter" to the subscription, name it "priorityFilter". -* Set the expression __Priority = 'lowpriority'__ in the filter body. -* Click on "Save changes". + Open a terminal, clone the repository, and navigate to the `priority-queue` directory. -* Start Visual Studio. -* Open the solution you want to explore from the subfolders where you downloaded the examples. -* Edit the local.settings.json file in all the projects and change the ServiceBusConnection__fullyQualifiedNamespace setting by changing the placeholder "" to your Azure Service Bus Namespace name. + ```shell + git clone https://github.com/mspnp/cloud-design-patterns.git + cd cloud-design-patterns + cd priority-queue + ``` +1. Log into Azure and create an empty resource group. -* If you want to run the example in the local Windows Azure emulator: - * Set the PriorityQueueSender project as startup. - * Press F5 in Visual Studio to start the example running. The function will start sending messages to the topic, every 30 seconds. - * Stop the execution, change the startup project to either the PriorityQueueConsumerHigh or PriorityQueueConsumerLow - * Press F5 in Visual Studio to start the execution of the consumer function - * In the Azure Functions Core Tools Console, you can view the diagnostic information generated by Trace statements in the code. + Create an empty resource group to hold the resources for this example. The location you select in the resource group creation command below is the Azure region that your resources will be deployed in; modify as needed. -* If you want to run the example on Azure: - * On every function project, right click and select publish. - * Select "Azure" as publishing target. - * Select "Azure Function App" as specific target. - * In the Functions instance step, select you Azure Function app or create a new one using the link button. - * Since your app settings are different you need to deploy every Azure Function in a separate Azure Funcion App. - * You can select an existing resource group and storage account or create new ones. - * In "Hosting" section, click on the the three dots (...) in the upper right corner. - * Select "Manage Azure App Service Settings". - * You need to add these two settings: + ```azurecli + az login + az account set -s - - ServiceBusConnection__fullyQualifiedNamespace: + RESOURCE_GROUP_NAME=rg-priority-queue + az group create -n $RESOURCE_GROUP_NAME -l eastus2 + ``` +1. Provision a Service Bus messaging namespace. - Set the value to: + ```azurecli + SERVICE_BUS_NAMESPACE_NAME=sbns-priority-queue-pattern + az servicebus namespace create -g $RESOURCE_GROUP_NAME -n $SERVICE_BUS_NAMESPACE_NAME + ``` +1. Create a Service Bus topic named "messages". - .servicebus.windows.net + ```azurecli + SERVICE_BUS_TOPIC_NAME=messages + az servicebus topic create --name $SERVICE_BUS_TOPIC_NAME --namespace-name $SERVICE_BUS_NAMESPACE_NAME --resource-group $RESOURCE_GROUP_NAME + ``` +1. Create a Service Bus subscription for high priority messages. - Replacing the placeholder with you Azure Service Bus Namespace name. + ```azurecli + SERVICE_BUS_SUBSCRIPTION_NAME_HIGH=highPriority + az servicebus topic subscription create --name $SERVICE_BUS_SUBSCRIPTION_NAME_HIGH --namespace-name $SERVICE_BUS_NAMESPACE_NAME --resource-group $RESOURCE_GROUP_NAME --topic-name $SERVICE_BUS_TOPIC_NAME + ``` +1. Create a Service Bus subscription for low priority messages. - * Once the Functions are deployed you need to restrict the maximum number of instances your app service consumption can scale out to: + ```azurecli + SERVICE_BUS_SUBSCRIPTION_NAME_LOW=lowPriority + az servicebus topic subscription create --name $SERVICE_BUS_SUBSCRIPTION_NAME_LOW --namespace-name $SERVICE_BUS_NAMESPACE_NAME --resource-group $RESOURCE_GROUP_NAME --topic-name $SERVICE_BUS_TOPIC_NAME + ``` +1. Add a filter to the high priority subscription. - * From the portal go to the Function App that contains the "PriorityQueueConsumerLow" Azure Function - * Navigate to Scale Out on the left menu - * On the App Scale Out dialog set the "Enforce Scale Out Limit" to "Yes" - * Set the Maximum Scale Out Limit to 1 instance - - You don't need to modify these settings for the Function App containing the "PriorityQueueConsumerHigh" Azure Function since the default setting is 200; this ensures that high priority messages are read from the queue more quickly than low priority messages. + ```azurecli + az servicebus topic subscription rule create --name priorityFilter --namespace-name $SERVICE_BUS_NAMESPACE_NAME --resource-group $RESOURCE_GROUP_NAME --topic-name $SERVICE_BUS_TOPIC_NAME --subscription-name $SERVICE_BUS_SUBSCRIPTION_NAME_HIGH --filter-sql-expression "Priority = 'highpriority'" + ``` +1. Add a filter to the low priority subscription. - * Now you need to configure the managed identities role assignments: + ```azurecli + az servicebus topic subscription rule create --name priorityFilter --namespace-name $SERVICE_BUS_NAMESPACE_NAME --resource-group $RESOURCE_GROUP_NAME --topic-name $SERVICE_BUS_TOPIC_NAME --subscription-name $SERVICE_BUS_SUBSCRIPTION_NAME_LOW --filter-sql-expression "Priority = 'lowpriority'" + ``` +1. Retrieve the primary connection string for the Service Bus namespace. Copy and retain this value as it will be needed in later steps. - In the Azure portal, navigate to the Azure Service Bus Namespace that was provisioned in the first step. - Select Access Control (IAM). This is where you can view and configure who has access to the resource. - Click Add and select add role assignment. - From the list, select "Azure Service Bus Data Sender", click Next - In "Assign access to", radio button list, select "Managed identity" - Click on "Select members", the "Select Manage identities" dialog will show up - In the Managed Identity dropdown list select "Function App" - Find the PriorityQueueSender function app click on it - Click "Select" - On the main dialog, click "Review + Assign" + ```azurecli + SERVICE_BUS_CONNECTION_STRING=$(az servicebus namespace authorization-rule keys list --resource-group $RESOURCE_GROUP_NAME --namespace-name $SERVICE_BUS_NAMESPACE_NAME --name RootManageSharedAccessKey --query primaryConnectionString --output tsv) + echo $SERVICE_BUS_CONNECTION_STRING + ``` - For the Azure Consumer Function Apps, repeat the process but in this case use the role - "Azure Service Bus Data Reader" - * Once the Functions are deployed you can configure monitoring by following these steps: +## Run the example locally - - From the Azure Portal, go to the Function App Service - - Click on "Functions" and select the azure function - - From the "developer" left menu click on "Monitor" - - Turn on application insights - - Refresh the screen - - Once refreshed you will see two tabs, "invocation" and "logs" - - From the invocations tab you can see the twenty most recent function invocation traces. For more advanced analysis, run the query in Application Insights. - - From the logs tab you can see the logging information that your functions are sending. \ No newline at end of file + +1. Open the `priority-queue.sln` file in Visual Studio. + +1. Edit the `local.settings.json` file for each project set the value of `ServiceBusConnectionString` to the **SERVICE_BUS_CONNECTION_STRING** retained from earlier. + +1. Set the `PriorityQueueSender` project as startup. +1. Press F5 in Visual Studio to start running the example. The function will begin sending messages to the topic every 30 seconds. +1. Stop the execution, change the startup project to either the `PriorityQueueConsumerHigh` or `PriorityQueueConsumerLow`. +1. Press F5 in Visual Studio to start the execution of the consumer function. +1. In the Azure Functions Core Tools Console, you can view the diagnostic information generated by statements in the code. + + +## Deploy the example to Azure +To deploy the example to Azure, you need to publish each Azure Functions to Azure. You can do so from Visual Studio by right clicking each function and selecting `Publish` from the menu. Use the same resource group and region from earlier. Be sure to enable Application Insights. + +Once each function is published, a new App Setting must be added to store the connection string to the Service Bus namespace. This is the same value that was used in the `SERVICE_BUS_CONNECTION_STRING` variable in the previous steps. + +For each function, run the following: +```azurecli +az webapp config appsettings set -n -g $RESOURCE_GROUP_NAME --settings ServiceBusConnectionString=$SERVICE_BUS_CONNECTION_STRING +``` + +Once the functions are deployed you need to restrict the maximum number of instances the `PriorityQueueConsumerLow` function can scale out to. + +From the Azure portal: + - Visit the Function App that contains `PriorityQueueConsumerLow` + - Navigate to Scale Out on the left menu + - On the App Scale Out dialog, set the `Enforce Scale Out Limit` to `Yes` + - Set the `Maximum Scale Out Limit` to `1` instance + + +Once the functions are deployed you can visit Application Insights to view the most recent activity for each function. + + +## :broom: Clean up resources + +Be sure to delete Azure resources when not using them. Since all resources were deployed into a new resource group, you can simply delete the resource group. + +```azurecli +az group delete -n $RESOURCE_GROUP_NAME +``` \ No newline at end of file