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

# AWS

> Connect your AWS account to give TierZero access to cloud infrastructure

## Overview

Connect your AWS account to give TierZero read and (optional) write access to your cloud infrastructure. TierZero uses a cross-account IAM role to query CloudWatch metrics and logs, inspect resource configurations, and correlate infrastructure state with incidents without storing long-lived credentials.

## Prerequisites

* Administrative access to your AWS account
* Permission to create IAM roles and attach policies

## Setup Instructions

### Step 1: Navigate to Integration Settings

1. Log into your TierZero dashboard
2. Go to **Settings → Integrations**
3. Click **Connect** next to AWS
4. Click **View Instructions** to see a setup guide with values specific to your organization (Account ID, External ID)

### Step 2: Create an IAM Role

Create a new IAM role in your AWS account that TierZero can assume via cross-account access.

1. In the AWS Console, go to **IAM → Roles → Create role**
2. Select **Custom trust policy** and paste the trust policy below (replacing the placeholder values with the ones from the TierZero setup wizard):

```json theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::851725519002:role/tierzero-managed"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "<your-external-id>"
        }
      }
    }
  ]
}
```

### Step 3: Attach Permissions

TierZero needs read-only access across your AWS services. Because AWS limits each role to **10 managed policy attachments**, we recommend using the [Terraform setup](#terraform-setup) below for full coverage.

If you prefer to attach managed policies manually, attach the following core set (up to the 10-policy limit):

| Policy                         | Purpose                           |
| ------------------------------ | --------------------------------- |
| `CloudWatchReadOnlyAccess`     | Logs, metrics, and alarms         |
| `AmazonEC2ReadOnlyAccess`      | EC2, VPC, and networking metadata |
| `AmazonS3ReadOnlyAccess`       | S3 bucket metadata                |
| `AmazonRDSReadOnlyAccess`      | RDS instance metadata             |
| `AWSLambda_ReadOnlyAccess`     | Lambda function metadata          |
| `IAMReadOnlyAccess`            | IAM role and policy metadata      |
| `AWSBillingReadOnlyAccess`     | Billing and cost data             |
| `ElasticLoadBalancingReadOnly` | Load balancer metadata            |
| `AutoScalingReadOnlyAccess`    | Auto Scaling group metadata       |
| `AmazonEKSMCPReadOnlyAccess`   | EKS cluster and Kubernetes access |

<Warning>
  The managed policy approach covers the most common services but is limited to 10 policies. For comprehensive coverage including DynamoDB, ElastiCache, EBS/EFS, Route53, CloudFront, SQS, and more, use the [Terraform setup](#terraform-setup) which consolidates all permissions into inline policies.
</Warning>

4. Name the role (e.g., `TierZeroAccess`) and click **Create role**

### Step 4: Provide the Role ARN

1. Copy the IAM Role ARN from your AWS console
2. Paste it into the TierZero setup wizard
3. Click **Connect**

## Terraform Setup

If you manage infrastructure as code, use the following Terraform configuration to create the cross-account IAM role with comprehensive read-only access. This approach uses inline policies to consolidate permissions from 18+ AWS managed policies, avoiding the 10-policy attachment limit.

<Info>
  Replace the `external_id` variable value with the External ID shown in your TierZero setup wizard.
</Info>

```hcl theme={null}
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.0"
    }
  }
}

variable "role_name" {
  description = "Name for the IAM role"
  type        = string
  default     = "TierZeroAccess"
}

variable "external_id" {
  description = "External ID from TierZero setup wizard"
  type        = string
}

# IAM Role with Trust Policy

resource "aws_iam_role" "tierzero" {
  name = var.role_name

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::851725519002:role/tierzero-managed"
        }
        Action = "sts:AssumeRole"
        Condition = {
          StringEquals = {
            "sts:ExternalId" = var.external_id
          }
        }
      }
    ]
  })

  tags = {
    ManagedBy = "Terraform"
    Purpose   = "TierZero cross-account access"
  }
}

# EKS MCP Read-Only (managed policy — no inline equivalent)

resource "aws_iam_role_policy_attachment" "eks_mcp_readonly" {
  role       = aws_iam_role.tierzero.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSMCPReadOnlyAccess"
}

# Compute & Networking Read-Only
# Covers: EC2, Lambda, ECS, ELB, Auto Scaling, VPC, CloudFormation, Step Functions

resource "aws_iam_role_policy" "compute_networking_readonly" {
  name = "compute-networking-readonly"
  role = aws_iam_role.tierzero.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "EC2ReadOnly"
        Effect = "Allow"
        Action = [
          "ec2:Describe*",
          "ec2:Get*",
          "ec2:List*",
          "ec2:Search*",
        ]
        Resource = "*"
      },
      {
        Sid    = "LambdaReadOnly"
        Effect = "Allow"
        Action = [
          "lambda:Get*",
          "lambda:List*",
        ]
        Resource = "*"
      },
      {
        Sid    = "ECSReadOnly"
        Effect = "Allow"
        Action = [
          "ecs:Describe*",
          "ecs:List*",
        ]
        Resource = "*"
      },
      {
        Sid    = "ELBReadOnly"
        Effect = "Allow"
        Action = [
          "elasticloadbalancing:Describe*",
          "elasticloadbalancing:Get*",
        ]
        Resource = "*"
      },
      {
        Sid    = "AutoScalingReadOnly"
        Effect = "Allow"
        Action = [
          "autoscaling:Describe*",
          "application-autoscaling:DescribeScalableTargets",
          "application-autoscaling:DescribeScalingActivities",
          "application-autoscaling:DescribeScalingPolicies",
        ]
        Resource = "*"
      },
      {
        Sid    = "CloudFormationReadOnly"
        Effect = "Allow"
        Action = [
          "cloudformation:DescribeStacks",
          "cloudformation:ListStacks",
          "cloudformation:ListStackResources",
        ]
        Resource = "*"
      },
    ]
  })
}

# Storage & Database Read-Only
# Covers: S3, RDS, DynamoDB, DAX, EBS, EFS, ElastiCache, Data Pipeline, Kinesis

resource "aws_iam_role_policy" "storage_database_readonly" {
  name = "storage-database-readonly"
  role = aws_iam_role.tierzero.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "S3MetadataOnly"
        Effect = "Allow"
        Action = [
          "s3:ListAllMyBuckets",
          "s3:ListBucket",
          "s3:ListBucketVersions",
          "s3:ListBucketMultipartUploads",
          "s3:ListMultipartUploadParts",
          "s3:Describe*",
          "s3:GetBucket*",
          "s3:GetAccessPoint*",
          "s3:GetMultiRegionAccessPoint*",
          "s3:GetStorageLens*",
          "s3:GetEncryptionConfiguration",
          "s3:GetLifecycleConfiguration",
          "s3:GetReplicationConfiguration",
          "s3:GetAccelerateConfiguration",
          "s3:GetAnalyticsConfiguration",
          "s3:GetIntelligentTieringConfiguration",
          "s3:GetInventoryConfiguration",
          "s3:GetMetricsConfiguration",
          "s3:GetObjectTagging",
          "s3:GetObjectVersionTagging",
          "s3:GetObjectAcl",
          "s3:GetObjectVersionAcl",
          "s3:GetObjectRetention",
          "s3:GetObjectLegalHold",
          "s3:GetObjectAttributes",
          "s3:GetObjectVersionAttributes",
        ]
        Resource = "*"
      },
      {
        Sid    = "RDSReadOnly"
        Effect = "Allow"
        Action = [
          "rds:Describe*",
          "rds:ListTagsForResource",
        ]
        Resource = "*"
      },
      {
        Sid    = "DynamoDBMetadataOnly"
        Effect = "Allow"
        Action = [
          "dynamodb:Describe*",
          "dynamodb:List*",
          "dynamodb:GetAbacStatus",
          "dynamodb:GetResourcePolicy",
        ]
        Resource = "*"
      },
      {
        Sid    = "EBSReadOnly"
        Effect = "Allow"
        Action = [
          "ec2:DescribeVolumes",
          "ec2:DescribeVolumeStatus",
          "ec2:DescribeVolumeAttribute",
          "ec2:DescribeSnapshots",
          "ec2:DescribeSnapshotAttribute",
          "ec2:DescribeSnapshotTierStatus",
          "ec2:ListSnapshotsInRecycleBin",
          "ebs:ListSnapshotBlocks",
          "ebs:ListChangedBlocks",
        ]
        Resource = "*"
      },
      {
        Sid    = "EFSReadOnly"
        Effect = "Allow"
        Action = [
          "elasticfilesystem:Describe*",
          "elasticfilesystem:ListTagsForResource",
        ]
        Resource = "*"
      },
      {
        Sid    = "ElastiCacheReadOnly"
        Effect = "Allow"
        Action = [
          "elasticache:Describe*",
          "elasticache:List*",
        ]
        Resource = "*"
      },
      {
        Sid    = "KinesisReadOnly"
        Effect = "Allow"
        Action = [
          "kinesis:ListStreams",
          "kinesis:DescribeStream",
          "kinesis:DescribeStreamSummary",
        ]
        Resource = "*"
      },
      {
        Sid    = "DynamoDBContributorInsights"
        Effect = "Allow"
        Action = [
          "cloudwatch:GetInsightRuleReport",
        ]
        Resource = "arn:aws:cloudwatch:*:*:insight-rule/DynamoDBContributorInsights*"
      },
    ]
  })
}

# Monitoring & Observability Read-Only
# Covers: CloudWatch, Logs, Synthetics, RUM, Application Signals,
#         Observability Admin, CloudTrail

resource "aws_iam_role_policy" "monitoring_observability_readonly" {
  name = "monitoring-observability-readonly"
  role = aws_iam_role.tierzero.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "CloudWatchReadOnly"
        Effect = "Allow"
        Action = [
          "cloudwatch:BatchGet*",
          "cloudwatch:Describe*",
          "cloudwatch:GenerateQuery",
          "cloudwatch:Get*",
          "cloudwatch:List*",
        ]
        Resource = "*"
      },
      {
        Sid    = "LogsReadOnly"
        Effect = "Allow"
        Action = [
          "logs:Describe*",
          "logs:Get*",
          "logs:List*",
          "logs:StartQuery",
          "logs:StopQuery",
          "logs:TestMetricFilter",
          "logs:FilterLogEvents",
          "logs:StartLiveTail",
          "logs:StopLiveTail",
        ]
        Resource = "*"
      },
      {
        Sid    = "ApplicationSignalsReadOnly"
        Effect = "Allow"
        Action = [
          "application-signals:BatchGet*",
          "application-signals:Get*",
          "application-signals:List*",
        ]
        Resource = "*"
      },
      {
        Sid    = "SyntheticsReadOnly"
        Effect = "Allow"
        Action = [
          "synthetics:Describe*",
          "synthetics:Get*",
          "synthetics:List*",
        ]
        Resource = "*"
      },
      {
        Sid    = "RUMReadOnly"
        Effect = "Allow"
        Action = [
          "rum:BatchGet*",
          "rum:Get*",
          "rum:List*",
        ]
        Resource = "*"
      },
      {
        Sid    = "ObservabilityAdminReadOnly"
        Effect = "Allow"
        Action = [
          "observabilityadmin:Get*",
          "observabilityadmin:List*",
          "observabilityadmin:Test*",
          "observabilityadmin:Validate*",
        ]
        Resource = "*"
      },
      {
        Sid    = "CloudTrailReadOnly"
        Effect = "Allow"
        Action = [
          "cloudtrail:ListChannels",
        ]
        Resource = "*"
      },
      {
        Sid    = "CloudTrailAppSignalsScoped"
        Effect = "Allow"
        Action = [
          "cloudtrail:GetChannel",
        ]
        Resource = "arn:aws:cloudtrail:*:*:channel/aws-service-channel/application-signals/*"
      },
      {
        Sid    = "ServiceQuotasScoped"
        Effect = "Allow"
        Action = [
          "servicequotas:GetServiceQuota",
        ]
        Resource = [
          "arn:aws:servicequotas:*:*:s3/*",
          "arn:aws:servicequotas:*:*:dynamodb/*",
          "arn:aws:servicequotas:*:*:kinesis/*",
          "arn:aws:servicequotas:*:*:sns/*",
          "arn:aws:servicequotas:*:*:bedrock/*",
          "arn:aws:servicequotas:*:*:lambda/*",
          "arn:aws:servicequotas:*:*:fargate/*",
          "arn:aws:servicequotas:*:*:elasticloadbalancing/*",
          "arn:aws:servicequotas:*:*:ec2/*",
        ]
      },
    ]
  })
}

# Billing & Cost Read-Only

resource "aws_iam_role_policy" "billing_cost_readonly" {
  name = "billing-cost-readonly"
  role = aws_iam_role.tierzero.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "CostAnalysis"
        Effect = "Allow"
        Action = [
          "ce:Describe*",
          "ce:Get*",
          "ce:List*",
          "cur:Describe*",
          "cur:Get*",
          "budgets:Describe*",
          "budgets:View*",
        ]
        Resource = "*"
      },
    ]
  })
}

# Security, DNS, Messaging & CDN Read-Only
# Covers: IAM, STS, KMS, ACM, WAF, SQS, Route53, CloudFront

resource "aws_iam_role_policy" "security_dns_messaging_readonly" {
  name = "security-dns-messaging-readonly"
  role = aws_iam_role.tierzero.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "IAMReadOnly"
        Effect = "Allow"
        Action = [
          "iam:GenerateCredentialReport",
          "iam:GenerateServiceLastAccessedDetails",
          "iam:Get*",
          "iam:List*",
          "iam:SimulateCustomPolicy",
          "iam:SimulatePrincipalPolicy",
        ]
        Resource = "*"
      },
      {
        Sid    = "STSReadOnly"
        Effect = "Allow"
        Action = [
          "sts:GetCallerIdentity",
        ]
        Resource = "*"
      },
      {
        Sid    = "KMSReadOnly"
        Effect = "Allow"
        Action = [
          "kms:DescribeKey",
          "kms:ListAliases",
        ]
        Resource = "*"
      },
      {
        Sid    = "ACMReadOnly"
        Effect = "Allow"
        Action = [
          "acm:DescribeCertificate",
          "acm:ListCertificates",
        ]
        Resource = "*"
      },
      {
        Sid    = "WAFReadOnly"
        Effect = "Allow"
        Action = [
          "waf:ListWebACLs",
          "waf:GetWebACL",
          "wafv2:ListWebACLs",
          "wafv2:GetWebACL",
        ]
        Resource = "*"
      },
      {
        Sid    = "SQSReadOnly"
        Effect = "Allow"
        Action = [
          "sqs:GetQueueAttributes",
          "sqs:GetQueueUrl",
          "sqs:ListDeadLetterSourceQueues",
          "sqs:ListQueues",
          "sqs:ListMessageMoveTasks",
          "sqs:ListQueueTags",
        ]
        Resource = "*"
      },
      {
        Sid    = "Route53ReadOnly"
        Effect = "Allow"
        Action = [
          "route53:Get*",
          "route53:List*",
          "route53:TestDNSAnswer",
        ]
        Resource = "*"
      },
      {
        Sid    = "CloudFrontReadOnly"
        Effect = "Allow"
        Action = [
          "cloudfront:Describe*",
          "cloudfront:Get*",
          "cloudfront:List*",
          "cloudfront-keyvaluestore:Describe*",
          "cloudfront-keyvaluestore:Get*",
          "cloudfront-keyvaluestore:List*",
        ]
        Resource = "*"
      },
      {
        Sid    = "ResourceGroupsAndTags"
        Effect = "Allow"
        Action = [
          "resource-groups:ListGroups",
          "resource-groups:ListGroupResources",
          "resource-groups:GetGroup",
          "resource-groups:GetGroupQuery",
          "tag:GetResources",
        ]
        Resource = "*"
      },
    ]
  })
}

output "role_arn" {
  description = "ARN of the TierZero IAM role — provide this to TierZero"
  value       = aws_iam_role.tierzero.arn
}
```

Apply with:

```bash theme={null}
terraform init
terraform apply -var="external_id=<your-external-id>"
```

Then paste the outputted `role_arn` into the TierZero setup wizard.

### Inline Policy Summary

The Terraform configuration above creates **1 managed policy attachment + 5 inline policies**:

| Policy                                    | Services Covered                                                                        |
| ----------------------------------------- | --------------------------------------------------------------------------------------- |
| **Managed:** `AmazonEKSMCPReadOnlyAccess` | EKS clusters, Kubernetes API                                                            |
| `compute-networking-readonly`             | EC2, Lambda, ECS, ELB, Auto Scaling, CloudFormation, VPC                                |
| `storage-database-readonly`               | S3, RDS, DynamoDB, EBS, EFS, ElastiCache, Kinesis                                       |
| `monitoring-observability-readonly`       | CloudWatch, Logs, Synthetics, RUM, Application Signals, Observability Admin, CloudTrail |
| `billing-cost-readonly`                   | Cost Explorer, Cost & Usage Reports, Budgets                                            |
| `security-dns-messaging-readonly`         | IAM, STS, KMS, ACM, WAF, SQS, Route53, CloudFront                                       |

## Kubernetes (EKS)

If your team uses Amazon EKS, you can grant TierZero access to your Kubernetes clusters for inspecting workloads, pods, and cluster state during investigations. The `AmazonEKSMCPReadOnlyAccess` policy is already included in both the managed policy list and the Terraform configuration above.

### Create an EKS Access Entry

Grant the IAM role access to your EKS cluster:

1. In the AWS Console, go to **EKS → Clusters → *your cluster*  → Access**
2. Click **Create access entry**
3. Select the TierZero IAM role as the principal
4. Assign the **AmazonEKSViewPolicy** access policy with **Cluster** scope
5. Click **Create**

Repeat for each EKS cluster you want TierZero to access.

### Allow TierZero's IP Addresses (If Required)

If your EKS cluster's API server endpoint restricts access by IP, you need to add TierZero's outbound IP addresses to the allowlist:

1. In the AWS Console, go to **EKS → Clusters → *your cluster* → Networking**
2. Under **API server endpoint access**, click **Manage**
3. Add TierZero's outbound IP addresses (available in your TierZero dashboard under **Settings → Integrations → AWS**)

<Warning>
  This step is only required if your EKS cluster has IP-based restrictions on the API server endpoint. Clusters with public endpoint access enabled without CIDR restrictions do not need this.
</Warning>

## What TierZero Accesses

* **CloudWatch Logs & Metrics**: Query log groups, log streams, metrics, alarms, and X-Ray traces
* **Compute**: EC2 instances, Lambda functions, ECS services, ELB, Auto Scaling groups
* **Storage & Databases**: S3 buckets, RDS instances, DynamoDB tables, EBS volumes, EFS file systems, ElastiCache clusters
* **Networking**: VPCs, subnets, security groups, Route53 DNS, CloudFront distributions
* **EKS clusters**: Kubernetes workloads, pods, and cluster state (if configured)
* **IAM metadata**: Roles, policies, and trust relationships
* **Billing data**: Cost and usage, budgets, invoices

## Security

* TierZero uses **cross-account IAM role assumption**, so no long-lived credentials are stored
* An **External ID** is required to prevent [confused deputy attacks](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html)
* The default policies are **read-only**; write access is opt-in based on your needs
* Revoke access at any time by deleting the IAM role or updating its trust policy
* All API calls are logged in **AWS CloudTrail**

## Troubleshooting

### "Access Denied" Errors

* Verify the IAM role's trust policy allows TierZero's account (`851725519002`) to assume it
* Check that the External ID matches the value shown in the TierZero setup wizard
* Ensure all required policies are attached to the role (check both managed and inline policies)

### EKS Cluster Not Accessible

* Confirm the EKS access entry exists for the TierZero IAM role
* Verify the **AmazonEKSViewPolicy** is assigned with **Cluster** scope
* If using IP restrictions, ensure TierZero's outbound IPs are allowlisted

### Missing Data

* Verify the IAM role has the specific policy for the data type you expect (e.g., `monitoring-observability-readonly` for CloudWatch logs)
* Check that the role ARN entered in TierZero matches the role in your AWS account
* If using the managed policy approach, note that some services (DynamoDB, ElastiCache, EBS/EFS, etc.) require the Terraform setup for coverage
