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

# AWS

> Discover AWS resources and import them as Cymph assets.

## What Cymph uses it for

The AWS integration discovers cloud resources (EC2, Lambda, RDS, S3, IAM, etc.) and imports them as Cymph assets. See [Asset actions](/howto/asset_actions).

All operations are **read-only** — Cymph only calls `Describe*` / `List*` / `Get*` actions.

It supports two credential modes: **Access Key** and **IAM Role**. Cross-account IAM Role is the recommended mode, because no long-lived credentials are stored by either party.

## Access Key mode

You will need to supply an IAM user's access key ID and secret access key. Optionally a session token can be provided for temporary (STS) credentials.

| Field | Required | Secret | Notes |
| - | - | - | - |
| `access_key_id` | yes | no | Access key ID of the IAM user |
| `secret_access_key` | yes | yes | Secret access key of the IAM user |
| `session_token` | no | yes | For temporary (STS) credentials |
| `region` | yes | no | Default region for API calls |

The IAM user needs read access to the services Cymph discovers. The exact actions are listed under [What Cymph reads](#what-cymph-reads); a policy limited to those is sufficient, and `ReadOnlyAccess` or `SecurityAudit` both cover them.

## Cross-account IAM Role mode

Instead of long-lived keys, the customer creates a read-only IAM role that trusts Cymph's AWS account. Cymph assumes the role via STS `AssumeRole`, so **no long-lived credentials are stored by either party** and access is easy to rotate or revoke.

| Field | Required | Secret | Notes |
| - | - | - | - |
| `role_arn` | yes | no | ARN of the role to assume |
| `external_id` | no\* | no | Shared value in the role's trust policy (see below) |
| `region` | yes | no | Default region for API calls |

\* Optional as far as AWS is concerned, but **strongly recommended** — see [the confused-deputy problem](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html).

### Setup

1. **Create the role** in the customer's AWS account with a trust policy that allows Cymph's account to assume it, gated by an external ID:

   ```json theme={"system"}
   {
     "Version": "2012-10-17",
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": { "AWS": "arn:aws:iam::<CYMPH_ACCOUNT_ID>:root" },
         "Action": "sts:AssumeRole",
         "Condition": {
           "StringEquals": { "sts:ExternalId": "<AGREED_EXTERNAL_ID>" }
         }
       }
     ]
   }
   ```

2. **Attach a read-only policy.** The AWS managed policy `ReadOnlyAccess` (or `SecurityAudit`) covers the discovery calls; a tighter custom policy limited to the `Describe*` / `List*` / `Get*` actions used by the executor is preferable.

3. **Provide Cymph** the role ARN and the external ID. Enter them in the integration form (Authentication method → *IAM role*) and click **Test Connection** to verify the role can be assumed.

### Deployment-side IAM

In `Assume Role` mode Cymph calls STS using the **API host's own identity** (the ECS task role / EC2 instance role, or ambient credentials from the environment). That identity must be allowed to call `sts:AssumeRole` on the customer roles, e.g.:

```json theme={"system"}
{
  "Effect": "Allow",
  "Action": "sts:AssumeRole",
  "Resource": "arn:aws:iam::*:role/*"
}
```

There is no Cymph-side config for this — it is part of the deployment's IAM setup, not the integration record. This applies to [self-hosted deployments](/deployment/self-hosted/architecture); on a managed cloud tenant it is already in place.

## What Cymph reads

Every call below is a read. Cymph makes no `Create*`, `Put*`, `Update*`, `Delete*`, or `Modify*` call against your account.

| Service | API actions |
| - | - |
| STS | `sts:GetCallerIdentity` |
| EC2 | `ec2:DescribeRegions`, `ec2:DescribeInstances`, `ec2:DescribeHosts` |
| Auto Scaling | `autoscaling:DescribeAutoScalingGroups` |
| Lambda | `lambda:ListFunctions`, `lambda:GetFunctionConfiguration` |
| EKS | `eks:ListClusters`, `eks:DescribeCluster` |
| ECS | `ecs:ListClusters`, `ecs:ListServices`, `ecs:DescribeServices` |
| App Runner | `apprunner:ListServices`, `apprunner:DescribeService` |
| Elastic Beanstalk | `elasticbeanstalk:DescribeEnvironments` |
| RDS | `rds:DescribeDBInstances`, `rds:DescribeDBClusters` |
| DynamoDB | `dynamodb:ListTables`, `dynamodb:DescribeTable` |
| DocumentDB | `docdb:DescribeDBClusters` |
| ElastiCache | `elasticache:DescribeCacheClusters` |
| S3 | `s3:ListAllMyBuckets`, `s3:GetBucketLocation` |
| Elastic Load Balancing | `elasticloadbalancing:DescribeLoadBalancers` |
| Secrets Manager | `secretsmanager:ListSecrets`, `secretsmanager:DescribeSecret` |
| KMS | `kms:ListKeys`, `kms:DescribeKey` |
| IAM | `iam:ListUsers`, `iam:GetUser`, `iam:ListGroups`, `iam:GetGroup`, `iam:ListRoles`, `iam:GetRole`, `iam:ListInstanceProfiles`, `iam:GetInstanceProfile` |

<Note>
  Secrets Manager and KMS are read as **inventory only** — `DescribeSecret` and `DescribeKey` return metadata. Cymph never calls `GetSecretValue` and never reads key material.
</Note>

A least-privilege policy limited to exactly these actions is enough for discovery, and is preferable to `ReadOnlyAccess` if your security review needs one.

### Which regions are scanned

IAM identities and the S3 bucket listing are account-wide and are always read in full. Everything else is regional:

* If you name specific regions, only those are swept.
* If you leave the region selection empty, Cymph enumerates every region enabled on the account and sweeps them in parallel.

A region that fails — not opted in, throttled, or denied — contributes nothing and is reported as failed. It is never treated as a region that has been emptied, so nothing is deleted on that basis.

### How often discovery runs

A one-off import reads your account once, when you ask it to. To keep the resulting assets current, create a sync and give it a schedule — see [Create a sync](/synchronizer/create-a-sync). Frequencies range from every 5 minutes to monthly.

### Limits

| Limit | Value |
| - | - |
| Request timeout | 15 seconds |
| Connection timeout | 5 seconds |
| Retries per request | 3 attempts |
| Assumed-role session | 1 hour, refreshed automatically |

## Testing the connection

**Test Connection** calls `sts:GetCallerIdentity` with the credentials you supplied and reports what came back:

| Message | Meaning |
| - | - |
| **Valid AWS credentials (account *nnn*)** | The credentials work; the account they belong to is shown so you can confirm it is the right one |
| **Invalid AWS credentials or access denied** | The keys are wrong, the session token has expired, or the role could not be assumed — see below |
| **Connection timed out** | No response within 5 seconds; check network reachability and any egress restrictions |

<Note>
  **Invalid AWS credentials or access denied** covers several distinct causes, because AWS deliberately does not distinguish between them:

  * a mistyped or rotated access key ID or secret
  * an expired session token
  * an assume-role attempt the trust policy rejected — the wrong account, or an external ID that does not match
  * the Cymph host's own identity lacking `sts:AssumeRole` on the target role
</Note>

<Warning>
  Test Connection verifies the **credentials**, not the **permissions**. It stops at `GetCallerIdentity`. An integration can test successfully and still discover nothing, which almost always means the attached policy is missing the `Describe*` / `List*` / `Get*` actions listed above.
</Warning>

<Tip>
  A region that is not enabled on the account never fails the connection test — it is skipped during discovery and reported as a failed region. If a resource you expected is missing, check the region is opted in before suspecting the credentials.
</Tip>


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