Develop Message-Based Solutions
This guide covers the AZ-204 exam topics for developing message-based solutions using Azure Service Bus and Azure Queue Storage. We'll extend our TaskManagerWeb
(WebApp) and TaskManagerFunctions
(Function App) to handle task-related messages, integrating with our existing setup (Cosmos DB, API Management, Event Grid, and Event Hub). We'll use Service Bus for reliable task notifications and Queue Storage for background task processing.
Prerequisites
- Azure Subscription (Free tier or $200 credit recommended).
- Existing Resource Group (
az204exam
). - Deployed Apps:
- WebApp:
taskmanagerweb-yourname
(ASP.NET Core MVC, Free F1 tier). - Function App:
taskmanagerfunc-yourname
(.NET 8 isolated, Consumption plan). - Cosmos DB:
taskmanagercosmos
withTasksDB/Tasks
. - API Management:
taskmanagerapim-yourname
. - Tools:
- .NET SDK 8.0:
winget install Microsoft.DotNet.SDK.8
. - Azure CLI.
- Azure Functions Core Tools.
Implement Solutions that Use Azure Service Bus
We'll use Azure Service Bus to send task creation notifications from the WebApp, processed by a Function App to log or simulate sending emails.
Steps
Follow these steps to implement Azure Service Bus:
-
Create a Service Bus Namespace and Queue.
-
Get the Service Bus connection string and add it to the Function App.
-
Update the WebApp to send messages to Service Bus.
-
Create a Service Bus Trigger Function to process messages.
-
Test locally (optional).
-
Deploy and test the solution in Azure.
Step 1: Create Service Bus Namespace and Queue
In the Azure Portal, go to Create a resource > Service Bus > Create. Configure:
- Resource group:
az204exam
. - Namespace name:
taskmanagersb-yourname
(globally unique). - Location: Same as apps (e.g., West US 3).
- Pricing tier: Basic (exam-friendly, low cost).
Inside the namespace, create a queue:
- Name:
tasknotifications
. - Defaults: Max size 1 GB, message TTL 14 days.
Alternatively, use the Azure CLI:
az servicebus namespace create --name taskmanagersb-yourname --resource-group az204exam --location westus3 --sku Basic
az servicebus queue create --name tasknotifications --namespace-name taskmanagersb-yourname --resource-group az204exam
Step 2: Get Connection String and Add to Function App
In taskmanagersb-yourname
> Shared access policies > RootManageSharedAccessKey, copy the Primary Connection String.
Add it to the Function App:
- In
taskmanagerfunc-yourname
> Settings > Environment Variables. - Add:
- Name:
ServiceBusConnection
. - Value:
[your-connection-string]
. - Save.
Step 3: Update WebApp to Send Messages
In TaskManagerWeb
, add the Service Bus SDK and update the controller to send messages.
cd TaskManagerWeb
dotnet add package Azure.Messaging.ServiceBus
Update Controllers/HomeController.cs
:
using Microsoft.AspNetCore.Mvc;
using TaskManagerWeb.Models;
using System.Collections.Generic;
using Azure.Messaging.ServiceBus;
using System.Text.Json;
namespace TaskManagerWeb.Controllers
{
public class HomeController : Controller
{
private static List<TaskItem> _tasks = new List<TaskItem>();
private readonly ServiceBusClient _serviceBusClient;
public HomeController()
{
var connectionString = Environment.GetEnvironmentVariable("ServiceBusConnection") ?? "your-local-connection-string";
_serviceBusClient = new ServiceBusClient(connectionString);
}
public IActionResult Index()
{
return View(_tasks);
}
[HttpPost]
public async Task<IActionResult> Create(string title, string description, string dueDate)
{
var task = new TaskItem
{
Title = title,
Description = description,
DueDate = dueDate
};
_tasks.Add(task);
// Send message to Service Bus
var sender = _serviceBusClient.CreateSender("tasknotifications");
var message = new ServiceBusMessage(JsonSerializer.Serialize(task));
await sender.SendMessageAsync(message);
await sender.DisposeAsync();
return RedirectToAction("Index");
}
}
}
Update appsettings.json
:
{
"ServiceBusConnection": "",
"EventHubConnection": ""
}
Deploy the WebApp and add the environment variable:
az webapp up --name taskmanagerweb-yourname --resource-group az204exam
In taskmanagerweb-yourname
> Settings > Environment Variables, add:
- Name:
ServiceBusConnection
. - Value:
[your-connection-string]
.
Step 4: Create Service Bus Trigger Function
In TaskManagerFunctions
, create the Service Bus Trigger Function and update its code.
cd TaskManagerFunctions
func new --template "Azure Service Bus Queue Trigger" --name ProcessTaskNotification
Update ProcessTaskNotification.cs
:
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
namespace TaskManagerFunctions
{
public class ProcessTaskNotification
{
private readonly ILogger<ProcessTaskNotification> _logger;
public ProcessTaskNotification(ILogger<ProcessTaskNotification> logger)
{
_logger = logger;
}
[Function("ProcessTaskNotification")]
public void Run([ServiceBusQueueTrigger("tasknotifications", Connection = "ServiceBusConnection")] string message)
{
_logger.LogInformation($"Service Bus message: {message}");
// Simulate email notification or logging
}
}
}
Step 5: Test Locally (Optional)
Update local.settings.json
in TaskManagerFunctions
:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
"CosmosDBConnection": "[your-cosmos-connection]",
"EventHubConnection": "[your-eventhub-connection]",
"ServiceBusConnection": "[your-servicebus-connection]"
}
}
Run the WebApp and Function locally to test:
cd TaskManagerWeb
dotnet run
Create a task via https://localhost:5001
.
Run the Function in a separate terminal:
cd ../TaskManagerFunctions
func start
Check the Function's console for Service Bus message: {"id":"[guid]","Title":"Test Task",...}
.
Step 6: Deploy and Test
Deploy the Function and test in Azure.
func azure functionapp publish taskmanagerfunc-yourname
Test:
- Access
https://taskmanagerweb-yourname.azurewebsites.net
and create a task. - In the Azure Portal, go to
taskmanagerfunc-yourname
> Functions >ProcessTaskNotification
> Monitor > Logs. - Verify logs show
Service Bus message
with task data. - Check Service Bus metrics in
taskmanagersb-yourname
> Queues >tasknotifications
> Metrics; confirmMessages
count increases.
Why
Service Bus ensures reliable, ordered message delivery for notifications, a key exam topic, integrating seamlessly with our WebApp.
Implement Solutions that Use Azure Queue Storage Queues
We'll use Queue Storage to queue tasks for background processing (e.g., validation or formatting), triggered by a Function.
Steps
Follow these steps to implement Azure Queue Storage:
-
Create a Storage Account and Queue (if not already created).
-
Get the Storage connection string and confirm it's in the Function App.
-
Update the Function App to enqueue tasks.
-
Create a Queue Trigger Function to process queue messages.
-
Test locally (optional).
-
Deploy and test the solution in Azure.
Step 1: Create Storage Account and Queue
If not already created (from functions.md
), create a Storage Account:
az storage account create --name taskmanagerstoreyourname --resource-group az204exam --location westus3 --sku Standard_LRS
Create a queue in the Storage Account:
In the Azure Portal, go to taskmanagerstoreyourname
> Queues > + Queue. Name: taskqueue
.
Alternatively, use the Azure CLI:
az storage queue create --name taskqueue --account-name taskmanagerstoreyourname
Step 2: Get Connection String and Confirm in Function App
In taskmanagerstoreyourname
> Access keys, copy the Connection string for key1
.
Confirm it's added to the Function App (should already be added as StorageConnection
from functions.md
):
- In
taskmanagerfunc-yourname
> Settings > Environment Variables. - Confirm:
- Name:
StorageConnection
. - Value:
[your-connection-string]
.
Step 3: Update Function App to Enqueue Tasks
In TaskManagerFunctions
, modify ManageTask.cs
to send tasks to Queue Storage and add the Storage SDK.
dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Storage
Update ManageTask.cs
:
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Functions.Worker.Extensions.Storage;
namespace TaskManagerFunctions
{
public class ManageTask
{
private readonly ILogger<ManageTask> _logger;
private readonly CosmosClient _cosmosClient;
public ManageTask(ILogger<ManageTask> logger)
{
_logger = logger;
_cosmosClient = new CosmosClient(Environment.GetEnvironmentVariable("CosmosDBConnection"), new CosmosClientOptions { ConnectionMode = ConnectionMode.Direct });
}
public class TaskItem
{
public string id { get; set; } = Guid.NewGuid().ToString();
public string Title { get; set; }
public string Description { get; set; }
public string DueDate { get; set; }
}
[Function("ManageTask")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", "get")] HttpRequest req,
[QueueOutput("taskqueue", Connection = "StorageConnection")] IAsyncCollector<string> queueOutput)
{
_logger.LogInformation("Processing task in Cosmos DB.");
var container = _cosmosClient.GetContainer("TasksDB", "Tasks");
if (req.Method == "POST")
{
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
TaskItem task = JsonConvert.DeserializeObject<TaskItem>(requestBody);
if (string.IsNullOrEmpty(task?.Title))
{
return new BadRequestObjectResult("Please provide a title.");
}
task.id = Guid.NewGuid().ToString();
await container.CreateItemAsync(task, new PartitionKey(task.id));
_logger.LogInformation($"Created task: {task.Title}");
// Add task to queue
await queueOutput.AddAsync(JsonConvert.SerializeObject(task));
return new OkObjectResult(task);
}
else // GET
{
string taskId = req.Query["taskId"];
if (string.IsNullOrEmpty(taskId))
{
return new BadRequestObjectResult("Please provide taskId.");
}
try
{
var task = await container.ReadItemAsync<TaskItem>(taskId, new PartitionKey(taskId));
_logger.LogInformation($"Read task: {task.Resource.Title}");
return new OkObjectResult(task.Resource);
}
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return new NotFoundResult();
}
}
}
}
}
Step 4: Create Queue Trigger Function
In TaskManagerFunctions
, create the Queue Trigger Function and update its code.
func new --template "Azure Queue Storage Trigger" --name ProcessTaskQueue
Update ProcessTaskQueue.cs
:
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
namespace TaskManagerFunctions
{
public class ProcessTaskQueue
{
private readonly ILogger<ProcessTaskQueue> _logger;
public ProcessTaskQueue(ILogger<ProcessTaskQueue> logger)
{
_logger = logger;
}
[Function("ProcessTaskQueue")]
public void Run([QueueTrigger("taskqueue", Connection = "StorageConnection")] string queueMessage)
{
_logger.LogInformation($"Queue message: {queueMessage}");
// Simulate task validation or processing
}
}
}
Step 5: Test Locally (Optional)
Update local.settings.json
in TaskManagerFunctions
:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
"CosmosDBConnection": "[your-cosmos-connection]",
"EventHubConnection": "[your-eventhub-connection]",
"ServiceBusConnection": "[your-servicebus-connection]",
"StorageConnection": "[your-storage-connection]"
}
}
Run the Function locally to test:
cd TaskManagerFunctions
func start
Trigger a task creation via APIM:
curl -X POST https://taskmanagerapim-yourname.azure-api.net/functions/managetask -H "Ocp-Apim-Subscription-Key: [your-key]" -H "Content-Type: application/json" -d '{"Title":"Queue Task","Description":"Test","DueDate":"2025-04-15"}'
Check the Function's console for Queue message: {"id":"[guid]","Title":"Queue Task",...}
.
Step 6: Deploy and Test
Deploy the Function and test in Azure.
func azure functionapp publish taskmanagerfunc-yourname
Test:
- Trigger a task creation via APIM (same curl command as above).
- In the Azure Portal, go to
taskmanagerfunc-yourname
> Functions >ProcessTaskQueue
> Monitor > Logs. - Verify logs show
Queue message
with task data. - Check Queue metrics in
taskmanagerstoreyourname
> Queues >taskqueue
> Metrics; confirmQueue Message Count
increases.
Why
Queue Storage provides simple, cost-effective message queuing for background tasks, complementing Service Bus and covering the exam’s requirements.
Verify All Topics
- Service Bus: Sent task notifications from WebApp, processed by
ProcessTaskNotification
. - Queue Storage: Enqueued tasks from
ManageTask
, processed byProcessTaskQueue
.
Clean Up (Optional)
To avoid costs, delete all resources:
az group delete -n az204exam --no-wait --yes
Next Steps
- Enhance
ProcessTaskNotification
to simulate email sending (e.g., log to Cosmos DB). - Add error handling to
ProcessTaskQueue
(e.g., retry logic). - Review all guides (
acr.md
,auth.md
,cosmos.md
,functions.md
,webapp.md
,insights.md
,apimanagement.md
,eventbased.md
,messagebased.md
) for exam prep.