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 login

Uploading 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 login

The —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 login

Metadata 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 login

Overwrite 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 login

For upload-batch, use —overwrite false:

az storage blob upload-batch \
  --account-name $ACCOUNT \
  --destination $CONTAINER \
  --source ./website/ \
  --overwrite false \
  --auth-mode login

Using 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>
Note

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

  1. Not setting content types on upload. Azure defaults to application/octet-stream for 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-type on upload.
  2. 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.
  3. 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-overwrite or check existence first.
  4. Not verifying uploads in automated pipelines. Run az storage blob exists or az storage blob show after uploads in CI/CD pipelines to confirm the blob landed where you expected it. Silent failures are harder to debug than explicit verification failures.

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.

Last verified: 19 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.