---
cuppy_hub: azure
cuppy_path: reference/storage/blob-data-protection.md
cuppy_section: docs
cuppy_doc: P6BT-4A7F
cuppy_doc_uuid: 341817ec-e7e9-45bb-85ce-b327e1334763
cuppy_permalink: https://cuppy.io/azure/d/P6BT-4A7F
cuppy_version: 1
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/azure/docs/reference/storage/blob-data-protection
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 011474c7c9a8b9c7aa332882a7a4e118183d1b9330dfecf56db7885e741780c7
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# Blob data protection

## What each feature does and needs

| Feature | Protects against | Requires / limits |
|---|---|---|
| **Blob soft delete** | Deleted or overwritten blobs, snapshots, versions | Retention **1–365 days** (Microsoft recommends at least 7). Restore with **Undelete Blob**; soft-deleted data can't be read until restored, and is billed as active data |
| **Container soft delete** | A deleted container and everything in it | Retention 1–365 days (default 7). Restores **only to the original name**: if a new container took the name, rename or delete it first. Not retroactive. Blocks upgrading a flat account to a hierarchical namespace while on |
| **Versioning** | Overwrites and deletes (every write makes a version) | Not on accounts with a hierarchical namespace, nor for blobs written with Data Lake APIs. All versions of a blob must be the same blob type. Can't be disabled while object replication policies exist |
| **Change feed** | Audit log of every change, in `$blobchangefeed` | Not on hierarchical-namespace accounts. Retention 1–146,000 days, or indefinite by default. `$blobchangefeed` doesn't appear in List Containers: list its blobs directly |
| **Point-in-time restore** | Bulk corruption or deletes across block blobs | Standard GPv2 only, hot and cool block blobs, no hierarchical namespace. **Needs soft delete, versioning and change feed all on**, and its retention must be **at least one day less than blob soft delete's**. Doesn't bring back deleted containers |
| **Immutability** (WORM) | Any change or delete, even by admins | Time-based retention (1–146,000 days) and/or legal hold, at container or version level (version level needs versioning) |
| **Object replication** | Region loss, as an async copy of block blobs | GPv2 or premium block blob on both sides, **versioning on both**, change feed on the source; no hierarchical namespace; no customer-managed failover on either account |

Neither soft delete nor versioning survives the deletion of the **storage account**: put a `CanNotDelete` lock on it (`reference/arm/locks-and-tags.md`). A deleted account can sometimes be recovered within 14 days if no account with the same name has been created since.

## Turning it on

```bicep
// Blob data protection on an existing StorageV2 account (flat namespace)
param storageAccountName string

@minValue(7)
@maxValue(365)
param softDeleteDays int = 14

resource sa 'Microsoft.Storage/storageAccounts@2026-06-01' existing = {
  name: storageAccountName
}

resource blobs 'Microsoft.Storage/storageAccounts/blobServices@2026-06-01' = {
  parent: sa
  name: 'default'
  properties: {
    deleteRetentionPolicy: {
      enabled: true
      days: softDeleteDays
    }
    containerDeleteRetentionPolicy: {
      enabled: true
      days: softDeleteDays
    }
    isVersioningEnabled: true
    changeFeed: {
      enabled: true
      retentionInDays: 90
    }
    restorePolicy: {
      enabled: true
      days: softDeleteDays - 1 // must be less than the blob soft delete days
    }
  }
}
```

CLI equivalent:

```bash
az storage account blob-service-properties update -g <rg> -n <acct> \
  --enable-delete-retention true --delete-retention-days 14 \
  --enable-container-delete-retention true --container-delete-retention-days 14 \
  --enable-versioning true --enable-change-feed true --change-feed-days 90 \
  --enable-restore-policy true --restore-days 13
```

Restore to a time (UTC, inside the retention window): `az storage blob restore -g <rg> --account-name <acct> -t 2026-10-19T08:00:00Z` (optionally `-r <start> <end>` blob ranges). One restore runs at a time per account, can't be cancelled, and needs **Storage Account Contributor** at account scope.

## Versions and who may delete them

Deleting the current version needs `…/blobs/delete` (Storage Blob Data Contributor). **Deleting a previous version needs `…/blobs/deleteBlobVersion/action`**, which only **Storage Blob Data Owner** has built in; a SAS needs the `x` permission. With soft delete on, a deleted version is kept until soft delete's retention ends. Versions are kept until deleted: use a lifecycle management rule to tier or delete old ones, or costs grow with every write. Stop taking snapshots of block blobs once versioning is on.

## Immutability

- **Unlocked** policies (for testing) can be shortened, extended or deleted. **Locked** policies can only be extended, at most five times at container level, and can't be deleted. Lock within about a day for SEC 17a-4(f) compliance.
- Writes and deletes fail with 409 `BlobImmutableDueToPolicy` / `BlobImmutableDueToLegalHold` (`reference/storage/data-errors.md`).
- **The account can't be deleted** while any container has version-level immutability enabled, or while a legal hold or a locked policy is in effect at container scope.
- CLI: `az storage container immutability-policy create|lock|extend`, `az storage container legal-hold set|clear`; account-level version WORM at creation with `az storage account create --enable-alw`.

## Object replication

Create the policy on the **destination** account first, then the same policy (with the returned policy ID) on the source: `az storage account or-policy create`. Up to 1,000 rules per policy, one source and one destination container each, both existing beforehand.

| Symptom | Cause → fix |
|---|---|
| 409 writing to a destination container | Destination containers are read-only while a rule targets them: remove the rule or the policy first |
| Can't disable versioning | Delete the object replication policies first |
| Policy create fails naming only the account | Use the full resource ID; **cross-tenant replication is off by default** for accounts created since December 15, 2023 (`allowCrossTenantReplication`) |
| Can't set `allowCrossTenantReplication` false | Existing cross-tenant policies: delete them first |


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 107 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/azure/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
