Skip to main content

Theoretical Foundation: Configure Blob Versioning


1. Initial Intuition​

Imagine you edit an important document and save over the original file. An hour later, you realize the previous version was correct. Without version history, the data is lost.

Now imagine that every time you save the file, the system automatically keeps a numbered copy of the previous version. You can go back to any previous version at any time. This is exactly how tools like Git work for code.

Blob Versioning applies this same concept to Azure Blob Storage: every time a blob is created, modified, or replaced, Azure automatically preserves the previous version. The complete modification history is available, and any previous version can be recovered with a single operation.

The fundamental difference compared to snapshots (which we saw in Azure Files) is that versioning is automatic: you don't need to remember to create a copy before modifying. The system does it for you on every write operation.


2. Context​

2.1 Blob Versioning within the protection strategy​

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

2.2 What Blob Versioning is and what it is not​

Is: An automatic content versioning system for Block Blobs in Azure Blob Storage.

Is not:

  • Available for Append Blobs or Page Blobs
  • A complete replacement for backups
  • Free (each version takes space and is charged)
  • Available in Premium accounts (only Standard GPv2)

2.3 Critical dependencies​

Blob Versioning is a prerequisite for other functionalities:

  • Object Replication: Requires versioning enabled on both accounts (source and destination)
  • Point-in-time Restore: Requires versioning + soft delete + change feed simultaneously
  • Lifecycle Management of versions: Requires versioning to have version section in rules

3. Building the Concepts​

3.1 Current version and previous versions​

When Blob Versioning is enabled, each blob has:

Current version: The most recent state of the blob. This is what you normally access through the blob's default URL.

Previous versions: Read-only copies of previous states of the blob. Each version has a unique Version ID, which is a timestamp in the format 2025-01-15T10:30:00.0000000Z.

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

3.2 What generates a new version​

Not every operation creates a new version. It's important to understand exactly which operations preserve the previous version:

OperationCreates previous version?Result
Upload new blob (POST/PUT)No (blob didn't exist)Creates current version
Replace existing blob (PUT)YesCurrent version becomes previous version; new content becomes current version
Modify metadataYesVersion with old metadata becomes previous version
Modify index tagsNoTags don't create new version
Set Blob Tier (tier change)NoTier changes on current version
Delete BlobYes (special)Current version becomes previous version; blob no longer has current version

Non-obvious behavior when deleting with versioning: When you delete a blob with active versioning, the current version is not destroyed. It becomes a previous version without a corresponding current version. The blob appears deleted (default URL returns 404), but all previous versions continue to exist and are charged.


3.3 Version ID: the unique identifier​

Each version has a Version ID that is automatically generated at creation time. The format is an ISO 8601 timestamp with 100-nanosecond precision:

2025-01-15T10:30:45.1234567Z

To access a specific version, add versionid as a parameter to the URL:

https://myaccount.blob.core.windows.net/container/report.xlsx?versionid=2025-01-15T10:30:45.1234567Z

3.4 Interaction between Versioning and Soft Delete​

When both are enabled, the behavior changes significantly:

Without Versioning, with Soft Delete:

  • Delete blob β†’ blob goes to soft-deleted β†’ can be restored

With Versioning, with Soft Delete:

  • Delete blob β†’ current version becomes previous version β†’ Soft Delete is not triggered for the blob itself
  • Soft Delete now protects previous versions when they are explicitly deleted

This is one of the most important interactions and most tested in AZ-104.

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

3.5 Point-in-time Restore and Versioning​

Point-in-time Restore (PITR) is a functionality that allows restoring the complete state of all blobs in a container to a specific moment in the past. It depends on:

  1. Blob Versioning enabled
  2. Soft Delete enabled
  3. Change Feed enabled

With PITR, you can say: "restore all blobs in this container to how they were at 2:00 PM yesterday". Azure uses the Change Feed and versions to reconstruct that state.


4. Structural View​

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

5. Practical Operation​

5.1 Restoring a blob to a previous version​

The restoration process is a copy operation: you copy a previous version over the current version. This creates a new current version with the content from the version you want to restore, and the version that was current becomes another previous version.

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

The copy operation is done with the Copy Blob command or az storage blob copy start specifying the source Version ID.


5.2 Promoting previous version to current version​

The official way to "restore" a version is using Copy Blob from URL with the Version ID:

# Copy previous version to make it the current version
az storage blob copy start \
--account-name myaccount \
--destination-container mycontainer \
--destination-blob report.xlsx \
--source-uri "https://myaccount.blob.core.windows.net/mycontainer/report.xlsx?versionid=2025-01-14T08:00:00.0000000Z&<SAS-TOKEN>" \
--auth-mode login

5.3 Versioning behavior with containers and blobs​

Versioning is configured at the Storage Account level and applies to all blobs in the account. It's not possible to enable versioning only for specific containers or blobs.


6. Implementation Methods​

6.1 Azure Portal​

Enabling Blob Versioning:

Storage Account > Data Protection > Enable versioning for blobs

The option is on the same screen as Soft Delete and Change Feed, allowing you to configure all data protections in one place.

Listing versions in the portal:

Storage Account > Containers > [container] > [blob] > Versions

The portal displays a list of all blob versions with Version ID, date, and size. You can download any version or promote it to current version.


6.2 Azure CLI​

Enabling Blob Versioning:

az storage account blob-service-properties update \
--account-name myaccount \
--resource-group myRG \
--enable-versioning true

Checking if versioning is enabled:

az storage account blob-service-properties show \
--account-name myaccount \
--resource-group myRG \
--query "isVersioningEnabled"

Listing all versions of a blob:

az storage blob list \
--account-name myaccount \
--container-name mycontainer \
--prefix "report.xlsx" \
--include v \
--auth-mode login \
--output table

The --include v parameter instructs to include versions in the listing. The output shows the versionId of each version.

Downloading a specific version:

az storage blob download \
--account-name myaccount \
--container-name mycontainer \
--name "report.xlsx" \
--version-id "2025-01-14T08:00:00.0000000Z" \
--file "/local/path/report-v1.xlsx" \
--auth-mode login

Deleting a specific version:

az storage blob delete \
--account-name myaccount \
--container-name mycontainer \
--name "report.xlsx" \
--version-id "2025-01-14T08:00:00.0000000Z" \
--auth-mode login

Deleting all versions of a blob:

# Delete current version (main blob)
az storage blob delete \
--account-name myaccount \
--container-name mycontainer \
--name "report.xlsx" \
--auth-mode login

# List and delete each previous version
az storage blob list \
--account-name myaccount \
--container-name mycontainer \
--prefix "report.xlsx" \
--include v \
--auth-mode login \
--query "[].versionId" \
--output tsv | while read VID; do
az storage blob delete \
--account-name myaccount \
--container-name mycontainer \
--name "report.xlsx" \
--version-id "$VID" \
--auth-mode login
done

6.3 Azure PowerShell​

$ctx = New-AzStorageContext `
-StorageAccountName "myaccount" `
-UseConnectedAccount

# List versions of a blob
Get-AzStorageBlob `
-Container "mycontainer" `
-Prefix "report.xlsx" `
-Context $ctx `
-IncludeVersion

# Get specific version
$version = Get-AzStorageBlob `
-Container "mycontainer" `
-Blob "report.xlsx" `
-VersionId "2025-01-14T08:00:00.0000000Z" `
-Context $ctx

# Promote previous version to current version
# (copies the specific version over the current blob)
Start-AzStorageBlobCopy `
-SrcContainer "mycontainer" `
-SrcBlob "report.xlsx" `
-SrcBlobSnapshotTime "2025-01-14T08:00:00.0000000Z" `
-DestContainer "mycontainer" `
-DestBlob "report.xlsx" `
-Context $ctx

6.4 Bicep​

resource blobServiceProperties 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = {
name: '${storageAccount.name}/default'
properties: {
isVersioningEnabled: true
// Enable together with other protections
deleteRetentionPolicy: {
enabled: true
days: 7
}
containerDeleteRetentionPolicy: {
enabled: true
days: 30
}
changeFeed: {
enabled: true
retentionInDays: 7
}
}
}

6.5 Lifecycle Management for versions​

This is one of the most critical points: old versions accumulate cost indefinitely without a lifecycle policy to manage them.

{
"rules": [
{
"name": "version-management",
"enabled": true,
"type": "Lifecycle",
"definition": {
"filters": {
"blobTypes": ["blockBlob"]
},
"actions": {
"version": {
"tierToCool": {
"daysAfterCreationGreaterThan": 30
},
"tierToArchive": {
"daysAfterCreationGreaterThan": 90
},
"delete": {
"daysAfterCreationGreaterThan": 365
}
}
}
}
}
]
}

Attention: daysAfterCreationGreaterThan in the context of version refers to the date that specific version was created (the moment it became a previous version), not the original blob creation date.


7. Control and Security​

7.1 Permissions to access versions​

Access to specific blob versions requires the same permissions as accessing the current blob:

OperationMinimum role
List versionsStorage Blob Data Reader
Read version contentStorage Blob Data Reader
Delete specific versionStorage Blob Data Contributor
Promote version to current (copy)Storage Blob Data Contributor
Enable/disable versioningStorage Account Contributor

7.2 Versioning and immutability​

When Immutability Policy is active along with Versioning:

  • Versions protected by the policy cannot be deleted during the immutability period
  • This creates very robust protection: each modification creates a new version, and versions cannot be deleted by attackers
  • Even an administrator cannot delete immutable versions before the deadline

This combination is highly recommended for regulatory compliance that requires auditable modification history.


7.3 Versioning and SAS tokens​

To access a specific version via SAS token, the SAS needs to include the versionid in the URL or the user needs permission to specify it. Service SAS and Account SAS tokens support access to versions.

User Delegation SAS (generated with Azure AD credentials) is the most secure option and also supports access to specific versions.


7.4 The cost problem with versioning​

Enabling versioning without a lifecycle policy for version control is a recipe for increasing costs. Each blob modification creates a previous version that:

  • Takes space proportional to the blob size
  • Is charged in the tier it's in (usually Hot, since versions inherit the blob's tier)
  • Is never automatically deleted without lifecycle policy

For a 1 GB blob modified 10 times per day, after 30 days you could have 300 previous versions potentially occupying 300 GB in Hot.


8. Decision Making​

8.1 Versioning vs Snapshots​

AspectBlob VersioningManual snapshots
AutomationAutomatic on every writeManual (you decide when to create)
GranularityPer write operationOnly when you create
ManagementRequires lifecycle policyRequires manual cleanup process
CostPotentially high without lifecycleControllable (you control when to create)
RecoveryBy specific Version IDBy snapshot timestamp
Recommended useContinuous protection against overwriteBackup before specific operation

8.2 When to enable Versioning​

SituationEnable Versioning?Reason
Critical data modified frequentlyYesAutomatic protection against overwrite
Write-once blobs (logs, backups)Not necessarilyData is never overwritten; Soft Delete is sufficient
Object Replication requirementYes (mandatory)Prerequisite
Point-in-time Restore requirementYes (mandatory)Prerequisite
Premium Block Blob accountNot availablePremium doesn't support versioning
Account with high volume and costEvaluateLifecycle policy is mandatory to control cost

FunctionalityEnable with Versioning?Reason
Blob Soft DeleteYesProtects individually deleted versions
Container Soft DeleteYesProtects container and all versions
Change FeedYes for PITRNecessary for Point-in-time Restore
Lifecycle policy for versionsAlwaysMandatory cost control
Immutability PolicyFor complianceMakes versions auditable and immutable

9. Best Practices​

  • Always configure lifecycle policy for versions when enabling versioning. Never enable versioning without simultaneously defining expiration rules for version.
  • Move old versions to cheaper tiers before deleting them. Use tierToCool after 30 days and delete after 365 days as a starting point.
  • Combine with Soft Delete for layered protection: versioning protects against overwrites, soft delete protects versions against explicit deletion.
  • Enable versioning along with Change Feed if Point-in-time Restore is needed, as PITR requires both.
  • Use Version ID when referencing versions in restoration scripts, never assume which version is the "second most recent" by position.
  • Restrict who can delete specific versions using granular RBAC. Only designated administrators should have permission to delete versions.
  • Monitor BlobCapacity by blob type separating versions from active content to understand the real cost of versioning.
  • Document the restoration process for the team: how to list versions, how to identify the correct version and how to promote it.

10. Common Errors​

ErrorWhy it happensHow to avoid
Explosive cost after enabling versioningNo lifecycle policy for versions; frequently modified blobsAlways create lifecycle policy for version simultaneously
"Deleted" blob still appears in costsDelete with versioning doesn't remove previous versionsExplicitly delete each version after deleting current blob
Restoration creates unwanted extra versionCopy from previous version creates new current versionExpected behavior; document and include in lifecycle policy
Versioning doesn't work on Premium accountPremium Block Blob doesn't support versioningUse Standard GPv2 for data that needs versioning
Object Replication failsVersioning not enabled on one of the accountsEnable versioning on both accounts before configuring replication
PITR not availableVersioning, Soft Delete or Change Feed not enabledEnable all three simultaneously
Versions in Hot not included in lifecycleLifecycle policy without version sectionAlways include version section in lifecycle rules
Version ID hard to findNot documented after upload operationCapture Version ID from HTTP response and store in inventory system

11. Operation and Maintenance​

11.1 Monitoring versions and cost​

# See total capacity of versions (in Azure Monitor)
az monitor metrics list \
--resource <storage-account-id> \
--metric BlobCapacity \
--dimension BlobType \
--interval P1D

Filter by BlobType=BlockBlobPreviousVersions to see only space occupied by previous versions.

Log Analytics query to track created versions:

StorageBlobLogs
| where OperationName == "PutBlob" and isnotempty(ResponseStatus)
| where ResponseStatus == "Success"
| extend VersionId = extract("versionid=([^&]+)", 1, RequestUri)
| where isnotempty(VersionId)
| summarize count() by bin(TimeGenerated, 1d)

11.2 Version inventory with Azure Storage Blob Inventory​

For accounts with large data volumes, use Blob Inventory to generate a CSV report of all blobs and versions:

az storage account blob-inventory-policy create \
--account-name myaccount \
--resource-group myRG \
--policy '{
"enabled": true,
"rules": [{
"name": "version-inventory",
"enabled": true,
"destination": "inventory-container",
"definition": {
"format": "Csv",
"schedule": "Weekly",
"objectType": "Blob",
"schemaFields": ["Name", "VersionId", "IsCurrentVersion", "ContentLength", "BlobType", "AccessTier", "LastModified"],
"filters": {
"includeSnapshots": false,
"includeBlobVersions": true
}
}
}]
}'

The report is generated weekly in the specified container and can be analyzed with Azure Synapse or Data Factory to identify versions candidates for cleanup.


11.3 Important limits​

AspectLimit
Supported blob typesBlock Blobs only
Supported account typesStandard GPv2
Maximum number of versions per blobNo documented limit (practical: control via lifecycle)
Version IDTimestamp with 100ns precision; unique per blob
Versioning disablingPossible, but existing versions remain

Important about disabling: If you disable versioning on an account that already had accumulated versions, existing versions are not deleted. They continue to exist, continue to be charged, but new versions are no longer created. To clean up existing versions, you need to delete them explicitly or use lifecycle policy that will continue to apply even after disabling versioning.


12. Integration and Automation​

12.1 Point-in-time Restore: complete enablement​

PITR requires three simultaneous functionalities. Enable all via CLI:

# Enable versioning, soft delete and change feed (PITR prerequisites)
az storage account blob-service-properties update \
--account-name myaccount \
--resource-group myRG \
--enable-versioning true \
--enable-delete-retention true \
--delete-retention-days 7 \
--enable-change-feed true \
--change-feed-retention-days 7

# Enable Point-in-time Restore with 6-day window
az storage account blob-service-properties update \
--account-name myaccount \
--resource-group myRG \
--enable-restore-policy true \
--restore-days 6

The PITR period must be shorter than the soft delete period. If soft delete is 7 days, PITR can be at most 6 days.

Executing a restore:

az storage blob restore \
--account-name myaccount \
--resource-group myRG \
--time-to-restore "2025-01-14T10:00:00Z" \
--blob-ranges '[{"startRange": "", "endRange": ""}]'

The blob-ranges parameter with empty startRange and endRange means "restore all blobs". You can specify prefixes to restore only part of the container.


12.2 Version inventory automation with Azure Function​

import azure.functions as func
from azure.storage.blob import BlobServiceClient
from datetime import datetime, timedelta

def main(timer: func.TimerRequest):
"""
Runs weekly and identifies versions
older than 90 days in Hot tier,
generating report of candidates for Archive.
"""
client = BlobServiceClient.from_connection_string(conn_str)
cutoff = datetime.utcnow() - timedelta(days=90)

candidates = []
for container in client.list_containers():
container_client = client.get_container_client(container['name'])
for blob in container_client.list_blobs(include=['versions']):
if (blob.get('version_id') and
not blob.get('is_current_version') and
blob.get('last_modified', datetime.utcnow()) < cutoff and
blob.get('blob_tier') == 'Hot'):
candidates.append({
'container': container['name'],
'blob': blob['name'],
'version_id': blob['version_id'],
'size_gb': blob['size'] / (1024**3),
'tier': blob['blob_tier']
})

# Save report as blob for analysis
report_client = client.get_blob_client("reports", f"version-audit-{datetime.utcnow().date()}.json")
report_client.upload_blob(str(candidates), overwrite=True)

13. Final Summary​

Essential concepts:

  • Blob Versioning automatically preserves previous copies of a blob on each write, overwrite or delete operation.
  • Each version has a unique Version ID (ISO 8601 timestamp) used to access or restore the specific version.
  • The current version is accessed through the default URL. Previous versions require ?versionid= in the URL.
  • Versioning is a Storage Account setting and applies to all Block Blobs in the account.

Critical differences:

  • Versioning vs Snapshots: Versioning is automatic on each write. Snapshots are manual (you decide when to create).
  • Delete with Versioning: Deleting a blob with active versioning turns the current version into a previous version, but doesn't destroy it. The blob appears deleted but versions exist and are charged.
  • Soft Delete with Versioning: Soft Delete now protects individually deleted versions, not the blob itself (since the "deleted" blob already becomes a previous version with versioning).
  • Versioning and cost: Without lifecycle policy for version, cost grows indefinitely. This is the most critical operational consequence.

What needs to be remembered:

  • Versioning is only available on Standard GPv2. Premium accounts don't support it.
  • Versioning only affects Block Blobs. Append and Page Blobs are not supported.
  • Versioning is a prerequisite for Object Replication and Point-in-time Restore.
  • Always configure lifecycle policy for the version section when enabling versioning.
  • Disabling versioning doesn't delete existing versions; they continue to exist and be charged.
  • Restoring a previous version is a copy operation, which creates a new current version and adds one more entry to the history.
  • Versions in Hot tier are charged as normal blobs in Hot. Use lifecycle to move them to Cool or Archive as they age.