Blob data protection — soft delete, versioning, change feed, point-in-time restore, immutability and object replication

Which blob protection features to turn on, what each one requires and blocks — blob and container soft delete (1–365 days), versioning and who may delete versions, change feed, point-in-time restore (needs all three, retention less than soft delete), immutability (time-based retention, legal hold, locked policies, account deletion blocked), object replication (versioning on both sides, read-only destination, 409), and what isn't supported with a hierarchical namespace — with Bicep and CLI.

markdownreferencev1P6BT-4A7Fstage: designtrust: officialservice: storageinterface: iacinterface: cliblob soft deletecontainer soft deletedeleteRetentionPolicycontainerDeleteRetentionPolicyisVersioningEnabledblob versioningdeleteBlobVersion/actionStorage Blob Data Owner delete versionSAS permission xchange feed$blobchangefeedrestorePolicypoint-in-time restoreaz storage blob restoreUndelete BlobRestore Containerimmutable storagetime-based retention policylegal holdlocked immutability policyversion-level WORM--enable-alwBlobImmutableDueToPolicyobject replicationobjectReplicationPoliciesaz storage account or-policy createAllowCrossTenantReplicationdestination container write 409recover deleted storage account 14 dayshierarchical namespace versioning not supportedaz storage account blob-service-properties update

Created 21 Oct 2026 · Modified 23 Oct 2026 · 1 version

Blob data protection

What each feature does and needs

FeatureProtects againstRequires / limits
Blob soft deleteDeleted or overwritten blobs, snapshots, versionsRetention 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 deleteA deleted container and everything in itRetention 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
VersioningOverwrites 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 feedAudit log of every change, in $blobchangefeedNot 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 restoreBulk corruption or deletes across block blobsStandard 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 adminsTime-based retention (1–146,000 days) and/or legal hold, at container or version level (version level needs versioning)
Object replicationRegion loss, as an async copy of block blobsGPv2 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

// 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:

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.

SymptomCause → fix
409 writing to a destination containerDestination containers are read-only while a rule targets them: remove the rule or the policy first
Can't disable versioningDelete the object replication policies first
Policy create fails naming only the accountUse the full resource ID; cross-tenant replication is off by default for accounts created since December 15, 2023 (allowCrossTenantReplication)
Can't set allowCrossTenantReplication falseExisting cross-tenant policies: delete them first

Read this document as markdown — the same bytes an agent receives.