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
- Setting up managed identities for Blob Storage access
- Coding with managed identity authentication
- Configuring storage permissions via RBAC
- Migration from connection strings
Problem
Your application stores:
- Connection strings in config files
- Storage account keys in code
- Secrets that could be exposed in git
- Manual secret rotation requirements
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
| Role | Permissions |
|---|---|
| Storage Blob Data Owner | Full access to blob data |
| Storage Blob Data Contributor | Read, write, delete blobs |
| Storage Blob Data Reader | Read blobs and containers |
| Storage Blob Delegator | Get user delegation key |
Best Practices
| Practice | Why |
|---|---|
| Use system-assigned identity | Automatic lifecycle management |
| Grant minimum required role | Principle of least privilege |
| Use user-assigned for multiple resources | Share single identity |
| Monitor access with Azure Monitor | Track who accesses what |
Summary
- Managed identities eliminate secrets from code
- Use DefaultAzureCredential for automatic detection
- Grant appropriate RBAC roles to the identity
- Works with Functions, VMs, Container Apps, and more
- Simplifies compliance and security posture