hashicorp/terraform · error
error snapshotting Blob
Error message
error snapshotting Blob %q (Container %q / Account %q): %+v
What it means
Thrown by RemoteClient.Put when snapshot = true and c.giovanniBlobClient.Snapshot fails before overwriting the state blob. Snapshot creates a point-in-time copy of the existing blob; failure aborts the write to protect the prior state. Causes are RBAC (cannot snapshot), lease conflict, account in read-only/locked state, or snapshot restrictions on the SKU.
Solutions
- Ensure the identity has Storage Blob Data Contributor (or equivalent) on the account/container.
- Set snapshot = false in the backend if snapshots are not required.
- Release any conflicting leases before retrying.
- Remove an Azure resource lock on the storage account if it blocks snapshots.
- Check the storage account's soft-delete / WORM policy for snapshot conflicts.
Example fix
// before
terraform {
backend "azurerm" {
snapshot = true
}
}
// after
terraform {
backend "azurerm" {
snapshot = false
}
} Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: confirm the identity can snapshot blobs before enabling snapshot = true.
func canSnapshotBlob(ctx context.Context, acct, container, blob string) error {
// attempt a snapshot on a throwaway test blob, or check role assignments
cmd := exec.CommandContext(ctx, "az", "role", "assignment", "list", "--assignee", os.Getenv("ARM_CLIENT_ID"), "--role", "Storage Blob Data Contributor")
if out, err := cmd.Output(); err != nil || len(out) == 0 {
return fmt.Errorf("identity lacks Storage Blob Data Contributor; cannot snapshot")
}
return nil
} Try / catch
// If snapshotting is non-critical, retry Put without it.
if diags.HasErrors() && snapshotEnabled {
log.Printf("snapshot failed; retrying Put without snapshot")
client.Snapshot = false
diags = client.Put(data)
} Prevention
- Grant Storage Blob Data Contributor when using snapshot = true.
- Set snapshot = false for read-only or low-churn state where history isn't needed.
- Remove Azure resource locks (CanNotDelete) on storage accounts that must snapshot.
When it happens
Trigger: (a) Identity lacks permission to create blob snapshots (Storage Blob Data Contributor needed). (b) Blob is currently leased by another client. (c) Account is read-only or has a deny-assignment. (d) Soft-deleted / under recovery. (e) Premium SKU snapshot restrictions.
Common situations: Using AAD auth without Storage Blob Data Contributor; concurrent apply holds the lease; storage account has a CanNotDelete lock; account under immutable-blob / WORM policy.
Related errors
- listing blobs
- blob metadata was empty
- Error unlocking Azure state. Lock ID
- failed to lock azure state
- failed to retrieve lock info
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/750c91c43dec6138.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/azure/client.go:102
setOptions := blobs.SetPropertiesInput{}
putOptions := blobs.PutBlockBlobInput{}
options := blobs.GetInput{}
if c.leaseID != "" {
options.LeaseID = &c.leaseID
getOptions.LeaseID = &c.leaseID
setOptions.LeaseID = &c.leaseID
putOptions.LeaseID = &c.leaseID
}
ctx := newCtx()
if c.snapshot {
snapshotInput := blobs.SnapshotInput{LeaseID: options.LeaseID}
log.Printf("[DEBUG] Snapshotting existing Blob %q (Container %q / Account %q)", c.keyName, c.containerName, c.accountName)
if _, err := c.giovanniBlobClient.Snapshot(ctx, c.containerName, c.keyName, snapshotInput); err != nil {
return diags.Append(fmt.Errorf("error snapshotting Blob %q (Container %q / Account %q): %+v", c.keyName, c.containerName, c.accountName, err))
}
log.Print("[DEBUG] Created blob snapshot")
}
blob, err := c.giovanniBlobClient.GetProperties(ctx, c.containerName, c.keyName, getOptions)
if err != nil {
if !response.WasNotFound(blob.HttpResponse) {
return diags.Append(err)
}
}
contentType := "application/json"
putOptions.Content = &data
putOptions.ContentType = &contentType
putOptions.MetaData = blob.MetaData
_, err = c.giovanniBlobClient.PutBlockBlob(ctx, c.containerName, c.keyName, putOptions)
View on GitHub (pinned to d32a084675)