Azure  

Authentication for Smart Grid APIs: Managed Identities in Azure Functions

Table of Contents

  • Introduction

  • Why Managed Identities Are Non-Negotiable in Critical Infrastructure

  • Real-World Scenario: Real-Time Grid Load Balancing System

  • How Managed Identities Work Under the Hood

  • End-to-End Implementation in .NET 8

  • Secure Integration with Azure Key Vault and Cosmos DB

  • Testing and Validation Strategy

  • Enterprise Operational Best Practices

  • Conclusion

Introduction

In the world of critical infrastructure—like national power grids—every secret is a liability. A leaked connection string or API key doesn’t just risk data; it risks blackouts, safety systems, and public trust.

As a senior cloud architect who’s modernized grid control systems for major utilities, I enforce one principle:

“If your code handles credentials, you’ve already failed.”

This article shows how Managed Identities in Azure Functions eliminate secrets entirely, using a real-time smart grid load-balancing system as our live scenario.

Why Managed Identities Are Non-Negotiable in Critical Infrastructure

Utilities operate under NERC CIP and ISO 27001. They require:

  • No hardcoded secrets

  • Audit trails for every data access

  • Automatic credential rotation

Managed Identities solve this by giving your Azure Function an identity in Azure AD, which can be granted permissions to other Azure services—without passwords, keys, or certificates.

Real-World Scenario: Real-Time Grid Load Balancing System

A national utility uses Azure Functions to:

  1. Ingest real-time telemetry from 50,000+ smart meters (via Event Hubs)

  2. Analyze grid load using Cosmos DB for historical patterns

  3. Fetch dynamic pricing rules from Azure Key Vault

  4. Dispatch load-shedding commands to substations

The mandate?

“The function must access Cosmos DB and Key Vault without any secrets in code, config, or deployment pipelines.”

Managed Identity is the only compliant solution.

PlantUML Diagram

How Managed Identities Work Under the Hood

When you enable System-Assigned Managed Identity on a Function App:

  • Azure creates a service principal in your Azure AD tenant

  • The runtime automatically acquires an access token from the Instance Metadata Service (IMDS)

  • SDKs like Azure.Identity use this token to authenticate to Azure services

No credentials. No rotation. No risk.

End-to-End Implementation in .NET 8

Step 1: Enable Managed Identity (via Azure CLI)

az functionapp identity assign \
  --name grid-load-balancer-func \
  --resource-group energy-rg \
  --query principalId -o tsv

Step 2: Grant Permissions

# Get Cosmos DB account name
COSMOS_ACCOUNT="grid-telemetry-cosmos"

# Grant Data Reader role
az cosmosdb sql role assignment create \
  --account-name $COSMOS_ACCOUNT \
  --resource-group energy-rg \
  --scope "/" \
  --principal-id <principalId-from-above> \
  --role-definition-id 00000000-0000-0000-0000-000000000001  # Built-in Cosmos DB Reader

# Grant Key Vault secret reader
az keyvault set-policy \
  --name grid-config-kv \
  --object-id <principalId> \
  --secret-permissions get list

Step 3: .NET 8 Function Code (GridBalancerFunction.cs)

using System.Text.Json;
using Azure.Identity;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Cosmos;

namespace Energy.Grid.Functions;

public class GridBalancerFunction
{
    private readonly CosmosClient _cosmosClient;
    private readonly SecretClient _secretClient;
    private readonly ILogger<GridBalancerFunction> _logger;

    public GridBalancerFunction(
        ILogger<GridBalancerFunction> logger,
        // Managed Identity is used automatically by Azure SDKs
        CosmosClient cosmosClient,
        SecretClient secretClient)
    {
        _logger = logger;
        _cosmosClient = cosmosClient;
        _secretClient = secretClient;
    }

    [Function("BalanceGridLoad")]
    public async Task<HttpResponseData> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
    {
        _logger.LogInformation("Grid balancing triggered");

        try
        {
            // 1. Fetch dynamic pricing rules from Key Vault
            var pricingSecret = await _secretClient.GetSecretAsync("DynamicPricingRule");
            var pricingRule = JsonSerializer.Deserialize<PricingRule>(pricingSecret.Value.Value);

            // 2. Query Cosmos DB for real-time load
            var container = _cosmosClient.GetContainer("GridTelemetry", "Meters");
            var query = $"SELECT VALUE c.load FROM c WHERE c.timestamp > {(DateTimeOffset.UtcNow.AddMinutes(-5).ToUnixTimeSeconds())}";
            var loads = new List<double>();
            
            using var feed = container.GetItemQueryStreamIterator(query);
            while (feed.HasMoreResults)
            {
                var response = await feed.ReadNextAsync();
                var results = await JsonSerializer.DeserializeAsync<List<double>>(response.Content);
                loads.AddRange(results);
            }

            var avgLoad = loads.Count > 0 ? loads.Average() : 0;

            // 3. Apply business logic
            var action = avgLoad > pricingRule.Threshold 
                ? "Initiate load shedding" 
                : "Maintain normal operation";

            // 4. Return result
            var response = req.CreateResponse(System.Net.HttpStatusCode.OK);
            await response.WriteStringAsync(JsonSerializer.Serialize(new { 
                action, 
                averageLoad = avgLoad, 
                ruleApplied = pricingRule.Name 
            }));
            return response;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Grid balancing failed");
            var errorResponse = req.CreateResponse(System.Net.HttpStatusCode.InternalServerError);
            await errorResponse.WriteStringAsync("Processing failed");
            return errorResponse;
        }
    }
}

public record PricingRule(string Name, double Threshold);

Step 4: Program.cs – Register Azure Clients with Managed Identity

using Azure.Identity;
using Microsoft.Azure.Cosmos;
using Azure.Security.KeyVault.Secrets;
using Microsoft.Extensions.Azure;

var builder = Host.CreateApplicationBuilder(args);
builder.ConfigureFunctionsWorkerDefaults();

// Use DefaultAzureCredential (Managed Identity in Azure, CLI/auth in dev)
var credential = new DefaultAzureCredential();

// Register Cosmos DB client
builder.Services.AddSingleton<CosmosClient>(sp =>
    new CosmosClient(
        accountEndpoint: "https://grid-telemetry-cosmos.documents.azure.com:443/",
        tokenCredential: credential
    ));

// Register Key Vault client
builder.Services.AddSingleton<SecretClient>(sp =>
    new SecretClient(
        vaultUri: new Uri("https://grid-config-kv.vault.azure.net/"),
        credential: credential
    ));

var host = builder.Build();
host.Run();

Step 5: local.settings.json (for local development)

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

Run az login as a user with Key Vault and Cosmos DB access. DefaultAzureCredential will use your CLI identity automatically.

Secure Integration with Azure Key Vault and Cosmos DB

  • Key Vault: Stores DynamicPricingRule as a JSON secret

  • Cosmos DB: Uses RBAC (not primary keys) with fine-grained roles

  • All communication: Uses Azure AD tokens—no connection strings anywhere

1

2

Testing and Validation Strategy

  1. Local

    • az login as authorized user

    • Run function with func start → SDK uses your identity

  2. Azure

    • Trigger function → check Application Insights for logs

    • Verify Key Vault audit logs show GetSecret by Function identity

    • Confirm Cosmos DB requests appear in diagnostic logs

  3. Break-glass test:

    • Remove Key Vault policy → function should fail with 403, not expose secrets

Enterprise Operational Best Practices

  • Prefer System-Assigned over User-Assigned unless sharing identity across resources

  • Enable diagnostic logs on Key Vault and Cosmos DB for full audit trail

  • Use least-privilege roles: e.g., Cosmos DB Built-in Data Reader, not Owner

  • Rotate secrets in Key Vault without redeploying code

  • Fail fast: Validate connectivity at startup using IHostedService

Conclusion

In critical systems like smart grids, security isn’t a feature—it’s a requirement. Managed Identities in Azure Functions deliver zero-secret authentication that’s simple, scalable, and compliant. By leveraging DefaultAzureCredential and Azure RBAC, we’ve built a system where:

  • No secrets exist in code, config, or pipelines

  • Every data access is auditable

  • Credentials rotate automatically

This isn’t just best practice—it’s how modern critical infrastructure stays resilient, secure, and always on. As cloud architects, our job is to make the secure path the only path. Managed Identities make that possible.