Stop the bleeding first
Before recovering anything, stop whatever is deleting: disable the pipeline, pause the lifecycle rule, revoke the SAS or key a rogue script uses. Recovering while a job is still deleting just races it.
1. What protection is on?
az storage account blob-service-properties show -g rg-data -n stproddata \
--query "{blobSoftDelete:deleteRetentionPolicy, containerSoftDelete:containerDeleteRetentionPolicy, versioning:isVersioningEnabled, pitr:restorePolicy}"
| Feature on | You can recover |
|---|---|
| Blob soft delete | Deleted blobs and overwritten snapshots within retention |
| Versioning | Previous versions of overwritten or deleted blobs |
| Container soft delete | Whole deleted containers |
| Point-in-time restore | Block blobs in a range, back to a timestamp |
| None of these | Only from a backup copy (Azure Backup for blobs, an AzCopy copy, a replica) |
2. Deleted container
az storage container list --account-name stproddata --include-deleted --auth-mode login \
--query "[?deleted].{name:name, version:version}" -o table
az storage container restore --account-name stproddata --auth-mode login \
--name invoices --deleted-version <version>
You cannot restore over a new container that was created with the same name after the delete.
3. Deleted blobs (soft delete, no versioning)
az storage blob list --account-name stproddata -c invoices --include d --auth-mode login \
--query "[?deleted].name" -o tsv > deleted.txt
wc -l deleted.txt
while read -r b; do
az storage blob undelete --account-name stproddata -c invoices -n "$b" --auth-mode login
done < deleted.txt
With versioning on, a delete leaves the previous version behind. Promote it by copying the version over the base blob (portal: blob → Versions → Make current version, or az storage blob copy start with the versioned URL as the source).
4. Mass overwrite or corruption: point-in-time restore
az storage blob restore --account-name stproddata -g rg-data \
--time-to-restore 2026-10-24T06:00:00Z \
--blob-range invoices/ invoices0
Caution
Point-in-time restore rewrites every blob in the range, including good changes made after the restore time. Restore a narrow prefix and, if you can, copy current data aside first.
5. Whole storage account deleted
A deleted account can be recovered from the portal (Storage accounts → Restore deleted account) for a limited period, as long as the resource group still exists and the name has not been reused. If the option is missing, open a Sev A support case immediately — time matters.
6. Who deleted it
StorageBlobLogs
| where OperationName in ("DeleteBlob", "DeleteContainer")
| summarize deletes = count() by bin(TimeGenerated, 5m), AuthenticationType, RequesterObjectId, CallerIpAddress
| order by TimeGenerated desc
This needs a diagnostic setting sending blob logs to Log Analytics. Account deletion is in the Activity log.
Prevent it next time
- Turn on blob soft delete, container soft delete and versioning on every production account.
- Add a
CanNotDeletelock to the account. - Use Azure Backup for blobs (vaulted backup) for data you cannot lose; soft delete is not a backup if someone can turn it off.
- Give pipelines least privilege:
Storage Blob Data Contributoron one container, not the whole account.