Azure Blob Storage can receive media directly from Tornado, but a completed upload and a usable download link are separate checks. Choose the correct configuration route, verify access from the consuming application, and budget for the time a file spends waiting in your queue before its SAS link is used.
This guide focuses on Azure authorization and delayed processing. For the broader distinction between stored bytes and temporary access, see storage access and retention.
Choose the Azure configuration route
Tornado's Azure setup guide documents two different interfaces:
| Route | Container field | Credentials |
|---|---|---|
| Dashboard default storage destination | container_name | account_name and account_key |
Per-key POST /user/blob API | container | account_name and an account_key or sas_token |
Do not paste an API payload into the dashboard. Its Azure configuration accepts the three documented fields only. The API also has folder options; use its current reference when configuring those.
Create the container before connecting it. The dashboard's endpoint uses the global public-cloud suffix blob.core.windows.net; it does not expose a custom endpoint setting for sovereign clouds or custom domains. Confirm that your existing account and security policy are compatible before configuring delivery. A policy that requires managed identity cannot be satisfied by entering an account key in this dashboard route.
Keep the container private. A SAS authorizes a request without requiring anonymous blob access. An account key carries broad authority, so treat choosing that route as an integration decision, not merely a form-filling step.
Understand which credential reaches the reader
The two credential paths have different consequences. As documented in custom storage, an account key lets Tornado sign a blob delivery URL. When you supply a SAS instead, the delivery URL reuses that token with its existing permissions and expiration. If you supply both, the account key takes precedence.
That distinction matters when passing a result to another service. A supplied token intended for uploading can carry more authority than a downstream reader needs. Do not assume that a returned URL automatically narrows it to read-only access. If the consumer needs a separate, more restricted credential, arrange that through your own authorized Azure integration.
Microsoft's SAS overview explains how permissions, resource scope and time limits constrain delegated access. Protect the complete URL as a credential, use HTTPS, and keep it out of analytics events and ordinary application logs. A future expiry is not proof that a request is authorized.
Verify delivery from the consumer's environment
The Azure dashboard connection test writes a probe and attempts cleanup. It does not fetch the delivered SAS URL. A successful test therefore does not establish that a separate transcription worker or editing service can read the result.
For your first representative delivery, keep the job ID and the exact destination reference. After completion, read the media using the same identity or URL mechanism and network path your consumer will use. Confirm that the media parser accepts the output before queuing a larger workload. An administrator's successful portal preview does not exercise that path.
Separate the evidence in your application: configuration accepted, job completed, consumer access checked, and media accepted. These are useful troubleshooting boundaries rather than interchangeable definitions of success.
Budget for a delayed reader
Suppose your worker may wait in a queue before reading the blob. A link that works when the job finishes may be unusable when that worker starts. Store account, container and blob identity separately from the temporary credential, then check the access window near consumption time.
The following application-side policy uses an expiry supplied by your trusted credential issuer. It deliberately does not parse or validate a SAS. Some SAS configurations use a stored access policy, and a query string alone is not a complete account of effective access.
from datetime import datetime, timedelta, timezone
def access_window_ok(now, expires_at, queue_seconds,
read_seconds, margin_seconds):
for value in (now, expires_at):
if not isinstance(value, datetime) or value.utcoffset() is None:
raise ValueError("Use timezone-aware timestamps")
budgets = (queue_seconds, read_seconds, margin_seconds)
if any(type(value) is not int or value < 0 for value in budgets):
raise ValueError("Budgets must be nonnegative integer seconds")
required_until = now + timedelta(seconds=sum(budgets))
return expires_at > required_until
now = datetime(2026, 10, 9, 9, 0, tzinfo=timezone.utc)
expires_at = now + timedelta(minutes=20)
assert access_window_ok(now, expires_at, 300, 120, 60)
assert not access_window_ok(now, expires_at, 1100, 120, 60)
All durations are synthetic examples, not recommended timeout settings or measured Tornado performance. Choose budgets from your own queue and transfer observations. The margin is your policy for scheduling uncertainty, retries and clock differences. Recheck after unexpected delays.
The example and rejection cases were executed locally without Azure credentials or a media job. Passing this function proves only that the supplied timestamps satisfy your budget. It does not test a signature, network access, blob existence, revocation, permissions or the media. Your real read remains necessary.
If the budget is insufficient, obtain appropriate fresh access through your authorized storage integration or defer processing until access is resolved. Polling a job cannot extend a supplied SAS token. Do not edit its expiry text or repeatedly submit the original media job to repair an access problem.
Diagnose a denial before changing anything
For a 403 response, capture the Azure error code and request time without logging the SAS. Check the relevant cause: expired or not-yet-valid credentials, missing permissions, a signing-key change, or a network restriction. Microsoft's 403 troubleshooting guide provides the diagnostic branches. A 403 alone does not establish that the blob was deleted.
Avoid resolving an unexplained denial by making the container public or granting broader access. First identify whether the failed operation is the upload, the recipient's read, or a later retry. Preserve the job and destination references while you investigate. The expired-link recovery guide covers recovery when the bytes may still exist.
Start with the first media workflow, connect your Azure destination using the documented route, and validate one representative result from your actual consumer. Then add the access-window check where your queue hands off work. This makes the next failure actionable without confusing delivery, authorization and downstream completion.