Skip to content

fix(deployer-iam): split EFS mount targets out of tag-conditioned statement; add ServiceLinkedRoles#2

Open
EnriqueCanals wants to merge 2 commits into
mainfrom
fix/deployer-iam-efs-slr
Open

fix(deployer-iam): split EFS mount targets out of tag-conditioned statement; add ServiceLinkedRoles#2
EnriqueCanals wants to merge 2 commits into
mainfrom
fix/deployer-iam-efs-slr

Conversation

@EnriqueCanals

Copy link
Copy Markdown
Member

Two CloudFormation deployment failures caused by deployer IAM policy gaps:

Fix 1 — EFS mount targets can't be tag-conditioned: The single EFSManage statement bundled file-system + mount-target actions under aws:ResourceTag/Name=bclaw-data. Mount targets are untaggable, so the condition always evaluated to false and implicitly denied CreateMountTarget (403). Split into EFSManageTagged (tag-conditioned: DeleteFileSystem, Create/DeleteAccessPoint, CreateTags, TagResource) and EFSMountTargets (unconditional Resource:*: Create/Delete/DescribeMountTargets).

Fix 2 — Missing iam:CreateServiceLinkedRole:
When a service-linked role doesn't exist, AWS auto-creates it using the caller's credentials. The deployer had no iam:CreateServiceLinkedRole at all, so ECS service creation failed with 400 ("Unable to assume the service linked role"). Added a ServiceLinkedRoles statement scoped via iam:AWSServiceName to three SLRs: AWSServiceRoleForAmazonElasticFileSystem (EFS), AWSServiceRoleForECS (ECS), AWSServiceRoleForApplicationAutoScaling_ECSService (autoscaling). Note the naming inconsistency: ECS SLR is AWSServiceRoleForECS (no "Amazon" prefix), EFS SLR is AWSServiceRoleForAmazonElasticFileSystem (with).

Docs: README policy tables, setup-bclaw SKILL prerequisites (factual, no narrative per AGENTS.md skill rules), and references/iam-and-template-lessons.md (full discovery narrative + diagnostic approach — author-only, not shipped).

Test: test/policy.test.mjs (TDD — written RED first, 6 assertions guarding the split, the unconditional mount-target statement, and the SLR scope/naming).

…tement; add ServiceLinkedRoles

Two CloudFormation deployment failures caused by deployer IAM policy gaps:

Fix 1 — EFS mount targets can't be tag-conditioned:
The single EFSManage statement bundled file-system + mount-target actions
under aws:ResourceTag/Name=bclaw-data. Mount targets are untaggable, so the
condition always evaluated to false and implicitly denied CreateMountTarget
(403). Split into EFSManageTagged (tag-conditioned: DeleteFileSystem,
Create/DeleteAccessPoint, CreateTags, TagResource) and EFSMountTargets
(unconditional Resource:*: Create/Delete/DescribeMountTargets).

Fix 2 — Missing iam:CreateServiceLinkedRole:
When a service-linked role doesn't exist, AWS auto-creates it using the
caller's credentials. The deployer had no iam:CreateServiceLinkedRole at all,
so ECS service creation failed with 400 ("Unable to assume the service
linked role"). Added a ServiceLinkedRoles statement scoped via
iam:AWSServiceName to three SLRs: AWSServiceRoleForAmazonElasticFileSystem
(EFS), AWSServiceRoleForECS (ECS), AWSServiceRoleForApplicationAutoScaling_ECSService
(autoscaling). Note the naming inconsistency: ECS SLR is AWSServiceRoleForECS
(no "Amazon" prefix), EFS SLR is AWSServiceRoleForAmazonElasticFileSystem (with).

Docs: README policy tables, setup-bclaw SKILL prerequisites (factual, no
narrative per AGENTS.md skill rules), and references/iam-and-template-lessons.md
(full discovery narrative + diagnostic approach — author-only, not shipped).

Test: test/policy.test.mjs (TDD — written RED first, 6 assertions guarding
the split, the unconditional mount-target statement, and the SLR scope/naming).
@EnriqueCanals EnriqueCanals requested a review from capotej July 9, 2026 19:06
Comment thread template/bclaw-deploy-policy.json Outdated
"Sid": "EFSMountTargets",
"Effect": "Allow",
"Action": [
"elasticfilesystem:CreateMountTarget", "elasticfilesystem:DeleteMountTarget",

@capotej capotej Jul 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

confirm we actually need elasticfilesystem:DeleteMountTarget for teardown (this may get cleaned up automatically when the stack gets torn down)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Mount targets are CloudFormation-owned resources (AWS::EFS::MountTarget, default DeletionPolicy: Delete), so CloudFormation's stack-delete handler calls DeleteMountTarget using the deployer's credentials. The deployer never calls it directly — it's only consumed by the CFN handler.

Without the permission, every stack delete would 403 on the mount targets and force the FORCE_DELETE_STACK recovery path. The end state would be the same (Phase 3's delete-file-system auto-cleans any surviving mount targets), but the normal clean-delete path would be broken on every teardown.

That said, the latest commit (97353af) folds DeleteMountTarget back into the tag-conditioned EFSManage statement rather than leaving it unconditional. The IAM definition confirms mount-target actions evaluate against the file-system resource type (which supports aws:ResourceTag), so the tag condition scopes it to the claw's own file system. The original 403 may have been from the missing iam:CreateServiceLinkedRole (the other fix on this branch), not the tag condition. A live deploy is the right place to confirm, so I'll give that another test with a fresh bclaw install.

…ioned EFSManage

The AWS Service Authorization Reference (checked via policy_sentry's IAM
definition database) confirms there is NO mount-target resource type in
EFS IAM. All three mount-target actions (CreateMountTarget,
DeleteMountTarget, DescribeMountTargets) evaluate against the file-system
resource type, which DOES support aws:ResourceTag. The EFS file system
IS tagged Name=bclaw-data, so the tag condition scopes mount-target
actions correctly -- the original single EFSManage statement was right.

The previous commit split EFSManage into EFSManageTagged (tag-conditioned)
+ EFSMountTargets (unconditional Resource: *) based on the assumption that
mount targets are untaggable. That diagnosis was likely wrong -- the real
deploy failure was probably caused by the missing iam:CreateServiceLinkedRole
(also fixed in this branch), not the tag condition.

Changes:
- Revert to single EFSManage statement with all EFS manage/mutate actions
  under aws:ResourceTag/Name = bclaw-data
- Remove the EFSMountTargets statement (tighter: no unconditional Resource: *)
- Update tests to assert the unified structure
- Update README, setup/teardown skills, and references docs
- Add tmp/ to .gitignore (scratch dir from previous session)

Not verified with a real deploy -- see references/iam-and-template-lessons.md
for the caveat. Needs a live deploy before merging.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants