AzCopy is the fastest command-line way to move files in and out of Azure Storage. It is a single executable, it retries on its own, and it can resume a failed job. This post covers what you need for everyday copies.
Install and check
Download AzCopy v10 from Microsoft's AzCopy page (Windows zip, Linux tar) and put it on your PATH. Cloud Shell already has it.
azcopy --version
Sign in: Entra ID first, SAS second
Entra ID sign-in avoids storage keys. Your account needs a data role such as Storage Blob Data Contributor on the account or container.
azcopy login # browser / device code
azcopy login --identity # on a VM with a managed identity
azcopy login status
az role assignment create --assignee me@contoso.com \
--role "Storage Blob Data Contributor" \
--scope $(az storage account show -n stdata01 --query id -o tsv)
If you must use a SAS token, append it to the URL in quotes. Keep its expiry short and never paste it into tickets.
Warning
A SAS URL is a password. Anyone who sees it in shell history or a log can use it until it expires.
Copy
# one file up
azcopy copy "./report.csv" "https://stdata01.blob.core.windows.net/inbox/report.csv"
# a folder up, recursively
azcopy copy "./exports" "https://stdata01.blob.core.windows.net/inbox" --recursive
# only some files
azcopy copy "./exports/*" "https://stdata01.blob.core.windows.net/inbox" --include-pattern "*.csv;*.json"
# download a container
azcopy copy "https://stdata01.blob.core.windows.net/inbox" "./restore" --recursive
# account to account, server side (data does not flow through your machine)
azcopy copy "https://stsrc.blob.core.windows.net/data" "https://stdst.blob.core.windows.net/data" --recursive
Note the folder rule: copying ./exports creates an exports folder at the destination; copying ./exports/* copies its contents only.
Sync
sync copies only what changed, compared by last-modified time.
azcopy sync "./site" "https://stweb01.blob.core.windows.net/\$web" --recursive
azcopy sync "./site" "https://stweb01.blob.core.windows.net/\$web" --recursive --delete-destination=true
Caution
--delete-destination=truedeletes blobs that are not in the source. Run once with--dry-runand read the list before you use it on real data.
azcopy sync "./site" "https://stweb01.blob.core.windows.net/\$web" --recursive --delete-destination=true --dry-run
List and remove
azcopy list "https://stdata01.blob.core.windows.net/inbox"
azcopy remove "https://stdata01.blob.core.windows.net/inbox/old" --recursive --dry-run
When a job fails
Every run is a job with a log and a plan file.
azcopy jobs list
azcopy jobs show <job-id> --with-status=Failed
azcopy jobs resume <job-id>
Logs live under ~/.azcopy (Linux/macOS) or %USERPROFILE%\.azcopy (Windows); set AZCOPY_LOG_LOCATION to move them.
| Error in log | Cause | Fix |
|---|---|---|
403 AuthorizationPermissionMismatch | Signed in, but no data role | Grant a Storage Blob Data * role, wait a few minutes |
403 AuthorizationFailure | Storage firewall blocks your IP | Add your IP, or run from inside the VNet / private endpoint |
This request is not authorized to perform this operation using this permission | SAS missing write/list | Generate a SAS with the right permissions |
Many 503 ServerBusy | Throttling | Lower concurrency: export AZCOPY_CONCURRENCY_VALUE=16 |
| Slow on a small VM | CPU or NIC bound | Bigger VM, or --cap-mbps on shared links |
Tuning basics
azcopy benchmark "https://stdata01.blob.core.windows.net/bench" # measures throughput, then cleans up
export AZCOPY_CONCURRENCY_VALUE=AUTO
azcopy copy ./big "https://..." --recursive --block-size-mb 16 --cap-mbps 500
Prevent it next time
- Use Entra ID or managed identity sign-in in scripts; keep SAS for one-off sharing.
- Always
--dry-runsync with delete and anyremove. - Turn on blob soft delete and versioning on accounts that receive bulk copies, so a mistake is recoverable.
- Keep AzCopy updated; check
azcopy --versionfor an "newer version available" notice.