Terraform does not apply changes to the wri account. Any modification to
tof-output's bucket policy is done manually via this runbook, using the
statement JSON that Terraform emits as an output.
Run this after any of:
- First-time stand-up of the
land-researchenv stack (grant the new Lambda role). - A change to
var.output_bucket_prefixesorvar.role_namein land-research. - A re-created Lambda role (new ARN).
- A rollback to a prior policy snapshot.
<wri_aws_profile>profile (or whicheverwriprofile you use) with current credentials — verify withaws sts get-caller-identity --profile <wri_aws_profile>.jqinstalled (brew install jq).- Most recent
terraform applyoninfra/terraform/envs/land-researchhas succeeded (it emits the statement JSON you'll use below). uvavailable (for runningscripts/merge_tof_output_policy.py).
SNAPSHOT=~/tof-output-policy-snapshot-$(date +%F-%H%M).json
aws s3api get-bucket-policy --bucket tof-output --profile <wri_aws_profile> \
| jq -r .Policy | jq . > "$SNAPSHOT"
echo "Snapshot: $SNAPSHOT"If get-bucket-policy returns NoSuchBucketPolicy, the bucket has no policy
yet — create an empty stub:
echo '{"Version":"2012-10-17","Statement":[]}' | jq . > "$SNAPSHOT"Do not commit $SNAPSHOT to git. Keep it in your home directory or a
secure notes app.
terraform -chdir=infra/terraform/envs/land-research output -raw cross_account_policy_statements_json \
| jq . > /tmp/tf-cross-account-statements.json
cat /tmp/tf-cross-account-statements.jsonYou should see a JSON array with two Sids — CrossAccountListBucket and
CrossAccountReadWrite — each granting the Lambda role (lithops-execution-role)
the relevant S3 actions on tof-output (or the scoped prefixes, if you set
output_bucket_prefixes).
Sanity check the role ARN:
terraform -chdir=infra/terraform/envs/land-research output -raw lambda_role_arn
# should end in :role/lithops-execution-roleuv run python scripts/merge_tof_output_policy.py \
--current "$SNAPSHOT" \
--append /tmp/tf-cross-account-statements.json \
--out /tmp/tof-output-policy-merged.jsonThe script:
- Dedupes by
Sid— re-running the runbook replaces old copies of the Lambda-role statements instead of stacking duplicates. - Validates the merged policy is under S3's 20 KB bucket-policy size limit.
- Prints an
Added/Replaced/Untouchedsummary.
Any pre-existing statement whose Sid does not start with CrossAccount…
is preserved verbatim in the Untouched list.
diff <(jq -S . "$SNAPSHOT") <(jq -S . /tmp/tof-output-policy-merged.json) | lessExpect only additions (> lines). If you see removals (< lines) for
statements you didn't intend to touch, stop. Investigate:
- Is the removed SID a legacy statement that this runbook should preserve? Re-run Step 1 on a freshly fetched policy and re-check.
- Did someone modify the policy between the snapshot and now? Re-snapshot.
aws s3api put-bucket-policy \
--bucket tof-output \
--policy file:///tmp/tof-output-policy-merged.json \
--profile <wri_aws_profile>No output on success. AWS rejects the call if the JSON is malformed or violates policy-size limits.
Count statements:
aws s3api get-bucket-policy --bucket tof-output --profile <wri_aws_profile> \
| jq -r .Policy | jq '.Statement | length'Expect: (count from $SNAPSHOT) + (new-or-replaced statements from Step 3).
Smoke test from the land-research side — requires <land_research_aws_profile> profile and
AWS_PROFILE=<land_research_aws_profile>:
AWS_PROFILE=<land_research_aws_profile> LITHOPS_ENV=land-research \
uv run python scripts/predict_lambda_smoke.pyThe smoke script exercises both read (ARD) and write (predicted TIF) against
tof-output. SMOKE TEST PASSED confirms the grant works end-to-end.
If the new policy breaks anything:
aws s3api put-bucket-policy \
--bucket tof-output \
--policy file://"$SNAPSHOT" \
--profile <wri_aws_profile>Re-verify with the smoke script or a manual aws s3 ls probe.
| Symptom | Likely cause | Fix |
|---|---|---|
NoSuchBucketPolicy on Step 1 |
Bucket has never had a policy | Use the empty-stub snapshot shown in Step 1. |
An error occurred (AccessDenied) on put-bucket-policy |
<wri_aws_profile> profile lacks s3:PutBucketPolicy on tof-output |
Coordinate with the bucket owner to get the permission; do not proceed. |
Merge script exits merged policy is N bytes, exceeds … |
Cumulative statements too large | Audit the Untouched list — clean up stale statements in a separate manual edit before re-merging. |
| Smoke test fails with 403 after apply | Role ARN in the applied policy doesn't match the current Lambda role | Re-run Step 2 against a freshly applied land-research stack, confirm the role ARN output matches aws lambda get-function-configuration --function-name ttc-predict-dev --query Role. |