Caller vs Service Role

The Caller's Permissions Versus the Stack's Service Role

By default, CloudFormation 24 acts with a temporary session made from your credentials, so you need dynamodb:CreateTable yourself to deploy a table. With --role-arn, it assumes a service role, trusted by cloudformation.amazonaws.com, and creates resources with the role's permissions. Callers then need only CloudFormation permissions and iam:PassRole on that role, restricted to CloudFormation:

infra/policies/cfn-caller.json: what a deployer needs when a service role does the workJSON
{
  "Version": "2012-10-17",
  "Statement": [
    {"Sid": "BookNestStacks", "Effect": "Allow", "Action": "cloudformation:*",
     "Resource": "arn:aws:cloudformation:*:*:stack/booknest-*/*"},
    {"Sid": "PassOnlyTheDeployerRole", "Effect": "Allow", "Action": "iam:PassRole",
     "Resource": "arn:aws:iam::*:role/booknest-cfn-deployer",
     "Condition": {"StringEquals": {"iam:PassedToService": "cloudformation.amazonaws.com"}}}
  ]
}

AWS 24 warns that a stack keeps its service role for every later operation, that the role cannot be removed, and that anyone allowed to update the stack uses it even without iam:PassRole, so a generous role is an escalation path. LocalStack 4.13.1 63,725 checks none of this: a deploy with --role-arn naming a role that does not exist reached CREATE_COMPLETE, where AWS fails at once because the role cannot be assumed.