Azure Blob Lifecycle Policy Analyzer

Read an Azure Blob Storage lifecycle policy for what it costs: transitions that leave a tier before its minimum storage duration and are billed for the rest anyway, rules that match nothing, and deletes with no prefix filter.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Check the policy. Nothing leaves this tab.

Wanted a different tool?

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

Archive on day 60, delete on day 90

Thirty days in a tier with a 180 day minimum, so 150 days are billed for storage that was never used

{"rules":[{"name":"archive-then-delete","enabled":true,"type":"Lifecycle","definition":{"filters":{"blobTypes":["blockBlob"],"prefixMatch":["logs/"]},"actions":{"baseBlob":{"tierToArchive":{"daysAfterModificationGreaterThan":60},"delete":{"daysAfterModificationGreaterThan":90}}}}}]}

A rule that matches nothing

Last access time needs access tracking enabled on the account, and it is off by default, so this deploys and does nothing

{"rules":[{"name":"clean-unused","enabled":true,"type":"Lifecycle","definition":{"filters":{"blobTypes":["blockBlob"],"prefixMatch":["uploads/"]},"actions":{"baseBlob":{"delete":{"daysAfterLastAccessTimeGreaterThan":180}}}}}]}

A delete with no filter

An empty prefixMatch is the whole storage account, including containers created after this policy was written

{"rules":[{"name":"retention","enabled":true,"type":"Lifecycle","definition":{"filters":{"blobTypes":["blockBlob"]},"actions":{"baseBlob":{"delete":{"daysAfterModificationGreaterThan":365}}}}}]}

The wrong condition for the target

Base blobs measure from last modified and snapshots from creation. The service rejects the pair, so the whole policy does not apply.

{"rules":[{"name":"wrong","enabled":true,"type":"Lifecycle","definition":{"filters":{"blobTypes":["blockBlob"],"prefixMatch":["logs/"]},"actions":{"baseBlob":{"delete":{"daysAfterCreationGreaterThan":30}}}}}]}

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Deleting a blob before its tier's minimum storage duration

    Cool is 30 days, cold is 90, archive is 180, and leaving early is billed for the remainder anyway. Archiving on day 60 and deleting on day 90 charges 180 days of archive for 30 days of storage, so the rule that looks like a saving is a penalty.

    Instead:Either delay the delete past the minimum, or skip the intermediate tier entirely for data with that lifetime.

  2. Using daysAfterLastAccessTimeGreaterThan without enabling access tracking

    Tracking is off by default. The rule is accepted, validates and deploys, and no blob ever matches, because nothing has a last access time. There is no warning, because the policy itself is legal.

    Instead:Enable it first with az storage account blob-service-properties update --enable-last-access-tracking true, and expect a further wait: it only records from that moment on.

  3. Archiving data something still reads

    Archive is offline. A GET fails until the blob is rehydrated, which takes up to 15 hours at standard priority and costs a rehydration fee on top of the tier price. The error names the tier rather than the blob, so it reads as a permission problem at first.

    Instead:Use cold for anything that might be read: it is online and its 90 day minimum is still most of the saving.

  4. Writing a delete rule with no prefixMatch

    An empty filter is the whole storage account rather than a narrow scope. A retention rule written for one container deletes from all of them, including containers created later by somebody who never saw the policy.

    Instead:Add a prefixMatch, remembering that the first segment is the CONTAINER name: ["logs/"] means the logs container, not a logs folder in each one.

  5. Expecting the policy to run when you save it

    It runs once a day, and up to 48 hours can pass before a new policy takes effect. On a large account the run itself takes time and processes blobs in no particular order.

    Instead:Check the following day. Delete anything you need gone now directly rather than waiting.

The tier you leave early is the tier you pay for anyway

A lifecycle policy is a short JSON document that decides a large part of a storage bill, and its most expensive rule is one the portal never computes for you.

Every cool tier has a minimum storage duration

Cool is 30 days, cold is 90, archive is 180. Move a blob out before the minimum, whether to another tier or to a delete, and the remaining days are billed anyway at that tier's rate. So a policy that archives on day 60 and deletes on day 90 charges 180 days of archive for 30 days of storage. It reads as a saving and is a penalty, and the arithmetic is the whole reason this page exists.

tierToArchive  day  60
delete         day  90     30 days in archive

archive minimum is 180
you are billed for 180 and used 30

lastAccessTime does nothing until access tracking is on

Access time tracking is off by default on a storage account. A rule using daysAfterLastAccessTimeGreaterThan is accepted, validates, deploys, and matches no blob ever, because nothing has a last access time to compare against. There is no warning, because the policy itself is perfectly legal. This is the rule that gets written, shipped and forgotten for a year while the data it was meant to remove accumulates.

az storage account blob-service-properties update \
  --enable-last-access-tracking true

It records from that moment on, so the rule
still will not fire until the threshold has
passed since you enabled it.

Archive is offline

A blob in archive cannot be read at all. A GET fails until it has been rehydrated, which takes up to 15 hours at standard priority, under an hour at high priority, and costs a rehydration fee on top of the tier price. Anything that reads this prefix starts failing on the day the rule fires, with an error about the tier rather than about the blob. Cold is online and its 90 day minimum is still most of the saving.

Base blobs measure from modification, snapshots from creation

daysAfterModificationGreaterThan applies to a base blob. daysAfterCreationGreaterThan applies to a snapshot or a version, which are immutable and have no modification time. Swap them and the service rejects the policy, which is at least loud, and the field names are similar enough that it happens on the first attempt most times.

An empty filter is the whole storage account

Not a narrow filter, all of it. A retention rule written for one container deletes from every container, including ones created later by somebody who never saw this policy. prefixMatch takes a list, and the first segment is the CONTAINER name rather than a path inside it, so ["logs/"] means the logs container and not a logs folder in each of them.

Nothing happens today

The policy runs once a day. After adding or editing one, up to 24 hours pass before it is applied and up to another 24 before the actions complete. On a large account the run itself takes time and blobs are processed in no particular order. So the first afternoon of "it deleted nothing" is the expected behaviour, and deleting something you need gone now means deleting it directly.

What this cannot see

It reads the policy, not the account. It does not know which tier a blob is in now, whether access tracking is enabled, how much data each prefix holds, or whether anything still reads what you are about to archive. Those decide what the policy actually costs, and all four need a look at the account rather than at the JSON. What it can tell you is which rules will not work and which transitions are billed for time you do not use.