insolvia-deploy-role-permissions
How to change what the Insolvia CI deploy role is allowed to do — and why those changes can't be applied by CI. Use this WHENEVER a change touches the GitHub Actions deploy role's IAM permissions: edi
SKILL.md.claude/skills/insolvia-deploy-role-permissions/SKILL.md
GitHubChanging the CI deploy role's permissions (Insolvia)
The rule in one line
The deploy role is not in this repo, and CI cannot grant itself permissions.
When an insolvia deploy fails on an IAM AccessDenied, the fix is a PR to
insolvia-ai/insolvia-services
editing its
infra/envs/ci-trust/main.tf,
merged there, then a human runs that repo's
scripts/apply-ci-trust.sh
from the insolvia-services checkout (../insolvia-services). Then re-run the
failed deploy here.
The role (insolvia-shared-deploy-role) trusts both repositories; the
staging seed role trusts this one only. Both moved with the shared
infrastructure on 2026-10-11 —
ADR 0025.
Why a human, and why "just merge it" is wrong
The ci-trust root carries an explicit Deny (DenySelfPrivilegeEscalation)
on the role modifying its own policy, so an apply run as the deploy role
fails by design, and there is deliberately no workflow that applies it. A
privilege change to the pipeline takes effect only when someone holding admin
applies it consciously. The full reasoning is insolvia-services'
docs/runbooks/aws-bootstrap.md
§ "The ci-trust anchor".
So when you see the signal, tell the user: PR to insolvia-services → merge →
run scripts/apply-ci-trust.sh there → re-run the deploy here. Nothing in
this repo's diff can fix it.
Getting the grant right first time
Each attempt costs a cross-repo round-trip, so read the error for the exact
<service>:<Action> and the resource it was evaluated against, and grant
the coherent set at once. The traps that have cost round-trips before:
lambda:CreateEventSourceMappingis fenced by thelambda:FunctionArncondition (function:insolvia-*); the by-UUID mapping actions (Get/Update/DeleteEventSourceMapping,ListTags,Tag/UntagResource) needresources = ["*"]— aFunctionArncondition or anevent-source-mapping:*resource both deny them. Trust the deny'son resource: *over the Service Authorization Reference.- A SecureString SSM parameter needs KMS too (
kms:Encrypt/Decryptunderkms:ViaService = ssm.<region>.amazonaws.com), not onlyssm:*. - A rename that moves a resource out of a fenced ARN family
(
insolvia-*-case-*,alias/insolvia-*-cases, …) silently drops a deny rather than failing — seeinfra/CLAUDE.mdand theinsolvia-aws-namingskill.
Scope every grant by ARN pattern or condition and say why in a comment — that file documents the reasoning for each grant.
Do NOT
- Weaken
DenySelfPrivilegeEscalation/DenyTrustAnchorMutation, or add a workflow that appliesci-trust. Either deletes the security property. - Apply
ci-trustwithout reading the plan — a destroy/replace of the role or the OIDC provider locks CI out of AWS in both repositories at once.
Credential mechanics for the human apply live in the insolvia-aws-auth
skill.