返回资讯中心
外部精选
软件工程
#开发者实时流

Who Created This AWS Resource? Find Out with CloudTrail, Even After 90 Days

There’s an m5.2xlarge in the shared services account called test-2. It has no tags except Name, it’s been running for over a year, and nobody in the platform channel admits to it. Someone suggests checking CloudTrail. E…

Hacker News 最新movingmovie2026/10/21 · 25 天前30 分钟阅读

以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。

There’s an m5.2xlarge in the shared services account called test-2. It has no tags except Name, it’s been running for over a year, and nobody in the platform channel admits to it. Someone suggests checking CloudTrail. Event history comes back empty, because the instance is older than 90 days. Or it comes back with a RunInstances event whose user is AWSReservedSSO_AdministratorAccess_4f1c..., or the Auto Scaling service role, or a role session called botocore-session-1692304152, none of which is a person you can message.

CloudTrail does record who created a resource. Getting from that record to a person you can ask takes a few more steps. This guide covers them in about 30 minutes, using read-only access and the logs you probably already have.

Nothing in this guide changes, tags or deletes a resource. The only thing it creates is an Athena table definition in Step 4, which points at logs you already store.

Most AWS resources have no “created by” field. Here’s where the obvious places run out.

Event history only goes back 90 days, one account and one Region at a time. It’s free and on in every account, but it covers management events for the last 90 days, in the Region where the call happened (global services such as IAM are usually recorded in US East (N. Virginia)). The resources that cause the most confusion are usually older than that.

The identity is often a role, not a person. Most people reach AWS through IAM Identity Center, a SAML identity provider, CI/CD or an assumed role. CloudTrail records the role session that made the call. Sometimes the session name is the person, sometimes it’s a random string.

The caller is often a service. Auto Scaling, CloudFormation, EKS node groups and Karpenter launch instances on someone’s behalf. The RunInstances event is real, but the useful answer is “who configured the thing that launched it”.

aws:createdBy only helps going forward. AWS can apply an aws:createdBy cost allocation tag, but only after someone in the management account activates it, only to resources created after that, only for a listed set of resource types and Regions, and only in billing data. It won’t appear in Tag Editor or on the resource.

CloudTrail Lake isn’t an option for most new readers. AWS closed CloudTrail Lake to new customers on May 31, 2026. If you already have an event data store, you can keep using it (there’s a query for it in Step 4). If you don’t, S3 and Athena are the path.

Before you search any logs, check two things: how old the resource is, and whether AWS already tagged it with the thing that created it.

For EC2, there’s a catch. LaunchTime is the time the instance was last started, not first launched. Stop and start an instance and LaunchTime moves. AWS’s own API reference says to use the attachment time of the primary network interface for the original launch:

aws ec2 describe-instances --instance-ids i-0abc123def4567890
--query "Reservations[].Instances[].{ last_start: LaunchTime, first_launch: NetworkInterfaces[?Attachment.DeviceIndex==`0`].Attachment.AttachTime | [0], tags: Tags}"
--output json

Then look at the tags. Even untagged instances often carry tags AWS or a controller added at launch, and these point at the real creator:

If one of these is present, you’re looking for the creator of the stack, group or cluster, not the instance. Those parents usually live longer, and their creation events are easier to find.

Now compare the creation date with today. If it’s within the last 90 days, go to Step 2. If it’s older, go straight to Step 4.

Other resource types have their own creation timestamps: CreateTime on EBS volumes, StartTime on snapshots, InstanceCreateTime on RDS instances, CreatedTime on load balancers, CreationDate on S3 buckets. Treat the S3 date with care: AWS notes that it can change when you change the bucket, for example by editing its bucket policy. Elastic IPs don’t expose an allocation time, so you’ll be relying on CloudTrail.

Event history can filter on one attribute per call. ResourceName with the resource ID is usually the quickest. Run it in the resource’s Region:

aws cloudtrail lookup-events --region us-east-2
--lookup-attributes AttributeKey=ResourceName,AttributeValue=i-0abc123def4567890
--query "Events[?EventName=='RunInstances'].CloudTrailEvent"
--output json | jq -r '.[] | fromjson | { time: .eventTime, type: .userIdentity.type, arn: .userIdentity.arn, invokedBy: .userIdentity.invokedBy, sourceIdentity: .userIdentity.sessionContext.sourceIdentity, accessKeyId: .userIdentity.accessKeyId, sourceIP: .sourceIPAddress, userAgent: .userAgent }'

A few things to know about this call:

The creation event depends on the resource type. These are the ones you’ll look for most:

If a ResourceName lookup comes back empty for a resource you know is recent, the creating event may not list it as a resource (this filter only matches what the event records in its resource list). Fall back to the event name, and search the event body for the ID:

aws cloudtrail lookup-events --region us-east-2
--lookup-attributes AttributeKey=EventName,AttributeValue=CreateFleet
--start-time 2026-09-01T00:00:00Z --end-time 2026-09-03T00:00:00Z
--query "Events[].CloudTrailEvent" --output json | jq -r '.[] | select(contains("i-0abc123def4567890")) | fromjson | [.eventTime, .userIdentity.arn] | @tsv'

If you don’t know which Region the resource was created in (an S3 bucket, say), loop over the enabled Regions. The multi-account script later in this guide does that for you.

You now have a userIdentity. Use its type, its ARN and invokedBy to work out what you’re looking at:

For an Identity Center user, the ARN looks like this:

arn:aws:sts::111122223333:assumed-role/AWSReservedSSO_PowerUser_4f1c2a9b8e7d6c5a/jsmith@example.com

The role name tells you the permission set (PowerUser) and the session name tells you the user. If your usernames aren’t readable, look the user up in the identity store. You need access to the Identity Center instance, which usually means the management account or a delegated administrator:

aws sso-admin list-instances --query "Instances[].IdentityStoreId" --output text

aws identitystore get-user-id --identity-store-id d-1234567890
--alternate-identifier '{"UniqueAttribute":{"AttributePath":"userName","AttributeValue":"jsmith@example.com"}}'

aws identitystore describe-user --identity-store-id d-1234567890
--user-id 544894e8-80c1-707f-60e3-3ba6510dfac1
--query "{name: DisplayName, user: UserName, emails: Emails[].Value}"

One related change: since July 2025, the sign-in and portal events that Identity Center itself emits identify users by onBehalfOf.userId rather than a username. That doesn’t change the RunInstances event in the member account.

When the session name tells you nothing, find out who obtained the credentials. The creating event has an accessKeyId starting with ASIA: temporary credentials, issued by a call to AssumeRole, AssumeRoleWithSAML or AssumeRoleWithWebIdentity. That issuing call has the same key in its responseElements.

In event history, search the role’s account for AssumeRole calls shortly before the creation time and match the key. For federated sessions, the event name is AssumeRoleWithSAML or AssumeRoleWithWebIdentity instead:

aws cloudtrail lookup-events --region us-east-1
--lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRole
--start-time 2026-09-02T13:00:00Z --end-time 2026-09-02T14:10:00Z
--query "Events[].CloudTrailEvent" --output json | jq -r '.[] | fromjson | select(.responseElements.credentials.accessKeyId == "ASIAEXAMPLEKEYID1234") | [.eventTime, .userIdentity.type, (.userIdentity.arn // .userIdentity.accountId), .requestParameters.roleSessionName] | @tsv'

Check the Region you search. STS calls are logged in the Region of the STS endpoint that served them, and calls to the global endpoint, sts.amazonaws.com, are logged in US East (N. Virginia). For a cross-account assumption, the call is logged in both the caller’s account (with the caller’s identity) and the role’s account (where the caller often shows only as an account ID). If the caller was itself a role, repeat the step. That’s role chaining, and each hop has its own AssumeRole.

For Auto Scaling, CloudFormation and EKS, repeat Step 2 for the parent. Look up CreateAutoScalingGroup with ResourceName set to the group name, or CreateStack with the stack name. CloudFormation also keeps its own record of the stack:

aws cloudformation describe-stacks --stack-name payments-api-prod
--query "Stacks[].{created: CreationTime, updated: LastUpdatedTime, role: RoleARN, tags: Tags}"

If the stack uses a service role (RoleARN), the resources were created under that role, and the person who ran CreateStack or UpdateStack is the one to ask. If RoleARN is empty, CloudFormation made the calls with the credentials of whoever ran the stack operation, so the ARN on the creation event is already that person or pipeline.

If the resource is older than 90 days, you need the copy of your CloudTrail logs in S3. First, check that a trail exists and where it writes:

aws cloudtrail describe-trails
--query "trailList[].[Name, S3BucketName, S3KeyPrefix, IsOrganizationTrail, IsMultiRegionTrail, HomeRegion]"
--output table

aws cloudtrail get-trail-status --name org-trail --query "{logging: IsLogging, latest: LatestDeliveryTime}"

Then check how long the bucket keeps logs. A lifecycle rule that expires objects after 365 days means you can’t see further back than that:

aws s3api get-bucket-lifecycle-configuration --bucket my-org-cloudtrail-logs

If there’s no trail, or it was created after the resource, the record is gone. Mark it “creator unknown”, fall back to the ownership signals in our unowned resources guide, and create an organization trail now so it doesn’t happen again.

Athena’s documentation includes a CREATE TABLE statement for CloudTrail logs that uses partition projection, so you don’t have to load partitions by hand. The version below adapts it for an organization trail, whose objects live under AWSLogs/<org-id>/<account-id>/CloudTrail/<region>/YYYY/MM/DD/, by projecting account and Region as well as date. It keeps only the columns this guide needs, and adds sessioncontext.sourceidentity. Replace the bucket, prefix, organization ID, account list and Region list with your own:

CREATE EXTERNAL TABLE cloudtrail_org ( eventversion STRING, useridentity STRUCT< type: STRING, principalid: STRING, arn: STRING, accountid: STRING, invokedby: STRING, accesskeyid: STRING, username: STRING, sessioncontext: STRUCT< attributes: STRUCT<mfaauthenticated: STRING, creationdate: STRING>, sessionissuer: STRUCT<type: STRING, principalid: STRING, arn: STRING, accountid: STRING, username: STRING>, sourceidentity: STRING>>, eventtime STRING, eventsource STRING, eventname STRING, awsregion STRING, sourceipaddress STRING, useragent STRING, errorcode STRING, requestparameters STRING, responseelements STRING, eventid STRING, readonly STRING, resources ARRAY<STRUCT<arn: STRING, accountid: STRING, type: STRING>>, recipientaccountid STRING ) PARTITIONED BY (account STRING, region STRING, timestamp STRING) ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe' STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' LOCATION 's3://my-org-cloudtrail-logs/AWSLogs/o-exampleorgid/' TBLPROPERTIES ( 'projection.enabled'='true', 'projection.account.type'='enum', 'projection.account.values'='111122223333,222233334444,333344445555', 'projection.region.type'='enum', 'projection.region.values'='us-east-1,us-east-2,us-west-2,eu-west-1', 'projection.timestamp.type'='date', 'projection.timestamp.format'='yyyy/MM/dd', 'projection.timestamp.interval'='1', 'projection.timestamp.interval.unit'='DAYS', 'projection.timestamp.ra

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →