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

# AWS

> Connect CloudWatch, DynamoDB, Lambda, SQS, S3 and Redshift with a cross-account role and a minimal IAM policy

Danube has six AWS connectors. Each one is its own service in the catalog with its own connection, so you can grant exactly the access each needs:

| Connector                                | What agents can do                                                                                         |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| [AWS CloudWatch](/connectors/cloudwatch) | Search logs, run Logs Insights queries, pull recent errors, read metrics and alarms                        |
| [AWS DynamoDB](/connectors/dynamodb)     | Describe tables, get, query and scan items; put, update and delete on a `read_write` connection            |
| [AWS Lambda](/connectors/lambda)         | List functions, read configuration, see recent invocations and errors; invoke on a `read_write` connection |
| [AWS SQS](/connectors/sqs)               | Queue depth, oldest message age, dead-letter queue depth                                                   |
| [AWS S3](/connectors/s3)                 | List buckets and objects, read metadata and small text objects                                             |
| [Amazon Redshift](/connectors/redshift)  | List schemas and tables, run read-only SQL through the Redshift Data API                                   |

For application logs outside AWS, the Datadog connector covers the same questions.

## How the connection works

Every AWS connection stores a **region** and one of two ways to authenticate:

* **Cross-account role (recommended).** You create an IAM role in your account that trusts Danube's AWS account and requires an **external id**. Danube calls `sts:AssumeRole` for every connection, gets credentials that last 15 minutes, and never holds a long-lived key of yours. The external id is generated by Danube for your Danube account and shown on the connection form; it is what stops another Danube customer from pointing their connection at your role.
* **Access keys.** An IAM user's access key id and secret access key (and a session token for temporary keys). This works, but a key that never expires is a bigger risk than a role. Test Connection warns when the keys belong to the account root user.

Each connection is for one region. To reach resources in two regions, store two connections.

**Endpoint URL** is optional. Leave it empty for AWS itself. Set it to a VPC interface endpoint when the traffic must stay on your network (run the [data-plane agent](/organizations/data-plane) inside that VPC), or to LocalStack for testing. Danube only calls public addresses directly, so a private endpoint needs the data-plane agent.

## Create the role

<Steps>
  <Step title="Open the connection form">
    In the dashboard, open the AWS connector you want and click **Connect**. Keep **Authentication** on *Cross-account role* and copy the **Danube AWS account id** and the **External ID** the form shows.
  </Step>

  <Step title="Create the role in IAM">
    In the IAM console choose **Roles**, **Create role**, **Custom trust policy**, and paste this trust policy, replacing `DANUBE_AWS_ACCOUNT_ID` and `YOUR_EXTERNAL_ID` with the values from the form:

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

  <Step title="Attach a permission policy">
    Attach one of the policies below (or several, if one role serves several connectors). Name the role, for example `danube-readonly`, and create it.
  </Step>

  <Step title="Save the connection">
    Paste the role ARN (`arn:aws:iam::123456789012:role/danube-readonly`) and the region into the form and save. Run **Test Connection**: it shows the account and role Danube assumed and lists any IAM action the role is missing.
  </Step>
</Steps>

If Test Connection says Danube could not assume the role, check the trust policy: the principal must be Danube's account and the external id must match the form exactly.

## Minimal IAM policies

Each policy grants only what the connector's tools call. Narrow `Resource` further to the log groups, tables, functions, queues and buckets agents should see.

### CloudWatch

```json theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DanubeCloudWatchRead",
      "Effect": "Allow",
      "Action": [
        "logs:DescribeLogGroups",
        "logs:FilterLogEvents",
        "logs:StartQuery",
        "logs:GetQueryResults",
        "logs:StopQuery",
        "cloudwatch:GetMetricData",
        "cloudwatch:DescribeAlarms"
      ],
      "Resource": "*"
    }
  ]
}
```

### DynamoDB

Read and write are separate statements. Leave out the second one for a read-only role; then even a connection stored as `read_write` cannot change data.

```json theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DanubeDynamoDBRead",
      "Effect": "Allow",
      "Action": [
        "dynamodb:ListTables",
        "dynamodb:DescribeTable",
        "dynamodb:GetItem",
        "dynamodb:Query",
        "dynamodb:Scan"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DanubeDynamoDBWrite",
      "Effect": "Allow",
      "Action": ["dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:DeleteItem"],
      "Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/orders"
    }
  ]
}
```

### Lambda

Recent Invocations and Errors reads the function's CloudWatch log group and metrics, so the read statement includes those. Invoke is its own statement; leave it out unless agents should run functions.

```json theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DanubeLambdaRead",
      "Effect": "Allow",
      "Action": [
        "lambda:ListFunctions",
        "lambda:GetFunctionConfiguration",
        "logs:FilterLogEvents",
        "cloudwatch:GetMetricData"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DanubeLambdaInvoke",
      "Effect": "Allow",
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:us-east-1:123456789012:function:report-*"
    }
  ]
}
```

### SQS

```json theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DanubeSQSRead",
      "Effect": "Allow",
      "Action": [
        "sqs:ListQueues",
        "sqs:GetQueueUrl",
        "sqs:GetQueueAttributes",
        "sqs:ListDeadLetterSourceQueues",
        "cloudwatch:GetMetricData"
      ],
      "Resource": "*"
    }
  ]
}
```

No `sqs:ReceiveMessage`: reading a message would hide it from your consumers.

### S3

Scope `s3:GetObject` to the buckets and prefixes agents may read.

```json theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    { "Sid": "DanubeS3Buckets", "Effect": "Allow", "Action": "s3:ListAllMyBuckets", "Resource": "*" },
    {
      "Sid": "DanubeS3List",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::my-app-logs"
    },
    {
      "Sid": "DanubeS3Read",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-app-logs/exports/*"
    }
  ]
}
```

A bucket encrypted with a customer-managed KMS key also needs `kms:Decrypt` on that key.

### Redshift

The Redshift Data API needs its own actions plus one way to log in to the database: temporary credentials for a database user on a provisioned cluster, or a Secrets Manager secret.

```json theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DanubeRedshiftData",
      "Effect": "Allow",
      "Action": [
        "redshift-data:ExecuteStatement",
        "redshift-data:DescribeStatement",
        "redshift-data:GetStatementResult",
        "redshift-data:ListSchemas",
        "redshift-data:ListTables"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DanubeRedshiftLogin",
      "Effect": "Allow",
      "Action": "redshift:GetClusterCredentialsWithIAM",
      "Resource": "arn:aws:redshift:us-east-1:123456789012:dbname:analytics/dev"
    }
  ]
}
```

With a secret instead, replace the second statement with `secretsmanager:GetSecretValue` on that one secret's ARN. Serverless workgroups use `redshift-serverless:GetCredentials` on the workgroup. The database user itself should hold `SELECT` grants only; see [Redshift](/connectors/redshift).

## Safety

* **Read-only by default.** A connection's mode is `read_only` unless you store it as `read_write`. The write tools (DynamoDB Put, Update and Delete Item, Lambda Invoke) are refused on a read-only connection before anything reaches AWS, and every call on a `read_write` connection needs a confirmation. The IAM policy is the second layer: leave out the write statements and AWS refuses them too.
* **Limits on every call.** Calls time out after 15 s by default (up to 55 s). Lists stop at 500 items and 1 MB; a cut result says `truncated: true` and carries a token to continue.
* **No secrets in results.** Secret keys, session tokens and the temporary credentials Danube assumes are removed from every error. Lambda environment variable values are never returned, only their names.
* **Every call is audited** with the tool, the caller, the row count and the outcome.
