When a platform operates in your account#

Many tools work directly inside your AWS account: deployment platforms that create networks, databases and containers; observability services that read metrics; cost optimizers that scan your inventory. They all need permissions, and how you grant them determines how much risk you take on.

A common pattern in integration guides is to create an IAM user, attach a couple of broad policies covering a long list of services (EC2, RDS, ECS, EKS, DynamoDB, ElastiCache, OpenSearch, CloudWatch, S3, Route 53 and more), and paste its access keys into the platform. It works, but it leaves long-term credentials outside your control and often more permissions than needed.

This guide covers the alternative AWS recommends, a role the platform assumes with an external ID, how to scope its permissions and how to audit access. At the end you will see what to do if the platform only accepts access keys.

Cross-account role or access keys#

CriterionIAM user with access keysRole with external ID
Credential typeLong-term; valid until you deactivate itTemporary; issued by STS for each session
Where the secret livesIn the platform's systemsNo shared secret; the platform uses its own account
Revoking accessDeactivate or delete the keyEdit or delete the role; applies to new sessions
Confused deputy protectionNone specificsts:ExternalId condition in the trust policy

AWS recommends roles so third parties can access your resources without sharing your security credentials, and generally prefers temporary credentials over access keys.

Documentation: AWS IAM · Third-party access ↗ · AWS IAM · Access keys ↗ · AWS IAM · Best practices ↗

The confused deputy problem and the external ID#

The confused deputy problem is a security issue where an entity without permission for an action gets a more privileged entity to perform it. Here: the platform serves many customers and assumes roles in all their accounts. If another customer learned your role ARN, they could ask the platform to operate on your account.

The external ID prevents that. The platform generates a unique identifier for you, you require it in the trust policy, and the platform sends it with every AssumeRole call. Only requests made on your behalf carry the right value.

  • The platform should generate it, one per customer, and it should not be guessable.
  • It is not a secret: anyone allowed to view the role can read it. Its job is to prevent confusion, not to authenticate.
  • It accepts 2 to 1,224 alphanumeric characters plus the symbols + = , . @ : / -.

Documentation: AWS IAM · The confused deputy problem ↗ · AWS IAM · External ID ↗ · AWS STS · AssumeRole ↗

Create the role#

You need three things from the platform: its AWS account ID, your external ID, and the list of permissions it requires. The trust policy lets that account assume the role only with the correct external ID:

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::444455556666:root" },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": { "sts:ExternalId": "UNIQUE-ID-ISSUED-BY-THE-PLATFORM" }
      }
    }
  ]
}
Trust policy. 444455556666 stands for the platform's account; replace it along with the external ID.
bash
# Create the role with the trust policy above (1-hour sessions, the default)
aws iam create-role \
  --role-name external-platform \
  --assume-role-policy-document file://trust.json \
  --max-session-duration 3600 \
  --tags Key=owner,Value=external-platform

# Attach only the permissions the platform documents it needs
aws iam put-role-policy \
  --role-name external-platform \
  --policy-name least-privilege \
  --policy-document file://permissions.json

# Hand the platform only the role ARN (and confirm the external ID)
aws iam get-role --role-name external-platform --query 'Role.Arn'
Creation with the AWS CLI. max-session-duration accepts 1 to 12 hours; if omitted, 1 hour applies.

Documentation: AWS CLI · iam create-role ↗ · AWS CLI · iam put-role-policy ↗

Scope the permissions#

The trust policy decides who gets in; the permissions policy decides what they can do. Combine these techniques:

  • Start from the platform's official list and check service by service whether you will really use it. If you do not use SageMaker or OpenSearch, leave them out.
  • Scope by resource where the service allows it: specific ARNs, name prefixes or tags.
  • If the service supports tag-based access control, use the aws:ResourceTag condition so the platform can only modify what it created and tagged.
  • Add a permissions boundary if the platform creates roles or users itself, so they can never exceed a ceiling.
  • In AWS Organizations, SCPs cap the whole account, for example the allowed regions.
json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "OnlyPlatformManagedResources",
      "Effect": "Allow",
      "Action": ["ec2:StopInstances", "ec2:StartInstances", "ec2:TerminateInstances"],
      "Resource": "arn:aws:ec2:*:111122223333:instance/*",
      "Condition": {
        "StringEquals": { "aws:ResourceTag/ManagedBy": "external-platform" }
      }
    }
  ]
}
Illustrative tag-scoped permission: the platform can only stop, start or terminate instances tagged ManagedBy=external-platform.

Documentation: AWS IAM · Controlling access with tags ↗ · AWS IAM · Permissions boundaries ↗ · AWS Organizations · SCPs ↗

Audit and trim over time#

  • IAM Access Analyzer produces external access findings: it shows which roles and resources an entity outside your account or organization can use, so you can confirm only the platform's account appears.
  • Access Analyzer policy validation catches errors and overly broad permissions before you save.
  • Policy generation builds a policy from the actual activity recorded in CloudTrail, useful for trimming a broad initial policy.
  • IAM last accessed information shows which services have not been used; if the platform never touches one, remove it.

CloudTrail records AssumeRole calls and every action taken with the role, so you can attribute each change in your account to the platform.

Documentation: IAM Access Analyzer ↗ · Access Analyzer · Findings ↗ · Access Analyzer · Validation ↗ · Access Analyzer · Policy generation ↗ · IAM · Last accessed information ↗ · AWS CloudTrail ↗

If the platform only accepts access keys#

Some tools only support an access key pair. In that case, reduce the risk:

  • Create customer managed policies from the JSON the platform publishes (in the IAM console: Policies, Create policy, JSON editor) and tag them with an owner.
  • Create a dedicated IAM user for that platform only, with no console access, and attach just those policies.
  • Generate the access key and store the secret right away: AWS only shows it once.
  • Rotate it regularly, deactivate it if you stop using the platform, and watch its activity in CloudTrail.

Documentation: AWS IAM · Creating policies in the console ↗ · AWS IAM · Managing access keys ↗

Checklist#

  • Cross-account role with the platform's account ID and a required sts:ExternalId.
  • Permissions limited to the services and resources the platform manages.
  • Permissions boundary if the platform creates IAM identities.
  • Access Analyzer shows no unexpected external access findings.
  • Access keys only when there is no alternative, with a dedicated user and rotation.

Sources and scope

Documentation checked on September 25, 2026. Examples and decision criteria are editorial proposals; adapt them to your application's contract and validate them in an authorized test environment.

From design to decision

Compare cloud options

Review pricing, limits, conditions and sources for each option (in Spanish).

Open comparison