> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xpander.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# AWS Operator Setup

> Configure the AWS APIs Operator for self-hosted xpander environments - IAM roles, Pod Identity, and security groups for AWS skills

## 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.

***

## How It Works

When an agent calls an AWS skill (for example, "query this Redshift table"), the request flows through the **Agent Controller**'s embedded skill relay (`agent-controller`). The relay assumes an IAM role via Pod Identity and hands the call to the **AWS Operator** service. The role is scoped to specific AWS services and resources, and the AWS API is called on behalf of the agent, all within your VPC. The Agent Controller runs as the `agent-controller` ServiceAccount, and every other component runs as the namespace's `default` ServiceAccount. Create the Pod Identity association below for both; do not scope it to a single pod.

The IAM role requires three trust principals:

| Principal | Purpose |
| - | - |
| `pods.eks.amazonaws.com` | EKS Pod Identity - lets xpander pods assume the role |
| `arn:aws:iam::<ACCOUNT_ID>:root` | Cross-account assume with External ID (`organizationId`) for xpander |
| The role's own ARN | Self-assume - the Agent Controller re-assumes its own role internally to get service-specific credentials |

<Warning>
  The self-assume statement must **not** have an `ExternalId` condition. The Agent Controller calls `sts:AssumeRole` on its own role without passing an external ID. If this statement has a condition, you'll get `AccessDenied` errors.
</Warning>

***

## 1. Create the IAM Role

```bash theme={"dark"}
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:

```bash theme={"dark"}
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 skills your agents will use. Only grant what's needed.

<AccordionGroup>
  <Accordion title="Redshift">
    ```bash theme={"dark"}
    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, skill configuration), see [Amazon Redshift (Self-Hosted)](/connectors/redshift).
  </Accordion>

  <Accordion title="Athena">
    ```bash theme={"dark"}
    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>
    ```
  </Accordion>
</AccordionGroup>

## 3. Associate Role with Pod Identity

```bash theme={"dark"}
# one association per ServiceAccount the chart runs pods as: agent-controller and default
for sa in agent-controller default; do
  aws eks create-pod-identity-association \
    --cluster-name <CLUSTER_NAME> \
    --namespace xpander \
    --service-account "$sa" \
    --role-arn arn:aws:iam::<ACCOUNT_ID>:role/xpander-aws-access \
    --region <REGION> --profile <PROFILE>
done
```

## 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:

```bash theme={"dark"}
# 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 skills in Xpander Chat. Each skill's connection 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 Skills

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

<CardGroup cols={3}>
  <Card title="Redshift" icon="database" href="/connectors/redshift">
    Query private Redshift clusters
  </Card>

  <Card title="Athena" icon="magnifying-glass" href="/connectors/index">
    Run Athena queries
  </Card>

  <Card title="Power BI" icon="chart-line" href="/connectors/power-bi">
    Connect to Power BI datasets
  </Card>
</CardGroup>

See the [skills catalog](/connectors/index) for all available skills.

***

## Credential Management with AWS Secrets Manager

Instead of storing a skill's 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 skill's connection settings 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:

```bash theme={"dark"}
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>
```

<Tip>
  Use specific secret ARNs instead of wildcards. Organize secrets by environment (`xpander/prod/`, `xpander/staging/`) and enable CloudTrail for audit logging.
</Tip>

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="sts:TagSession AccessDenied">
    Add `sts:TagSession` to both the trust policy AND as a permission policy on the role.
  </Accordion>

  <Accordion title="sts:AssumeRole AccessDenied (self-assume)">
    The Agent Controller 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.
  </Accordion>

  <Accordion title="Service connection timeout">
    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.
  </Accordion>
</AccordionGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.