Skip to content

Resource creators (CloudTrail)

Point StaleSweep at the S3 bucket your CloudTrail trail delivers to, and every flagged resource shows who created it — used as its owner wherever no ownership tag says otherwise.

Most accounts tag only some of what they run, and the first question anyone asks of a flagged resource is whose it is. CloudTrail recorded the call that created it, with the identity that made it. StaleSweep reads those records from your trail’s bucket with the same read-only role it already uses — nothing is created in your account, and nothing in the bucket is changed.

  • Only the days that matter. Each flagged resource records when it was created. The lookup opens only the log folders for the days around that moment (two, or three across midnight), in the resource's region, and reads each log file at most once — never the whole bucket.
  • Only who made the call. From the matching create event it keeps the identity: its type, ARN and name, an assumed role's session name, the AWS service that called on your behalf, and the STS source identity, with the event's name and time. No request parameters, IP addresses or user agents are stored.
  • Tags still win. A resource's owner is its ownership tag when it has one, and the CloudTrail creator otherwise. Both appear in the Owner section on the Resources page and in the owner and creator fields of AI access (MCP) results.
  • As far back as your trail keeps. It reads the bucket rather than CloudTrail's event history API, which only covers 90 days. Most waste is older than that; the bucket holds whatever your trail's retention keeps.
Tip: It’s optional and off until an admin turns it on for a connection. For an AWS Organization, set it up once on the management account: one organization trail covers every member account.

Before you start

  1. A trail that delivers management events to S3 — ideally a multi-region trail, or an organization trail for an AWS Organization.
  2. The trail’s bucket and prefix: in the AWS console, open CloudTrail → Trails and pick the trail; its storage location shows both. The prefix is the folder that holds AWSLogs/, if the trail was given one.
  3. If the trail encrypts its logs with a KMS key (SSE-KMS), that key’s ARN — the …:key/… form, not an alias.

Set it up while connecting an account

  1. In the connect wizard, on the Create the IAM role step, turn on Show who created each resource.
  2. Enter the bucket (pasting s3://bucket/prefix fills both fields), the prefix if any, and the KMS key ARN if the trail uses one. The template and the deploy link update to set CloudTrailBucket and CloudTrailKmsKeyArn for you — deploy only once the wizard says they do.
  3. Deploy the stack and paste the role ARN, as usual.
  4. Once the connection is made, StaleSweep turns the lookup on with that bucket. If AWS refuses the read, the last step says why — the connection itself is fine — and offers the guided setup with your bucket already filled in.

Add it to an existing connection

1. Let the role read the bucket

In CloudFormation, update your StaleSweep stack and set the CloudTrailBucket parameter (and CloudTrailKmsKeyArn for a KMS-encrypted trail). If the stack predates these parameters, choose Replace existing template with the current template:

https://stalesweep-cloudformation.s3.us-west-2.amazonaws.com/cloudformation/stalesweep-scan-role.yaml

For an AWS Organization, update the stack in the management account.

2. Point the lookup at the bucket

  1. On the Providers page, open the connection’s Edit dialog and choose Set up CloudTrail.
  2. Find my trail fills in the bucket and prefix from the trails your account reports; or enter them yourself.
  3. Test connectionruns the same check saving does — the role can read the bucket, and it holds this account’s logs — without saving anything.
  4. Save and start reading. The Edit dialog then shows when the lookup last ran and how many resources it has answered.

AWS Organizations

An organization trail delivers every account’s events to one bucket, under AWSLogs/<organization-id>/<account-id>/. Set the lookup up on the management account’s connection and it answers for every member account too, reading each member from its own folder with the management account’s role — members need no stack change and no setup of their own. A member’s Edit dialog shows Via organization with the last run. A member the bucket has no folder for is named on the status line; one whose logs live elsewhere can have a lookup of its own, which then takes over for it.

A bucket in another account

Organization trails usually deliver to a Log Archive account’s bucket. The stack can grant only what your own account controls, so that bucket’s policy must also allow StaleSweep’s role. The wizard shows the exact role ARN to use. Add these statements to the bucket policy, keeping the ones CloudTrail needs:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "StaleSweepListTrailLogs",
      "Effect": "Allow",
      "Principal": { "AWS": "<StaleSweep role ARN shown in the wizard>" },
      "Action": ["s3:ListBucket", "s3:GetBucketLocation"],
      "Resource": "arn:aws:s3:::<trail-bucket>"
    },
    {
      "Sid": "StaleSweepReadTrailLogs",
      "Effect": "Allow",
      "Principal": { "AWS": "<StaleSweep role ARN shown in the wizard>" },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::<trail-bucket>/<prefix/>AWSLogs/*/CloudTrail/*"
    }
  ]
}

If the trail encrypts with a KMS key in that account, add this statement to the key policy too:

{
  "Sid": "StaleSweepDecryptTrailLogs",
  "Effect": "Allow",
  "Principal": { "AWS": "<StaleSweep role ARN shown in the wizard>" },
  "Action": "kms:Decrypt",
  "Resource": "*",
  "Condition": { "StringLike": { "kms:ViaService": "s3.*.amazonaws.com" } }
}
Warning: GovCloud and China accounts use their own partition in ARNs (arn:aws-us-gov:, arn:aws-cn:). Use the role ARN exactly as the wizard shows it.

What the role is granted

Setting CloudTrailBucketadds one managed policy to the scan role. It may list and read anything under the bucket’s AWSLogs/ folders (at the root or under a prefix), and decrypts only through S3. Left blank, it grants nothing.

StaleSweepCloudTrailReadPolicy:   # only when CloudTrailBucket is set
  Statement:
    - Effect: Allow                  # list the AWSLogs/ folders only
      Action: [s3:ListBucket, s3:GetBucketLocation]
      Resource: arn:aws:s3:::<CloudTrailBucket>
      Condition:
        StringLikeIfExists:
          s3:prefix: ['AWSLogs/*', '*/AWSLogs/*']
    - Effect: Allow                  # read objects under AWSLogs/ only
      Action: s3:GetObject
      Resource:
        - arn:aws:s3:::<CloudTrailBucket>/AWSLogs/*
        - arn:aws:s3:::<CloudTrailBucket>/*/AWSLogs/*
    - Effect: Allow                  # only when CloudTrailKmsKeyArn is set
      Action: kms:Decrypt
      Resource: <CloudTrailKmsKeyArn>
      Condition:
        StringLike:
          kms:ViaService: ['s3.*.amazonaws.com', 's3.*.amazonaws.com.cn']
Warning: AWSLogs/ is where every AWS service delivers logs, not only CloudTrail. If load balancer access logs, VPC flow logs or AWS Config snapshots go to the same bucket, the role can read those too, although the lookup only ever opens files under …/CloudTrail/. To keep the access to CloudTrail alone, deliver the trail to a bucket of its own. For a bucket in another account, the bucket policy above already limits reads to the CloudTrail/ folders.

What you’ll see

The lookup runs in the background about once an hour, each run within a fixed budget of files, bytes and time; a large backlog takes several runs. Each resource ends in one of these states:

CloudTrail lookup statuses
StatusWhat it means
FoundThe create call was in the logs. Its creator is shown, and used as the owner when the resource has no ownership tag.
Not foundEvery log file for the creation days was read and none created this resource. Common for resources created as a side effect of another call — an EBS volume launched with an instance, or a snapshot made by CreateImage — which have no create event of their own.
No logsThe trail held no log files at all for the days the resource was created: its retention had already expired them, the trail was created later, or it does not log that region. This says nothing about who created it.
Can't be looked upAWS reported no creation date for the resource, so there is no day to open; or its kind has no create event a trail records (incomplete multipart uploads are an S3 data event).
PendingNot judged yet. Resources created in the last two days wait for CloudTrail to finish delivering their logs, and a large backlog is worked through over several runs.

The S3 requests are made in your account: a listing and a handful of reads for each day folder opened. For most accounts that is a few cents a month.

Troubleshooting

Common CloudTrail setup problems
What you seeWhat to do
“StaleSweep’s role isn’t allowed to read this bucket yet”The stack has no CloudTrailBucket set, or names another bucket — update it (see above). A bucket in another account also needs its bucket policy; a trail encrypted with KMS needs CloudTrailKmsKeyArn and, across accounts, the key policy.
“Readable, but no logs for this account”The prefix doesn’t match the trail’s storage location. Use the folder that holds AWSLogs/. For an organization trail, set it up on the management account’s connection, with the organization trail’s own bucket.
A member account named on the status lineThe organization trail’s bucket holds no folder for that account: the trail doesn’t cover it, or its logs go elsewhere. Its resources stay pending; give that member its own lookup if its logs live in another bucket.
“Took too long” or “more folders than a trail writes”Point the prefix at the trail’s own folder (the one holding AWSLogs/), not the bucket root of a bucket shared with other logs.
Many resources show No logsYour trail’s retention is shorter than the age of your oldest waste, or the trail doesn’t log every region. Longer retention covers resources created from then on.

Turning it off

Edit connection → Turn off stops the lookup at once; the bucket is kept for next time. To withdraw the read itself, clear CloudTrailBucket on the stack. See Security & privacy for how the read fits StaleSweep’s access model.