← Back to ArticlesStorage

Blob Storage — Using Managed Identities for Secure Access

Authenticating to Azure Blob Storage using managed identities instead of connection strings for improved security.

Blob Storage — Using Managed Identities for Secure Access

Overview

Managed identities provide a secure way to access Azure resources without storing secrets in your code or configuration. Instead of connection strings with keys, your applications can use Azure's identity system to authenticate to Blob Storage. This eliminates the risk of credential exposure and simplifies secret management.

What You'll Learn


Problem

Your application stores:


Solution

Step 1: Enable Managed Identity

# Enable system-assigned identity for Function App
az functionapp identity assign \
  --name my-function-app \
  --resource-group my-rg

# Enable for VM
az vm identity assign \
  --name my-vm \
  --resource-group my-rg

# Enable for Container App
az containerapp update \
  --name my-container-app \
  --resource-group my-rg \
  --identity-system

Step 2: Grant Storage Permissions via RBAC

# Grant blob data contributor to the identity
az role assignment create \
  --assignee <identity-principal-id> \
  --role "Storage Blob Data Contributor" \
  --scope "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/<storage>"

# Or at container level
az role assignment create \
  --assignee <identity-principal-id> \
  --role "Storage Blob Data Reader" \
  --scope "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/<storage>/blobServices/default/containers/<container>"

Step 3: Code with Managed Identity

// .NET - Using DefaultAzureCredential
using Azure.Identity;
using Azure.Storage.Blobs;

public class BlobService
{
    public async Task UploadFileAsync(string containerName, string blobName, Stream content)
    {
        // Use DefaultAzureCredential (checks managed identity, service principal, etc.)
        var credential = new DefaultAzureCredential();
        
        var blobServiceClient = new BlobServiceClient(
            new Uri("https://mystorageaccount.blob.core.windows.net"),
            credential);
        
        var containerClient = blobServiceClient.GetBlobContainerClient(containerName);
        var blobClient = containerClient.GetBlobClient(blobName);
        
        await blobClient.UploadAsync(content, overwrite: true);
    }
    
    public async Task DownloadFileAsync(string containerName, string blobName, string localPath)
    {
        var credential = new DefaultAzureCredential();
        var blobServiceClient = new BlobServiceClient(
            new Uri("https://mystorageaccount.blob.core.windows.net"),
            credential);
        
        var blobClient = blobServiceClient.GetBlobContainerClient(containerName)
            .GetBlobClient(blobName);
        
        await blobClient.DownloadToAsync(localPath);
    }
}

Step 4: Azure Functions with Managed Identity

public class FunctionWithManagedIdentity
{
    [Function("UploadToBlob")]
    public async Task Run(
        [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
        ILogger log)
    {
        // Function has managed identity enabled
        // DefaultAzureCredential automatically uses it
        
        var credential = new DefaultAzureCredential();
        var blobServiceClient = new BlobServiceClient(
            new Uri("https://mystorageaccount.blob.core.windows.net"),
            credential);
        
        var containerClient = blobServiceClient.GetBlobContainerClient("uploads");
        
        var blobName = Guid.NewGuid().ToString();
        var blobClient = containerClient.GetBlobClient(blobName);
        
        await blobClient.UploadAsync(req.Body, overwrite: true);
        
        log.LogInformation("Uploaded blob: {BlobName}", blobName);
    }
}

Step 5: JavaScript/TypeScript

import { DefaultAzureCredential } from "@azure/identity";
import { BlobServiceClient } from "@azure/storage-blob";

async function uploadWithManagedIdentity(
    containerName: string, 
    blobName: string, 
    content: string
): Promise<void> {
    const credential = new DefaultAzureCredential();
    
    const blobServiceClient = new BlobServiceClient(
        "https://mystorageaccount.blob.core.windows.net",
        credential
    );
    
    const containerClient = blobServiceClient.getContainerClient(containerName);
    const blockBlobClient = containerClient.getBlockBlobClient(blobName);
    
    await blockBlobClient.upload(content, content.length);
}

Real-Time Scenarios

Scenario 1: Web App Uploads to Storage

// ASP.NET Core with managed identity
public class UploadController : Controller
{
    private readonly BlobServiceClient _blobClient;
    
    public UploadController(IConfiguration config)
    {
        var storageUri = config["Storage:BlobUri"];
        var credential = new DefaultAzureCredential();
        _blobClient = new BlobServiceClient(new Uri(storageUri), credential);
    }
    
    [HttpPost("upload")]
    public async Task<IActionResult> Upload(IFormFile file)
    {
        var container = _blobClient.GetBlobContainerClient("uploads");
        var blob = container.GetBlobClient(Guid.NewGuid() + Path.GetExtension(file.FileName));
        
        using var stream = file.OpenReadStream();
        await blob.UploadAsync(stream);
        
        return Ok(new { url = blob.Uri });
    }
}

Scenario 2: Azure Function Processing

public class ImageProcessorFunction
{
    [Function("ProcessImage")]
    public async Task Run(
        [BlobTrigger("uploads/{name}")] Stream image,
        string name,
        [Blob("thumbnails")] BlobContainerClient thumbContainer)
    {
        // Read image
        using var input = Image.Load(image);
        
        // Resize
        input.Mutate(x => x.Resize(200, 200));
        
        // Save thumbnail using managed identity
        var thumbName = Path.GetFileNameWithoutExtension(name) + "_thumb.jpg";
        var thumbBlob = thumbContainer.GetBlobClient(thumbName);
        
        using var ms = new MemoryStream();
        input.SaveAsJpeg(ms);
        ms.Position = 0;
        
        await thumbBlob.UploadAsync(ms);
    }
}

Scenario 3: Container App Processing

// Container Apps with managed identity
var blobServiceClient = new BlobServiceClient(
    new Uri("https://mystorage.blob.core.windows.net"),
    new DefaultAzureCredential());

var container = blobServiceClient.GetBlobContainerClient("data");
await foreach (var blob in container.GetBlobsAsync())
{
    // Process each blob
}

Migration from Connection String

Before (With Connection String)

// OLD - Not recommended
var connectionString = "DefaultEndpointsProtocol=https;AccountName=mystorage;AccountKey=...";
var blobServiceClient = new BlobServiceClient(connectionString);

After (With Managed Identity)

// NEW - Recommended
var blobServiceClient = new BlobServiceClient(
    new Uri("https://mystorage.blob.core.windows.net"),
    new DefaultAzureCredential());

Configuration

// local.settings.json (for local development)
{
  "Values": {
    "AZURE_CLIENT_ID": "<your-client-id>",
    "AZURE_TENANT_ID": "<your-tenant-id>",
    "AZURE_CLIENT_SECRET": "<your-client-secret>"
  }
}

In Azure, these are automatically set for managed identity.


RBAC Roles for Storage

RolePermissions
Storage Blob Data OwnerFull access to blob data
Storage Blob Data ContributorRead, write, delete blobs
Storage Blob Data ReaderRead blobs and containers
Storage Blob DelegatorGet user delegation key

Best Practices

PracticeWhy
Use system-assigned identityAutomatic lifecycle management
Grant minimum required rolePrinciple of least privilege
Use user-assigned for multiple resourcesShare single identity
Monitor access with Azure MonitorTrack who accesses what

Summary