Aller au contenu

Récupérer des objets S3 supprimés avec le versionnage (et le piège du marqueur de suppression)

Comment récupérer des objets S3 supprimés sur un bucket versionné : trouver et retirer le marqueur de suppression, restaurer des versions précises, et éviter les règles de cycle de vie qui rendent la suppression permanente.

Si le bucket avait le versionnage activé au moment de la suppression, les données sont presque certainement encore là : un delete ordinaire sur un bucket versionné ne retire rien — il ajoute un marqueur de suppression au sommet de la pile de versions, et retirer ce marqueur ramène l’objet exactement comme il était. Si le versionnage était désactivé (ou si le delete précisait un ID de version), l’objet a quitté S3 lui-même, et vos options sont les copies de réplication, les sauvegardes, ou rien. Vérifiez dans quel monde vous êtes avant de promettre une récupération à qui que ce soit.

Ce qui se passe

Sur un bucket avec versionnage, chaque clé d’objet porte une pile de versions. Un appel DeleteObject sans ID de version n’en touche aucune ; il pousse un marqueur de suppression — un espace réservé de zéro octet — au sommet de la pile et en fait la version « la plus récente ». Les GET retournent 404 parce que la version la plus récente est un marqueur, et c’est pourquoi l’objet semble disparu. Chaque version précédente est intacte en dessous et récupérable par son ID de version.

Le piège a trois dents. Un : supprimer « l’objet » de nouveau ne fait qu’empiler un autre marqueur — des gens cliquent supprimer à répétition dans la console et bâtissent des tours de marqueurs pendant que les données dorment en dessous. Deux : un DeleteObject avec un --version-id précis est un retrait permanent et irrécupérable de cette version — y compris quand l’ID passé est celui d’un marqueur (ça, c’est le geste de récupération) ou celui d’une version de données (ça, c’est de la destruction). La même commande change de direction selon l’ID qu’on lui donne. Trois : les règles de cycle de vie avec NoncurrentVersionExpiration purgent silencieusement et définitivement les vieilles versions après N jours — le versionnage ne vous protège que jusqu’à ce que la politique de cycle de vie emporte les versions, et la fenêtre de récupération est donc limitée dans le temps sur bien des buckets sans que personne s’en souvienne.

Le versionnage suspendu est l’état intermédiaire : les versions existantes survivent, mais les nouvelles écritures écrasent la version null, et les suppressions créent quand même des marqueurs.

Quoi vérifier en premier

  • Le versionnage était-il activé, et depuis quand? Les objets écrits avant l’activation ont une seule version null et les mêmes règles de récupération s’y appliquent.
  • Comment le delete a-t-il été émis — console, CLI sans ID de version, ou quelque chose passant --version-id? Le dernier cas peut être irrécupérable.
  • Des règles de cycle de vie expirent-elles les versions non courantes? Si oui, notez la fenêtre de rétention — le chronomètre tourne.
  • Y a-t-il de la réplication ou AWS Backup sur le bucket? Si la récupération côté S3 échoue, la réplique ou le coffre est le plan B.

Comment vérifier

Confirmez l’état du versionnage et trouvez les marqueurs :

aws s3api get-bucket-versioning --bucket my-bucket
# "Status": "Enabled" est ce que vous voulez voir

aws s3api list-object-versions --bucket my-bucket \
  --prefix reports/q2-2026.xlsx \
  --query '{Markers:DeleteMarkers[].[VersionId,IsLatest,LastModified],Versions:Versions[].[VersionId,IsLatest,LastModified,Size]}'

Une situation récupérable montre un marqueur de suppression avec IsLatest: true et une ou plusieurs versions de données en dessous. Vérifiez qu’une version candidate est lisible avant de toucher à quoi que ce soit :

aws s3api get-object --bucket my-bucket \
  --key reports/q2-2026.xlsx \
  --version-id 3sL4kqtJlcpXroDTDmJ+rmSpXd3dIbrHY+MTRCxf3vjVBH40Nr8X8gdRQBpUMLUo \
  /tmp/q2-check.xlsx

Chemin console : S3 > Buckets > my-bucket, activez Show versions — marqueurs et versions apparaissent en ligne. Vérifiez la menace de cycle de vie sous Management > Lifecycle rules, en cherchant Permanently delete noncurrent versions of objects.

Le runbook

  1. Récupérer un objet : supprimez son marqueur de suppression. C’est le geste contre-intuitif — vous supprimez la suppression :
aws s3api delete-object --bucket my-bucket \
  --key reports/q2-2026.xlsx \
  --version-id 8QWCvJgb8jNDL7q3jTQm9dTSGnQK2fDx
# version-id ici est l'id du MARQUEUR DE SUPPRESSION, tiré de list-object-versions

La version de données précédente redevient la plus récente et les GET réussissent immédiatement. Vérifiez trois fois que vous passez l’ID du marqueur, pas celui d’une version de données.

  1. Alternative : copier une bonne version par-dessus. Quand vous voulez une version antérieure précise (pas juste la plus récente) ou préférez ne pas retirer de marqueurs, copiez-la sur la même clé — elle devient une nouvelle version la plus récente et l’historique complet est préservé :
aws s3 cp "s3://my-bucket/reports/q2-2026.xlsx" \
  "s3://my-bucket/reports/q2-2026.xlsx" \
  --source-version-id 3sL4kqtJlcpXroDTDmJ+rmSpXd3dIbrHY+MTRCxf3vjVBH40Nr8X8gdRQBpUMLUo
  1. Récupération de masse : énumérez les marqueurs, supprimez-les par lots. Pour un mauvais script qui a supprimé tout un préfixe, paginez list-object-versions, filtrez DeleteMarkers[?IsLatest==true], et passez les paires clé+versionId à delete-objects par lots d’au plus 1 000. Faites d’abord la liste à sec et comptez — le nombre doit correspondre au rayon de l’incident, pas le dépasser.
  2. Si des versions ont été supprimées définitivement, passez aux copies secondaires. Répliques même-région ou inter-régions, points de restauration AWS Backup, ou consommateurs en aval qui détiennent encore les données. Faites vite si c’est l’expiration de cycle de vie qui a emporté les versions — les répliques partagent peut-être la même erreur de cycle de vie.
  3. Après la récupération, éliminez la cause. Corrigez le chemin de code qui supprimait, et resserrez les permissions s3:DeleteObjectVersion / s3:PutLifecycleConfiguration — très peu d’identités ont besoin de l’une ou l’autre.

Quand c’est la mauvaise approche

Le versionnage n’est pas une stratégie de sauvegarde, et ce runbook n’est pas de la reprise après sinistre. Il ne protège pas contre la suppression du bucket, une identité compromise avec s3:DeleteObjectVersion (qui peut purger toutes les versions), ni les problèmes à l’échelle d’une région. Si les données comptent, il leur faut une copie hors du bucket — réplication vers un autre compte, ou AWS Backup — et si vous êtes en plein incident de sécurité, arrêtez et préservez l’état avant de « réparer » : les marqueurs et les entrées CloudTrail sont des preuves.

Erreurs fréquentes

  • Passer l’ID d’une version de données à delete-object en voulant retirer un marqueur — la frappe qui transforme une récupération en perte permanente. Lister d’abord, lire deux fois, supprimer une fois.
  • Supposer que le versionnage était activé parce que « c’est notre standard » — vérifiez get-bucket-versioning avant de promettre une récupération.
  • Oublier que le NoncurrentVersionExpiration du cycle de vie jetait silencieusement les vieilles versions, si bien que la version voulue a expiré il y a des semaines.
  • Restaurer l’objet sans remarquer la classe de stockage : les versions passées aux paliers Glacier exigent une demande de restauration avant d’être lisibles, ce qui ajoute des heures et de la mécanique de récupération.

Prévention

  • Activez le versionnage sur chaque bucket dont le contenu vous manquerait, et jumelez-le à une fenêtre NoncurrentVersionExpiration assez longue pour survivre à une longue fin de semaine plus le temps de découverte — 30 jours est un plancher sain.
  • Refusez s3:DeleteObjectVersion à tout sauf un rôle d’urgence ; les marqueurs deviennent alors la seule forme de suppression accessible à la plupart des identités, et chaque suppression reste réversible.
  • Considérez S3 Object Lock (mode conformité ou gouvernance) pour les données à obligations de rétention — versionnage plus verrou est le palier résistant à l’altération ; détails sur https://docs.aws.amazon.com/AmazonS3/ .
  • Alertez sur les pointes inhabituelles d’appels DeleteObject via CloudTrail pour attraper un script emballé en quelques minutes.
  • Testez le chemin de récupération une fois, volontairement, en staging — la danse du marqueur de suppression ne devrait pas s’apprendre pendant un incident.

La récupération complète prend habituellement cinq minutes une fois qu’on lit la sortie de list-object-versions calmement — le risque est entièrement dans la précipitation sur les ID de version. Si personne dans votre équipe ne possède vraiment l’environnement AWS de bout en bout, c’est la racine de la plupart de ces problèmes — Stack Harbor opère des comptes AWS au quotidien sous Opérations AWS, et vous pouvez réserver une revue AWS.