This page covers skill access to AWS services (Redshift, Athena, S3, EKS). It does not cover model access: Amazon Bedrock is called with the pods’ own credentials, so
bedrock:InvokeModel* and bedrock:Converse* belong on the role bound directly to the pods, not on a skill role behind the base role’s sts:AssumeRole chain. See Amazon Bedrock without a key and, for Hybrid, Amazon Bedrock: keyless IAM or API key.Architecture Overview
xpander self-hosted uses a three-tier IAM role architecture for skill authentication:Why Three Tiers?
- Least privilege - Each skill only has access to its specific service. The Redshift skill can’t access EKS and vice versa.
- Auditability - CloudTrail shows exactly which skill role accessed which resource. No shared credentials.
- Independent lifecycle - Add or remove skills without touching other roles. Revoke one skill’s access without affecting others.
- Role chaining - The agent-controller’s skill relay (
agent-controller) assumes the appropriate skill role per request. This is standard AWS role chaining, fully supported by xpander.
If you’re using a single IAM role for all skills (the setup from AWS Operator Setup), the instructions on this page show how to split that into per-skill roles for better isolation and auditability.
Step 1: Base Role
The base role is the only role attached to pods. It has no service permissions - it can only assume other roles.xpander-base-role role, as an inline policy named AssumeConnectorRoles (update the Resource list as you add skills):
Step 2: Skill Roles
Each skill role needs a trust policy with four statements:Trust Policy Template
Permission Policies Per Skill
Each skill role needs its own service permissions plus self-assume.Self-Assume + Session Tagging (required for all skill roles)
Redshift Skill
Redshift Skill
EKS Skill
EKS Skill
Step 3: Configure in xpander UI
In the skill’s connection settings, set the IAM Role ARN to the skill-specific role (not the base role):Adding a New Skill
When adding a new AWS skill (e.g., S3, DynamoDB, Athena):1
Create the skill role
Use the trust policy template above.
2
Attach service-specific permissions
Add the service permissions + the self-assume policy.
3
Update the base role
Add the new role ARN to the
AssumeConnectorRoles policy on the base role.4
Configure in xpander UI
Set the skill’s IAM Role ARN.
5
Restart the skill relay
Role Creation Order
IAM roles can’t reference themselves during creation. Follow this order:- Create the role with only
PodIdentity+CrossAccountWithExternalIdtrust statements - Wait 10 seconds for IAM propagation
- Update the trust policy to add
SelfAssume+AllowFromBaseRolestatements
Troubleshooting
AccessDenied: sts:AssumeRole (self-assume)
AccessDenied: sts:AssumeRole (self-assume)
Cause: The
SelfAssume trust statement has an ExternalId condition, or the self-assume permission policy is missing.Fix: The SelfAssume and AllowFromBaseRole statements must NOT have conditions. The skill relay does not pass an ExternalId during internal role operations.AccessDenied: sts:AssumeRole (from base role)
AccessDenied: sts:AssumeRole (from base role)
Cause: Base role doesn’t have permission to assume the skill role, or the skill role’s trust policy doesn’t list the base role.Fix: Check both sides:
- Base role permission policy lists the skill role ARN
- Skill role trust policy has
AllowFromBaseRolestatement
Skill works in cloud but not self-hosted
Skill works in cloud but not self-hosted
Cause: In cloud mode, xpander assumes the role directly with the ExternalId. In self-hosted mode, the skill relay chains through the base role without an ExternalId.Fix: Add the
AllowFromBaseRole trust statement to the skill role.Security Recommendations
- Scope resources - Use specific ARNs in each skill role’s permission policy, not
"Resource": "*"where possible - Separate roles per data source - Don’t combine Redshift + EKS permissions in one role
- Rotate ExternalId - The organization ID is used as ExternalId. If it is compromised, contact xpander to have it rotated
- Monitor with CloudTrail - Each skill role appears separately in CloudTrail, making it easy to audit which skill accessed which resource
- Tag roles - Tag all roles with
Environment,Project,ManagedByfor governance - Restrict base role - The base role should ONLY have
sts:AssumeRole+sts:TagSession. Never attach service permissions directly to it

