Overview
Tornado can deliver completed downloads straight into a blob container in your own Azure Storage account. The dashboard asks for exactly three values: the storage account name, the container name, and a storage account access key. This page covers the dashboard path (the onboarding wizard and Settings › Default storage destination), which writes an organization-level configuration throughPOST /settings/storage. Credentials are stored in Azure Key Vault and are returned masked when the configuration is read back.
The dashboard route is not the same contract as the per-API-key endpoint
POST /user/blob. That endpoint
names the container field container and additionally accepts sas_token,
folder_prefix and base_folder. The dashboard route names the field
container_name and accepts three fields only. Do not copy a payload from
one to the other.Current limitations
Read these before you create anything in Azure. Both are properties of the dashboard route as it exists today, not Azure restrictions.1. Create a storage account
Skip to step 2 if you already have a dedicated account for Tornado deliveries. Read Protect your access keys first if you plan to reuse an existing one.1
Open the storage account creation flow
In the Azure portal, select Storage accounts
from the portal menu, then select Create. Portal layouts change; if the
menu item is not visible, search for “Storage accounts” from the top search
bar.
2
Fill in the Basics tab
Pick a subscription and resource group, then set Storage account name.The name must be between 3 and 24 characters and can contain only lowercase
letters and numbers. It is globally unique because it becomes a DNS label:
<name>.blob.core.windows.net.Standard performance on a general-purpose v2 account is the right
default for download delivery. Choose a region close to wherever you consume
the files.3
Leave Shared Key authorization enabled
Tornado authenticates with the account key. If your organization sets
Allow storage account key access to Disabled (the
allowSharedKeyAccess property, under Settings › Configuration on the
account), Tornado cannot write to that account at all. See
Troubleshooting.2. Create a container
1
Open Containers
In your storage account, find the Data storage section and select
Containers.
2
Add the container
Select + Container, give it a name, and select Create.Container names must be 3 to 63 characters, must start with a letter or a
number, and may contain only lowercase letters, numbers and the dash (
-)
character.3
Keep anonymous access off
Set the anonymous access level to Private (no anonymous access). Tornado
does not need public read: download URLs in webhook payloads are signed
per-object, and signing works on a private container.
3. Copy an access key
1
Open Access keys
In your storage account, find the Security + networking section and
select Access keys.
2
Reveal and copy
Select Show keys. Under key1, copy the Key value.Copy the Key, not the Connection string. The dashboard expects the
bare base64 key. A connection string pasted into the key field fails at
signing time before any request reaches Azure.
Microsoft.Storage/storageAccounts/listkeys/action permission, which is carried by the Owner, Contributor and Storage Account Key Operator Service Role built-in roles.
An account key is not a container credential
Because the credential cannot be narrowed, narrow the account instead. Create a storage account used only for Tornado deliveries. Concretely, that buys you:- Blast radius. The key you hand over reaches nothing but delivered downloads. On a shared account, the same string reaches your application data.
- Rotation on your own schedule. You can regenerate the key whenever you want without coordinating with other workloads that hold the same key.
- Policies that fit one job. Lifecycle rules, retention, soft delete, firewall rules and diagnostic logging can be tuned for delivery traffic without side effects elsewhere.
- Clean attribution. Storage metrics, transaction costs and account-level throughput limits reflect Tornado alone.
Minimum and recommended access
Azure is the odd one out here. Every other provider lets you write down a permission set; this route accepts a Shared Key, and a Shared Key has no permission set to write down. So the minimum and the recommendation are both about the account, and the RBAC and SAS vocabularies below are yardsticks for judging what you are handing over rather than things you can configure.Minimum
The RBAC row is a measurement, not an instruction. Assigning Storage Blob Data Contributor to a principal changes nothing for Tornado, because there is no Microsoft Entra ID, managed identity or user-delegation-SAS path in this route. It is here so you can see how far the account key over-grants: Contributor is three data actions on one container, the account key is everything in the account.
Recommended
The minimum plus two things, neither of which narrows the key, because the key cannot be narrowed:
Nothing else is worth adding. Container-level anonymous access, blob public access and account-level firewall exceptions are all things to leave alone or off.
If you use the per-API-key route instead
POST /user/blob does accept a sas_token. Sized correctly, that is an account SAS with signed services ss=b (blob), signed resource types srt=o (object), and the permission string sp=rwd. A service SAS scoped to the container (sr=c) with the same rwd works equally well; nothing Tornado does needs account-level or container-level operations.
Why those three letters and no others: Azure’s permission table defines w as “Create or write content, properties, metadata, or block list”, which is exactly what Put Blob, Put Block and Put Block List need, and c alone is not enough because a retry overwrites an existing blob. d covers the probe cleanup. r covers reads. l (list), a (add), t (tags), i and p are not used by anything, and l is not even valid on a blob-scoped SAS.
4. Enter the three fields
Open the dashboard, choose Azure Blob as the destination, and fill in the fields. Labels differ slightly between the onboarding wizard and the settings page; both write the same three API fields.
The equivalent payload:
Setting the storage destination is an organization-level action restricted to
Admin and Owner members, and one provider is active at a time. Saving
an Azure configuration replaces whatever provider was configured before.
5. Test connection
The onboarding wizard’s Test connection button runs a real round trip against your container before saving anything. On success the button reports the measured latency, for example “test object written and deleted in 340 ms”. What the probe actually does:- Opens a client against
https://<account_name>.blob.core.windows.netwith your account name and key as a Shared Key credential. - Uploads a small blob to
tornado-connection-test/<random>.txtinsidecontainer_name, with overwrite enabled. - Deletes that blob.
The Azure probe is not the S3 probe. The S3 branch of the same endpoint was extended to read the probe object back and to open and abort a multipart upload, so it now proves the read half. The Azure branch was not: it is still write then delete. Do not carry over what the AWS S3 or Cloudflare R2 pages say about the read leg.
What a green result proves
What a green result proves
<account_name>.blob.core.windows.netresolves and is reachable from Tornado’s network.- Shared Key authorization is permitted on the account and your key is accepted.
- The container exists and is spelled correctly.
- The credential can both create and delete a blob in that container.
- The measured latency is a real network round trip, not an estimate.
What it does not prove
What it does not prove
- Nothing about reads. The probe issues no GET. On Azure this is a smaller gap than the same sentence means on S3, and it is worth being precise about why. There is no read-only failure mode here: the credential is the account key, so anything it can write it can also read, and the download URL is a service SAS minted from that same key. What the probe cannot see is a restriction that bites on the read path specifically, such as an immutability policy, an encryption-scope rule, or a network rule that your customers hit and Tornado does not.
- Nothing about durability of the result. It is a point-in-time check, not a monitor. Regenerating the key, tightening the account firewall, or disabling Shared Key access after the test all break delivery silently on the next job.
-
Nothing about policies that trigger later. Immutability policies, legal holds, encryption-scope restrictions, capacity limits or lifecycle rules that do not apply to the
tornado-connection-test/prefix will not surface here. - Nothing about the block upload path. Blobs above 256 MB are uploaded as blocks and committed with Put Block List. The probe writes a few bytes in a single Put Blob, so it never exercises that path. Both are covered by the same write permission, so this is a throughput question rather than a permissions one.
-
Nothing about the signed download URLs included in
job.completedwebhook payloads. Those are signed locally, without contacting Azure. -
It does not run on every save. Only the onboarding wizard probes. Saving from Settings › Default storage destination writes the configuration without testing it. To re-verify later, call
POST /settings/storage/testwith the same body you would send toPOST /settings/storage.
Troubleshooting
Failures from the Azure branch of the connection test are formatted as<AzureErrorCode>: azure:blob, where the code is the error code Azure Storage returned. Match on the part before the colon.
Rotating the access key
The two keys exist so that rotation does not cause an outage. Update Tornado before you regenerate.1
Switch Tornado to the other key
If Tornado holds
key1, save key2 in the dashboard and confirm delivery
on a test job.2
Regenerate the key you retired
Back in Access keys, select Regenerate next to
key1.