# 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:

```hcl
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

```hcl
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

```csharp
// 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:

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

> ℹ️ `AddAzureBlobServiceClient` automatically uses `DefaultAzureCredential` when deployed to Azure and connection strings for local development.

#### Minimal API Examples

```csharp
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

```bash
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)

## 🔗 Related Resources

- [Azure Blob Storage Documentation](https://learn.microsoft.com/en-us/azure/storage/blobs/)
- [Aspire Storage Integration](https://learn.microsoft.com/en-us/dotnet/aspire/components/azure-storage-blobs)
- [Azure Identity & RBAC](https://learn.microsoft.com/en-us/azure/role-based-access-control/)
- [Example: aspire-blobstorage](../../../foundry/aspire-blobstorage.md)

## ❓ 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.
