AF.Umbraco.Azure.Blob.Media.Storage 2.0.0

dotnet add package AF.Umbraco.Azure.Blob.Media.Storage --version 2.0.0
                    
NuGet\Install-Package AF.Umbraco.Azure.Blob.Media.Storage -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AF.Umbraco.Azure.Blob.Media.Storage" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AF.Umbraco.Azure.Blob.Media.Storage" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="AF.Umbraco.Azure.Blob.Media.Storage" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AF.Umbraco.Azure.Blob.Media.Storage --version 2.0.0
                    
#r "nuget: AF.Umbraco.Azure.Blob.Media.Storage, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AF.Umbraco.Azure.Blob.Media.Storage@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AF.Umbraco.Azure.Blob.Media.Storage&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=AF.Umbraco.Azure.Blob.Media.Storage&version=2.0.0
                    
Install as a Cake Tool

AF.Umbraco.Azure.Blob.Media.Storage

Azure Blob media storage provider for Umbraco 15/16/17/18 on .NET 9/10.

This package configures Umbraco to use Azure Blob for:

  • Media filesystem
  • ImageSharp cache
  • Startup fail-fast checks using the provider-configured Azure clients
  • Bounded, multi-instance-safe ImageSharp cache-retention cleanup

No Program.cs changes are required.

Compatibility

  • Umbraco CMS: 15.x, 16.x, 17.x, 18.x
  • .NET: 9.0, 10.0 (Umbraco 18.x requires 10.0)

Dependencies

  • Umbraco.StorageProviders.AzureBlob
  • Umbraco.StorageProviders.AzureBlob.ImageSharp

Compatibility Hosts and Local Smoke Checks

  • Local compatibility hosts are included under src/Umbraco.Cms.15.x, src/Umbraco.Cms.16.x, src/Umbraco.Cms.17.x, and src/Umbraco.Cms.18.x.
  • Each host supports local overrides through appsettings.Local.json.
  • VS Code Run and Debug configurations and their build tasks are available for every compatibility host.
  • The All hosts VS Code compound starts every Kestrel host in one action, each with its own debug session. See Development for prerequisites and verification steps.
  • Automated unit tests target both .NET 9 and .NET 10.
Host HTTP HTTPS
Umbraco 15 http://localhost:5015 https://localhost:44375
Umbraco 16 http://localhost:5016 https://localhost:44376
Umbraco 17 http://localhost:5017 https://localhost:44377
Umbraco 18 http://localhost:5018 https://localhost:44378

Umbraco 18 uses Umbraco:CMS:Imaging:HMACSecretKey. The tracked appsettings.json contains an empty placeholder; set the real value only in the git-ignored appsettings.Local.json.

Installation

dotnet add package AF.Umbraco.Azure.Blob.Media.Storage --version 2.0.0

Upgrading from 1.0.0

2.0.0 is a major release because the public type AzureBlobSmokeTestsMiddleware is no longer published: diagnostic smoke support moved out of the package and AzureBlobComposer is now its only public type.

Installations that rely on auto-composition - the documented and expected usage - need no change: the composer, configuration keys, startup modes, and retention behaviour are unchanged. Only a host that referenced the smoke middleware directly in its own code will fail to compile; that host must drop the reference, since the middleware now lives in a repository-only project that is not distributed.

Build and Test

dotnet restore src/AF.Umbraco.Azure.Blob.Media.Storage.sln
dotnet build src/AF.Umbraco.Azure.Blob.Media.Storage.sln -c Release --no-restore --maxcpucount:1 /p:GeneratePackageOnBuild=false
dotnet test src/AF.Umbraco.Azure.Blob.Media.Storage.Tests/AF.Umbraco.Azure.Blob.Media.Storage.Tests.csproj -c Release --no-build --no-restore
dotnet format src/AF.Umbraco.Azure.Blob.Media.Storage.sln --verify-no-changes --no-restore

Set AFUABMS_AZURITE_CONNECTION_STRING and run the Category=Azurite test filter to execute the storage-backed lease-contention and lease-loss regressions. See Testing for the command and safety notes.

Each Umbraco compatibility host must use a dedicated smoke-test database. The smoke script disables unattended upgrades, so a database created by another Umbraco major makes the run fail at startup instead of being migrated in place. Do not reuse a database across Umbraco 15, 16, 17, and 18. See Testing for the recommended names and per-host commands.

Generate a local NuGet package only when release metadata is ready:

dotnet pack src/AF.Umbraco.Azure.Blob.Media.Storage/AF.Umbraco.Azure.Blob.Media.Storage.csproj -c Release --no-build

Required Configuration

Required sections:

  • Umbraco:Storage:AzureBlob:Media
  • Umbraco:Storage:AzureBlob:ImageSharp

Required keys in both sections:

  • ConnectionString
  • ContainerName

The named ImageSharp filesystem additionally requires:

  • VirtualPath

If a required section/key is missing, startup is blocked.

Optional Configuration

The following key is optional in both the Media and ImageSharp sections:

  • ContainerRootPath

The following key is optional only in the Media section; it is required by the official JSON schema for the named ImageSharp filesystem:

  • VirtualPath

The Startup and ImageSharp:CacheRetention sections are also optional. The package applies the documented defaults when these sections or individual keys are omitted.

Startup Validation

Configuration path:

  • AFUABMS:Startup:Mode

The Startup section and its Mode key are optional. When omitted, Mode defaults to CreateIfMissing.

Supported modes:

  • CreateIfMissing (default): verifies access and creates either container when absent.
  • ValidateOnly: verifies that both containers already exist and never attempts creation.

Startup validation uses the same named BlobContainerClient factories as the media and ImageSharp providers. Custom credentials, endpoints, client options, and retry policies therefore apply consistently to startup and runtime operations. Azure 401/403 failures remain visible as authorization errors and block startup.

Cache Retention Cleanup

Configuration path:

  • Umbraco:Storage:AzureBlob:ImageSharp:CacheRetention

The CacheRetention section and every key below are optional. Omitting the entire section keeps cleanup disabled.

Key Required Default Notes
Enabled No false Enables normal retention cleanup.
NumberOfDays No 90 Maximum cache age in normal mode. Clamped to 1-36500 days.
MaxBlobsPerSweep No 10000 Clamped to 1-1000000.
SweepJitterSeconds No 300 Clamped to 0-3600.
TestModeEnable No false Enables the test-mode schedule and maximum age.
TestModeSweepSeconds No 30 Minimum effective value: 5.
TestModeMaxAgeMinutes No 10 Minimum effective value: 1.

Behavior:

  • normal mode: deletes cache blobs older than NumberOfDays every 12 hours, plus randomized jitter
  • test mode: sweep/max-age controlled by TestModeSweepSeconds / TestModeMaxAgeMinutes

Only one application instance sweeps a cache root at a time. Instances coordinate through the renewable cache/.af-cache-retention.lock blob, and the active sweep stops if lease renewal fails. Each sweep is bounded by MaxBlobsPerSweep and logs scanned, deleted, failed, limit, and duration values.

Storage Layout

Recommended setup: separate containers.

  • Media.ContainerName = umbraco17-media
  • ImageSharp.ContainerName = umbraco17-cache

Shared-container setup is supported, but use explicit root isolation:

  • Media.ContainerRootPath = media
  • ImageSharp.ContainerRootPath = cache

Without explicit isolation, media and cache paths can overlap.

The ImageSharp ContainerRootPath is the complete cache prefix. When it is omitted or blank, the package uses cache. The retention service uses the same resolved prefix and never scans fallback prefixes outside that root.

Configuration Example

{
  "AFUABMS": {
    "Startup": {
      "Mode": "CreateIfMissing"
    }
  },
  "Umbraco": {
    "Storage": {
      "AzureBlob": {
        "Media": {
          "ConnectionString": "DefaultEndpointsProtocol=http;AccountName=azurite-storage;AccountKey=...;BlobEndpoint=http://127.0.0.1:10000/azurite-storage;",
          "ContainerName": "umbraco17",
          "ContainerRootPath": "media",
          "VirtualPath": "~/media"
        },
        "ImageSharp": {
          "ConnectionString": "DefaultEndpointsProtocol=http;AccountName=azurite-storage;AccountKey=...;BlobEndpoint=http://127.0.0.1:10000/azurite-storage;",
          "ContainerName": "umbraco17",
          "ContainerRootPath": "cache",
          "VirtualPath": "~/media",
          "CacheRetention": {
            "Enabled": false,
            "NumberOfDays": 90,
            "MaxBlobsPerSweep": 10000,
            "SweepJitterSeconds": 300,
            "TestModeEnable": false,
            "TestModeSweepSeconds": 30,
            "TestModeMaxAgeMinutes": 10
          }
        }
      }
    }
  }
}

Managed Identity

Verification status. Managed Identity support is covered by unit tests and by the startup validation paths, but the opt-in end-to-end verification against a live Azure Storage account with RBAC has not been run for this release. Validate it in your own environment before relying on passwordless authentication in production. The procedure is documented in Testing.

For passwordless Azure deployments, add the Azure.Identity package to the Umbraco host, set each ConnectionString value to the Blob service URI, for example https://myaccount.blob.core.windows.net, and configure the official provider client factory with an Azure Identity credential:

using Azure.Identity;
using Azure.Storage.Blobs;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.StorageProviders.AzureBlob.IO;

builder.Services.PostConfigure<AzureBlobFileSystemOptions>("Media", options =>
    options.TryCreateBlobContainerClientUsingUri(uri =>
        new BlobContainerClient(uri, new ManagedIdentityCredential(ManagedIdentityId.SystemAssigned))));

builder.Services.PostConfigure<AzureBlobFileSystemOptions>("ImageSharp", options =>
    options.TryCreateBlobContainerClientUsingUri(uri =>
        new BlobContainerClient(uri, new ManagedIdentityCredential(ManagedIdentityId.SystemAssigned))));

Use DefaultAzureCredential for local passwordless development when appropriate, and prefer an explicit ManagedIdentityCredential in Azure production environments. For a user-assigned identity, use ManagedIdentityId.FromUserAssignedClientId(clientId). Pre-provision containers, grant Storage Blob Data Contributor at the narrowest practical scope, and select ValidateOnly when the application identity must not create containers. The opt-in live verification procedure is documented in Testing. See the Azure SDK authentication guidance.

Smoke Endpoints (Opt-In)

Smoke endpoints are not part of the published package. They live in the repository-only AF.Umbraco.Azure.Blob.Media.Storage.SmokeTests project, which the compatibility hosts reference to verify the package through its public API. A consumer installing the NuGet package never receives them. Running them from a clone requires both the Development environment and the explicit feature flag:

ASPNETCORE_ENVIRONMENT=Development
AF_SMOKE_TESTS=1

Endpoints:

  • GET /smoke/health - reports the Umbraco runtime level; 503 unless it reached Run
  • GET /smoke/debug-test
  • POST /smoke/media-upload
  • POST /smoke/image-transform

The media check validates upload, overwrite, read-back, and delete. The image check uploads a temporary PNG, generates HMAC-aware width, height, mode=crop, and v query variants through Umbraco's image URL generator, exercises them through the local ImageSharp pipeline, verifies that missing media returns 404, and deletes the original PNG. Generated ImageSharp cache entries remain subject to the configured retention policy. Never enable diagnostic routes in staging or production.

Security

  • Keep connection strings, HMAC keys, database credentials, and unattended-install passwords in environment variables, user secrets, or git-ignored appsettings.Local.json files.
  • Grant only the Azure Storage permissions required by the selected startup mode, media/cache operations, and optional retention cleanup and lease coordination.
  • Keep AF_SMOKE_TESTS disabled outside local or isolated CI environments.
  • Review dependency advisories before publishing each release.

Documentation

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 40 9/11/2026
1.0.0 165 2/16/2026

2.0.0 - Breaking: the public type AzureBlobSmokeTestsMiddleware is no longer published and diagnostic smoke support moved out of the package, leaving AzureBlobComposer as the only public type and shipping runtime code only. Installations relying on auto-composition are unaffected. Also hardens the compatibility hosts against unattended database upgrades and drops the redundant Umbraco.Cms.Web.Common dependency.