Skip to content

Implement Secure Azure Solutions

This guide covers the AZ-204 exam topics for implementing secure Azure solutions:

  • Secure app configuration data by using App Configuration or Azure Key Vault
  • Develop code that uses keys, secrets, and certificates stored in Azure Key Vault
  • Implement Managed Identities for Azure resources

We'll enhance our TaskManagerWeb (WebApp) and TaskManagerFunctions (Function App) to demonstrate these security practices.

Prerequisites

  • .NET SDK 8.0 (Install via winget: winget install Microsoft.DotNet.SDK.8)
  • Azure CLI (Install)
  • Azure Functions Core Tools (Install)
  • Azure Subscription (WGU Student Azure account with resource group az204exam)
  • Microsoft 365 Developer Program Subscription (for Microsoft Entra ID access)
  • Existing Apps:
  • WebApp: taskmanagerweb-yourname (ASP.NET Core MVC, Free F1 tier)
  • Function App: taskmanagerfunc-yourname (.NET 8 isolated, Consumption plan)
  • Cosmos DB: taskmanagercosmos with TasksDB/Tasks
  • Storage Account: taskmanagerstorage with tasks container

Secure App Configuration Data Using Azure Key Vault

We'll store sensitive configuration data (e.g., Cosmos DB connection string) in Azure Key Vault and access it in our WebApp, ensuring secrets aren't hardcoded.

Steps

Follow these steps to secure app configuration data:

  1. Create an Azure Key Vault.

  2. Add the Cosmos DB connection string as a secret in Key Vault.

  3. Configure the WebApp to access Key Vault.

  4. Update the WebApp to use the secret.

  5. Test locally (optional).

  6. Deploy and test in Azure.

Step 1: Create an Azure Key Vault

In the Azure Portal (WGU Azure tenant), go to Create a resource > Key Vault > Create. Configure:

  • Resource group: az204exam.
  • Key vault name: taskmanagerkv-yourname (must be unique).
  • Region: Same as your apps (e.g., West US).
  • Pricing tier: Standard.

Click Create.

Step 2: Add the Cosmos DB Connection String as a Secret

In your Key Vault (taskmanagerkv-yourname):

  • Go to Secrets > Generate/Import.
  • Name: CosmosDBConnection.
  • Value: [your-cosmos-connection-string] (copy from Cosmos DB > Keys > Primary Connection String).
  • Click Create.

Alternatively, use the Azure CLI:

az keyvault secret set --vault-name taskmanagerkv-yourname --name CosmosDBConnection --value "[your-cosmos-connection-string]"

Step 3: Configure the WebApp to Access Key Vault

Enable Managed Identity for the WebApp (already done in webapp.md, but we'll verify):

  • In the Azure Portal, go to taskmanagerweb-yourname > Identity > System assigned.
  • Ensure Status is On. If not, turn it on and save.

Grant the WebApp access to Key Vault:

  • In taskmanagerkv-yourname > Access policies > Create.
  • Permissions: Select Get and List under Secret permissions.
  • Principal: Search for and select taskmanagerweb-yourname.
  • Click Next > Create.

Step 4: Update the WebApp to Use the Secret

Add the Azure Key Vault SDK to TaskManagerWeb:

cd TaskManagerWeb
dotnet add package Azure.Identity
dotnet add package Azure.Security.KeyVault.Secrets
dotnet add package Microsoft.Extensions.Configuration.AzureKeyVault

Update Program.cs to load secrets from Key Vault:

using Microsoft.Identity.Web;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Microsoft.Extensions.Configuration;

var builder = WebApplication.CreateBuilder(args);

// Add Key Vault configuration
var keyVaultName = "taskmanagerkv-yourname"; // Replace with your Key Vault name
var keyVaultUri = $"https://{keyVaultName}.vault.azure.net/";
builder.Configuration.AddAzureKeyVault(
    new Uri(keyVaultUri),
    new DefaultAzureCredential());

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

// Add Microsoft Identity authentication
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddAuthorization();

// Add Key Vault client as a service
builder.Services.AddSingleton(x =>
{
    var client = new SecretClient(new Uri(keyVaultUri), new DefaultAzureCredential());
    return client;
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();
app.MapControllers();

app.Run();

Update appsettings.json to remove the hardcoded Cosmos DB connection string (we'll fetch it from Key Vault):

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "yourtenant.onmicrosoft.com",
    "TenantId": "common",
    "ClientId": "your-client-id",
    "CallbackPath": "/signin-oidc"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Create a service to access Cosmos DB using the Key Vault secret. Create Services/CosmosDbService.cs:

using Microsoft.Azure.Cosmos;
using TaskManagerWeb.Models;
using Azure.Security.KeyVault.Secrets;

namespace TaskManagerWeb.Services
{
    public class CosmosDbService
    {
        private readonly CosmosClient _cosmosClient;
        private readonly Container _container;

        public CosmosDbService(SecretClient secretClient)
        {
            var connectionStringResponse = secretClient.GetSecret("CosmosDBConnection");
            var connectionString = connectionStringResponse.Value.Value;
            _cosmosClient = new CosmosClient(connectionString, new CosmosClientOptions { ConnectionMode = ConnectionMode.Direct });
            _container = _cosmosClient.GetContainer("TasksDB", "Tasks");
        }

        public async Task<List<TaskItem>> GetTasksAsync()
        {
            var query = new QueryDefinition("SELECT * FROM c");
            var iterator = _container.GetItemQueryIterator<TaskItem>(query);
            var tasks = new List<TaskItem>();

            while (iterator.HasMoreResults)
            {
                var response = await iterator.ReadNextAsync();
                tasks.AddRange(response);
            }

            return tasks;
        }

        public async Task AddTaskAsync(TaskItem task)
        {
            task.id = Guid.NewGuid().ToString();
            await _container.CreateItemAsync(task, new PartitionKey(task.id));
        }
    }
}

Update Models/TaskItem.cs to include an id property (if not already present):

namespace TaskManagerWeb.Models
{
    public class TaskItem
    {
        public string id { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string DueDate { get; set; }
    }
}

Update Controllers/HomeController.cs to use the CosmosDbService and persist tasks:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using TaskManagerWeb.Models;
using TaskManagerWeb.Services;
using System.Collections.Generic;
using Microsoft.Graph;

namespace TaskManagerWeb.Controllers
{
    [Authorize]
    public class HomeController : Controller
    {
        private readonly GraphServiceClient _graphClient;
        private readonly CosmosDbService _cosmosDbService;

        public HomeController(GraphServiceClient graphClient, CosmosDbService cosmosDbService)
        {
            _graphClient = graphClient;
            _cosmosDbService = cosmosDbService;
        }

        public async Task<IActionResult> Index()
        {
            // Fetch user profile from Microsoft Graph
            var user = await _graphClient.Me.Request().GetAsync();
            ViewBag.UserProfile = new { DisplayName = user.DisplayName, Email = user.Mail ?? user.UserPrincipalName };

            // Fetch tasks from Cosmos DB
            var tasks = await _cosmosDbService.GetTasksAsync();
            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
            };
            await _cosmosDbService.AddTaskAsync(task);
            return RedirectToAction("Index");
        }

        public IActionResult SignOut()
        {
            return SignOut("Cookies", OpenIdConnectDefaults.AuthenticationScheme);
        }
    }
}

Step 5: Test Locally (Optional)

To test locally, you'll need to authenticate with Azure to access Key Vault. Ensure you’re logged into the Azure CLI with your WGU Azure tenant credentials:

az login

Run the WebApp locally:

cd TaskManagerWeb
dotnet run

Sign in with a Microsoft 365 Developer tenant user at https://localhost:5001. Verify that tasks are saved to Cosmos DB (TasksDB/Tasks) and retrieved correctly. The Cosmos DB connection string should be securely fetched from Key Vault.

Step 6: Deploy and Test

Deploy to Azure (in your WGU Azure tenant):

az webapp up --name taskmanagerweb-yourname --resource-group az204exam

Test at https://taskmanagerweb-yourname.azurewebsites.net. Sign in with a Microsoft 365 Developer tenant user. Verify that tasks are saved to Cosmos DB and the app functions as expected.

Why

Using Azure Key Vault to store sensitive configuration data like connection strings prevents hardcoding secrets in the app, enhancing security and meeting exam requirements.

Develop Code that Uses Keys, Secrets, and Certificates Stored in Azure Key Vault

We'll add a Function to TaskManagerFunctions that uses a secret from Key Vault to encrypt task data before storing it in Cosmos DB.

Steps

Follow these steps to use secrets in Azure Key Vault:

  1. Store an encryption key in Key Vault.

  2. Grant the Function App access to Key Vault.

  3. Update the Function to use the encryption key.

  4. Test locally (optional).

  5. Deploy and test in Azure.

Step 1: Store an Encryption Key in Key Vault

Generate a simple encryption key (for demo purposes, we'll use a string; in production, use a proper key):

  • In taskmanagerkv-yourname > Secrets > Generate/Import.
  • Name: EncryptionKey.
  • Value: MySuperSecretKey1234567890abcdef (32 characters for AES-256 demo).
  • Click Create.

Alternatively, use the Azure CLI:

az keyvault secret set --vault-name taskmanagerkv-yourname --name EncryptionKey --value "MySuperSecretKey1234567890abcdef"

Step 2: Grant the Function App Access to Key Vault

Enable Managed Identity for the Function App:

  • In the Azure Portal, go to taskmanagerfunc-yourname > Identity > System assigned.
  • Turn Status to On > Save.

Grant access to Key Vault:

  • In taskmanagerkv-yourname > Access policies > Create.
  • Permissions: Select Get and List under Secret permissions.
  • Principal: Search for and select taskmanagerfunc-yourname.
  • Click Next > Create.

Step 3: Update the Function to Use the Encryption Key

Add the necessary packages to TaskManagerFunctions:

cd TaskManagerFunctions
dotnet add package Azure.Identity
dotnet add package Azure.Security.KeyVault.Secrets
dotnet add package System.Security.Cryptography.Algorithms

Update ManageTask.cs to encrypt the task's Description field before saving to Cosmos DB:

using System;
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 Azure.Security.KeyVault.Secrets;
using Azure.Identity;
using System.Security.Cryptography;
using System.Text;

namespace TaskManagerFunctions
{
    public class ManageTask
    {
        private readonly ILogger<ManageTask> _logger;
        private readonly CosmosClient _cosmosClient;
        private readonly SecretClient _secretClient;
        private readonly string _encryptionKey;

        public ManageTask(ILogger<ManageTask> logger)
        {
            _logger = logger;
            _cosmosClient = new CosmosClient(Environment.GetEnvironmentVariable("CosmosDBConnection"), new CosmosClientOptions { ConnectionMode = ConnectionMode.Direct });

            var keyVaultName = "taskmanagerkv-yourname"; // Replace with your Key Vault name
            var keyVaultUri = $"https://{keyVaultName}.vault.azure.net/";
            _secretClient = new SecretClient(new Uri(keyVaultUri), new DefaultAzureCredential());
            var secret = _secretClient.GetSecret("EncryptionKey").Value;
            _encryptionKey = secret.Value;
        }

        public class TaskItem
        {
            public string id { get; set; } = Guid.NewGuid().ToString();
            public string Title { get; set; }
            public string Description { get; set; } // Will be encrypted
            public string DueDate { get; set; }
        }

        [Function("ManageTask")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", "get")] HttpRequest req)
        {
            _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.");
                }

                // Encrypt the description
                if (!string.IsNullOrEmpty(task.Description))
                {
                    task.Description = EncryptString(task.Description, _encryptionKey);
                }

                task.id = Guid.NewGuid().ToString();
                await container.CreateItemAsync(task, new PartitionKey(task.id));
                _logger.LogInformation($"Created task: {task.Title}");
                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));
                    // Decrypt the description
                    if (!string.IsNullOrEmpty(task.Resource.Description))
                    {
                        task.Resource.Description = DecryptString(task.Resource.Description, _encryptionKey);
                    }
                    _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();
                }
            }
        }

        private string EncryptString(string plainText, string key)
        {
            using (Aes aes = Aes.Create())
            {
                aes.Key = Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0, 32)); // Ensure 32 bytes for AES-256
                aes.GenerateIV();
                var iv = aes.IV;

                using (var encryptor = aes.CreateEncryptor(aes.Key, iv))
                using (var ms = new MemoryStream())
                {
                    ms.Write(iv, 0, iv.Length); // Prepend IV to the ciphertext
                    using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                    using (var sw = new StreamWriter(cs))
                    {
                        sw.Write(plainText);
                    }
                    return Convert.ToBase64String(ms.ToArray());
                }
            }
        }

        private string DecryptString(string cipherText, string key)
        {
            var fullCipher = Convert.FromBase64String(cipherText);
            using (Aes aes = Aes.Create())
            {
                aes.Key = Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0, 32));
                var iv = new byte[16];
                Buffer.BlockCopy(fullCipher, 0, iv, 0, iv.Length); // Extract IV from the beginning
                aes.IV = iv;

                using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
                using (var ms = new MemoryStream(fullCipher, iv.Length, fullCipher.Length - iv.Length))
                using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                using (var sr = new StreamReader(cs))
                {
                    return sr.ReadToEnd();
                }
            }
        }
    }
}

Step 4: Test Locally (Optional)

Ensure local.settings.json has the CosmosDBConnection:

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
        "CosmosDBConnection": "[your-cosmos-connection-string]"
    }
}

Log in to the Azure CLI with your WGU Azure tenant credentials:

az login

Run the Function locally:

func start

POST a task:

curl -X POST http://localhost:7071/api/ManageTask -H "Content-Type: application/json" -d '{"Title":"Secure Task","Description":"Sensitive data","DueDate":"2025-04-15"}'

GET the task (use id from POST response):

curl http://localhost:7071/api/ManageTask?taskId=[task-id]

Verify in Cosmos DB (TasksDB/Tasks) that the Description is encrypted. The GET response should show the decrypted description.

Step 5: Deploy and Test

Deploy the Function to Azure:

func azure functionapp publish taskmanagerfunc-yourname

Test:

curl -X POST https://taskmanagerfunc-yourname.azurewebsites.net/api/managetask?code=[your-key] -H "Content-Type: application/json" -d '{"Title":"Azure Secure Task","Description":"Sensitive data","DueDate":"2025-04-15"}'
curl https://taskmanagerfunc-yourname.azurewebsites.net/api/managetask?code=[your-key]&taskId=[task-id]

Verify the encryption/decryption process works as expected.

Why

Using Azure Key Vault to store encryption keys ensures secure data handling, a key exam skill, and demonstrates proper secret management.

Implement Managed Identities for Azure Resources

We’ve already enabled Managed Identities for both the WebApp and Function App to access Key Vault. Let’s verify and use it in the Function App to access Cosmos DB without a connection string in the configuration.

Steps

Follow these steps to implement Managed Identities:

  1. Grant the Function App access to Cosmos DB using Managed Identity.

  2. Update the Function to use Managed Identity for Cosmos DB.

  3. Test locally (optional).

  4. Deploy and test in Azure.

Step 1: Grant the Function App Access to Cosmos DB

Ensure Managed Identity is enabled for taskmanagerfunc-yourname (already done in the previous section).

Grant the Managed Identity access to Cosmos DB:

  • In the Azure Portal, go to taskmanagercosmos > Access control (IAM) > Add role assignment.
  • Role: Cosmos DB Built-in Data Contributor.
  • Assign access to: Managed identity > Select taskmanagerfunc-yourname.
  • Click Save.

Step 2: Update the Function to Use Managed Identity

Update ManageTask.cs to use Managed Identity for Cosmos DB access, removing the need for a connection string:

using System;
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 Azure.Security.KeyVault.Secrets;
using Azure.Identity;
using System.Security.Cryptography;
using System.Text;
using Azure.Core;

namespace TaskManagerFunctions
{
    public class ManageTask
    {
        private readonly ILogger<ManageTask> _logger;
        private readonly CosmosClient _cosmosClient;
        private readonly SecretClient _secretClient;
        private readonly string _encryptionKey;

        public ManageTask(ILogger<ManageTask> logger)
        {
            _logger = logger;

            // Use Managed Identity to authenticate with Cosmos DB
            var credential = new DefaultAzureCredential();
            var cosmosEndpoint = "https://taskmanagercosmos.documents.azure.com:443/"; // Replace with your Cosmos DB endpoint
            _cosmosClient = new CosmosClient(cosmosEndpoint, credential, new CosmosClientOptions { ConnectionMode = ConnectionMode.Direct });

            // Fetch encryption key from Key Vault
            var keyVaultName = "taskmanagerkv-yourname"; // Replace with your Key Vault name
            var keyVaultUri = $"https://{keyVaultName}.vault.azure.net/";
            _secretClient = new SecretClient(new Uri(keyVaultUri), credential);
            var secret = _secretClient.GetSecret("EncryptionKey").Value;
            _encryptionKey = secret.Value;
        }

        public class TaskItem
        {
            public string id { get; set; } = Guid.NewGuid().ToString();
            public string Title { get; set; }
            public string Description { get; set; } // Will be encrypted
            public string DueDate { get; set; }
        }

        [Function("ManageTask")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", "get")] HttpRequest req)
        {
            _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.");
                }

                // Encrypt the description
                if (!string.IsNullOrEmpty(task.Description))
                {
                    task.Description = EncryptString(task.Description, _encryptionKey);
                }

                task.id = Guid.NewGuid().ToString();
                await container.CreateItemAsync(task, new PartitionKey(task.id));
                _logger.LogInformation($"Created task: {task.Title}");
                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));
                    // Decrypt the description
                    if (!string.IsNullOrEmpty(task.Resource.Description))
                    {
                        task.Resource.Description = DecryptString(task.Resource.Description, _encryptionKey);
                    }
                    _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();
                }
            }
        }

        private string EncryptString(string plainText, string key)
        {
            using (Aes aes = Aes.Create())
            {
                aes.Key = Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0, 32)); // Ensure 32 bytes for AES-256
                aes.GenerateIV();
                var iv = aes.IV;

                using (var encryptor = aes.CreateEncryptor(aes.Key, iv))
                using (var ms = new MemoryStream())
                {
                    ms.Write(iv, 0, iv.Length); // Prepend IV to the ciphertext
                    using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                    using (var sw = new StreamWriter(cs))
                    {
                        sw.Write(plainText);
                    }
                    return Convert.ToBase64String(ms.ToArray());
                }
            }
        }

        private string DecryptString(string cipherText, string key)
        {
            var fullCipher = Convert.FromBase64String(cipherText);
            using (Aes aes = Aes.Create())
            {
                aes.Key = Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0, 32));
                var iv = new byte[16];
                Buffer.BlockCopy(fullCipher, 0, iv, 0, iv.Length); // Extract IV from the beginning
                aes.IV = iv;

                using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
                using (var ms = new MemoryStream(fullCipher, iv.Length, fullCipher.Length - iv.Length))
                using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                using (var sr = new StreamReader(cs))
                {
                    return sr.ReadToEnd();
                }
            }
        }
    }
}

Step 3: Test Locally (Optional)

Remove the CosmosDBConnection from local.settings.json since we're using Managed Identity:

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
    }
}

Log in to the Azure CLI with your WGU Azure tenant credentials:

az login

Run the Function locally:

func start

POST a task:

curl -X POST http://localhost:7071/api/ManageTask -H "Content-Type: application/json" -d '{"Title":"Managed Identity Task","Description":"Sensitive data","DueDate":"2025-04-15"}'

GET the task (use id from POST response):

curl http://localhost:7071/api/ManageTask?taskId=[task-id]

Verify the task is saved and retrieved from Cosmos DB using Managed Identity.

Step 4: Deploy and Test

Deploy the Function to Azure:

func azure functionapp publish taskmanagerfunc-yourname

Test:

curl -X POST https://taskmanagerfunc-yourname.azurewebsites.net/api/managetask?code=[your-key] -H "Content-Type: application/json" -d '{"Title":"Azure Managed Identity Task","Description":"Sensitive data","DueDate":"2025-04-15"}'
curl https://taskmanagerfunc-yourname.azurewebsites.net/api/managetask?code=[your-key]&taskId=[task-id]

Verify the task is saved and retrieved using Managed Identity.

Why

Managed Identities eliminate the need to manage credentials, enhancing security and simplifying access to Azure resources, a key exam objective.

Clean Up (Optional)

To avoid costs, delete all resources:

az group delete -n az204exam --no-wait --yes

Next Steps

  • Explore additional Key Vault features, such as using keys for encryption or certificates for authentication.
  • Review all guides for comprehensive AZ-204 exam preparation.