Skip to main content

Overview

The AWS APIs Operator is the component that lets xpander agents interact with AWS services in your account — Redshift, Athena, Power BI, and more. In a self-hosted deployment, the operator runs inside your VPC and uses IAM roles with EKS Pod Identity to authenticate. No AWS credentials leave your cluster. This guide covers the IAM and networking setup that your IT or platform team needs to complete before developers can use AWS connectors in their agents.

How It Works

When an agent calls an AWS connector (e.g., “query this Redshift table”), the request flows through the AI Gateway, which assumes an IAM role via Pod Identity. The role is scoped to specific AWS services and resources. The AI Gateway then calls the AWS API on behalf of the agent — all within your VPC. The IAM role requires three trust principals:
PrincipalPurpose
pods.eks.amazonaws.comEKS Pod Identity — lets xpander pods assume the role
arn:aws:iam::<ACCOUNT_ID>:rootCross-account assume with External ID (organizationId) for the xpander platform
The role’s own ARNSelf-assume — the AI Gateway re-assumes its own role internally to get service-specific credentials
The self-assume statement must not have an ExternalId condition. The AI Gateway calls sts:AssumeRole on its own role without passing an external ID. If this statement has a condition, you’ll get AccessDenied errors.

1. Create the IAM Role

cat <<TRUST > /tmp/aws-operator-trust.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PodIdentity",
      "Effect": "Allow",
      "Principal": { "Service": "pods.eks.amazonaws.com" },
      "Action": ["sts:AssumeRole", "sts:TagSession"]
    },
    {
      "Sid": "CrossAccountWithExternalId",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<ACCOUNT_ID>:root"
      },
      "Action": ["sts:AssumeRole", "sts:TagSession"],
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "<ORGANIZATION_ID>"
        }
      }
    },
    {
      "Sid": "SelfAssume",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<ACCOUNT_ID>:role/xpander-aws-access"
      },
      "Action": ["sts:AssumeRole", "sts:TagSession"]
    }
  ]
}
TRUST

aws iam create-role \
  --role-name xpander-aws-access \
  --assume-role-policy-document file:///tmp/aws-operator-trust.json \
  --profile <PROFILE>
Add the self-assume permission policy:
aws iam put-role-policy \
  --role-name xpander-aws-access \
  --policy-name SelfAssumeAndTagSession \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Action": ["sts:AssumeRole", "sts:TagSession"],
      "Resource": "arn:aws:iam::<ACCOUNT_ID>:role/xpander-aws-access"
    }]
  }' \
  --profile <PROFILE>

2. Attach Service Permissions

Attach permission policies based on which AWS connectors your agents will use. Only grant what’s needed.
aws iam put-role-policy \
  --role-name xpander-aws-access \
  --policy-name RedshiftAccess \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": [
          "redshift:GetClusterCredentials",
          "redshift:GetClusterCredentialsWithIAM",
          "redshift:DescribeClusters"
        ],
        "Resource": [
          "arn:aws:redshift:<REGION>:<ACCOUNT_ID>:cluster:*",
          "arn:aws:redshift:<REGION>:<ACCOUNT_ID>:dbname:*/*",
          "arn:aws:redshift:<REGION>:<ACCOUNT_ID>:dbuser:*/*"
        ]
      },
      {
        "Effect": "Allow",
        "Action": [
          "redshift-data:ExecuteStatement",
          "redshift-data:GetStatementResult",
          "redshift-data:DescribeStatement",
          "redshift-data:ListStatements",
          "redshift-data:CancelStatement",
          "redshift-data:BatchExecuteStatement",
          "redshift-data:ListDatabases",
          "redshift-data:ListSchemas",
          "redshift-data:ListTables",
          "redshift-data:DescribeTable"
        ],
        "Resource": "*"
      }
    ]
  }' \
  --profile <PROFILE>
For a full Redshift walkthrough (cluster creation, IAM-mapped user, connector config), see Amazon Redshift (Self-Hosted).
aws iam put-role-policy \
  --role-name xpander-aws-access \
  --policy-name AthenaAccess \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": [
          "athena:StartQueryExecution",
          "athena:GetQueryExecution",
          "athena:GetQueryResults",
          "athena:ListWorkGroups"
        ],
        "Resource": "*"
      },
      {
        "Effect": "Allow",
        "Action": [
          "s3:GetObject",
          "s3:ListBucket",
          "s3:PutObject"
        ],
        "Resource": [
          "arn:aws:s3:::<RESULTS_BUCKET>",
          "arn:aws:s3:::<RESULTS_BUCKET>/*"
        ]
      },
      {
        "Effect": "Allow",
        "Action": [
          "glue:GetTable",
          "glue:GetTables",
          "glue:GetDatabase",
          "glue:GetDatabases"
        ],
        "Resource": "*"
      }
    ]
  }' \
  --profile <PROFILE>

3. Associate Role with Pod Identity

aws eks create-pod-identity-association \
  --cluster-name <CLUSTER_NAME> \
  --namespace xpander \
  --service-account xpander \
  --role-arn arn:aws:iam::<ACCOUNT_ID>:role/xpander-aws-access \
  --region <REGION> --profile <PROFILE>

4. Network Access

If the AWS service runs inside your VPC (e.g., a private Redshift cluster), ensure the service’s security group allows traffic from the EKS cluster security group:
# Get the EKS cluster security group
EKS_SG=$(aws eks describe-cluster --name <CLUSTER_NAME> \
  --region <REGION> --profile <PROFILE> \
  --query 'cluster.resourcesVpcConfig.clusterSecurityGroupId' --output text)

# Allow from EKS to the service (e.g., Redshift port 5439)
aws ec2 authorize-security-group-ingress \
  --group-id <SERVICE_SG_ID> \
  --protocol tcp --port <SERVICE_PORT> \
  --source-group $EKS_SG \
  --region <REGION> --profile <PROFILE>
For services outside your VPC (e.g., S3, Athena), no security group changes are needed — the NAT Gateway handles outbound access.

5. Configure in xpander

Once the IAM role and network access are set up, developers can configure individual AWS connectors in the xpander UI. Each connector requires:
  • IAM Role ARN — the role created above
  • Region — your AWS region
  • Auth Method — IAM
  • Service-specific parameters (cluster ID, database name, bucket name, etc.)

Supported AWS Connectors

The AWS APIs Operator supports any AWS service accessible via IAM. Common connectors:

Redshift

Query private Redshift clusters

Athena

Run Athena queries

Power BI

Connect to Power BI datasets
See the full connectors library for all available connectors.

Credential Management with AWS Secrets Manager

Instead of storing connector API keys directly in xpander, you can pull them from AWS Secrets Manager at runtime. This keeps credentials out of xpander’s database entirely.

How It Works

  1. Enable “Use AWS Secrets Manager” in the connector configuration in xpander
  2. Provide the secret ARN (e.g., arn:aws:secretsmanager:us-east-1:123456789012:secret:my-api-key-AbCdEf)
  3. The xpander pod uses its IAM role to retrieve the secret at runtime
  4. Credentials are never persisted in xpander’s storage

Required Permissions

Add Secrets Manager access to the IAM role:
aws iam put-role-policy \
  --role-name xpander-aws-access \
  --policy-name SecretsManagerAccess \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret"
      ],
      "Resource": [
        "arn:aws:secretsmanager:<REGION>:<ACCOUNT_ID>:secret:xpander/*"
      ]
    }]
  }' \
  --profile <PROFILE>
Use specific secret ARNs instead of wildcards. Organize secrets by environment (xpander/prod/, xpander/staging/) and enable CloudTrail for audit logging.

Troubleshooting

Add sts:TagSession to both the trust policy AND as a permission policy on the role.
The AI Gateway re-assumes its own role internally. The trust policy needs a SelfAssume statement for the role’s own ARN without an ExternalId condition. Also add sts:AssumeRole as a permission policy.
Check the service’s security group allows the relevant port from the EKS cluster security group. For VPC-internal services, they must be in the same VPC or have VPC peering/transit gateway configured.