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

# AWS

> Connect AWS with a read-only IAM role so account layout, IAM, security-group, encryption, TLS, backup, redundancy, auto-scaling, CloudWatch, and GuardDuty evidence stays current automatically.

The AWS integration connects one AWS account to Keldyn's continuous-evidence engine. You create a **read-only IAM role** that trusts Keldyn's AWS account with a per-organization External ID. Keldyn assumes that role on a schedule, reads your infrastructure posture, and turns it into live evidence for the related SOC 2 controls — so you don't have to re-check and re-attach that evidence by hand.

Keldyn never asks for AWS access keys. It stores only the role ARN and External ID, mints a one-hour session for each refresh, and discards those credentials when the pull finishes.

<Note>
  Integrations and automated evidence are typically managed by workspace admins.
  See [Evidence & audits](/evidence-and-audits) for how automated evidence fits
  alongside manual evidence.
</Note>

## What it's used for

Keldyn uses the data it reads from AWS to keep evidence for environment separation, access boundaries, network edge, encryption, backups, redundancy, auto-scaling, and monitoring current. Each daily refresh updates the evidence and the freshness status shown on the integration's **Controls covered** table.

The integration contributes evidence to these SOC 2 controls:

| Control                                                     | Topic                                                                                                   | Coverage |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -------- |
| **Segregated change environments**                          | Account list and VPC/subnet layout showing production separated from a named non-production environment | Full     |
| **Boundaries of confidential data**                         | IAM users, roles, groups, and attached permissions                                                      | Full     |
| **Production environment restriction**                      | Who holds access to production resources                                                                | Full     |
| **Firewalls at access points**                              | Security-group ingress and egress rules                                                                 | Full     |
| **Critical data encryption at rest**                        | RDS, S3, and EBS encryption with KMS keys                                                               | Full     |
| **Encryption in transit**                                   | Load-balancer TLS policies and certificates                                                             | Full     |
| **System redundancy**                                       | RDS Multi-AZ / cross-zone settings                                                                      | Full     |
| **Backup configuration and schedule**                       | AWS Backup plans, schedule, retention, and job history                                                  | Full     |
| **Backup restoration tests**                                | AWS Backup restore-job history                                                                          | Full     |
| **Auto-scaling configuration**                              | Auto Scaling group settings and scaling activity                                                        | Full     |
| **Network monitoring alerts**                               | CloudWatch alarm configuration plus alarm history and GuardDuty findings                                | Full     |
| **Segregation of duties**                                   | IAM listing as supporting access evidence                                                               | Partial  |
| **User listings showing unique IDs**                        | IAM principal listing                                                                                   | Partial  |
| **Privilege access restriction**                            | Privileged IAM attachments                                                                              | Partial  |
| **Backup policy**                                           | Operating backup plans and restore history                                                              | Partial  |
| **Cryptography policy**                                     | Encryption-at-rest and in-transit settings                                                              | Partial  |
| **Cryptographic key management**                            | KMS key usage on stores and listeners                                                                   | Partial  |
| **Disaster recovery testing**                               | Restore-job history                                                                                     | Partial  |
| **Critical data processing error detection and correction** | Alarm and GuardDuty history                                                                             | Partial  |
| **Asset inventory**                                         | EC2, RDS, S3, and load-balancer listing                                                                 | Partial  |
| **Data register**                                           | The same resource listing                                                                               | Partial  |

<Info>
  **Full** coverage means the data is the artifact the control asks for.
  **Partial** coverage means the data usefully contributes but does not, on its
  own, replace everything the control requires (for example, **Backup policy**
  still needs the approved policy document). Keldyn labels partial coverage in
  the auto-generated evidence so it isn't mistaken for complete coverage.
</Info>

<Note>
  **System redundancy** reflects Multi-AZ configuration, not a tested failover.
  **Auto-scaling configuration** reflects that capacity is configured to scale,
  not a load-tested outcome. **Critical data encryption at rest** does not
  prove your KMS key-rotation policy. **Network monitoring alerts** still needs
  your incident register as the response record. **Segregated change
  environments** needs an explicitly identified production environment *and* a
  named non-production environment — untagged accounts and VPCs are treated as
  unknown, not as production. See [Tag environments for separation
  evidence](#tag-environments-for-separation-evidence).
</Note>

## Before you begin

<Steps>
  <Step title="Use an AWS IAM administrator">
    Creating the role and attaching policies requires `iam:CreateRole`,
    `iam:AttachRolePolicy`, `iam:PutRolePolicy`, and permission to edit the
    role trust policy. You do **not** need to share any AWS access keys with
    Keldyn.
  </Step>

  <Step title="Plan a stable role name">
    The connect screen uses `KeldynEvidence` as the example. Using that name
    is recommended so the role is easy to find later. Any role name works as
    long as you paste the full ARN back into Keldyn.
  </Step>

  <Step title="Keldyn only ever reads">
    Attach **SecurityAudit**, **ViewOnlyAccess**, and a small AWS Backup read
    grant. Keldyn never creates, updates, or deletes resources in your account.
  </Step>
</Steps>

## Set up the integration

Do this in order: copy the values from Keldyn, create the role in AWS, then
paste the role ARN back. Keldyn verifies the role by assuming it once before
the connection is saved.

<Steps>
  <Step title="Open Integrations">
    In the Keldyn web app, go to **Integrations** and select **AWS**.
  </Step>

  <Step title="Copy the Keldyn account ID and External ID">
    The connect card shows three values you will put on the role:

    * **Keldyn AWS account ID** — the AWS account the role must trust.
    * **External ID** — unique to *your* Keldyn organization. Do not reuse
      another team's External ID.
    * **Trust-policy JSON** — a ready-to-paste snippet that already includes
      both values.

    Leave this tab open. The External ID is required on every later refresh;
    if you omit it from the trust policy, connect is refused.
  </Step>

  <Step title="Create the IAM role in AWS">
    In the [IAM roles console](https://console.aws.amazon.com/iam/home#/roles),
    select **Create role**.

    1. Trusted entity type: **Custom trust policy**.
    2. Paste the trust-policy JSON from Keldyn. It must allow
       `sts:AssumeRole` for the Keldyn principal **and** include
       `sts:ExternalId` set to your organization's External ID. A role that
       can be assumed *without* that External ID is rejected (confused-deputy
       protection).
    3. Name the role `KeldynEvidence` (recommended) and create it.
  </Step>

  <Step title="Attach the read-only policies">
    Open the new role → **Permissions** and attach:

    1. AWS managed policy **[SecurityAudit](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/SecurityAudit.html)**.
    2. AWS managed policy **[ViewOnlyAccess](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/ViewOnlyAccess.html)**.
    3. A custom inline policy for AWS Backup reads (those APIs are not fully
       covered by the two managed policies):

    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "BackupRead",
          "Effect": "Allow",
          "Action": [
            "backup:List*",
            "backup:Get*",
            "backup:Describe*"
          ],
          "Resource": "*"
        }
      ]
    }
    ```

    Do not attach AdministratorAccess, PowerUserAccess, or any write policy.
  </Step>

  <Step title="Paste the role ARN and connect">
    Copy the role ARN (it looks like
    `arn:aws:iam::123456789012:role/KeldynEvidence`) into **IAM role ARN** on
    the Keldyn AWS page and select **Connect**. Keldyn assumes the role once
    to verify trust and permissions, stores only `{ role ARN, External ID }`,
    and discards the session.
  </Step>

  <Step title="Run the first sync">
    Select **Sync now** to pull evidence immediately, or wait for the next
    scheduled daily refresh. **Controls covered** and **Evidence sources**
    then show what was collected and how fresh it is.
  </Step>
</Steps>

<Tip>
  From the integration detail page, **Controls covered** shows whether AWS is
  actively feeding each control and whether the control is satisfied.
  **Evidence sources** shows each data fetch, the controls it covers, and its
  freshness.
</Tip>

### Trust policy (what Keldyn shows you)

The snippet on the connect card matches this shape. Use the live values from
Keldyn — do not invent an External ID.

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

If the connect card shows a more specific Keldyn principal ARN (a user or
role, not account root), leave that value as-is. The External ID condition
must stay.

### AWS CLI alternative

If you prefer the CLI, copy the account ID and External ID from Keldyn first,
then:

```bash theme={null}
# Save the trust policy from the Keldyn connect card as trust-policy.json

aws iam create-role \
  --role-name KeldynEvidence \
  --assume-role-policy-document file://trust-policy.json

aws iam attach-role-policy \
  --role-name KeldynEvidence \
  --policy-arn arn:aws:iam::aws:policy/SecurityAudit

aws iam attach-role-policy \
  --role-name KeldynEvidence \
  --policy-arn arn:aws:iam::aws:policy/job-function/ViewOnlyAccess

# Save the Backup read JSON from the step above as backup-read.json
aws iam put-role-policy \
  --role-name KeldynEvidence \
  --policy-name KeldynBackupRead \
  --policy-document file://backup-read.json

aws iam get-role --role-name KeldynEvidence \
  --query 'Role.Arn' --output text
```

Paste that ARN into Keldyn and connect.

<Note>
  `ViewOnlyAccess` is a job-function policy. Its ARN is
  `arn:aws:iam::aws:policy/job-function/ViewOnlyAccess`. In the console it
  still appears as **ViewOnlyAccess**.
</Note>

## Tag environments for separation evidence

**Segregated change environments** only passes when Keldyn can see
**production** and a named non-production environment (**development** or
**preview** / test / staging). Untagged resources are recorded as `unknown`,
not assumed to be production.

Keldyn looks at account aliases, AWS Organizations account names (when
readable), and VPC names plus these tags:

| Tag key                                | Example values that map                                                                       |
| -------------------------------------- | --------------------------------------------------------------------------------------------- |
| `Environment`, `environment`, or `Env` | `production` / `prod`, `development` / `dev`, `test` / `testing` / `qa` / `staging` / `stage` |

If this AWS account is in an organization, granting the role
`organizations:ListAccounts` (typically only possible on the management
account) lets Keldyn use member-account names as extra separation evidence.
From a member account that call is denied; that is expected and is **not** a
sync failure. Keldyn then relies on this account's alias and VPC tags.

Keldyn assumes **only the role you paste**. It does not assume into other
member accounts. Connect each account you want evidenced as its own
connection if you need per-account coverage.

## What data Keldyn collects and why

Keldyn requests **read-only** access and never writes to your AWS account. It
collects only the data below, and only to build control evidence. Regional
APIs are read in every enabled region.

| Data collected                    | What it contains                                                                                                                         | Why Keldyn collects it                                                                                                  | Refresh |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------- |
| **Account and network inventory** | Account id/alias, enabled regions, VPCs and subnets (and Organizations account names when readable)                                      | Shows whether production is separated from a named non-production environment                                           | Daily   |
| **IAM inventory**                 | Users, roles, groups, and attached policy names                                                                                          | Shows who can access production resources                                                                               | Daily   |
| **Security group rules**          | Full ingress and egress rule export                                                                                                      | Evidences that only intended traffic is allowed at the network edge                                                     | Daily   |
| **Data-store encryption**         | RDS instance/cluster encryption, S3 bucket encryption (read in the bucket's region), EBS volume encryption, and related KMS key metadata | Evidences encryption at rest for critical stores                                                                        | Daily   |
| **Load-balancer TLS**             | Load balancers, listeners, TLS policies, and ACM certificate metadata                                                                    | Evidences that connections require an approved TLS version (TLS 1.2+). HTTP-only listeners are recorded as a gap        | Daily   |
| **Database redundancy**           | RDS Multi-AZ / multi-AZ cluster settings                                                                                                 | Evidences system redundancy across zones                                                                                | Daily   |
| **Backup configuration**          | AWS Backup plans, schedule, retention, and backup-job history                                                                            | Evidences that backups complete on schedule                                                                             | Daily   |
| **Backup restore tests**          | AWS Backup restore-job history                                                                                                           | Evidences that restores were actually run                                                                               | Daily   |
| **Auto Scaling configuration**    | Auto Scaling group settings and scaling activity                                                                                         | Evidences that capacity is configured to adjust to demand                                                               | Daily   |
| **CloudWatch alarms**             | Alarm configuration                                                                                                                      | Evidences that monitoring alerts are defined                                                                            | Daily   |
| **Alarm and GuardDuty history**   | CloudWatch alarm history and GuardDuty findings where a detector is enabled                                                              | Evidences that alerts fire and threat findings are recorded. GuardDuty disabled is noted, not treated as a pull failure | Daily   |
| **Resource inventory**            | EC2, RDS, S3, and load-balancer listing reused from the same pull                                                                        | Supporting evidence for the asset inventory and data register                                                           | Daily   |

<Note>
  Keldyn does not collect object contents, secret values, or KMS plaintext.
  A missing Multi-AZ setting, an HTTP listener, or a security group that
  allows `0.0.0.0/0` or `::/0` on all ports, SSH (22), or RDP (3389) is
  recorded as real (unfavorable) evidence — not a sync failure.
</Note>

### Authentication

Keldyn authenticates with **cross-account `sts:AssumeRole`** plus your
organization's External ID. That is the same third-party pattern AWS
[documents for vendors](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_common-scenarios_third-party.html).

* You never paste access keys.
* Session credentials last about an hour and are never stored.
* Disconnecting in Keldyn, or deleting/updating the role so AssumeRole fails,
  immediately stops further reads.

### Permissions Keldyn uses

These are the reads that matter for evidence. The two managed policies plus
the Backup grant cover them; you do not need to attach each API by hand.

| Area                         | Typical APIs                                                                                                                                           | Purpose                                                                        |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ |
| **Identity**                 | `sts:GetCallerIdentity`, `iam:GetAccountAuthorizationDetails`, `iam:ListAccountAliases`                                                                | Confirm the assumed role and export the IAM listing                            |
| **Organizations** (optional) | `organizations:ListAccounts`                                                                                                                           | Account-name inventory for environment separation                              |
| **Regions and network**      | `ec2:DescribeRegions`, `ec2:DescribeVpcs`, `ec2:DescribeSubnets`, `ec2:DescribeSecurityGroups`, `ec2:DescribeInstances`, `ec2:DescribeVolumes`         | Region discovery, environment layout, firewall rules, compute/volume inventory |
| **Data stores**              | `rds:DescribeDBInstances`, `rds:DescribeDBClusters`, `s3:ListAllMyBuckets`, `s3:GetBucketLocation`, `s3:GetEncryptionConfiguration`, `kms:DescribeKey` | Encryption and redundancy                                                      |
| **Edge TLS**                 | `elasticloadbalancing:DescribeLoadBalancers`, `elasticloadbalancing:DescribeListeners`, `acm:DescribeCertificate`                                      | Encryption in transit                                                          |
| **Backup**                   | `backup:List*`, `backup:Get*`, `backup:Describe*`                                                                                                      | Backup plans, jobs, and restore tests                                          |
| **Auto Scaling**             | `autoscaling:DescribeAutoScalingGroups`, `autoscaling:DescribeScalingActivities`                                                                       | Auto-scaling configuration and history                                         |
| **Monitoring**               | `cloudwatch:DescribeAlarms`, `cloudwatch:DescribeAlarmHistory`, `guardduty:ListDetectors`, `guardduty:ListFindings`                                    | Alert configuration and history                                                |

## Troubleshooting "Access required"

A control shows **Access required** (or stays unknown) when the role cannot
read the matching data in every enabled region. Common causes:

* **SecurityAudit** or **ViewOnlyAccess** is missing.
* The Backup inline policy is missing, which blocks backup-plan, job, and
  restore-test evidence.
* A service-control policy (SCP) or permissions boundary denies a read in
  some regions. Incomplete regional coverage keeps the control from passing
  on a partial snapshot.
* Load-balancer **listeners** are unreadable
  (`elasticloadbalancing:DescribeListeners`), so encryption-in-transit cannot
  be proven.
* An S3 bucket encryption policy cannot be read in the bucket's own region.
* AWS Organizations `ListAccounts` is denied (normal on a member account).
  Environment-separation then depends on this account's alias and VPC tags.

Reconnect is not required after you fix IAM — run **Sync now**. Controls not
affected by the missing access continue to refresh normally.

If AssumeRole itself fails, Keldyn marks the connection **Reauth required**.
Typical causes:

* The role ARN is wrong, renamed, or deleted.
* The trust policy principal does not match the Keldyn account shown on the
  connect card.
* The `sts:ExternalId` condition is missing or does not match the External
  ID on the connect card.
* The role can be assumed *without* an External ID — Keldyn refuses that
  configuration. Add the condition and connect again.

## Keeping evidence current

Keldyn refreshes AWS evidence automatically each day. Evidence that hasn't
refreshed within its expected cadence is marked **stale**, and failed pulls
are marked **failed** so you can spot gaps. You can also trigger an immediate
refresh at any time with **Sync now**.

AWS ages out some history on its own (for example, Auto Scaling activity is
kept for about six weeks, and Backup job lists are bounded). Keldyn
**accumulates** backup jobs, restore tests, scaling activity, and
alarm/GuardDuty history rather than replacing them on each refresh, so a
record captured while it was live stays available for an audit period longer
than AWS's own retention window. The first sync after connecting shows only
what AWS still holds.

## Next steps

<Card title="Evidence & audits" icon="clipboard-check" href="/evidence-and-audits" arrow={true}>
  Learn how automated integration evidence works alongside manual evidence and
  audits.
</Card>
