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

# AWS

> Connect an AWS account for resource autodiscovery and S3 log delivery

The AWS Cloud Account uses a cross-account IAM role to access your AWS infrastructure for resource autodiscovery and log forwarding.

## What Formal Discovers

<AccordionGroup>
  <Accordion title="RDS Autodiscovery" icon="database">
    Automatically discover PostgreSQL, MySQL, and MongoDB RDS instances and add
    them as Formal resources.
  </Accordion>

  <Accordion title="Redshift Autodiscovery" icon="warehouse">
    Discover Redshift clusters and data warehouses.
  </Accordion>

  <Accordion title="EKS Autodiscovery" icon="dharmachakra">
    Discover EKS clusters for Kubernetes access control.
  </Accordion>

  <Accordion title="EC2 Autodiscovery" icon="server">
    Discover EC2 instances for SSH access.
  </Accordion>

  <Accordion title="ECS Autodiscovery" icon="cubes">
    Discover ECS clusters, services, and containers for SSH access via the
    Desktop App.
  </Accordion>

  <Accordion title="S3 Bucket Autodiscovery" icon="cubes">
    Discover S3 buckets for access via the Desktop App.
  </Accordion>

  <Accordion title="S3 Log Forwarding" icon="bucket">
    Forward Formal Connector logs to S3 buckets for long-term storage and
    compliance.
  </Accordion>
</AccordionGroup>

## Architecture

Formal uses AWS's [Cross-Account IAM Role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_common-scenarios_third-party.html) architecture:

1. **CloudFormation Stack**: deploys an IAM role in your AWS account
2. **IAM Role**: grants Formal specific, scoped permissions
3. **External ID**: a unique identifier that prevents unauthorized access
4. **Role Chaining**: Formal uses IAM User → IAM Role → your IAM Role for added security

## Supported Regions

The CloudFormation stack can be deployed in:

* `us-east-1`, `us-east-2`, `us-west-1`, `us-west-2`
* `eu-central-1`, `eu-west-1`, `eu-west-2`, `eu-west-3`

**The IAM role provides global access**, allowing Formal to discover resources across all AWS regions.

## Setup

<Tabs>
  <Tab title="Web Console">
    <Steps>
      <Step title="Navigate to Cloud Accounts">
        Go to [Cloud Accounts](https://app.formal.ai/cloud-accounts)
      </Step>

      <Step title="Add Integration">
        Click **Add Integration**
      </Step>

      <Step title="Select AWS">
        Choose AWS as your cloud provider
      </Step>

      <Step title="Deploy CloudFormation">
        Click the provided link to deploy the CloudFormation template in your AWS account.

        The template creates an IAM role with the necessary permissions.
      </Step>

      <Step title="Configure Permissions">
        Enable the features you need:

        * RDS Autodiscovery
        * Redshift Autodiscovery
        * EKS Autodiscovery
        * EC2 Autodiscovery
        * ECS Autodiscovery
        * S3 Autodiscovery
        * S3 Write Access (for log forwarding)
      </Step>

      <Step title="Complete Setup">
        Once CloudFormation deployment completes, your AWS integration is active.
      </Step>
    </Steps>
  </Tab>

  <Tab title="Terraform">
    ```hcl theme={null}
    # 1. Create S3 bucket for logs (optional)
    resource "aws_s3_bucket" "formal_logs" {
      bucket = "formal-connector-logs"
    }

    # 2. Create Formal Cloud Integration
    resource "formal_integration_cloud" "aws" {
      name         = "production-aws"
      cloud_region = "us-east-1"
      
      aws {
        template_version              = "1.4.0"  # Pin to specific version
        enable_rds_autodiscovery      = true
        enable_redshift_autodiscovery = true
        enable_eks_autodiscovery      = true
        enable_ec2_autodiscovery      = true
        enable_ecs_autodiscovery      = true
        enable_s3_autodiscovery       = true
        allow_s3_access               = true
        s3_bucket_arn                 = "${aws_s3_bucket.formal_logs.arn}/*"
      }
    }

    # 3. Deploy CloudFormation Stack
    resource "aws_cloudformation_stack" "formal" {
      name          = formal_integration_cloud.aws.aws_formal_stack_name
      template_body = formal_integration_cloud.aws.aws_template_body
      
      parameters = {
        FormalIntegrationId         = formal_integration_cloud.aws.id
        FormalIAMRoleId             = formal_integration_cloud.aws.aws_formal_iam_role
        FormalSNSTopicARN           = formal_integration_cloud.aws.aws_formal_pingback_arn
        EnableRDSAutodiscovery      = formal_integration_cloud.aws.aws_enable_rds_autodiscovery
        EnableRedshiftAutodiscovery = formal_integration_cloud.aws.aws_enable_redshift_autodiscovery
        EnableEKSAutodiscovery      = formal_integration_cloud.aws.aws_enable_eks_autodiscovery
        EnableEC2Autodiscovery      = formal_integration_cloud.aws.aws_enable_ec2_autodiscovery
        EnableECSAutodiscovery      = formal_integration_cloud.aws.aws_enable_ecs_autodiscovery
        EnableS3Autodiscovery       = formal_integration_cloud.aws.aws_enable_s3_autodiscovery
        AllowS3Access               = formal_integration_cloud.aws.aws_allow_s3_access
        S3BucketARN                 = formal_integration_cloud.aws.aws_s3_bucket_arn
      }
      
      capabilities = ["CAPABILITY_NAMED_IAM"]
    }
    ```
  </Tab>

  <Tab title="Terraform (without CloudFormation)">
    <Note>
      If not using CloudFormation, the IAM role name needs to be pre-declared. This is because the role needs the Formal principal ARN for its trust policy, but the integration needs the role ARN to generate that principal. Since IAM role ARNs are predictable, you can pass the ARN before the role exists, allowing the below example to create both resources in a single apply.
    </Note>

    ```hcl theme={null}
    locals {
      formal_integration_role_name = "FormalIntegrationRole"
      s3_log_forwarding_bucket_arn = "arn:aws:s3:::formal-logs-forwarder/*"
    }

    resource "formal_integration_cloud" "aws" {
      name         = "production-aws"
      cloud_region = "us-east-1"

      aws {
        aws_customer_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${locals.formal_integration_role_name}"
        enable_eks_autodiscovery = true
        enable_ec2_autodiscovery = true
        enable_ecs_autodiscovery = true
        enable_rds_autodiscovery = true
        enable_s3_autodiscovery = true
        allow_s3_access = true
        s3_bucket_arn = locals.s3_log_forwarding_bucket_arn
      }
    }

    resource "aws_iam_role" "formal" {
      name = locals.formal_integration_role_name

      assume_role_policy = jsonencode({
        Version = "2012-10-17"
        Statement = [
          {
            Action = "sts:AssumeRole"
            Effect = "Allow"
            Principal = {
              AWS = formal_integration_cloud.aws.aws_formal_iam_role
            }
            Condition = {
              StringEquals = {
                sts:ExternalId = formal_integration_cloud.aws.id
              }
            }
          }
        ]
      })

      inline_policy = {
        name = "FormalIntegrationRolePolicy"
        policy = jsonencode({
          Version = "2012-10-17"
          Statement = [
            {
              Effect = "Allow"
              Action = [
                "sts:GetCallerIdentity",
                "eks:ListClusters",
                "eks:DescribeCluster",
                "ec2:DescribeInstances",
                "ec2:DescribeRegions",
                "ecs:ListClusters",
                "ecs:DescribeClusters",
                "ecs:ListTasks",
                "ecs:DescribeTasks",
                "ecs:ListServices",
                "ecs:DescribeServices",
                "rds:DescribeDBInstances",
                "s3:ListAllMyBuckets",
                "s3:HeadBucket",
              ]
              Resource = ["*"]
            },
            {
              Effect = "Allow"
              Action = [
                "s3:PutObject",
              ]
              Resource = [locals.s3_log_forwarding_bucket_arn]
            }
          ]
        })
      }
    }
    ```
  </Tab>
</Tabs>

## CloudFormation Parameters

| Parameter                     | Description                                         | Default |
| ----------------------------- | --------------------------------------------------- | ------- |
| `EnableRDSAutodiscovery`      | Discover RDS databases (PostgreSQL, MySQL, MongoDB) | `false` |
| `EnableRedshiftAutodiscovery` | Discover Redshift clusters                          | `false` |
| `EnableEKSAutodiscovery`      | Discover EKS clusters                               | `false` |
| `EnableEC2Autodiscovery`      | Discover EC2 instances                              | `false` |
| `EnableECSAutodiscovery`      | Discover ECS clusters and containers                | `false` |
| `EnableS3Autodiscovery`       | Discover S3 buckets                                 | `false` |
| `AllowS3Access`               | Allow log forwarding to S3                          | `false` |
| `S3BucketARN`                 | S3 bucket ARN for logs (if `AllowS3Access` is true) | `""`    |

## Versioning

The CloudFormation template is versioned for stability:

```hcl theme={null}
aws {
  template_version = "1.2.0"  # Pin to specific version
  # ...
}
```

**Benefits of version pinning:**

* Predictable IAM permissions
* Controlled feature adoption
* Easier compliance auditing
* Prevents unexpected changes

**Using latest:**

```hcl theme={null}
aws {
  template_version = "latest"  # Always use newest version
  # ...
}
```

See the [CloudFormation Changelog](/docs/changelog/terraform-provider#aws-template-versions) for version history.

## Security

Formal implements multiple security layers:

1. **Role Chaining**: IAM User → IAM Role in Formal account → IAM Role in your account
2. **External ID**: a unique per-customer identifier that prevents cross-tenant access
3. **Least Privilege**: CloudFormation grants only required permissions
4. **Scoped Permissions**: enable only the features you need

Example: if you only enable RDS autodiscovery, the IAM role cannot access EKS, EC2, or S3.

## Discovered Resources

Once the integration is active, Formal scans your AWS account and creates matching Formal resources:

* RDS instance `prod-postgres-1` → Formal resource `prod-postgres-1`
* EKS cluster `production-eks` → Formal resource `production-eks`
* EC2 instance `i-abc123` → Formal resource `ec2-i-abc123`

Discovery is continuous and resource tags sync to Formal. See [Resource Autodiscovery](/docs/guides/integrations/clouds/introduction#resource-autodiscovery) for how discovered resources behave and how to override them.

## Multiple Accounts

Create a separate integration for each AWS account to keep environments isolated:

```hcl theme={null}
# Production account
resource "formal_integration_cloud" "aws_prod" {
  name         = "aws-production"
  cloud_region = "us-east-1"

  aws {
    template_version         = "1.2.0"
    enable_rds_autodiscovery = true
  }
}

# Staging account
resource "formal_integration_cloud" "aws_staging" {
  name         = "aws-staging"
  cloud_region = "us-west-2"

  aws {
    template_version         = "1.2.0"
    enable_rds_autodiscovery = true
  }
}
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="CloudFormation deployment fails">
    **Possible causes:**

    * Insufficient IAM permissions to create roles
    * Stack name conflict
    * Region not supported

    **Solution:**

    1. Ensure the deploying user has IAM role creation permissions
    2. Use the unique stack name provided by Formal
    3. Deploy in a supported region
  </Accordion>

  <Accordion title="Resources not discovered">
    **Possible causes:**

    * Autodiscovery not enabled for that resource type
    * Resources in an unsupported region
    * IAM permissions insufficient

    **Solution:**

    1. Verify the autodiscovery toggle is enabled in the Formal console
    2. Check the resources exist in AWS
    3. Review the CloudFormation IAM role permissions
    4. Wait a few minutes for the initial scan
  </Accordion>

  <Accordion title="S3 log forwarding not working">
    **Possible causes:**

    * S3 bucket ARN incorrect
    * IAM role lacks S3 permissions
    * Log integration not configured

    **Solution:**

    1. Verify `s3_bucket_arn` matches the permissions you want to grant on objects (e.g., use a `/*` suffix for all files)
    2. Ensure `allow_s3_access` is enabled
    3. Create a [Log Integration](/docs/guides/integrations/log) pointing to this cloud integration
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Log Integration" icon="pipe" href="/docs/guides/integrations/log">
    Forward logs to S3
  </Card>

  <Card title="Add Resources" icon="database" href="/docs/guides/core-concepts/resources/introduction">
    Manually configure discovered resources
  </Card>

  <Card title="Terraform" icon="code" href="/docs/guides/configuration/terraform">
    Manage integrations as code
  </Card>

  <Card title="View Resources" icon="list" href="https://app.formal.ai/resources">
    See all autodiscovered resources
  </Card>
</CardGroup>
