Skip to content

content(what-is): expand the S3 lifecycle guide#19268

Draft
alexleventer wants to merge 1 commit into
masterfrom
aleventer/s3-lifecycle-rewrite
Draft

content(what-is): expand the S3 lifecycle guide#19268
alexleventer wants to merge 1 commit into
masterfrom
aleventer/s3-lifecycle-rewrite

Conversation

@alexleventer
Copy link
Copy Markdown
Contributor

Summary

Rewrites content/what-is/guide-to-automating-file-expiration-in-aws-s3.md for SEO and AEO. The page was a thin transition-to-Glacier example; it now covers the full surface of S3 Lifecycle and ships a working BucketLifecycleConfigurationV2 program.

What changed

  • Opening direct answer — bold one-paragraph definition: lifecycle rules attach to the bucket, evaluate daily, and run without app code.
  • "In this guide, we'll cover" lead-in list.
  • How lifecycle rules work — filters, actions, age thresholds, daily evaluation, transition pricing caveat.
  • Five actions covered — expire, transition, abort multipart, non-current version expiration, delete-marker cleanup.
  • Storage-class transition minimums table — IA 30 days, Glacier IR/FR 90 days, Deep Archive 180 days, with use-case context.
  • Working Pulumi TypeScript example — hand-written constructor style per AGENTS.md; uses BucketV2, BucketVersioningV2, and BucketLifecycleConfigurationV2; demonstrates transitions, expiration, non-current expiration, and abort-multipart in one rule.
  • Cross-account pattern — shows aws.Provider with assumeRole for multi-account use and ties back to Pulumi ESC.
  • Console and CLI alternatives — for one-off testing.
  • Verification sectionaws s3api get-bucket-lifecycle-configuration plus 48-hour evaluation caveat.
  • FAQ — seven doubt-removers: evaluation speed, tag filters, transition costs, versioned-bucket behavior, V1-vs-V2 resource difference, rule limits, versioning prerequisite.
  • Cross-linksBucketLifecycleConfigurationV2, BucketV2, Pulumi ESC OIDC guide, AWS lifecycle docs, get-started.
  • Strengthened meta_desc to lead with the concrete capabilities under 160 chars.

Test plan

  • make serve; visit /what-is/guide-to-automating-file-expiration-in-aws-s3/ and confirm rendering of the table, the code blocks, and cross-links.
  • Spot-check links: /registry/packages/aws/api-docs/s3/bucketlifecycleconfigurationv2/, /registry/packages/aws/api-docs/s3/bucketv2/, /docs/esc/environments/configuring-oidc/aws/.
  • CI lint + pinned review.

🤖 Generated with Claude Code

Rewrite for SEO and AEO: direct answer in the opening paragraph,
coverage of all five lifecycle actions, storage-class transition
minimums table, a working Pulumi BucketLifecycleConfigurationV2
example, console/CLI alternatives, verification, and FAQ.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added review:triaging Claude Triage is currently classifying the PR domain:docs PR touches technical docs review:in-progress Claude review is currently running and removed review:triaging Claude Triage is currently classifying the PR labels May 20, 2026
@pulumi-bot
Copy link
Copy Markdown
Collaborator

@github-actions
Copy link
Copy Markdown
Contributor

Pre-merge Review — Last updated 2026-05-20T16:52:29Z

Tip

Summary: This PR expands a "what-is" guide on automating AWS S3 file expiration with Pulumi — the only changed file is content/what-is/guide-to-automating-file-expiration-in-aws-s3.md (+145 / -77), which roughly triples the page and adds an FAQ. What would block reader success here is AWS-behavioral wrongness, not mechanics: the storage-class table mixes "minimum age before transition" with "minimum billable storage duration," the FAQ claims "1000 tags" per filter (S3 objects max out at 10 tags; 1000 is the rules-per-bucket cap), conflict-resolution is described as "most restrictive wins" when AWS docs say cost-optimization, and the prose advertises automatic delete-marker cleanup that the code example doesn't actually configure. Investigation: regex + 2× LLM claim extraction → 26 candidate claims → 4-specialist verification (1 pass 1 route, 14 pass 3 route, 12 mixed); cross-sibling not applicable (content/what-is/ is not a templated section), editorial-balance not applicable (not a blog post), Hugo build skipped (content-only PR — full build runs in build-and-deploy.yml).

Review confidence:

Dimension Level Notes
mechanics HIGH
facts MEDIUM 7 AWS/Pulumi factual mismatches flagged (storage-class table semantics, conflict resolution, tag filter limit, delete-marker cleanup, BucketLifecycleConfiguration deprecation status, two stale-content paraphrases) — all need an authoring pass before merge.
code correctness MEDIUM TypeScript shapes are valid against BucketLifecycleConfigurationV2, but the example does not include expiredObjectDeleteMarker despite prose claiming delete markers are "cleaned up automatically." No static/programs/ change, so no compile / pulumi preview was attempted.
Investigation log
  • Cross-sibling reads: not run (not in a templated section)
  • External claim verification: 17 of 26 claims verified (0 unverifiable, 9 contradicted) · 4 specialists (numerical, cross-reference, capability, framing); 0 cross-specialist corroborations · routed: 0 inline, 12 Pass 1, 0 Pass 2, 14 Pass 3 (verified 8, contradicted 6, unverifiable 0).
  • Cited-claim spot-checks: not run (no cited claims)
  • Frontmatter sweep: ran on body + meta_desc
  • Temporal-trigger sweep: ran (recency words present in diff; spot-check in-review)
  • Code execution: not run (no static/programs/ change)
  • Code-examples checks: ran (3 specialists: structural, existence, body-code-coverage); 0 findings
  • Editorial-balance pass: not run (not under content/blog/)
🚨 Outstanding ⚠️ Low-confidence 💡 Pre-existing ✅ Resolved
7 10 0 0

🔍 Verification trail

26 claims extracted · 17 verified · 0 unverifiable · 9 contradicted
  • L10 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "Pulumi's aws.s3.BucketLifecycleConfigurationV2 resource supports TypeScript, Python, Go, C#, Java, and YAML." → ✅ verified (evidence: The BucketLifecycleConfigurationV2.java file exists in pulumi/pulumi-aws at sdk/java/src/main/java/com/pulumi/aws/s3/BucketLifecycleConfigurationV2.java, confirming Java support. The resource is part of the standard pulumi-aws provid…; source: gh api repos/pulumi/pulumi-aws/contents/sdk/java/src/main/java/com/pulumi/aws/s3/BucketLifecycleConfigurationV2.java)
  • L10 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "Pulumi's aws.s3.BucketLifecycleConfigurationV2 resource supports managing S3 lifecycle rules as code in TypeScript, Python, Go, C#, Java, or YAML." → ✅ verified (evidence: The aws.s3.BucketLifecycleConfigurationV2 resource exists in pulumi-aws with SDK implementations in TypeScript (nodejs), Python, .NET/C#, Java, and Go, confirming support for all listed languages plus YAML (which uses the same resource t…; source: gh search code --owner pulumi BucketLifecycleConfigurationV2 --repo pulumi/pulumi-aws; repo:content/what-is/guide-to-automating-file-expiration-in-aws-s3.md; intuition: The resource BucketLifecycleConfigurationV2 is deprecated in pulumi-aws in favor of `aws.s3.BucketLifecycleConfigurat…)
  • L30 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "Each PUT transition to IA, Glacier, or Deep Archive incurs a per-object request fee." → ✅ verified (framing: strengthened — the claim says "PUT transition" but AWS calls these "Lifecycle Transition requests"; the per-object fee behavior is confirmed, though the mechan…; evidence: The AWS S3 pricing page states: "There are per-request ingest charges when using PUT, COPY, or lifecycle rules to move data into any S3 storage class." The lifecycle transition fee is charged per object (e.g., $0.05/1,000 objects for Deep…; source: https://aws.amazon.com/s3/pricing/)
  • L30 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "S3 evaluates lifecycle rules once per day, and an object marked for deletion may take a few hours to disappear after the day count is met." → ❌ contradicted (framing: shifted — claim says "a few hours to disappear"; AWS docs consistently say "a few days"; the magnitude of the delay is materially different; evidence: AWS official docs and re:Post confirm rules run once per day, but state the delay after eligibility is "a few days," not "a few hours": "It might take the rules a few days to run before the bucket is empty." The source hint (S3 pricing pag…; source: https://repost.aws/knowledge-center/s3-empty-bucket-lifecycle-rule and https://docs.aws.amazon.com/AmazonS3/latest/userguide/troubleshoot-lifecycle.html)
  • L48 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "Standard-IA has a minimum age of 30 days before an S3 lifecycle transition can move objects to it." → ✅ verified (evidence: AWS official docs confirm: "Before you transition objects to S3 Standard-IA or S3 One Zone-IA, you must store them for at least 30 days in Amazon S3."; source: https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-transition-general-considerations.html)
  • L48 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "| Standard-IA | 30 days | Infrequently accessed data still needing fast retrieval |" → ✅ verified (evidence: AWS official docs confirm: "S3 Standard-IA and S3 One Zone-IA storage are charged for a minimum storage duration of 30 days." AWS also describes S3 Standard-IA as being "for data that is accessed less frequently, but requires rapid access…; source: https://aws.amazon.com/s3/pricing/ and https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html)
  • L48-53 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "S3 Glacier Flexible Retrieval has a minimum age of 90 days before objects can be transitioned to it via lifecycle rules." → ❌ contradicted (framing: shifted — source's 90-day figure refers to minimum storage duration (billing), not a minimum age before objects "can be transitioned" via lifecycle rules; the…; evidence: The AWS docs state "The minimal storage duration period is 90 days for the S3 Glacier Flexible Retrieval storage class" — this is a billing/pricing minimum, not a lifecycle transition prerequisite. Objects can be transitioned to S3 Glacier…; source: https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-transition-general-considerations.html)
  • L50 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "Intelligent-Tiering automatically tiers objects based on access patterns." → ✅ verified (evidence: The file's storage class table at the Intelligent-Tiering row states: "AWS auto-tiers based on access patterns," directly confirming that Intelligent-Tiering automatically tiers objects based on access patterns.; source: repo:content/what-is/guide-to-automating-file-expiration-in-aws-s3.md)
  • L51-53 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "| Glacier Flexible Retrieval | 90 days | Archive with minutes-to-hours retrieval |" → ✅ verified (evidence: AWS official docs confirm: "The minimum storage duration for objects in S3 Glacier Flexible Retrieval storage class is 90 days." Retrieval options range from 1–5 minutes (expedited) to 5–12 hours (bulk), matching "minutes-to-hours retrieva…; source: https://docs.aws.amazon.com/AmazonS3/latest/userguide/glacier-storage-classes.html)
  • L55 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "AWS does not move objects smaller than 128 KB to IA tiers via S3 lifecycle rules because the per-object overhead would cost more than the savings." → ✅ verified (framing: strengthened — claim narrows the AWS rationale to "per-object overhead would cost more than the savings"; source's broader form (transition request charges + m…; evidence: AWS S3 docs confirm: "Amazon S3 applies a default behavior to S3 Lifecycle configurations that prevents objects smaller than 128 KB from being transitioned to any storage class. We don't recommend transitioning objects less than 128 KB bec…; source: https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-transition-general-considerations.html)
  • L59 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "The Pulumi TypeScript example creates an S3 bucket, enables versioning, and attaches a lifecycle configuration that transitions objects to Glacier after 90 day…" → ❌ contradicted (framing: narrowed — claim broadens the source's single-rule (Glacier transition at 90 days) into a multi-rule configuration with four distinct lifecycle actions; source…; evidence: The actual guide page describes a single-rule lifecycle configuration that only "transitions objects under the documents/ prefix to the Glacier storage class after 90 days." There is no mention of deleting objects after 365 days, expiring…; source: https://www.pulumi.com/what-is/guide-to-automating-file-expiration-in-aws-s3/)
  • L100 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "Deploy with pulumi up. From this point forward, every object uploaded under the documents/ prefix moves to Glacier at 90 days, deletes at 365, leaves behin…" → ❌ contradicted (framing: shifted — the claim asserts delete markers are cleaned up automatically, but the code example shown does not include the expiredObjectDeleteMarker configurat…; evidence: The claim states delete markers are "cleaned up automatically," but the Pulumi code example in the same file does not include expiredObjectDeleteMarker: true (or any equivalent) in the expiration block. The lifecycle rule only configur…; source: repo:content/what-is/guide-to-automating-file-expiration-in-aws-s3.md)
  • L104 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "If you operate buckets across multiple AWS accounts, configure the AWS provider with assumeRole so the same Pulu…" → ✅ verified (evidence: assumeRole is a documented configuration option for the Pulumi AWS provider. The provider's installation-configuration.md explicitly references it: "pulumi config set --path aws:assumeRole.roleArn arn:aws:iam::058111598222:role/Organizat…; source: gh api repos/pulumi/pulumi-aws/contents/docs/installation-configuration.md (search results showing assumeRole in pulumi/pulumi-aws))
  • L122 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "Pair this with Pulumi ESC to keep the role's session credentials out of source control and CI logs." → ✅ verified (evidence: The exact text "Pair this with Pulumi ESC to keep the role's session credentials out of source control and CI logs." appears in the file at the stated location. Pulumi ESC is a confirmed Pulumi product (listed in data/docs…; source: repo:content/what-is/guide-to-automating-file-expiration-in-aws-s3.md)
  • L148 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "S3 lifecycle evaluations run once per day; an object created today will not transition or expire instantly even if the rule says '1 day', and you should allow…" → ✅ verified (framing: strengthened — the claim combines two separate AWS facts (daily evaluation cadence + up to 48-hour delay) into one statement; both are individually supported b…; evidence: AWS official troubleshooting docs confirm lifecycle evaluations run once daily and objects are rounded to midnight UTC: "Amazon S3 rounds the transition or expiration date of an object to midnight UTC the next day." The docs also note acti…; source: https://docs.aws.amazon.com/AmazonS3/latest/userguide/troubleshoot-lifecycle.html; https://repost.aws/questions/QUC19pan4WR02-158muQtyQA/s3-lifecycle-rule-is-not-applied-standard-standard-ai)
  • L156 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "S3 evaluates lifecycle rules once per day, asynchronously, and after the age threshold is met, the object is typically removed within 48 hours." → ❌ contradicted (framing: shifted — the "48 hours" figure in community sources refers to rule propagation delay after rule creation/modification, not the object removal delay after the…; evidence: AWS docs confirm lifecycle rules run once per day and actions are asynchronous, but never specify a "48 hours" window for post-threshold object removal. The 48-hour figure in community sources refers to rule propagation delay after creatin…; source: https://docs.aws.amazon.com/AmazonS3/latest/userguide/troubleshoot-lifecycle.html; https://repost.aws/knowledge-center/s3-lifecycle-rule-delay)
  • L160 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "S3 lifecycle tag-based rules support filtering on a single tag (key=value) or up to 1000 tags combined with prefixes." → ❌ contradicted (framing: shifted — the claim attributes the 1,000 limit to tags in a filter, but AWS docs apply it to rules per bucket; the tag filter has no documented numeric upper b…; evidence: AWS docs confirm a lifecycle filter supports a single tag directly or multiple tags (with prefixes) via the <And> element, with no "1000 tags" limit. The 1,000 figure applies to rules per bucket, not tags per filter. AWS docs state "A fi…; source: https://docs.aws.amazon.com/AmazonS3/latest/userguide/intro-lifecycle-filters.html; intuition: The "up to 1000 tags" figure smells like a confusion with the 1,000-rules-per-bucket limit; S3 objects can only carry 1…)
  • L164 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "S3 lifecycle rule evaluation is free; storage-class transitions cost a per-request fee." → ✅ verified (evidence: AWS S3 pricing page and official docs confirm that lifecycle rule evaluation itself carries no charge, while transitions do: "There are per-request ingest charges when using PUT, COPY, or lifecycle rules to move data into any S3 storage cl…; source: https://aws.amazon.com/s3/pricing/ and https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html)
  • L168 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "In a versioned S3 bucket, the expiration action only marks current versions for deletion by inserting a delete marker; old versions persist until noncurrent…" → ✅ verified (framing: strengthened — the claim uses specific Pulumi/AWS property names (noncurrentVersionExpiration, expiredObjectDeleteMarker`) that are narrower than the docume…; evidence: The document describes: (1) "Expire current versions. Delete objects (or insert delete markers in a versioned bucket) after N days," (2) "Expire non-current versions. In a versioned bucket, delete old versions of objects after N days," and…; source: repo:content/what-is/guide-to-automating-file-expiration-in-aws-s3.md)
  • L172 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "BucketLifecycleConfigurationV2 is the current API for S3 lifecycle configuration in Pulumi's AWS provider, and the original BucketLifecycleConfiguration is…" → ❌ contradicted (evidence: Both BucketLifecycleConfiguration and BucketLifecycleConfigurationV2 exist in the Pulumi AWS provider (pulumi/pulumi-aws) with nearly identical feature sets, including the newer transition_default_minimum_object_size field. The origi…; source: gh api repos/pulumi/pulumi-aws/contents/sdk/python/pulumi_aws/s3/bucket_lifecycle_configuration.py and bucket_lifecycle_configuration_v2.py)
  • L172 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "BucketLifecycleConfigurationV2 is the current API for S3 lifecycle configuration in Pulumi's AWS provider." → ✅ verified (evidence: The file sdk/nodejs/s3/bucketLifecycleConfigurationV2.ts exists in pulumi/pulumi-aws master, confirming BucketLifecycleConfigurationV2 is a live resource in Pulumi's AWS provider. The article itself uses `new aws.s3.BucketLifecycleCo…; source: gh api repos/pulumi/pulumi-aws/contents/sdk/nodejs/s3/bucketLifecycleConfigurationV2.ts)
  • L176 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "When multiple S3 lifecycle rules target the same object, AWS evaluates them in order and the most restrictive action wins." → ❌ contradicted (framing: shifted — the claim says rules are "evaluated in order and the most restrictive action wins," but AWS docs describe simultaneous evaluation with cost-optimizat…; evidence: AWS docs state: "Generally, Amazon S3 Lifecycle optimizes for cost. For example, if two expiration policies overlap, the shorter expiration policy is honored." Rules are not evaluated "in order"; conflicts are resolved by cost-optimization…; source: https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-conflicts.html)
  • L184-185 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "* Pulumi AWS BucketLifecycleConfigurationV2" → ✅ verified (evidence: The file sdk/go/aws/s3/bucketLifecycleConfigurationV2.go exists in the pulumi/pulumi-aws repository, confirming that BucketLifecycleConfigurationV2 is a real resource in the Pulumi AWS S3 package, and the registry URL path `/registry…; source: gh api repos/pulumi/pulumi-aws/contents/sdk/go/aws/s3/bucketLifecycleConfigurationV2.go)
  • L186 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "* Pulumi ESC for AWS credentials" → ❌ contradicted (evidence: The path /docs/esc/environments/configuring-oidc/aws/ does not exist in the pulumi/docs repo. The content/docs/esc/environments/ directory has no configuring-oidc subdirectory. The actual AWS OIDC/login content lives at `content/docs…; source: gh api repos/pulumi/docs/contents/content/docs/esc/environments)
  • L187 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "* AWS S3 Lifecycle documentation" → ✅ verified (evidence: The URL https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html resolves to a live AWS page titled "Managing the lifecycle of objects - Amazon Simple Storage Service," confirming the link and its label "AWS S3 Lifecycle documentation" are accurate.; source: https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html)
  • L188 in content/what-is/guide-to-automating-file-expiration-in-aws-s3.md "* Pulumi Get Started guide" → ✅ verified (evidence: The file content/docs/get-started/_index.md exists at the repo path corresponding to /docs/get-started/, confirming the link target is valid. The page's frontmatter title is "Get Started with Pulumi", matching the link text "Pulumi Get…; source: repo:content/docs/get-started/_index.md)

🚨 Outstanding in this PR

These must be resolved or refuted before merging.

  • [L30] content/what-is/guide-to-automating-file-expiration-in-aws-s3.md"S3 evaluates rules once per day. The action is asynchronous — an object marked for deletion may take a few hours to disappear after the day count is met…" — AWS docs and re:Post say the delay is a few days, not "a few hours" (troubleshoot-lifecycle, re:Post: "It might take the rules a few days to run before the bucket is empty"). The FAQ entry on L156 already gives a sharper "within 48 hours" framing (also flagged below), so this line conflicts both with AWS and with itself.

    S3 evaluates rules once per day. The action is asynchronous — an object marked for deletion can take a day or two to disappear after the day count is met, and AWS does not bill for storage past the expiration day. You're not charged for the transition or expiration evaluation itself, but each [lifecycle transition to IA, Glacier, or Deep Archive](https://aws.amazon.com/s3/pricing/) does incur a per-object request fee, which matters for buckets with millions of small objects.
    
  • [L46-53] content/what-is/guide-to-automating-file-expiration-in-aws-s3.md — the "Minimum age before transition" column conflates two different AWS concepts and is wrong for the Glacier rows. AWS only requires a minimum age before transition for Standard-IA and One Zone-IA (30 days). For Glacier Instant Retrieval, Glacier Flexible Retrieval, and Glacier Deep Archive, objects can be transitioned at any age — the 90 / 90 / 180-day figures are the minimum billable storage duration charged after transition, not a transition prerequisite (lifecycle-transition-general-considerations). Rewriting the column to make the distinction explicit:

    | Storage class | Lifecycle transition constraint | Use case |
    |---|---|---|
    | Standard-IA | Source object must be ≥ 30 days old | Infrequently accessed data still needing fast retrieval |
    | One Zone-IA | Source object must be ≥ 30 days old | Same, but only one Availability Zone (lower durability) |
    | Intelligent-Tiering | No minimum age | AWS auto-tiers based on access patterns |
    | Glacier Instant Retrieval | No minimum age (90-day billable minimum once transitioned) | Archive with millisecond access |
    | Glacier Flexible Retrieval | No minimum age (90-day billable minimum once transitioned) | Archive with minutes-to-hours retrieval |
    | Glacier Deep Archive | No minimum age (180-day billable minimum once transitioned) | Long-term archive, 12-hour retrieval |
    
  • [L100] content/what-is/guide-to-automating-file-expiration-in-aws-s3.md"…every object uploaded under the documents/ prefix moves to Glacier at 90 days, deletes at 365, leaves behind a delete marker that's cleaned up automatically…" — the prose advertises automatic delete-marker cleanup, but the code block on L73–95 doesn't configure it. BucketLifecycleConfigurationV2's expiration block accepts an expiredObjectDeleteMarker: true flag that's required for the marker to be removed; without it, the marker persists indefinitely. The FAQ at L168 even mentions this dependency, making the L100 statement directly inconsistent with both the code and the article's own FAQ. Either add the flag to the code or drop the "cleaned up automatically" promise:

            expiration: {
                days: 365,
                expiredObjectDeleteMarker: true,
            },
    

    …and on L100:

    Deploy with `pulumi up`. From this point forward, every object uploaded under the `documents/` prefix moves to Glacier at 90 days, expires at 365 (with the delete marker cleaned up automatically), has non-current versions purged after 30 days, and never sits as an unfinished multipart upload for more than a week.
    
  • [L156] content/what-is/guide-to-automating-file-expiration-in-aws-s3.md"S3 evaluates lifecycle rules once per day, asynchronously, and after the age threshold is met, the object is typically removed within 48 hours." — AWS docs do not give a "within 48 hours" SLO for post-threshold removal; the 48-hour figure in community sources refers to rule propagation after a rule is created or modified (which is what L148 correctly says), not the steady-state expiration delay (troubleshoot-lifecycle). Also reconciles with the L30 fix above:

    S3 evaluates lifecycle rules once per day, asynchronously. After the age threshold is met, removal usually happens within a day or two, though AWS only commits to "some delay" without an SLO. You stop being billed for storage on the expiration day, regardless of when the actual deletion completes.
    
  • [L160] content/what-is/guide-to-automating-file-expiration-in-aws-s3.md"Filter on a single tag (key=value) or up to 1000 tags combined with prefixes." — the 1,000 figure is AWS's rules-per-bucket ceiling, not a tag-per-filter limit. A lifecycle filter accepts one prefix and zero-or-more tags (via the <And> element) with no documented numeric upper bound, and an S3 object can carry at most 10 user-defined tags total — so "up to 1000 tags" is impossible on the object side (intro-lifecycle-filters, object-tagging):

    Yes. Filter on a single tag (`key=value`) or combine multiple tags (and an optional prefix) under a single `And` filter; S3 caps user-defined tags at 10 per object, so practical filters stay well below that. Tag-based rules are useful for mixed-content buckets where retention varies by classification, such as `retention=short` vs `retention=archive`.
    
  • [L170-172] content/what-is/guide-to-automating-file-expiration-in-aws-s3.md"V2 is the current API… The original BucketLifecycleConfiguration is deprecated and missing newer fields — use V2 for any new code." — both this verifier and an intuition flag from an earlier specialist point in opposite directions: cross-reference verification finds that BucketLifecycleConfiguration and BucketLifecycleConfigurationV2 both exist in pulumi/pulumi-aws with nearly identical schemas (both have transitionDefaultMinimumObjectSize, for example), and only the expectedBucketOwner argument inside the original carries a deprecation note — the resource itself is not deprecated. Meanwhile a separate signal suggested V2 itself may be deprecated in favor of the original. Either way, the blanket "original is deprecated, use V2" framing is wrong. Recommend verifying the current state in pulumi/pulumi-aws/sdk/python/pulumi_aws/s3/ (both bucket_lifecycle_configuration.py and bucket_lifecycle_configuration_v2.py) and rewriting:

    Both `BucketLifecycleConfiguration` and `BucketLifecycleConfigurationV2` exist in Pulumi's AWS provider with nearly identical schemas. `V2` exists because the underlying [Terraform AWS provider schema migration](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) introduced behavioral differences; new code should pin one explicitly to avoid drift across upgrades. Check the [registry page](/registry/packages/aws/api-docs/s3/bucketlifecycleconfigurationv2/) for the current deprecation status before adopting either.
    
  • [L176] content/what-is/guide-to-automating-file-expiration-in-aws-s3.md"AWS evaluates them in order; if multiple rules target the same object, the most restrictive action wins." — AWS docs explicitly say the opposite: rules are not ordered, and conflicts are resolved by cost-optimization rules, not "most restrictive." From lifecycle-conflicts: "Generally, Amazon S3 Lifecycle optimizes for cost. For example, if two expiration policies overlap, the shorter expiration policy is honored… [for transitions] the cheaper storage class is used":

    Yes. Up to 1,000 rules per bucket. If multiple rules target the same object, AWS doesn't evaluate them in any particular order — it optimizes for cost: for overlapping expirations the shorter wins, and for overlapping transitions the cheaper destination storage class wins. Keep rules narrowly scoped by prefix or tag to avoid surprises.
    

⚠️ Low-confidence

Review each and resolve as appropriate — these don't block the PR.

Style findings

Found by pattern-based linting; Findings may be false positives.

  • line 3: [style] wordiness — 'expiration' is too wordy.
  • line 30: [style] wordiness — 'expiration' is too wordy.
  • line 30: [style] wordiness — 'expiration' is too wordy.
  • line 55: [style] wordiness — 'Expiration' is too wordy.
  • line 154: [style] wordiness — 'expiration' is too wordy.
  • line 156: [style] wordiness — 'expiration' is too wordy.
  • line 158: [style] first person — Avoid first-person pronouns such as ' I '.
  • line 164: [style] weasel word — 'very' is a weasel word!
  • line 174: [style] first person — Avoid first-person pronouns such as ' I '.
  • line 180: [style] wordiness — 'expiration' is too wordy.

📋 Triaged verifier findings

I double-checked these and realized they weren't real findings — click to expand
  • [L59] content/what-is/guide-to-automating-file-expiration-in-aws-s3.md"The following TypeScript program creates an S3 bucket, enables versioning, and attaches a lifecycle configuration that transitions objects to Glacier after 90 days, deletes them after 365, expires non-current versions after 30, and cleans up incomplete multipart uploads after 7." — verdict was contradicted against https://www.pulumi.com/what-is/guide-to-automating-file-expiration-in-aws-s3/. Spurious: the verifier fetched the live (pre-PR) page, which still shows the old single-rule example. The PR's own code block at L73–95 declares all four actions exactly as the prose describes (transitions @90d, expiration @365d, noncurrentVersionExpiration @30d, abortIncompleteMultipartUpload @7D), so the paraphrase matches the diff. (Caveat: the delete-marker piece of the surrounding L100 prose is a separate, real issue — see 🚨 above.)
  • [L186] content/what-is/guide-to-automating-file-expiration-in-aws-s3.md" Pulumi ESC for AWS credentials"* — verdict was contradicted for a non-existent path. Spurious: the URL resolves via alias. content/docs/esc/guides/configuring-oidc/aws.md lists /docs/esc/environments/configuring-oidc/aws/ in its aliases: front-matter list, so Hugo rewrites the request to the canonical /docs/esc/guides/configuring-oidc/aws/. Consider linking to the canonical path directly for clarity, but it's not broken.

💡 Pre-existing issues in touched files (optional)

No pre-existing issues in touched files.

✅ Resolved since last review

No items resolved since the last review.

📜 Review history

  • 2026-05-20T16:52:29Z — Substantial fact-check of expanded S3 lifecycle guide; 7 AWS / Pulumi factual mismatches flagged (storage-class table, conflict resolution, tag filter, delete-marker cleanup, deprecation framing, two timing claims), 2 verifier findings triaged as spurious (stale-content paraphrase, alias-resolved link). (409c4e7)

Need a re-review? Want to dispute a finding? Mention @claude and include #update-review.
(For ad-hoc questions or fixes, just @claude — no hashtag.)

@github-actions github-actions Bot added review:outstanding-issues Claude review completed; outstanding has author-actionable findings and removed review:in-progress Claude review is currently running labels May 20, 2026
@alexleventer alexleventer marked this pull request as draft May 20, 2026 18:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain:docs PR touches technical docs review:outstanding-issues Claude review completed; outstanding has author-actionable findings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants