Azure Blob has no S3 compatibility, so it has its own adapter.
id:azure- Capabilities:
{ directUrl: <computed>, contentTypeMetadata: true, streamingPut: true }
Install
npm install @orthacms/media-provider-azureConfigure
# .env
MEDIA_AZURE_CONTAINER=media
# from the portal: Storage account → Access keys → Connection string
MEDIA_AZURE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=acmecms;AccountKey=...;EndpointSuffix=core.windows.net
# optional: redirect downloads to a SAS URL (needs an AccountKey)
MEDIA_DIRECT_SERVE=signed-urlBoth variables are required in a generated app’s config/media-storage.ts
(the reference server ships with local disk):
import type { AzureStorageConfig } from '@orthacms/media-provider-azure';
import { requireEnv } from '@orthacms/utils-server';
export function mediaStorage(): AzureStorageConfig {
return {
container: requireEnv('MEDIA_AZURE_CONTAINER'),
connectionString: requireEnv('MEDIA_AZURE_CONNECTION_STRING')
};
}All options
AzureStorageConfig: container (required), connectionString?,
accountName?, accountKey?, keyPrefix?, containerClient?. One of the
three credential shapes is required, or the factory throws.
Three ways to connect
| Shape | directUrl |
|---|---|
connectionString containing AccountKey | true |
connectionString with a SAS token | false |
accountName + accountKey | true |
containerClient (e.g. managed identity) | false |
// account name + key
createAzureStorageProvider({
container: 'media',
accountName: 'acmecms',
accountKey: process.env.AZURE_STORAGE_KEY!
});// managed identity — build the client yourself
import { DefaultAzureCredential } from '@azure/identity';
import { BlobServiceClient } from '@azure/storage-blob';
const containerClient = new BlobServiceClient(
'https://acmecms.blob.core.windows.net',
new DefaultAzureCredential()
).getContainerClient('media');
createAzureStorageProvider({ container: 'media', containerClient });Managed identity cannot sign a SAS
With a containerClient, directUrl is false, so
MEDIA_DIRECT_SERVE=signed-url is a boot error. Downloads are proxied.
Register
// apps/server/src/plugins.ts
import { createAzureStorageProvider } from '@orthacms/media-provider-azure';
MediaServerPlugin({
provider: createAzureStorageProvider(config.plugins.media.storage),
config: config.plugins.media
});Verify
verify() reads the container’s properties at boot. A wrong container, dead
account or expired key refuses the start.
Locally, with Azurite:
docker run -d -p 10000:10000 mcr.microsoft.com/azure-storage/azurite azurite-blob --blobHost 0.0.0.0
az storage container create --name media \
--connection-string "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;"Use the same connection string as MEDIA_AZURE_CONNECTION_STRING. Upload a
file (curl example), then:
az storage blob list --container-name media --connection-string "$MEDIA_AZURE_CONNECTION_STRING" -o tableWith signed-url, the download’s Location is a SAS URL that pins
rscd (disposition) and rsct (content type).
Behaviour worth knowing
- A failed upload deletes the blob, so no uncommitted blocks are left.
BlobNotFound/ 404 →ObjectNotFoundError(HTTP 404). Throttling and auth errors are rethrown, not disguised as missing.- A user-delegation SAS (signing with managed identity) is not implemented.