Uploading Files with Azure CLI
This page is a hands-on walkthrough for uploading files to Azure Blob Storage. You will see how to upload a single file, an entire folder, set content types and metadata, verify what was uploaded, and use AzCopy for large or high-speed transfers. Every command here is copy-pasteable with minor variable substitutions.
Before you start
You need the Azure CLI installed and logged in, and a storage account with at least one container. If you haven’t created those yet:
# Login
az login
# Create a resource group and storage account
az group create --name upload-demo-rg --location eastus
az storage account create \
--name uploaddemo123 \
--resource-group upload-demo-rg \
--location eastus \
--sku Standard_LRS \
--kind StorageV2
# Create a container
az storage container create \
--name files \
--account-name uploaddemo123 \
--auth-mode login
# Set environment variables to avoid repeating them
export ACCOUNT="uploaddemo123"
export CONTAINER="files"Uploading a single file
# Upload a file with an automatic name (uses the local filename)
az storage blob upload \
--account-name $ACCOUNT \
--container-name $CONTAINER \
--file ./report.pdf \
--name report.pdf \
--auth-mode login
# Upload and set a specific blob name (simulating a folder path)
az storage blob upload \
--account-name $ACCOUNT \
--container-name $CONTAINER \
--file ./report.pdf \
--name "reports/2026/march/report.pdf" \
--auth-mode login
# Upload with explicit content type
az storage blob upload \
--account-name $ACCOUNT \
--container-name $CONTAINER \
--file ./logo.png \
--name "images/logo.png" \
--content-type "image/png" \
--auth-mode login
# Upload and set cache headers (useful for static assets)
az storage blob upload \
--account-name $ACCOUNT \
--container-name $CONTAINER \
--file ./style.css \
--name "css/style.css" \
--content-type "text/css" \
--content-cache-control "max-age=86400" \
--auth-mode loginUploading an entire folder
The upload-batch command recursively uploads a local directory to a container. It preserves the folder structure as blob name prefixes.
# Upload everything in ./website/ to the container root
az storage blob upload-batch \
--account-name $ACCOUNT \
--destination $CONTAINER \
--source ./website/ \
--auth-mode login
# Upload only HTML files
az storage blob upload-batch \
--account-name $ACCOUNT \
--destination $CONTAINER \
--source ./website/ \
--pattern "*.html" \
--auth-mode login
# Upload to a specific prefix (virtual folder) inside the container
az storage blob upload-batch \
--account-name $ACCOUNT \
--destination "$CONTAINER/v2" \
--source ./website/ \
--auth-mode login
# Preview what would be uploaded without actually doing it
az storage blob upload-batch \
--account-name $ACCOUNT \
--destination $CONTAINER \
--source ./website/ \
--dryrun \
--auth-mode loginThe —dryrun flag prints what would happen without making any changes. Always useful before a large batch operation to verify the source and destination paths are correct.
Adding metadata during upload
Metadata is custom key-value pairs you attach to a blob. They’re visible in the properties but not in the blob’s contents. Useful for tracking upload source, processing status, or tagging.
# Upload with metadata
az storage blob upload \
--account-name $ACCOUNT \
--container-name $CONTAINER \
--file ./invoice.pdf \
--name "invoices/2026-03-001.pdf" \
--metadata uploaded-by=billing-service year=2026 month=03 \
--auth-mode loginMetadata keys are case-insensitive and stored as lowercase. Values are strings. You can have up to 8 KB of metadata per blob.
Verifying what was uploaded
# List all blobs in the container
az storage blob list \
--account-name $ACCOUNT \
--container-name $CONTAINER \
--auth-mode login \
--output table
# List blobs with a specific prefix
az storage blob list \
--account-name $ACCOUNT \
--container-name $CONTAINER \
--prefix "reports/2026/" \
--auth-mode login \
--output table
# Show details of a specific blob
az storage blob show \
--account-name $ACCOUNT \
--container-name $CONTAINER \
--name "reports/2026/march/report.pdf" \
--auth-mode login \
--output json
# Check if a blob exists without downloading it
az storage blob exists \
--account-name $ACCOUNT \
--container-name $CONTAINER \
--name "reports/2026/march/report.pdf" \
--auth-mode loginOverwrite behavior and idempotent uploads
By default, az storage blob upload overwrites an existing blob with the same name without warning. To prevent accidental overwrites, use the —no-overwrite flag:
# Fail if blob already exists
az storage blob upload \
--account-name $ACCOUNT \
--container-name $CONTAINER \
--file ./config.json \
--name "config/app-config.json" \
--no-overwrite \
--auth-mode loginFor upload-batch, use —overwrite false:
az storage blob upload-batch \
--account-name $ACCOUNT \
--destination $CONTAINER \
--source ./website/ \
--overwrite false \
--auth-mode loginUsing AzCopy for large or high-volume transfers
AzCopy is a separate command-line tool optimized for high-throughput blob transfers. It’s much faster than the az CLI for large files or large numbers of files because it uses multiple parallel connections and is written in Go (not Python like the az CLI).
# Login to AzCopy using Azure AD
azcopy login
# Upload a single large file
azcopy copy './large-dataset.zip' \
"https://$ACCOUNT.blob.core.windows.net/$CONTAINER/large-dataset.zip" \
--recursive
# Upload an entire directory
azcopy copy './data-export/' \
"https://$ACCOUNT.blob.core.windows.net/$CONTAINER/data-export/" \
--recursive
# Upload with a SAS token (no login needed)
SAS="sv=2021-08-06&ss=b&srt=sco&sp=rwdlacup&..."
azcopy copy './files/' \
"https://$ACCOUNT.blob.core.windows.net/$CONTAINER/?$SAS" \
--recursive
# Check transfer speed and progress
azcopy jobs list
azcopy jobs show <job-id>AzCopy stores job state locally, so if a transfer is interrupted you can resume it:
azcopy jobs resume <job-id>AzCopy is not installed with the Azure CLI. Download it separately from Microsoft’s website or install it with brew install azcopy on macOS. On Windows, download the standalone executable. AzCopy is also available as a Docker image if you want to run it in a CI/CD pipeline container.
Common mistakes
- Not setting content types on upload. Azure defaults to
application/octet-streamfor blobs without an explicit content type. If you’re serving blobs directly to browsers (for a static website or media assets), wrong content types cause images not to render, CSS not to apply, and JavaScript not to execute. Always set—content-typeon upload. - Using upload-batch when AzCopy would be faster. For folders with thousands of files or files over 1 GB, the az CLI upload-batch can take many times longer than AzCopy. If speed matters, reach for AzCopy. The az CLI is convenient for quick operations and scripting; AzCopy is the right tool for bulk data movement.
- Forgetting —no-overwrite on idempotent scripts. Scripts that re-run on failure should be idempotent. If your upload script silently overwrites blobs, a re-run after a partial failure might overwrite successfully uploaded files with different (older) versions. Use
—no-overwriteor check existence first. - Not verifying uploads in automated pipelines. Run
az storage blob existsoraz storage blob showafter uploads in CI/CD pipelines to confirm the blob landed where you expected it. Silent failures are harder to debug than explicit verification failures.
Summary
- Use
az storage blob uploadfor single files andaz storage blob upload-batchfor directories; both support content types, metadata, and overwrite control. - Always use
—auth-mode loginto authenticate with Azure AD rather than account keys. - For large files or high-volume transfers, AzCopy is significantly faster and supports resumable uploads; the az CLI is better for scripting and quick operations.
- Verify uploads explicitly with
az storage blob existsoraz storage blob show, especially in automated pipelines.
Frequently asked questions
What is the maximum file size I can upload with az storage blob upload?
The az storage blob upload command handles files up to 200 GB using automatic multipart upload. For larger files — up to 190.7 TB — use AzCopy, which is optimized for large-scale transfers and uses the block blob API directly.
How do I upload files faster?
For large batches, AzCopy is significantly faster than the az CLI because it uses multiple parallel connections and is written in Go rather than Python. The az storage blob upload-batch command is simpler to use but slower. For production data migrations or large uploads, always prefer AzCopy.
Can I resume an interrupted upload?
AzCopy supports resumable uploads using a job plan file stored locally. If a transfer is interrupted, re-running the same AzCopy command resumes from where it left off. The az CLI upload commands do not have built-in resume support — interrupted uploads must restart from the beginning.