Skip to content

Blob Storage

This guide explains how to enable and integrate Azure Blob Storage with API services using the Forge platform, covering both Terraform infrastructure setup and Aspire local development.

đŸŽ¯ Overview

Azure Blob Storage provides scalable, secure object storage for unstructured data. The Forge platform provides:

  • Terraform Module: Automated Azure Blob Storage Account provisioning
  • Aspire Integration: Local development and testing with storage emulator
  • RBAC Authentication: Secure, token-based access via managed identity
  • Feature Flags: Flexible enable/disable configuration

🔧 Infrastructure Setup (Terraform)

Enable Blob Storage Feature Flag

In your saif-api-service Terraform configuration, add the blob storage feature flag:

enable_blob_storage = true
blob_storage_settings = {
  containers           = ["blobcontainer"]
  contributor_group_id = "group-object-id-guid"
}

Configuration Options:

Property Type Description Example
enable_blob_storage bool Enable/disable blob storage deployment true
containers list(string) List of blob container names to create ["uploads", "archives"]
contributor_group_id string Azure AD group object ID for contributor access "12345678-1234-1234-1234-123456789abc"

Example Terraform Configuration

module "saif_api_service" {
  source = "app.terraform.io/SAIFCorp/saif-api-service/azure"

  environment              = "dev"
  project_id              = "myapi"
  owner                   = "Platform Team"
  resource_location       = "westus2"

  # Enable blob storage
  feature_flags = {
    enable_blob_storage = true
    blob_storage_settings = {
      containers = [
        "documents",
        "uploads",
        "archives"
      ]
      contributor_group_id = data.azuread_group.platform_team.object_id
    }
  }

  tags = {
    Environment = "Development"
    Application = "MyAPI"
  }
}

data "azuread_group" "platform_team" {
  display_name = "Platform Team"
}

Generated Resources

When enable_blob_storage = true, the following resources are automatically created:

  • đŸ›ī¸ Azure Storage Account (naming convention: {projectid}{environment}corp, e.g., myapitestcorp, myapiprodcorp)
  • đŸ“Ļ Blob Containers (with names from containers list)
  • 🔐 RBAC Role Assignments:
  • Storage Account Contributor: Assigned to the contributor group
  • Storage Blob Data Owner: Assigned to application managed identity
  • âš™ī¸ Environment Variables: Storage account connection details are automatically configured in Azure

đŸ–Ĩī¸ Local Development (Aspire)

Aspire Storage Emulator Setup

For local development, use the Azure Storage Emulator integrated with Aspire:

AppHost Configuration

// Program.cs in YourApp.AppHost

var builder = DistributedApplication.CreateBuilder(args);

// Add Azure Storage with emulator
var storage = builder
    .AddAzureStorage("storage")
    .RunAsEmulator()
    .AddBlobs("blobstorage");

// Reference storage in your backend service
var backend = builder
    .AddApi()
    .WithReference(storage);

// Add web frontend
var frontend = builder
    .AddWebFrontEnd(backend);

builder.Build().Run();

Configuration Options:

  • .RunAsEmulator() - Uses local storage emulator (no Azure account required)
  • .AddBlobs("blobstorage") - Adds blob storage resource with specified name (used to reference in services)
  • .WithReference() - Injects storage connection details into service

Using Blob Storage in Your Service

Service Registration

In your Program.cs, register the Azure Blob Service client:

// Program.cs
builder.AddAzureBlobServiceClient("blobstorage");

â„šī¸ AddAzureBlobServiceClient automatically uses DefaultAzureCredential when deployed to Azure and connection strings for local development.

Minimal API Examples

using Azure.Storage.Blobs;

// Upload endpoint
app.MapPost("/api/upload", async (IFormFile file, BlobServiceClient blobServiceClient) =>
{
    if (file is null || file.Length == 0)
    {
        return Results.BadRequest(new { Error = "No file provided" });
    }

    var containerClient = blobServiceClient.GetBlobContainerClient("uploads");
    await containerClient.CreateIfNotExistsAsync();

    var blobName = $"{Guid.NewGuid()}-{file.FileName}";
    var blobClient = containerClient.GetBlobClient(blobName);

    await using var stream = file.OpenReadStream();
    await blobClient.UploadAsync(stream, overwrite: true);

    return Results.Ok(new
    {
        FileName = file.FileName,
        BlobName = blobName,
        Size = file.Length,
        UploadedAt = DateTime.UtcNow
    });
});

// List files endpoint
app.MapGet("/api/files", async (BlobServiceClient blobServiceClient) =>
{
    var containerClient = blobServiceClient.GetBlobContainerClient("uploads");

    if (!await containerClient.ExistsAsync())
    {
        return Results.Ok(Array.Empty<object>());
    }

    var blobs = new List<object>();
    await foreach (var blob in containerClient.GetBlobsAsync())
    {
        blobs.Add(new
        {
            Name = blob.Name,
            Size = blob.Properties.ContentLength,
            CreatedOn = blob.Properties.CreatedOn
        });
    }

    return Results.Ok(blobs);
});

📚 Example: Aspire Blob Storage Project

The Forge platform includes a complete example at foundry/dotnet/aspire-blobstorage/:

Project Structure

aspire-blobstorage/
├── src/
│   ├── blobstorage.AppHost/          # Aspire orchestration
│   │   ├── AppHost.cs                # Service configuration
│   │   └── Extensions.cs             # Aspire extensions
│   ├── blobstorage/                  # API backend
│   │   └── [API endpoints]
│   ├── blobstorage.Frontend/         # React frontend
│   │   └── [UI components]
│   └── blobstorage.TypeSpec/         # OpenAPI definitions
└── README.md

Running the Example

cd foundry/dotnet/aspire-blobstorage

# Start with Aspire orchestration
dotnet run --project src/blobstorage.AppHost

# Dashboard available at: http://localhost:15000

The dashboard shows:

  • đŸ›ī¸ Storage Account (Emulator)
  • đŸ“Ļ Blob Containers
  • 🔄 Service interactions
  • 📊 Resource metrics

📋 Configuration Checklist

  • Infrastructure

  • Set enable_blob_storage = true in feature flags

  • Define container names in containers list
  • Obtain contributor group object ID
  • Apply Terraform configuration
  • Verify storage account created in Azure

  • Application

  • Add builder.AddAzureBlobServiceClient("blobstorage") to Program.cs

  • Implement blob upload/download endpoints using BlobServiceClient
  • Test with RBAC authentication

  • Local Development

  • Add Aspire storage emulator to AppHost with .AddAzureStorage().RunAsEmulator().AddBlobs("blobstorage")

  • Reference storage in backend service with .WithReference(storage)
  • Test upload/download locally

  • Testing

  • Test blob upload functionality
  • Test blob download functionality
  • Verify RBAC permissions in Azure
  • Test with Aspire emulator locally

🚀 Deployment

Monitoring & Diagnostics

The storage account automatically includes:

  • 📊 Diagnostic Settings (Azure Monitor)
  • 📈 Metrics (requests, latency, errors)
  • 📝 Activity Logs (operations audit trail)
  • 🔍 Storage Analytics (capacity and transaction metrics)

❓ FAQ

Q: Can I use connection strings instead of RBAC?

A: Yes, but RBAC is recommended for production. Connection strings are best for local development with the storage emulator.

Q: How do I add more containers after initial deployment?

A: Update the containers list in the feature flags and reapply Terraform.

Q: What happens if a container name is invalid?

A: Terraform validation will fail with a helpful error message. Container names must be 3-63 characters, lowercase, and contain only alphanumeric characters and hyphens.

Q: Can I access storage from multiple services?

A: Yes, multiple services can reference the same storage account. Just ensure they all have appropriate RBAC roles assigned.

Q: How do I monitor blob storage usage?

A: Use Azure Portal's Storage Account monitoring, Azure Monitor dashboards, or configure custom alerts on metrics like capacity and transaction count.