Repository navigation
Grant the DataOps deploy role least-privilege access to the mailing-export bucket policy #118
Description
Grant the DataOps deploy role least-privilege access to the mailing-export bucket policy
Status: pending
Tags: bug, infra, P1
Depends on: None
Blocks: #108
Scope
Update the authoritative GitHub Actions OIDC deploy-role template at ../aws-infra/sandbox/dataops/template.github-actions.yaml so CloudFormation can create, read, update, and remove only the AWS::S3::BucketPolicy attached to #108's MailingExportsBucket in the dataops-v1 application stack.
Add one dedicated IAM statement; do not extend the existing broad bucket-administration statement. Its exact permission boundary is:
- actions:
s3:GetBucketPolicy,s3:PutBucketPolicy, ands3:DeleteBucketPolicyonly; - resource:
arn:${AWS::Partition}:s3:::${FullDocsStackName}-mailingexportsbucket-*only (the CloudFormation physical-name pattern for logical resourceMailingExportsBucket); - no
Resource: "*", object ARN (/*), object-data action, access-point ARN, unrelated DataOps bucket, cross-region resource, or additional S3 permission.
Keep source and live infrastructure aligned. After the source change passes review and the normal issue gates, a credentialed AWS operator must apply that exact template to the dataops-github-actions stack in eu-west-1 with CAPABILITY_NAMED_IAM. The operator must review the change set before execution, confirm it changes only the deploy role's inline policy as expected, and reconcile the resulting live role policy and stack template against committed source. Do not hand-edit the IAM role in the console or with put-role-policy.
This issue is an infrastructure prerequisite for deploying #108: #118 can be implemented and source-tested independently, but #108's MailingExportsBucketPolicy cannot be deployed successfully until the live dataops-github-actions stack contains this permission. Do not mark #108 deployment unblocked from a source-only commit.
Acceptance Criteria
- The authoritative
../aws-infra/sandbox/dataops/template.github-actions.yamlcontains a dedicated mailing-export bucket-policy statement with exactlys3:GetBucketPolicy,s3:PutBucketPolicy, ands3:DeleteBucketPolicyonarn:${AWS::Partition}:s3:::${FullDocsStackName}-mailingexportsbucket-*. - Automated/static tests prove the statement includes all three required actions and reject
Resource: "*", object resources, unrelated bucket patterns, and additional S3 actions in that statement. - Existing deploy-role permissions, OIDC trust restrictions, parameters, and unrelated
../aws-infraresources are unchanged; the dirtysandbox/datamailer/work and other user-owned changes are neither modified nor included. - The template passes local CloudFormation lint/static validation and AWS
cloudformation validate-templateineu-west-1; validation output contains no secrets or private operational data. - [HUMAN] A credentialed AWS operator creates and reviews a change set for
dataops-github-actions, verifies the only intended live change is the deploy role inline-policy addition, then executes it withCAPABILITY_NAMED_IAMand confirms the stack reachesUPDATE_COMPLETE(orCREATE_COMPLETEif reconstructed). - [HUMAN] Post-apply reconciliation confirms (a) the live stack template matches the committed authoritative source for this statement, (b) the live deploy-role policy has exactly the three scoped bucket-policy actions/resource above, and (c) there was no console/direct-IAM drift.
- After the live prerequisite is reconciled, Automate private Mailchimp account exports and attach them to recurring work #108's normal GitHub Actions deployment can create/update
MailingExportsBucketPolicywithoutAccessDenied; no real Mailchimp export, archive download, or personal-data handling is required for this check. - Tester records exact commands, exit codes, validation/test counts where available, sanitized change-set/stack status, and source/live reconciliation evidence in Grant the DataOps deploy role least-privilege access to the mailing-export bucket policy #118 without posting account IDs, role ARNs, policy IDs, credentials, or other private operational data.
Test Scenarios
Scenario: Exact source-policy boundary
Given: the authoritative deploy-role template
When: the focused policy test inspects the mailing-export statement
Then: only the three bucket-policy actions are allowed, only the generated mailing-export bucket-name pattern is targeted, and object/unrelated/wildcard resources are absent
Scenario: Reviewed live apply
Given: the approved source template and the existing dataops-github-actions stack in eu-west-1
When: a credentialed operator creates and reviews the change set before executing it
Then: only the intended inline-policy permission changes, the stack completes successfully, and source/live reconciliation shows no direct-IAM drift
Scenario: #108 deployment unblock
Given: the reconciled live deploy role and #108's application template containing MailingExportsBucketPolicy
When: the standard main GitHub Actions deployment updates dataops-v1
Then: CloudFormation manages that bucket policy without an S3 bucket-policy authorization failure
Scenario: Failed apply and rollback
Given: validation, change-set review, stack update, or #108 deployment fails
When: the failure is inspected
Then: the operator preserves CloudFormation event evidence, allows/initiates stack rollback as appropriate, does not broaden the IAM resource/action scope as a shortcut, and returns the source/live state to the last known committed/reconciled version before retrying
Required Verification
- A focused automated test over
../aws-infra/sandbox/dataops/template.github-actions.yamlfor exact action and resource equality, including negative assertions for wildcard/object/unrelated S3 access. - Local CloudFormation lint/static validation available in
../aws-infra; if no repository-standard runner exists, use a non-mutating parser/linter and document the command. aws cloudformation validate-template --template-body file://template.github-actions.yaml --region eu-west-1from../aws-infra/sandbox/dataops/using the credentialed operator session.- A reviewed CloudFormation change set followed by the documented stack apply for
dataops-github-actions; record only sanitized summaries. - Read-only post-apply checks using
cloudformation get-template/describe-stacksand the live role policy, compared structurally with committed source. - The normal Automate private Mailchimp account exports and attach them to recurring work #108/DataOps deployment and On-Call monitoring after both issues have passed their required pipeline gates.
Rollback
- If the
dataops-github-actionsupdate fails, inspect sanitized stack events and let CloudFormation roll back to the prior stable role policy; do not patch IAM directly. - If the permission must be withdrawn, first remove or roll back Automate private Mailchimp account exports and attach them to recurring work #108's
MailingExportsBucketPolicydependency, then revert the dedicated statement in source and apply the reverteddataops-github-actionstemplate through CloudFormation. - Reconcile committed source, the live stack template, and the live role policy after rollback. A source revert without a stack apply is not a completed rollback.
Human / AWS Boundary
- Software Engineer owns the source-template change and automated tests only. Tester and PM review source and non-mutating validation evidence.
- Creating/executing the IAM change set, applying
dataops-github-actions, and exercising the live deploy role are AWS writes and require the explicitly authorized credentialed operator ([HUMAN]). - Read-only AWS inspection may support reconciliation, but no agent should expose account identifiers, credentials, private policy metadata, or Mailchimp/export data in this public issue.
../aws-infrahas no self-deploying CI/CD path; committing or pushing the template alone does not update the role.
Out of Scope
- Implementing, testing, or accepting Automate private Mailchimp account exports and attach them to recurring work #108's Mailchimp connector, API, portal UI, storage behavior, or real export.
- Adding object read/write permissions, changing Lambda runtime permissions, changing the mailing-export bucket policy itself, or modifying any other S3 bucket's permissions.
- Refactoring existing broad deploy-role statements or resolving unrelated
aws-infradrift. - Modifying
DataTalksClub/dataopsapplication code,../dtc-operations,../datatasks, or../podcast-assistant.
Activity
[-]Allow DataOps CI to manage the mailing-export bucket policy[/-][+]Grant the DataOps deploy role least-privilege access to the mailing-export bucket policy[/+]alexeygrigorev commentedon Jul 14, 2026
GROOMED
Issue #118 is now agent-ready as the least-privilege infrastructure prerequisite for #108.
Key decisions:
s3:GetBucketPolicy,s3:PutBucketPolicy, ands3:DeleteBucketPolicy.arn:${AWS::Partition}:s3:::${FullDocsStackName}-mailingexportsbucket-*; no object ARN, wildcard resource, unrelated bucket, or extra S3 action.dataops-github-actionsstack is applied and reconciled.[HUMAN];aws-infradoes not self-deploy.The existing dirty
sandbox/datamailer/and other user-owned changes are explicitly outside scope.alexeygrigorev commentedon Jul 14, 2026
Software Engineer handoff — ready for Tester
Implemented the groomed source-only portion in an isolated
aws-infraworktree on branchissue-118-mailing-bucket-policy, based on currentorigin/main(a457ece). No commit, push, change set, stack update, or direct IAM write was performed.Changed files
sandbox/dataops/template.github-actions.yamlMailingExportsBucketPolicystatement.s3:GetBucketPolicy,s3:PutBucketPolicy, ands3:DeleteBucketPolicy.arn:${AWS::Partition}:s3:::${FullDocsStackName}-mailingexportsbucket-*.sandbox/dataops/tests/test_template_github_actions.pyVerification
python3 -m unittest discover -s sandbox/dataops/tests -v— PASS, 7 tests.sam validate --lint --template-file sandbox/dataops/template.github-actions.yaml— PASS; valid SAM/CloudFormation template.aws cloudformation validate-template --template-body file://template.github-actions.yaml --region eu-west-1 --query 'Description' --output textfromsandbox/dataops/— PASS; returned the expected sanitized template description.git diff --check— PASS.aws-infracheckout, including itssandbox/datamailer/and template changes, was not modified.Acceptance status
[HUMAN]and intentionally not attempted before Tester/PM acceptance.alexeygrigorev commentedon Jul 14, 2026
TEST PASS — source/non-mutating Tester gate
Independent review verified the implementation in the isolated
aws-infra-118worktree. No file was edited, committed, pushed, deployed, or applied during testing.Commands and results
python3 -m unittest discover -s sandbox/dataops/tests -v— exit 0; 7/7 passed.sam validate --lint --template-file sandbox/dataops/template.github-actions.yaml— exit 0; valid SAM/CloudFormation template.sandbox/dataops/:aws cloudformation validate-template --template-body file://template.github-actions.yaml --region eu-west-1 --query 'Description' --output text— exit 0; returned the expected sanitized template description.git diff --check— exit 0.sandbox/datamailer/paths.describe-stacks,get-template, andget-role-policy, with structural-only output) — exit 0:UPDATE_COMPLETE;Note: an initial
validate-templateinvocation from repository root used the issue-relative filename and exited nonzero because that file is relative tosandbox/dataops/. The required command was rerun from the specified directory and passed as recorded above.Acceptance criteria
MailingExportsBucketPolicystatement has exactlys3:GetBucketPolicy,s3:PutBucketPolicy, ands3:DeleteBucketPolicy, withEffect: Allow, on onlyarn:${AWS::Partition}:s3:::${FullDocsStackName}-mailingexportsbucket-*.sandbox/datamailer/work are absent from the diff.AccessDenied.No UI changed, so screenshots are not applicable.
The implementation is approved for PM review and source commit. This verdict does not approve or claim the human AWS apply, live reconciliation, or #108 deployment unblock.
alexeygrigorev commentedon Jul 14, 2026
ACCEPTED — agent-verifiable source/non-mutating scope
PM final acceptance passes for the implementation that agents can safely verify.
issue-118-mailing-bucket-policyworktree is based onorigin/mainata457eceand contains only the authoritative template modification plus its focused test file.MailingExportsBucketPolicystatement hasEffect: Allow, exactlys3:GetBucketPolicy,s3:PutBucketPolicy, ands3:DeleteBucketPolicy, and exactlyarn:${AWS::Partition}:s3:::${FullDocsStackName}-mailingexportsbucket-*.sandbox/datamailer/checkout is outside this worktree and outside the candidate change.validate-templateineu-west-1, andgit diff --check. No UI changed, so screenshots are not applicable.dataops-github-actionsstack asUPDATE_COMPLETE, with 0 matching statements in both its live template and deploy-role policy. This is the expected pre-apply state, not live acceptance.The Software Engineer may now make the focused source commit. Because human criteria remain, the commit must reference rather than close #118, the
humanlabel must be added, and the issue must remain open.Still pending
[HUMAN]:dataops-github-actions, verify only the intended inline-policy addition, and execute it withCAPABILITY_NAMED_IAM.MailingExportsBucketPolicywithoutAccessDenied.No AWS write, commit, push, or deployment was performed during PM acceptance.
alexeygrigorev commentedon Jul 14, 2026
Human AWS gate remaining — issue stays open
Accepted source commit:
7f69ed581654284b11660fd0edcb30c54182373dinDataTalksClub/aws-infra(not yet pushed at the time of this comment). The source change is complete, but #118 must remain open until the committed template is applied and reconciled by a credentialed operator.Run these steps only after the accepted commit has been pushed to
aws-inframain, from a clean checkout of that exact commit insandbox/dataops/. Do not useiam put-role-policy, the AWS console policy editor, or any direct IAM write.Change-set review and apply
git status --shortis empty andgit rev-parse HEADis the accepted commit on the sharedmainhistory.python3 -m unittest discover -s tests -vsam validate --lint --template-file template.github-actions.yamlaws cloudformation validate-template --template-body file://template.github-actions.yaml --region eu-west-1dataops-github-actionswith--template-body file://template.github-actions.yaml,--capabilities CAPABILITY_NAMED_IAM,--region eu-west-1, andUsePreviousValue=truefor all existing parameters:GitHubOwner,GitHubRepo,GitHubBranch,DeployRoleName,FullDocsStackName,FullDocsRegion, andSamArtifactBucketName.aws cloudformation wait change-set-create-complete --stack-name dataops-github-actions --change-set-name <name> --region eu-west-1.aws cloudformation describe-change-set --stack-name dataops-github-actions --change-set-name <name> --region eu-west-1before execution. The only intended resource change is an in-placeModifyofGitHubActionsDeployRolefor the inline-policy addition; there must be no replacement, parameter change, trust-policy change, unrelated resource change, or additional IAM/S3 permission. Record only a sanitized summary in this public issue.aws cloudformation execute-change-set --stack-name dataops-github-actions --change-set-name <name> --region eu-west-1, then wait withaws cloudformation wait stack-update-complete --stack-name dataops-github-actions --region eu-west-1.aws cloudformation describe-stacks --stack-name dataops-github-actions --region eu-west-1 --query 'Stacks[0].StackStatus' --output textreturnsUPDATE_COMPLETE. Do not post account IDs, role ARNs, policy IDs, or raw policy documents.Source/live reconciliation
aws cloudformation get-template --stack-name dataops-github-actions --region eu-west-1and compare theMailingExportsBucketPolicystatement structurally with committedtemplate.github-actions.yaml.aws iam get-role-policy --role-name <role-name> --policy-name dataops-v1-deploy.s3:GetBucketPolicy,s3:PutBucketPolicy,s3:DeleteBucketPolicy;arn:${AWS::Partition}:s3:::${FullDocsStackName}-mailingexportsbucket-*after CloudFormation substitution;mainGitHub Actions deployment and verify CloudFormation can manageMailingExportsBucketPolicywithoutAccessDenied.Rollback checklist
describe-stack-eventsevidence and allow/wait for CloudFormation rollback to the prior stable state; do not broaden permissions or patch IAM directly.UPDATE_ROLLBACK_COMPLETE, reconcile the live template and role policy with the last known committed/applied version before retrying.MailingExportsBucketPolicy, revert this dedicated statement inaws-infrasource, review a new CloudFormation UPDATE change set, and apply the revert through CloudFormation withCAPABILITY_NAMED_IAM.alexeygrigorev commentedon Jul 14, 2026
ON-CALL / CREDENTIALED AWS APPLY — PASS
The accepted
aws-infrasource commit7f69ed581654284b11660fd0edcb30c54182373dis onDataTalksClub/aws-inframainand was applied through CloudFormation. No direct IAM or console edit was used.Preflight and source validation (all exit 0):
HEADandorigin/mainboth matched.python3 -m unittest discover -s tests -v— 7/7 passed.sam validate --lint --template-file template.github-actions.yaml— valid.aws cloudformation validate-template --template-body file://template.github-actions.yaml --region eu-west-1— valid; sanitized description matched.git diff --check— passed.DETECTION_COMPLETE,IN_SYNC.Reviewed change set:
CREATE_COMPLETE; execution statusAVAILABLEbefore execution.CAPABILITY_NAMED_IAM.ModifyofGitHubActionsDeployRole(AWS::IAM::Role),Replacement=False, property scope only.Policies, static/direct modification,RequiresRecreation=Never.MailingExportsBucketPolicyabsent.Apply and post-apply reconciliation:
UPDATE_COMPLETEand no failed stack events were present.7f69ed5.MailingExportsBucketPolicystatement.s3:GetBucketPolicy,s3:PutBucketPolicy, ands3:DeleteBucketPolicy; its resolved resource is exactly the expected mailing-export bucket-name pattern, is bucket-only, and has no object suffix, unrelated bucket, wildcard resource, or extra action.IN_SYNC, confirming no direct-IAM/console drift.The two
[HUMAN]AWS criteria are satisfied and #108 is now infrastructure-unblocked. #108 must still pass its own normalmainGitHub Actions deployment and confirmMailingExportsBucketPolicyis managed withoutAccessDenied; this report does not claim that separate application deployment or a real Mailchimp export occurred.ON-CALL DONE: source/live reconciled; prerequisite applied successfully
alexeygrigorev commentedon Jul 14, 2026
Closed after the reviewed CloudFormation apply and source/live reconciliation completed successfully. #108 is infrastructure-unblocked; its application deployment and real-provider human verification remain tracked there.