Skip to content

AWS

The AWS integration connects Escape ASM to your AWS account through a cross-account IAM role. Escape assumes your role via STS AssumeRole using a shared External ID. No long-lived access keys are stored.

Authentication differs from AWS Account, but ASM data pull is identical: CloudFront, API Gateway, Route 53, Load Balancers, Lambda, and EC2 Elastic IPs.

Discovered Resources

The AWS integration automatically discovers and inventories the following services from your AWS account:

  • CloudFront Distributions: CDN configurations and domain names serving your applications
  • API Gateway Domain Names: Custom domain names configured for API Gateway endpoints
  • API Gateway V2 APIs: HTTP and WebSocket APIs (API Gateway v2)
  • Route 53 DNS Records: DNS zones and records managed by Route 53
  • Load Balancers: Application Load Balancers (ALB) and Network Load Balancers (NLB). Classic Load Balancers aren't discovered
  • Lambda Functions: Functions with a Function URL enabled (an AWS Lambda Asset and its URL Host). API Gateway endpoints are covered by the API Gateway discovery above
  • EC2 Elastic IPs: Public IPs and domains attached to Elastic IP addresses

These discovered resources are automatically classified as Assets (APIs, DNS records, WebApps) in Escape's ASM, enabling continuous security monitoring and testing.

Create a Cross-Account IAM Role

Create an IAM role in your AWS account, then paste its ARN into Escape. The trusted account ID is 178659809426.

Copy the External ID

  1. Open the AWS integration page
  2. Copy the External ID

Create the Role

  1. Open the IAM roles page and click Create role

  2. Select Custom trust policy

  3. Paste the trust policy below, replacing YOUR_EXTERNAL_ID with the External ID

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "AWS": "arn:aws:iam::178659809426:role/escape-integration"
          },
          "Action": "sts:AssumeRole",
          "Condition": {
            "StringEquals": {
              "sts:ExternalId": "YOUR_EXTERNAL_ID"
            }
          }
        }
      ]
    }
    
  4. Click Next

  5. Attach the ReadOnlyAccess policy, or attach nothing and add the custom policy below

  6. Click Next

  7. Name the role EscapeIntegrationReadOnly

  8. Click Create role

  9. Copy the role ARN (arn:aws:iam::ACCOUNT_ID:role/EscapeIntegrationReadOnly)

Custom Policy

Skip this if you attached ReadOnlyAccess.

Adapt this policy to your infrastructure and security requirements. Keep the actions for the AWS resources you want Escape to discover, and account for any service control policies or permissions boundaries in your organization.

  1. Open the role on the IAM roles page
  2. Click Add permissions, then Create inline policy
  3. Open the JSON tab and paste the policy below
  4. Click Next
  5. Name the policy EscapeIntegrationRead
  6. Click Create policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "apigateway:GET",
        "cloudfront:ListDistributions",
        "ec2:DescribeAddresses",
        "elasticloadbalancing:DescribeLoadBalancers",
        "lambda:GetFunctionUrlConfig",
        "lambda:ListFunctions",
        "route53:ListHostedZones",
        "route53:ListResourceRecordSets"
      ],
      "Resource": "*"
    }
  ]
}

Finish in Escape

  1. Go back to the Escape tab you left open. Don't reopen the form, it generates a new External ID
  2. Keep the External ID already filled in and paste the role ARN
  3. Save the integration and wait for validation to complete

The role name is case sensitive and must match AWS.

Multi-Account (AWS Organizations)

Overview

A CloudFormation stack set creates EscapeIntegrationReadOnly in every account of an organization or organizational unit (OU). New accounts in that target get the role. Use the same External ID in every account.

The stack set creates the role. Create each Escape integration after that, in Register Each Account.

Prerequisites

  1. Access to the AWS Organizations management account.
  2. Trusted access between CloudFormation StackSets and AWS Organizations. See Enable trusted access with AWS Organizations.

Setup

Copy the External ID from the AWS integration page. You'll paste it into the stack set.

In the AWS CloudFormation console, open StackSets and click Create StackSet. Keep Service-managed permissions.

Choose a Template

Save the template below as escape-integration-role.yaml, then upload that file. It sets the role name, so acknowledge CAPABILITY_NAMED_IAM when CloudFormation asks.

AWSTemplateFormatVersion: '2010-09-09'
Description: Cross-account IAM role for the Escape AWS integration

Parameters:
  EscapePrincipalArn:
    Type: String
    Default: arn:aws:iam::178659809426:role/escape-integration
    Description: Account trusted to assume this role.
  EscapeExternalId:
    Type: String
    NoEcho: true
    MinLength: 2
    Description: External ID copied from Escape. Use the same value in every account.

Resources:
  EscapeIntegrationRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: EscapeIntegrationReadOnly
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Ref EscapePrincipalArn
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId: !Ref EscapeExternalId
      Policies:
        - PolicyName: EscapeIntegrationRead
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - apigateway:GET
                  - cloudfront:ListDistributions
                  - ec2:DescribeAddresses
                  - elasticloadbalancing:DescribeLoadBalancers
                  - lambda:GetFunctionUrlConfig
                  - lambda:ListFunctions
                  - route53:ListHostedZones
                  - route53:ListResourceRecordSets
                Resource: '*'

Specify StackSet Details

  1. Name the stack set escape-integration-role
  2. Set EscapeExternalId to the External ID you copied
  3. Leave EscapePrincipalArn as the default

Set Deployment Options

  1. Deploy to the organization, or to one or more OUs
  2. Leave Automatic deployment enabled so new accounts in the target get the role
  3. Under Specify regions, select one region, us-east-1

The stack creates a global IAM role. A second region tries to create the same role name and fails.

Review

Open Review and click Submit. Wait until the stack set has created the role in each account before you register them in Escape.

AWS CLI

From the management account, with the template file in the current directory:

export EXTERNAL_ID="<External ID from Escape>"
export REGION=us-east-1

aws cloudformation activate-organizations-access

ROOT_ID="$(aws organizations list-roots --query 'Roots[0].Id' --output text)"

aws cloudformation create-stack-set \
  --stack-set-name escape-integration-role \
  --template-body file://escape-integration-role.yaml \
  --permission-model SERVICE_MANAGED \
  --auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameters ParameterKey=EscapeExternalId,ParameterValue="$EXTERNAL_ID"

aws cloudformation create-stack-instances \
  --stack-set-name escape-integration-role \
  --deployment-targets OrganizationalUnitIds="$ROOT_ID" \
  --regions "$REGION"

Replace OrganizationalUnitIds with an OU ID (ou-xxxx-xxxxxxxx) to limit which accounts get the role. Child OUs are included.

Register Each Account

After the stack set has created the roles, create one Escape integration per account. This script requires AWS CLI v2, jq, and an authenticated escape-cli. See Getting Started.

: "${EXTERNAL_ID:?Set EXTERNAL_ID first}"

accounts="$(aws organizations list-accounts --output json)"
existing="$(escape-cli integrations list --kind aws)"

aws cloudformation list-stack-instances \
  --stack-set-name escape-integration-role \
  --output json \
| jq -r --argjson accounts "$accounts" --arg region "${REGION:-us-east-1}" '
    ($accounts.Accounts | map({key: .Id, value: .Name}) | from_entries) as $names
    | .Summaries[]
    | select(.Region == $region and .Status == "CURRENT")
    | [.Account, $names[.Account]]
    | @tsv
  ' \
| while IFS=$'\t' read -r account_id account_name; do
  if grep -Fq "(${account_id})" <<<"$existing"; then
    echo "Skipping ${account_id}: integration already exists"
    continue
  fi

  jq -n \
    --arg name "${account_name} (${account_id})" \
    --arg role_arn "arn:aws:iam::${account_id}:role/EscapeIntegrationReadOnly" \
    --arg external_id "$EXTERNAL_ID" \
    '{name: $name, parameters: {external_integration_role_arn: $role_arn, external_id: $external_id}}' \
    | escape-cli integrations create --kind aws
done

When a new account joins the target, run the loop again.

Uninstall

Delete the stack instances, then the stack set. See Delete a stack set. Delete the matching integrations in Escape.

Programmatic Setup

Public API

Create an integration with POST /v3/integrations/aws:

curl -X POST https://public.escape.tech/v3/integrations/aws \
  -H "X-ESCAPE-API-KEY: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production AWS",
    "parameters": {
      "external_integration_role_arn": "arn:aws:iam::123456789012:role/EscapeIntegrationReadOnly",
      "external_id": "escape-00000000-0000-0000-0000-000000000000"
    }
  }'
Field Description
external_integration_role_arn ARN of the IAM role in your account
external_id External ID in that role's trust policy. Reuse one value across accounts.

See the OpenAPI specification (createawsIntegration, listawsIntegrations) for the full request and response schemas.

CLI

escape-cli integrations create --kind aws <<'EOF'
{
  "name": "Production AWS",
  "parameters": {
    "external_integration_role_arn": "arn:aws:iam::123456789012:role/EscapeIntegrationReadOnly",
    "external_id": "escape-00000000-0000-0000-0000-000000000000"
  }
}
EOF

Use the External ID from the trust policy. The same value can be stored on every AWS integration in the organization.