J
JohnScott
In our recent Azure Federal Developer Connect series, we covered connecting to Azure Service Bus using Azure Functions in Azure US Government. The process for connecting to Azure Service Bus doesn't change much for the US Government cloud, but there are a couple of nuances I will call out here. This post will walk you through the code step by step, but if you're in a hurry and just want to download the completed sample, the code is available here.
Azure Service Bus is a fully managed enterprise message broker with message queues and publish-subscribe topics. Service Bus is used to decouple applications and services from each other, providing the following benefits:
What this means from a practical standpoint is that you will have some application which will publish messages to a queue or a topic, and one or many subscribing applications. Currently the Azure Service Bus client libraries support Python, C#, Java, and JavaScript, so the application communicating with Service Bus can take many forms, whether it's a C# or Python web app hosted on an App Service, a JavaScript running in a Static Web App, a containerized Java application, a no-code/low-code solution like Logic Apps - literally any application hosting mechanism which can leverage the Azure Service Bus client libraries can be used to communicate with Azure Service Bus.
For this demo I chose to use Azure Functions running C#. C# is of course a popular language, and the Azure Functions runtime allows for a lightweight, scalable means of communicating with Service Bus. Furthermore, using the Azure Functions Core Tools, we can test the function without actually needing to deploy the functions into Azure, and we can debug from our local machine, even though Azure Service Bus is running in the cloud.
The publisher application is an Azure Function using the HttpTrigger, which will use the request body of the POST and send that message to a queue or topic. The subscriber applications are using the Service Bus triggers, a ServiceBusQueueTrigger and ServiceBusTopicTrigger to read from the queues and topics, respectively. These allow us to seamlessly connect with Service Bus and instantly pick up messages as they arrive on the Queue or Topic Subscription.
To run this demo, you will need the following components:
*If you are using Visual Studio 2022, Azurite is available automatically. All steps described in this post can also be accomplished within Visual Studio, as well as Visual Studio code, or from the command line using any code editor of your choice.
Go into the Azure Portal and create a Service Bus resource. Azure Service Bus is available in three pricing tiers: Basic, Standard and Premium. For the purposes of this demo, we will want to create a Standard tier instance, as it will support Topics. You will need to pick a globally unique namespace, and you will want to make note of your fully qualified namespace in the form of servicebusnamespace.servicebus.usgovcloudapi.net. You can accept the defaults for the rest of the configuration.
Once your Service Bus resource is provisioned, go into the resource and select the "Access Control (IAM)" blade. Add your user to the Azure Service Bus Data Owner role, which will allow your user identity to read and write from the Service Bus. If you were to deploy the functions to Azure, you would create a managed identity for the Azure Function and assign it Azure Service Bus Data Owner, Azure Service Bus Data Reader, and/or Azure Service Bus Data Writer roles as appropriate. However, for the purposes of this demo we will be testing locally and will use your Azure credentials to authenticate to Service Bus.
Finally, find the Queue blade and create a queue (in the demo this is called 'testq' but you can name it whatever you'd like). Then find the Topic blade and create a new topic ('testTopic') and one or more subscriptions on the topic ('testSubscription1','testSubscription2', etc.).
To build the scaffolding for the Azure Function Apps, we will use the Azure Function Core Tools to first initialize the Function App, then create the skeleton for the functions themselves.
Open a Windows Terminal session and change directories to where you'd like your code to reside. Enter the following command:
Change directories to the "ServiceBusQueueFunc" directory (or whatever you called your function). Then add these functions:
And finally, we need to add two libraries which will add the Azure.Message.ServiceBus and Azure.Identity libraries so we can communicate with Service Bus.
When it comes to authenticating with Azure Service Bus, we have a couple of choices. A simple way to authenticate is via a connection string, but this requires using a shared access key credential that you have to store and maintain somewhere, and which could be potentially compromised by a bad actor. To mitigate this, we can use the Azure.Identity library and use a managed identity for our function. Of course, because we're testing locally, we do not have a managed identity, but the Azure.Identity library will also use our Azure CLI credentials to simulate one. To login to Azure CLI:
Now that we have the scaffolding of the Function App complete, it's time to write a little code. If you open your code editor and open the ServiceBusQueueWriter.cs file, you'll see the scaffolding code created by the Azure Functions Core Tools. As the Azure.Messaging.ServiceBus library uses Async methods, we will need to change the method to by async. Also, by default the function will accept both GET and POST requests, but as we're reading from the request body, we will eliminate the GET.
Next, we need to do is get the values for the Service Bus namespace and the name of the queue or topic we will be writing to. You can either hard code these values into your code, or retrieve them from your environment settings, e.g. a local.settings.json file.
In the case of the ServiceBusConnection value, this will be the fully qualified namespace you gave your Service Bus resource, e.g. myservicebus.servicebus.usgovcloudapi.net.
Next, we'll add a bit of code to parse the body of the HttpRequest so we can send this to the Service Bus queue or topic.
And finally, we will create the Service Bus client, create a sender attached to our queue or topic, and send the message. Note on lines 1 and 2 below, I am specifying that the AuthorityHost points at AzureGovernment. By default, the authentication will be attempted against Azure Commercial.
Spoiler (Highlight to read)
If you get an authentication error when sending the message, you may need to add the following to your environment variables (e.g. local.settings.json)
If you get an authentication error when sending the message, you may need to add the following to your environment variables (e.g. local.settings.json) "AZURE_TENANT_ID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
To configure the Service Bus Queue and Topic Reader Functions is a bit simpler. To configure these, you simply need to add the queue/topic name and subscription (in the case of the Topic Reader) and the ServiceBusConnection.
There is a bit of a "gotcha" here. This value will be read in from your environment variables (e.g. local.settings.json) but utilizes a "magic" word in the configuration. For example, if you title your Connection "ServiceBusConnection", the value in your environment variables needs to be:
For the Queue Reader:
For the Topic Reader:
Open a new terminal window and start the Azurite storage emulator - this will run and stay persistent as long as this terminal window is open.
If you are using the command line only, you can now build the function app and start the local function run time with the following command:
If you are using Visual Studio Code, you can enter debug mode (which will support breakpoints in code) by selecting Run->Start Debugging.
When the function host starts, you should see the following:
If you are using Visual Studio Code, set a breakpoint in both the Writer and Queue Reader code. Opening your REST client tool (e.g. Postman), copy and paste the URL from the Writer function into a new POST request. In the request body, add some content into the body (you can add JSON, XML or plain text, just make sure the content-type header matches your text type).
At this point, you should be able to send your request. If you are in Visual Studio Code and have your breakpoints set, you should expect to see first the Writer function light up - go ahead and step through or hit F5 to let the code continue. If your send is successful, you should next expect to see the Queue Reader function light up. Because we are using a Service Bus trigger, the function will execute immediately when an item lands in the queue.
Writing to a topic does not require changing any code in the Queue Writer, we just need to change the destination from the name of the queue to the name of the topic. When you run your test again against the topic, this time we expect the Topic Reader function to execute. If you look at your topic and related subscriptions in the Azure portal, you should expect to see 0 messages in the subscription you are reading from, and 1 message each in the other subscriptions.
Hopefully this has been informative about how easy it is to implement messaging for your applications and solutions using Azure Service Bus, as well as highlighting the small nuances for using this feature in Azure Government. Future Service Bus topics may involve more advanced topics such as ordered message delivery and multi-region resiliency, but please suggest topics that you would like to see in a future post, and please bookmark Azure Federal Developer Connect and register for our upcoming sessions!
Continue reading...
Azure Service Bus is a fully managed enterprise message broker with message queues and publish-subscribe topics. Service Bus is used to decouple applications and services from each other, providing the following benefits:
- Load-balancing work across competing workers
- Safely routing and transferring data and control across service and application boundaries
- Coordinating transactional work that requires a high degree of reliability
What this means from a practical standpoint is that you will have some application which will publish messages to a queue or a topic, and one or many subscribing applications. Currently the Azure Service Bus client libraries support Python, C#, Java, and JavaScript, so the application communicating with Service Bus can take many forms, whether it's a C# or Python web app hosted on an App Service, a JavaScript running in a Static Web App, a containerized Java application, a no-code/low-code solution like Logic Apps - literally any application hosting mechanism which can leverage the Azure Service Bus client libraries can be used to communicate with Azure Service Bus.
For this demo I chose to use Azure Functions running C#. C# is of course a popular language, and the Azure Functions runtime allows for a lightweight, scalable means of communicating with Service Bus. Furthermore, using the Azure Functions Core Tools, we can test the function without actually needing to deploy the functions into Azure, and we can debug from our local machine, even though Azure Service Bus is running in the cloud.
The publisher application is an Azure Function using the HttpTrigger, which will use the request body of the POST and send that message to a queue or topic. The subscriber applications are using the Service Bus triggers, a ServiceBusQueueTrigger and ServiceBusTopicTrigger to read from the queues and topics, respectively. These allow us to seamlessly connect with Service Bus and instantly pick up messages as they arrive on the Queue or Topic Subscription.
Running this demo
To run this demo, you will need the following components:
- An Azure Subscription
- Azure CLI
- dotnet SDK 8
- Azure Function Core Tools
- Azurite storage emulator*
- A code editor such as Visual Studio Code
- A REST client testing tool such as Postman
*If you are using Visual Studio 2022, Azurite is available automatically. All steps described in this post can also be accomplished within Visual Studio, as well as Visual Studio code, or from the command line using any code editor of your choice.
Create the Azure Service Bus resource
Go into the Azure Portal and create a Service Bus resource. Azure Service Bus is available in three pricing tiers: Basic, Standard and Premium. For the purposes of this demo, we will want to create a Standard tier instance, as it will support Topics. You will need to pick a globally unique namespace, and you will want to make note of your fully qualified namespace in the form of servicebusnamespace.servicebus.usgovcloudapi.net. You can accept the defaults for the rest of the configuration.
Once your Service Bus resource is provisioned, go into the resource and select the "Access Control (IAM)" blade. Add your user to the Azure Service Bus Data Owner role, which will allow your user identity to read and write from the Service Bus. If you were to deploy the functions to Azure, you would create a managed identity for the Azure Function and assign it Azure Service Bus Data Owner, Azure Service Bus Data Reader, and/or Azure Service Bus Data Writer roles as appropriate. However, for the purposes of this demo we will be testing locally and will use your Azure credentials to authenticate to Service Bus.
Finally, find the Queue blade and create a queue (in the demo this is called 'testq' but you can name it whatever you'd like). Then find the Topic blade and create a new topic ('testTopic') and one or more subscriptions on the topic ('testSubscription1','testSubscription2', etc.).
Create the Azure Functions app locally
To build the scaffolding for the Azure Function Apps, we will use the Azure Function Core Tools to first initialize the Function App, then create the skeleton for the functions themselves.
Open a Windows Terminal session and change directories to where you'd like your code to reside. Enter the following command:
func init ServiceBusQueueFunc --worker-runtime dotnet-isolated --target-framework net8.0
Change directories to the "ServiceBusQueueFunc" directory (or whatever you called your function). Then add these functions:
Code:
func new --name ServiceBusQueueWriter --template "HTTP trigger"
func new --name ServiceBusQueueReader --template "ServiceBusQueueTrigger"
func new --name ServiceBusTopicReader --template "ServiceBusTopicTrigger"
And finally, we need to add two libraries which will add the Azure.Message.ServiceBus and Azure.Identity libraries so we can communicate with Service Bus.
Code:
dotnet add package Azure.Messaging.ServiceBus
dotnet add package Azure.Identity
Authentication to Service Bus
When it comes to authenticating with Azure Service Bus, we have a couple of choices. A simple way to authenticate is via a connection string, but this requires using a shared access key credential that you have to store and maintain somewhere, and which could be potentially compromised by a bad actor. To mitigate this, we can use the Azure.Identity library and use a managed identity for our function. Of course, because we're testing locally, we do not have a managed identity, but the Azure.Identity library will also use our Azure CLI credentials to simulate one. To login to Azure CLI:
Code:
az cloud set --name AzureUSGovernment
az login --use-device-code #You may not need the use-device-code flag depending on your configuration
Configuring the Service Bus Writer Function
Now that we have the scaffolding of the Function App complete, it's time to write a little code. If you open your code editor and open the ServiceBusQueueWriter.cs file, you'll see the scaffolding code created by the Azure Functions Core Tools. As the Azure.Messaging.ServiceBus library uses Async methods, we will need to change the method to by async. Also, by default the function will accept both GET and POST requests, but as we're reading from the request body, we will eliminate the GET.
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req)
Next, we need to do is get the values for the Service Bus namespace and the name of the queue or topic we will be writing to. You can either hard code these values into your code, or retrieve them from your environment settings, e.g. a local.settings.json file.
Code:
string? fullyQualifiedNamespace = Environment.GetEnvironmentVariable("ServiceBusConnection__fullyQualifiedNamespace");
string? queueName = Environment.GetEnvironmentVariable("ServiceBusQueueName");
In the case of the ServiceBusConnection value, this will be the fully qualified namespace you gave your Service Bus resource, e.g. myservicebus.servicebus.usgovcloudapi.net.
Next, we'll add a bit of code to parse the body of the HttpRequest so we can send this to the Service Bus queue or topic.
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
And finally, we will create the Service Bus client, create a sender attached to our queue or topic, and send the message. Note on lines 1 and 2 below, I am specifying that the AuthorityHost points at AzureGovernment. By default, the authentication will be attempted against Azure Commercial.
Code:
DefaultAzureCredentialOptions options = new DefaultAzureCredentialOptions();
options.AuthorityHost = AzureAuthorityHosts.AzureGovernment;
await using var client = new ServiceBusClient(fullyQualifiedNamespace, new DefaultAzureCredential(options));
var sender = client.CreateSender(queueName);
await sender.SendMessageAsync(new ServiceBusMessage(requestBody));
Spoiler (Highlight to read)
If you get an authentication error when sending the message, you may need to add the following to your environment variables (e.g. local.settings.json)
"AZURE_TENANT_ID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
If you get an authentication error when sending the message, you may need to add the following to your environment variables (e.g. local.settings.json) "AZURE_TENANT_ID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
Configure the Service Bus Queue and Topic Reader Functions
To configure the Service Bus Queue and Topic Reader Functions is a bit simpler. To configure these, you simply need to add the queue/topic name and subscription (in the case of the Topic Reader) and the ServiceBusConnection.
There is a bit of a "gotcha" here. This value will be read in from your environment variables (e.g. local.settings.json) but utilizes a "magic" word in the configuration. For example, if you title your Connection "ServiceBusConnection", the value in your environment variables needs to be:
"ServiceBusConnection__fullyQualifiedNamespace": "myservicebus.servicebus.usgovcloudapi.net",
For the Queue Reader:
Code:
public async Task Run(
[ServiceBusTrigger("testq", Connection = "ServiceBusConnection")]
ServiceBusReceivedMessage message,
ServiceBusMessageActions messageActions)
For the Topic Reader:
Code:
public async Task Run(
[ServiceBusTrigger("testTopic", "testSubscription", Connection = "ServiceBusConnection")]
ServiceBusReceivedMessage message,
ServiceBusMessageActions messageActions)
Testing the Service Bus Queue Writer and Reader Functions
Open a new terminal window and start the Azurite storage emulator - this will run and stay persistent as long as this terminal window is open.
azurite
If you are using the command line only, you can now build the function app and start the local function run time with the following command:
func host start
If you are using Visual Studio Code, you can enter debug mode (which will support breakpoints in code) by selecting Run->Start Debugging.
When the function host starts, you should see the following:
If you are using Visual Studio Code, set a breakpoint in both the Writer and Queue Reader code. Opening your REST client tool (e.g. Postman), copy and paste the URL from the Writer function into a new POST request. In the request body, add some content into the body (you can add JSON, XML or plain text, just make sure the content-type header matches your text type).
At this point, you should be able to send your request. If you are in Visual Studio Code and have your breakpoints set, you should expect to see first the Writer function light up - go ahead and step through or hit F5 to let the code continue. If your send is successful, you should next expect to see the Queue Reader function light up. Because we are using a Service Bus trigger, the function will execute immediately when an item lands in the queue.
Testing the Topic Trigger
Writing to a topic does not require changing any code in the Queue Writer, we just need to change the destination from the name of the queue to the name of the topic. When you run your test again against the topic, this time we expect the Topic Reader function to execute. If you look at your topic and related subscriptions in the Azure portal, you should expect to see 0 messages in the subscription you are reading from, and 1 message each in the other subscriptions.
Conclusions
Hopefully this has been informative about how easy it is to implement messaging for your applications and solutions using Azure Service Bus, as well as highlighting the small nuances for using this feature in Azure Government. Future Service Bus topics may involve more advanced topics such as ordered message delivery and multi-region resiliency, but please suggest topics that you would like to see in a future post, and please bookmark Azure Federal Developer Connect and register for our upcoming sessions!
Continue reading...