Skip to content

content(what-is): rewrite the aws s3 sync with dynamic credentials guide#19265

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

content(what-is): rewrite the aws s3 sync with dynamic credentials guide#19265
alexleventer wants to merge 1 commit into
masterfrom
aleventer/run-s3-sync-rewrite

Conversation

@alexleventer
Copy link
Copy Markdown
Contributor

Summary

Rewrites content/what-is/run-aws-s3-sync-with-dynamic-credentials.md for SEO and AEO. The new version is ~210 tight lines covering all three sync directions, the size+mtime change-detection model that confuses most users, and the operational risks of --delete.

What changed

  • Quotable opening — bolded one-paragraph framing covering what the guide does, why dynamic credentials beat static, and that sync uses size+mtime (not content hashes).
  • "In this article, we'll cover" bullet list for skim/extract.
  • Why dynamic credentials beat static — five bullets covering no-disk secrets, role scoping, expiration, CloudTrail audit, central rotation.
  • "How sync decides what to transfer" section — explicitly documents the size+mtime rule and --exact-timestamps. This is the Switch to using typedoc for TypeScript documentation. #1 source of "why is sync re-uploading everything?" support tickets.
  • Direction/IAM table — local-to-S3, S3-to-local, S3-to-S3, plus the --delete flag, each with the minimum IAM permissions.
  • Prerequisites — Pulumi CLI/ESC, Pulumi Cloud account, AWS CLI v2, OIDC-trusted IAM role, an existing bucket.
  • Numbered step-by-step setup (5 steps) — login, OIDC config, create environment, paste the aws-login YAML, confirm empty aws configure list.
  • Four run sub-sections — upload, download, S3-to-S3, and --delete mirror — each with copy-pasteable commands.
  • CloudTrail verification showing the assumed-role event types (PutObject/GetObject/CopyObject/DeleteObject).
  • Common errors tableAccessDenied (ListObjects, PutObject/GetObject, DeleteObject), KMS.AccessDenied, InvalidClientTokenId, ExpiredToken, re-upload-loop.
  • FAQ (7 doubt-removers) — content-vs-mtime checks, re-upload loops, --delete risk, large-file/multipart behavior, credential lifetime, cross-region/cross-account, source verification.
  • Cross-links to /product/esc/, OIDC config docs, sibling run-aws pages, resolve-* error pages.
  • Tightened meta_desc to lead with the value prop (under 160 chars).

Test plan

  • make serve; visit /what-is/run-aws-s3-sync-with-dynamic-credentials/ and confirm both tables, code blocks, and headings render correctly
  • Spot-check cross-links (/product/esc/, OIDC docs, sibling run-aws pages, resolve-* pages)
  • CI lint + pinned review

🤖 Generated with Claude Code

Rewrite for SEO and AEO: quotable opening, semantic chunking,
explanation of the size+mtime change detection, IAM permissions
table for upload/download/bucket-to-bucket/--delete, common-errors
table, FAQ targeting doubt-removers, and cross-links to Pulumi ESC
and related guides.

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:51:31Z

Tip

Summary: This PR rewrites the existing content/what-is/run-aws-s3-sync-with-dynamic-credentials.md guide (a "Run X with dynamic credentials" page paralleling the s3-cp, s3-ls, and sts-get-caller-identity siblings under content/what-is/). The kind of wrongness that would block a reader's success is a mis-stated default duration (the reader copies 1h thinking it's the safe default but the ESC aws-login provider actually defaults to 2h, which then fails against AWS's stock 1h MaxSessionDuration), a flag-direction error on --exact-timestamps (the doc says "re-upload" but the flag is download-only per the AWS CLI), and a framing slip on the IAM MaxSessionDuration default. External claim verification and frontmatter/link checks ran; Hugo build was skipped (content-only PR).

Review confidence:

Dimension Level Notes
mechanics HIGH
facts MEDIUM 3 contradicted claims caught (duration defaults + flag direction); 4 unverifiable claims spot-checked in-review.
code correctness HIGH YAML + CLI snippets only; no compiled programs touched.
Investigation log
  • Cross-sibling reads: not run (not in a templated section)
  • External claim verification: 25 of 43 claims verified (7 unverifiable, 4 contradicted) · 4 specialists (numerical, cross-reference, capability, framing); 0 cross-specialist corroborations · routed: 0 inline, 34 Pass 1, 0 Pass 2, 9 Pass 3 (verified 4, contradicted 3, unverifiable 2).
  • 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
3 12 0 0

🔍 Verification trail

43 claims extracted · 25 verified · 7 unverifiable · 4 contradicted
  • L3 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "Pulumi ESC issues short-lived, OIDC-issued credentials (no static AWS keys), scoped by IAM role, and fully auditable in CloudTrail." → 🤷 unverifiable (evidence: verify-claims.py errored on this claim: RuntimeError: HTTP 429: {"type":"error","error":{"type":"rate_limit_error","message":"This request would exceed your organization's rate limit of 2,000,000 input tokens per minute (org: 85d1a054-3697…)
  • L10 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "aws s3 sync performs incremental, one-way synchronization between a local directory and an S3 prefix (or between two S3 prefixes)." → ✅ verified (evidence: The file at L10 states exactly: "aws s3 sync performs incremental, one-way synchronization between a local directory and an S3 prefix (or between two S3 prefixes), using file size and modification time to decide what to transfer." The AW…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md (L10); gh search code --owner aws aws-cli "s3 sync")
  • L10 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "aws s3 sync uses file size and modification time to decide what to transfer." → ✅ verified (evidence: (escalated from pass1) Multiple authoritative sources confirm this. The AWS CLI reference docs describe --size-only as making "the size of each key the only criteria used to decide whether to sync," implying the default uses both size an…; source: https://docs.aws.amazon.com/cli/latest/reference/s3/sync.html; https://www.datacamp.com/tutorial/aws-s3-sync)
  • L10 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "Pulumi ESC exchanges a Pulumi-issued OIDC token for an AWS STS session, scoped to a specific IAM role, expiring automatically." → 🤷 unverifiable (evidence: verify-claims.py errored on this claim: RuntimeError: HTTP 429: {"type":"error","error":{"type":"rate_limit_error","message":"This request would exceed your organization's rate limit of 2,000,000 input tokens per minute (org: 85d1a054-3697…)
  • L14 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "* Why dynamic credentials beat static IAM keys" → ➖ not-a-claim (evidence: The text "* Why dynamic credentials beat static IAM keys" is a bullet point in a document outline/table of contents — it is a section heading describing the PR author's own content structure, not a falsifiable third-party assertion about a…; source: content/what-is/run-aws-s3-sync-with-dynamic-credentials.md L14)
  • L22 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "## Why dynamic credentials beat static IAM keys" → ➖ not-a-claim (evidence: The text "## Why dynamic credentials beat static IAM keys" is a markdown section heading in the PR author's own document. It is a structural/editorial label, not a falsifiable factual assertion about a third-party system or source.; source: content/what-is/run-aws-s3-sync-with-dynamic-credentials.md L22)
  • L26 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "Pulumi ESC issues a fresh AccessKeyId, SecretAccessKey, and SessionToken on every esc run, and nothing persists locally." → ✅ verified (evidence: The file at line 26 states verbatim: "Pulumi ESC issues a fresh AccessKeyId, SecretAccessKey, and SessionToken on every esc run. Nothing persists locally." This is consistent with the ESC aws-login OIDC flow described in the same…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L28 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "ESC-issued sessions expire after the ESC environment's duration, which is 1 hour by default." → ❌ contradicted (framing: shifted — claim states "1 hour by default" but the source shows the default lifetime is 2h0m0s; the 1h values in docs are explicitly set in example configs, no…; evidence: The official ESC CLI docs show the default session lifetime is 2 hours, not 1 hour: "the lifetime of the opened environment (default 2h0m0s)". The duration: 1h seen in example YAML snippets is an explicitly configured value, not a defaul…; source: https://www.pulumi.com/docs/esc/cli/commands/esc_env_open/ and https://www.pulumi.com/docs/esc/cli/commands/esc_run/)
  • L29 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "CloudTrail records each S3 operation under the assumed-role ARN with the sessionName configured in the ESC environment." → ✅ verified (framing: strengthened — claim adds "in the ESC environment" to the source's "you configured"; the ESC YAML block in the same document confirms the sessionName is indeed…; evidence: The file at L29 states: "CloudTrail records each S3 operation under the assumed-role ARN with the sessionName you configured." The ESC environment YAML in the guide sets sessionName: pulumi-environments-session, and the "Verify in Clou…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L34 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "aws s3 sync compares source and destination by file size and last-modified time — not by content hash." → ✅ verified (evidence: The file itself states at line ~34: "aws s3 sync compares source and destination by file size and last-modified time — not by content hash." This is consistent with AWS CLI documentation behavior for aws s3 sync.; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L36 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "aws s3 sync copies an object when it exists in the source but not the destination, or when the source object's size differs from the destination's, or when t…" (also L38) → ✅ verified (evidence: The file at lines 36-38 states exactly: "An object is copied when: It exists in the source but not the destination, or The source object's size differs from the destination's, or The source object's last-modified time is newer than the des…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L40 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "--exact-timestamps causes aws s3 sync to re-upload files whose timestamps differ even by a second." → ❌ contradicted (framing: shifted — the source says the flag applies "when syncing from S3 to local" (downloads), but the claim says it causes "re-upload" (uploads); the direction is wr…; evidence: (escalated from pass1) The official AWS CLI docs state: "When syncing from S3 to local, same-sized items will be ignored only when the timestamps match exactly." The flag applies only to S3-to-local (download) syncs, not uploads; the claim…; source: https://docs.aws.amazon.com/cli/latest/reference/s3/sync.html)
  • L40 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "The AWS CLI's default for aws s3 sync treats an older-source-than-destination timestamp as a no-op." → 🤷 unverifiable (evidence: verify-claims.py errored on this claim: RuntimeError: HTTP 429: {"type":"error","error":{"type":"rate_limit_error","message":"This request would exceed your organization's rate limit of 2,000,000 input tokens per minute (org: 85d1a054-3697…)
  • L40 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "--delete removes objects from the destination that no longer exist in the source." → ✅ verified (evidence: The file at line ~40 states: "--delete removes objects from the destination that no longer exist in the source — be careful with this flag in production." This exactly matches the claim.; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L46-49 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "S3-to-S3 sync (aws s3 sync s3://src/ s3://dst/) requires s3:GetObject on the source, s3:PutObject on the destination, and s3:ListBucket on both." → ✅ verified (evidence: The file's IAM permissions table explicitly states for S3→S3: "s3:GetObject on source, s3:PutObject on destination, s3:ListBucket on both", matching the claim exactly.; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L51 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "s3:ListBucket is required for aws s3 sync to enumerate objects on both sides." → ✅ verified (evidence: The file itself states: "s3:ListBucket is required for sync to enumerate objects on both sides." The IAM permissions table in the same file lists s3:ListBucket as a minimum permission for every sync direction (Local→S3, S3→Local, S3→S3…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L56 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "* The Pulumi CLI and Pulumi ESC CLI installed" → 🤷 unverifiable (evidence: verification did not converge within 8 turns)
  • L57 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "* A Pulumi Cloud account and access to an organization" → ➖ not-a-claim (evidence: The text is a prerequisite bullet point linking to the Pulumi Cloud signup page (https://app.pulumi.com/signup). This is a standard navigation/signup URL reference, not a falsifiable factual assertion about Pulumi's capabilities or behavio…; source: content/what-is/run-aws-s3-sync-with-dynamic-credentials.md L57)
  • L58 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "* The AWS CLI v2 installed locally" → ✅ verified (evidence: The URL https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html resolves to a live AWS page titled "Installing or updating to the latest version of the AWS CLI," which covers AWS CLI v2 installation. The page confirms…; source: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
  • L59 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "* An IAM role with OIDC trust configured for Pulumi (see Configuring OIDC between Pulumi and AWS), with the S3…" → 🤷 unverifiable (evidence: verification did not converge within 8 turns)
  • L70 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "Follow the browser prompt or paste an access token from https://app.pulumi.com/account/tokens." → ➖ not-a-claim (evidence: The text is a UI instruction directing users to a well-known Pulumi Cloud URL for account tokens. The URL https://app.pulumi.com/account/tokens is a standard, stable Pulumi Cloud endpoint for managing access tokens — this is a navigation…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L74 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "The IAM role trust policy for Pulumi OIDC integration must accept a JWT from api.pulumi.com/oidc." → 🤷 unverifiable (evidence: verification did not converge within 8 turns)
  • L76 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "### 3. Create a new Pulumi ESC environment" → ➖ not-a-claim (evidence: The text "### 3. Create a new Pulumi ESC environment" is a markdown section heading (step 3 in a numbered tutorial sequence). It is not a falsifiable assertion — it is a structural label describing the PR author's own tutorial design/pipel…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L78 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "In Pulumi Cloud, open your organization, click Environments, then Create environment. Name it something like aws-prod-env." → ➖ not-a-claim (evidence: The line at L78 is a UI navigation instruction written by the PR author as part of their own step-by-step guide: "In Pulumi Cloud, open your organization, click Environments, then Create environment. Name it something like `aws-pro…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L99 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "The fn::open::aws-login function exchanges the Pulumi-issued OIDC token for AWS STS credentials." → ✅ verified (evidence: The file at the specified location contains the exact sentence: "The fn::open::aws-login function exchanges the Pulumi-issued OIDC token for AWS STS credentials." This is consistent with the broader article description that "Pulumi ESC e…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L99 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "The environmentVariables block in a Pulumi ESC environment exposes credentials to any subprocess esc run starts." → ✅ verified (evidence: The file at the relevant line states verbatim: "The environmentVariables block exposes them to any subprocess esc run starts." This is an exact match to the claim.; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L143 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "S3-to-S3 sync executes server-side CopyObject calls; bytes do not transit the user's local machine." → ✅ verified (framing: strengthened — claim says "user's local machine" while source says "your laptop"; these are equivalent phrasings of the same concept; evidence: The file at L143 states: "S3-to-S3 sync executes server-side CopyObject calls; bytes do not transit your laptop." This is consistent with AWS CLI's documented behavior where bucket-to-bucket sync uses the S3 CopyObject API (a server-side…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L154 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "The IAM role needs s3:DeleteObject on the destination for aws s3 sync --delete to succeed." → ✅ verified (evidence: The file at line ~154 states: "The IAM role needs s3:DeleteObject on the destination for this to succeed." — directly following the --delete sync example. The IAM permissions table earlier also confirms: "With --delete | `... --delet…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L158 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "In CloudTrail, aws s3 sync upload events appear as PutObject, download events as GetObject, S3-to-S3 events as CopyObject, and --delete events as De…" → ✅ verified (evidence: The file at the "Verify in CloudTrail" section states: "In CloudTrail the events appear as PutObject(uploads),GetObject(downloads),CopyObject(S3-to-S3), orDeleteObject (--delete), each with userIdentity.type=AssumedRole` a…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L167 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "A KMS.AccessDenied error occurs when the bucket uses SSE-KMS and the IAM role cannot decrypt or wrap; the fix is to grant kms:Decrypt (for download) or km…" → ➖ not-a-claim (framing: The claim is a verbatim restatement of the PR author's own content in the file being introduced; it is not attributing a behavior to a third-party source in a…; evidence: The claim is a faithful description of content authored in this PR's own file (content/what-is/run-aws-s3-sync-with-dynamic-credentials.md`, Common errors table): "KMS.AccessDenied | Bucket uses SSE-KMS and role can't decrypt or wrap | Gr…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L176 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "aws s3 sync compares size and last-modified time only; two different files with the same size and mtime will not be detected as different." → ✅ verified (framing: strengthened — the source's broader statement ("not by content hash") directly proves the claim's specific case (same size + mtime → not detected as different); evidence: The file itself states: "aws s3 sync compares source and destination by file size and last-modified time — not by content hash." Since only size and mtime are compared, two files with identical size and mtime but different conten…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md)
  • L180 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "After git clone, local file mtimes are set to checkout time, which can cause aws s3 sync to keep re-uploading the same files." → ✅ verified (evidence: Git does not preserve original file modification times; after git clone, all checked-out files receive the checkout timestamp as their mtime. Since aws s3 sync compares by file size and last-modified time (as confirmed in the document:…; source: repo:content/what-is/run-aws-s3-sync-with-dynamic-credentials.md (sync comparison logic) + well-established git behavior (git does not preserve mtimes on checkout))
  • L188 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "Files above the multipart threshold (8 MB by default) upload in parallel parts when using aws s3 sync." → ✅ verified (evidence: Multiple AWS sources confirm the default multipart threshold is 8 MB. AWS re:Post states "The default value for multipart_threshold is 8 MB," and AWS CLI docs confirm that aws s3 sync (a high-level S3 command) automatically performs mult…; source: https://repost.aws/knowledge-center/s3-improve-transfer-sync-command)
  • L192 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "The maximum session duration for ESC-issued credentials is bounded by the IAM role's MaxSessionDuration attribute, which is 1 hour for OIDC by default." → ❌ contradicted (framing: shifted — the claim attributes the 1-hour default specifically to "OIDC," but the source states the 1-hour default applies to all IAM roles universally, not ju…; evidence: AWS docs confirm the default MaxSessionDuration for IAM roles is 1 hour generally: "If you do not specify a value, the default maximum of 1 hour is applied." This default is not specific to OIDC — it applies to all IAM roles regardless of…; source: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_update-role-settings.html)
  • L196 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "For cross-account aws s3 sync, the source bucket policy must allow s3:GetObject and s3:ListBucket to the role's ARN, and the destination policy must allo…" → ✅ verified (framing: strengthened — claim correctly splits the permissions by direction (source bucket: s3:GetObject + s3:ListBucket; destination bucket: s3:PutObject + optional s3…; evidence: (escalated from pass1) AWS re:Post and official AWS docs confirm that for cross-account S3 access, the source bucket policy must grant s3:GetObject and s3:ListBucket to the role's ARN, and the destination policy must grant `s3:P…; source: https://repost.aws/knowledge-center/cross-account-access-s3)
  • L200 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "The page cross-references /what-is/run-aws-sts-get-caller-identity-with-dynamic-credentials/ as an existing guide." → ✅ verified (evidence: The file content/what-is/run-aws-sts-get-caller-identity-with-dynamic-credentials.md exists in the repository and is a complete guide titled "Run 'aws sts get-caller-identity' using Dynamic Credentials", confirming the cross-referenced p…; source: repo:content/what-is/run-aws-sts-get-caller-identity-with-dynamic-credentials.md)
  • L200 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "The page cross-references /what-is/run-aws-sts-get-caller-identity-with-dynamic-credentials/ as an existing guide for running aws sts get-caller-identity w…" → ✅ verified (evidence: The file content/what-is/run-aws-sts-get-caller-identity-with-dynamic-credentials.md exists in the repo and is a complete guide titled "Run 'aws sts get-caller-identity' using Dynamic Credentials", confirming the cross-referenced page is…; source: repo:content/what-is/run-aws-sts-get-caller-identity-with-dynamic-credentials.md)
  • L204-205 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "* Configuring OIDC between Pulumi and AWS" → ❌ contradicted (evidence: The path /docs/esc/environments/configuring-oidc/aws/ does not exist in the repo. The content/docs/esc/environments/ directory contains no configuring-oidc subdirectory. The closest matching content for OIDC/AWS is at `content/docs/e…; source: gh api repos/pulumi/docs/contents/content/docs/esc/environments)
  • L206-207 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "The page cross-references /what-is/run-aws-s3-cp-with-dynamic-credentials/ as an existing guide." → ✅ verified (evidence: The file content/what-is/run-aws-s3-cp-with-dynamic-credentials.md exists in the repository with title "Run 'aws s3 cp' using Dynamic Credentials", confirming the cross-referenced guide is a real, existing page.; source: repo:content/what-is/run-aws-s3-cp-with-dynamic-credentials.md)
  • L206-210 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "The page cross-references /what-is/run-aws-s3-cp-with-dynamic-credentials/ as an existing guide for running aws s3 cp with dynamic credentials." → ✅ verified (evidence: The file content/what-is/run-aws-s3-cp-with-dynamic-credentials.md exists in the repository with the title "Run 'aws s3 cp' using Dynamic Credentials", confirming the cross-referenced page is a real, existing guide.; source: repo:content/what-is/run-aws-s3-cp-with-dynamic-credentials.md)
  • L209 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "The page cross-references /what-is/resolve-unable-to-locate-credentials/ as an existing guide." → ✅ verified (evidence: The file content/what-is/resolve-unable-to-locate-credentials.md exists in the repository with a matching title "Unable to locate credentials", confirming the cross-referenced guide at /what-is/resolve-unable-to-locate-credentials/ is…; source: repo:content/what-is/resolve-unable-to-locate-credentials.md)
  • L209-210 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "The page cross-references /what-is/resolve-list-buckets-expired-token/ as an existing guide." → ✅ verified (evidence: The file content/what-is/resolve-list-buckets-expired-token.md exists in the repo with a matching title "An error occurred (ExpiredToken) when calling the ListBuckets operation", confirming the cross-referenced guide is a real, existing…; source: repo:content/what-is/resolve-list-buckets-expired-token.md)
  • L212 in content/what-is/run-aws-s3-sync-with-dynamic-credentials.md "Join our community on Slack to discuss further, and let us know what you build." → 🤷 unverifiable (evidence: verification did not converge within 8 turns)

Claim verification reported errors — some verdicts may be incomplete; spot-check the affected claims in-review.

🚨 Outstanding in this PR

These must be resolved or refuted before merging.

  • [L28] content/what-is/run-aws-s3-sync-with-dynamic-credentials.md"ESC-issued sessions expire after the ESC environment's duration (1 hour by default)." — verdict: contradicted; framing: shifted — claim states "1 hour by default" but the source shows the ESC aws-login provider's duration defaults to 2 hours; source: aws-login provider docs ("duration … [Optional] - The duration of the role session. Defaults to 2 hours."). The 1h in this guide's own YAML example is an explicit override, not the provider default. Suggested fix:

    * **Time-bound.** Sessions expire after the ESC environment's `duration` (the `aws-login` provider defaults to 2 hours; this guide's YAML below overrides that to `1h`). A leaked token is useless almost immediately.
    

    The same "1h default" framing also feeds the L192 FAQ, which compounds the issue — see that finding for the matching fix.

  • [L40] content/what-is/run-aws-s3-sync-with-dynamic-credentials.md"Use --exact-timestamps to also re-upload files whose timestamps differ even by a second (useful when downloading; the AWS CLI's default treats older-source-than-destination as no-op)." — verdict: contradicted; framing: shifted — --exact-timestamps is a download-only (S3→local) flag per the AWS CLI docs ("When syncing from S3 to local, same-sized items will be ignored only when the timestamps match exactly"). The verb "re-upload" sends readers the wrong direction; the parenthetical "(useful when downloading)" hints at the truth but the main clause overrides it. Suggested fix:

    Use `--exact-timestamps` to also re-transfer same-sized files whose timestamps differ even by a second — note this flag only applies when syncing from S3 to local (downloads), per the AWS CLI reference. `--delete` removes objects from the destination that no longer exist in the source — be careful with this flag in production.
    
  • [L192] content/what-is/run-aws-s3-sync-with-dynamic-credentials.md"The maximum is bounded by the IAM role's MaxSessionDuration attribute (1 hour for OIDC by default)." — verdict: contradicted; framing: shifted — per the AWS docs, the 1-hour MaxSessionDuration default applies to all IAM roles, not specifically to OIDC-trusted ones ("If you do not specify a value, the default maximum of 1 hour is applied"). Combined with the L28 fix above, this whole paragraph also needs to acknowledge that the aws-login provider's 2h default already exceeds the role's 1h cap, which is precisely why the YAML in this guide sets duration: 1h. Suggested fix:

    By default, the `aws-login` provider requests a 2-hour session, but the actual ceiling is the IAM role's `MaxSessionDuration` — which defaults to 1 hour for any newly created IAM role. The YAML in this guide sets `duration: 1h` so the request fits inside that cap. For multi-hour syncs, raise `MaxSessionDuration` on the role and increase `duration` to match.
    

⚠️ Low-confidence

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

  • [L3] content/what-is/run-aws-s3-sync-with-dynamic-credentials.md"Pulumi ESC issues short-lived, OIDC-issued credentials (no static AWS keys), scoped by IAM role, and fully auditable in CloudTrail." — verdict: unverifiable; evidence: verify-claims.py hit an HTTP 429 rate limit. Spot-check: the claim is a verbatim restatement of how fn::open::aws-login is documented to behave on the aws-login provider page, and matches the body of this guide. No author action required unless you'd like to add an inline link to the aws-login provider docs to back the meta_desc claim.

  • [L10] content/what-is/run-aws-s3-sync-with-dynamic-credentials.md"Pulumi ESC exchanges a Pulumi-issued OIDC token for an AWS STS session, scoped to a specific IAM role, expiring automatically." — verdict: unverifiable; evidence: HTTP 429 rate limit. Spot-check: this is the documented OIDC token-exchange flow; the cross-referenced Configuring OIDC between Pulumi and AWS guide describes the same mechanism. No author action required.

  • [L40] content/what-is/run-aws-s3-sync-with-dynamic-credentials.md"The AWS CLI's default for aws s3 sync treats an older-source-than-destination timestamp as a no-op." — verdict: unverifiable; evidence: HTTP 429 rate limit. Spot-check: this is implied by the L34/L36-38 verified claims (sync only copies when source is newer or sizes differ). If you reword the surrounding sentence per the L40 contradicted fix above, this becomes moot.

  • [L212] content/what-is/run-aws-s3-sync-with-dynamic-credentials.md"Join our community on Slack to discuss further, and let us know what you build." — verdict: unverifiable; evidence: verification did not converge within 8 turns. Spot-check: slack.pulumi.com is Pulumi's documented community Slack invite URL and is used as the canonical link in many published Pulumi docs. No author action required.

Style findings

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

  • line 24: [style] difficulty qualifier — Avoid difficulty qualifier 'easily' -- it judges difficulty for the reader (STYLE-GUIDE.md §Inclusive Language).
  • line 24: [style] wordiness — 'all of' is too wordy.
  • line 51: [style] wordiness — 'enumerate' is too wordy.
  • line 59: [style] directional reference — Directional reference ('from the table above') -- link directly to the target (an #anchor or relative path) rather than relying on 'above'/'below' (STYLE-GUIDE.md §Inclusive Language).
  • line 78: [style] substitution — Use 'select' instead of 'click' (STYLE-GUIDE.md).
  • line 192: [style] wordiness — 'maximum' is too wordy.
  • line 194: [style] first person — Avoid first-person pronouns such as ' I '.
  • line 198: [style] first person — Avoid first-person pronouns such as ' I '.

📋 Triaged verifier findings

I double-checked these and realized they weren't real findings — click to expand
  • [L56] content/what-is/run-aws-s3-sync-with-dynamic-credentials.md" The Pulumi CLI and Pulumi ESC CLI installed"* — verdict: unverifiable. Mis-sourced: verifier ran out of turns, but both URLs resolve in the repo — /docs/install/ is the canonical install page (content/docs/install/_index.md) and /docs/install/esc/ is a declared alias for /docs/esc/cli/download-install/.

  • [L59] content/what-is/run-aws-s3-sync-with-dynamic-credentials.md" An IAM role with OIDC trust configured for Pulumi (see Configuring OIDC between Pulumi and AWS)…"* — verdict: unverifiable. Mis-sourced: verifier ran out of turns, but /docs/esc/environments/configuring-oidc/aws/ is a declared alias on content/docs/esc/guides/configuring-oidc/aws.md (aliases: - /docs/esc/environments/configuring-oidc/aws/), so the link works at runtime.

  • [L74] content/what-is/run-aws-s3-sync-with-dynamic-credentials.md"The IAM role trust policy for Pulumi OIDC integration must accept a JWT from api.pulumi.com/oidc." — verdict: unverifiable. Mis-sourced: verifier ran out of turns, but api.pulumi.com/oidc is the documented Pulumi OIDC issuer URL — see content/docs/esc/guides/configuring-oidc/aws.md L30 ("For the Provider URL, provide the following URL: https://api.pulumi.com/oidc").

  • [L204-205] content/what-is/run-aws-s3-sync-with-dynamic-credentials.md" Configuring OIDC between Pulumi and AWS"* — verdict: contradicted. Spurious: the verifier checked for a file at content/docs/esc/environments/configuring-oidc/ but missed that content/docs/esc/guides/configuring-oidc/aws.md declares /docs/esc/environments/configuring-oidc/aws/ as an alias (L12-13). The link works.

💡 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:51:31Z — Caught 3 duration/flag-direction inaccuracies (aws-login 2h vs claimed 1h default, --exact-timestamps is download-only, MaxSessionDuration 1h default is not OIDC-specific); triaged 4 link/issuer claims the verifier missed due to aliases or turn-exhaustion. (48e5cd5)

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