Infrastructure

Azure Storage : valider l'impact d'une lifecycle policy avant suppression

Un runbook de production pour qualifier une lifecycle policy Azure Storage avec inventaire Blob, versions, snapshots, règles de suppression ou tiering, preuves, validation et rollback avant d'effacer des données.

25 juil. 2026 azurestorageblob-storagelifecycle-managementretentionobservabilitydata-protectionrunbookrollbackproduction

Une lifecycle policy Azure Storage paraît souvent simple : déplacer les blobs anciens vers Cool ou Archive, supprimer les snapshots obsolètes, nettoyer des exports temporaires. En production, c’est plus risqué. Une règle trop large peut toucher un préfixe partagé, supprimer une version encore utile, archiver un objet que l’application relit chaque nuit, ou rendre un rollback beaucoup plus lent que prévu.

Le cas d’usage est un compte Storage qui reçoit des exports applicatifs, des pièces jointes et des fichiers de diagnostic. L’équipe veut activer une règle de nettoyage pour réduire le bruit et les coûts. Le problème n’est pas d’écrire la policy. Le problème est de prouver ce qu’elle toucherait, quels chemins doivent être exclus, comment valider après activation, et quoi faire si une suppression utile est détectée.

Le but du runbook est de décider : activer la policy, la limiter, la reporter, ou rollbacker avant perte de preuve.

Définir le périmètre de données

Commence par nommer les conteneurs, préfixes et types d’objets concernés. Une lifecycle policy agit sur des blobs, versions et snapshots selon l’âge, le préfixe, le type et parfois les tags d’index. Elle ne comprend pas l’intention métier derrière un dossier.

yaml storage-lifecycle-scope.yml
storage_account: stprodappdata
subscription: production
resource_group: rg-data-prod

containers:
exports:
  intended_action: delete_after_45_days
  owners: product-operations
  restore_need: last_successful_export_for_replay
attachments:
  intended_action: no_delete_without_business_owner
  owners: application-team
  restore_need: user_visible_content
diagnostics:
  intended_action: move_to_cool_after_30_days_delete_after_180_days
  owners: platform-operations
  restore_need: incident_evidence

policy_change:
decision_needed: enable_limit_or_postpone
rollback_owner: platform-operations
validation_window: 48h
excluded_prefixes:
  - attachments/
  - exports/manual-replay/

Si ce contrat n’existe pas, la policy sera lue comme une optimisation de stockage alors qu’elle change la capacité de restauration, de preuve et parfois de conformité.

Lire la policy existante et l’historique

Avant de créer une règle, capture l’état actuel. Il faut savoir si une policy existe déjà, qui l’a modifiée récemment, et si le compte utilise soft delete, versioning, snapshots ou immutability.

bash 01-read-storage-lifecycle-state.sh
RG="rg-data-prod"
ACCOUNT="stprodappdata"

az storage account management-policy show --resource-group "$RG" --account-name "$ACCOUNT" --output json

az storage account blob-service-properties show --resource-group "$RG" --account-name "$ACCOUNT" --query "{deleteRetention:deleteRetentionPolicy,containerDeleteRetention:containerDeleteRetentionPolicy,versioning:isVersioningEnabled,changeFeed:changeFeed.enabled,restorePolicy:restorePolicy}" --output json

az monitor activity-log list --resource-group "$RG" --offset 14d --query "[?contains(operationName.value, 'managementPolicies') || contains(operationName.value, 'blobServices')].{time:eventTimestamp,operation:operationName.value,caller:caller,status:status.value}" --output table

Cette lecture évite deux erreurs fréquentes : activer une suppression sans fenêtre de récupération adaptée, ou modifier une policy déjà utilisée par une autre équipe.

Reconstituer l’impact avant activation

Azure Storage Lifecycle Management ne doit pas être traité comme un bouton avec dry-run magique. Avant activation, construis un inventaire des blobs qui seraient candidats selon les critères de la règle. L’objectif est d’obtenir une liste de chemins, d’âges, de tailles et de propriétaires probables.

bash 02-inventory-candidate-blobs.sh
ACCOUNT="stprodappdata"
CONTAINER="exports"
CUTOFF="2026-06-10T00:00:00Z"

az storage blob list --account-name "$ACCOUNT" --container-name "$CONTAINER" --auth-mode login --include m t v --query "[?properties.lastModified < '$CUTOFF'].{name:name,lastModified:properties.lastModified,accessTier:properties.blobTier,size:properties.contentLength,versionId:versionId,tags:tags}" --output json

Pour les gros volumes, préfère Blob Inventory ou un export contrôlé plutôt qu’une boucle CLI fragile. Le point important reste le même : produire un échantillon vérifiable et une estimation par préfixe avant d’écrire la règle.

Classer suppression, tiering et restauration

Toutes les actions n’ont pas le même risque. Déplacer vers Cool n’a pas le même impact qu’archiver, supprimer une version ou effacer un snapshot.

text lifecycle-action-risk.txt
Move to Cool
Risque principal: coût et latence de lecture différents
Validation: application relit-elle ces objets dans la fenêtre normale ?

Move to Archive
Risque principal: restauration lente et parcours applicatif cassé si lecture directe attendue
Validation: aucun job de production ne dépend d'une lecture immédiate

Delete base blob
Risque principal: disparition de l'objet visible par l'application
Validation: propriétaire métier, rétention, soft delete, preuve de non-usage

Delete version or snapshot
Risque principal: perte du chemin de rollback ou de preuve incident
Validation: versioning, snapshots utiles, restauration testée, fenêtre de conservation acceptée

Filter by prefix only
Risque principal: préfixe partagé ou convention non stable
Validation: inventaire par préfixe et exclusions explicites

Cette classification aide à limiter la première policy. Un bon premier changement peut tierer un préfixe de diagnostic bien compris, pas supprimer tout ce qui dépasse un âge global.

Vérifier les accès réels avant de conclure

Un blob ancien peut encore être utile. Avant de supprimer, cherche les lectures récentes, les erreurs applicatives et les dépendances batch. Les Storage logs ne remplacent pas la connaissance métier, mais ils évitent de décider uniquement à partir de la date de modification.

kusto 03-storage-access-before-delete.kql
let Window = 30d;
StorageBlobLogs
| where TimeGenerated > ago(Window)
| where AccountName == "stprodappdata"
| where ContainerName in ("exports", "diagnostics")
| summarize reads=countif(OperationName in ("GetBlob", "GetBlobProperties")),
          writes=countif(OperationName in ("PutBlob", "PutBlockList")),
          deletes=countif(OperationName has "Delete"),
          failures=countif(StatusCode >= 400),
          callers=make_set(AuthenticationType, 5),
          sampleUris=make_set(Uri, 5)
by ContainerName, bin(TimeGenerated, 1d)
| order by TimeGenerated desc

Si les logs ne sont pas disponibles, c’est une donnée de décision. On peut activer d’abord les diagnostics, réduire le périmètre, ou demander une validation propriétaire avant toute suppression.

Écrire une policy bornée

La policy doit être relisible. Nomme les règles, limite les préfixes, sépare suppression et tiering, et évite de mélanger des objets métier avec des objets techniques.

json 04-bounded-lifecycle-policy.json
{
"rules": [
  {
    "enabled": true,
    "name": "diagnostics-cool-after-30-days",
    "type": "Lifecycle",
    "definition": {
      "filters": {
        "blobTypes": ["blockBlob"],
        "prefixMatch": ["diagnostics/platform/"]
      },
      "actions": {
        "baseBlob": {
          "tierToCool": { "daysAfterModificationGreaterThan": 30 },
          "delete": { "daysAfterModificationGreaterThan": 180 }
        },
        "snapshot": {
          "delete": { "daysAfterCreationGreaterThan": 90 }
        },
        "version": {
          "delete": { "daysAfterCreationGreaterThan": 90 }
        }
      }
    }
  }
]
}

Ne commence pas par un préfixe trop haut comme exports/ si ce répertoire contient à la fois des exports rejouables, des fichiers utilisateur et des artefacts temporaires. Crée des préfixes d’exploitation stables avant d’automatiser la suppression.

Activer avec validation courte

Une fois la policy appliquée, la validation doit chercher trois choses : la policy est bien en place, les opérations attendues apparaissent, et aucun chemin exclu n’est touché.

bash 05-apply-policy-and-read-back.sh
RG="rg-data-prod"
ACCOUNT="stprodappdata"

az storage account management-policy create --resource-group "$RG" --account-name "$ACCOUNT" --policy @04-bounded-lifecycle-policy.json

az storage account management-policy show --resource-group "$RG" --account-name "$ACCOUNT" --query "policy.rules[].{name:name,enabled:enabled,filters:definition.filters,actions:definition.actions}" --output json

Complète avec une surveillance des suppressions et changements de tier.

kusto 06-watch-lifecycle-effects.kql
let Window = 48h;
StorageBlobLogs
| where TimeGenerated > ago(Window)
| where AccountName == "stprodappdata"
| where OperationName has_any ("Delete", "SetBlobTier")
| summarize operations=count(), statuses=make_set(StatusText, 10), sampleUris=make_set(Uri, 10)
by OperationName, ContainerName, bin(TimeGenerated, 1h)
| order by TimeGenerated desc

Si des suppressions apparaissent dans un conteneur ou un préfixe non prévu, coupe la règle immédiatement et conserve les logs avant toute correction.

Préparer le rollback

Le rollback d’une lifecycle policy ne restaure pas automatiquement les objets. Il arrête les prochaines actions. La restauration dépend ensuite de soft delete, versions, snapshots, sauvegardes ou réhydratation depuis Archive.

yaml lifecycle-rollback-card.yml
rollback:
stop_policy:
  action: disable_or_remove_rule
  validation: management_policy_read_back_without_rule

restore_deleted_blob:
  prerequisites:
    - soft_delete_enabled
    - retention_window_not_expired
    - exact_container_and_blob_name_known
  validation: blob_readable_from_application_path

restore_version_or_snapshot:
  prerequisites:
    - version_id_or_snapshot_timestamp_known
    - owner_approval_for_restore
  validation: restored_object_hash_or_size_matches_evidence

rehydrate_archive:
  prerequisites:
    - archive_tier_was_action_taken
    - business_accepts_rehydration_delay
  validation: object_available_before_batch_resume

evidence_to_keep:
  - policy_before_after
  - impacted_prefix_inventory
  - StorageBlobLogs_extract
  - business_owner_decision

La décision de rollback doit donc arriver vite. Plus la fenêtre de rétention est courte, plus la validation initiale doit être stricte.

Décider sans ambiguïté

La décision finale doit tenir sur une fiche d’exploitation.

text lifecycle-policy-decision.txt
Activer
Inventaire candidat relu par propriétaire
Préfixes bornés et exclusions explicites
Soft delete, versioning ou autre restauration adaptés au risque
Logs disponibles pour surveiller Delete et SetBlobTier
Rollback testé ou documenté

Limiter
Préfixe trop large
Incertitude sur lectures batch ou exports rejouables
Versions ou snapshots encore utiles
Restauration non testée

Reporter
Aucun propriétaire clair
Logs insuffisants
Rétention métier ou conformité non confirmée
Policy existante mal comprise

Rollbacker
Suppression ou tiering hors périmètre
Erreurs applicatives corrélées aux objets touchés
Objet utile restaurable uniquement pendant une fenêtre courte

Conclusion

Une lifecycle policy Azure Storage n’est pas seulement une règle de coût. C’est une action de production sur la disponibilité, la preuve et la restauration des données. Le runbook doit donc partir de l’inventaire, pas de la syntaxe JSON.

La bonne décision consiste à borner les préfixes, distinguer tiering et suppression, vérifier les accès réels, activer avec surveillance courte, puis garder un rollback qui arrête la policy et restaure les objets touchés quand c’est encore possible. Une suppression automatique fiable est une suppression prouvée avant d’être automatisée.