The attacker holds a working credential, an IAM access key, a Snowflake login, an assumed role, a leaked service-account JSON, and needs to know what it can reach before doing anything destructive or profitable with it. AWS answers that question with a small set of read-only APIs: DescribeInstances for EC2 fleets, DescribeDBInstances for RDS, ListBuckets for S3, ListFunctions for Lambda, ListRoles for IAM. None of these calls modify anything, none require special permissions beyond what most operational roles already carry, and each one individually looks exactly like the automation tooling a real DevOps team runs constantly.
What separates recon from routine operations is volume and caller identity, not the API name. A compromised CI/CD credential or a newly minted access key calling five or more distinct Describe/List/Get actions inside a ten-second window, especially across service boundaries (compute, storage, database, IAM) rather than one narrow area, is the pattern tools like Pacu, ScoutSuite, enumerate-iam.py and CloudFox automate on purpose: hit every enumeration endpoint fast, build an inventory, then decide where to go next. Azure and GCP mirror this with Microsoft.Resources read operations and cloudresourcemanager.projects.list respectively, so the same detection logic ports across providers with different field names.
Attackers frequently chain sts:GetCallerIdentity or its Azure/GCP equivalent immediately before the enumeration burst, since a stolen key's first job is confirming what account and permissions it landed in. That validation call, followed within seconds by a wide discovery sweep, is a stronger and more specific signal than either behavior alone, and it is exactly what an analyst should pivot on when triaging a burst alert.