This check flags active IAM access keys that haven't been used in over 90 days. Stale keys are forgotten credentials that attackers love, since nobody notices when they get used. Deactivate the key, confirm nothing breaks, then delete it.
Every active access key is a long-lived credential sitting in the open. When a key hasn't been used in three months, that usually means one of two things: the workload that needed it is gone, or someone created the key, copied it somewhere, and forgot about it. Either way you now have a credential that nobody is watching, which is exactly the kind of thing an attacker wants to find.
This check looks at every IAM user in your account, inspects their access keys, and reports any active key whose last-used timestamp is more than 90 days old (or that has never been used at all since creation).
What this check detects
IAM access keys are the static credential pairs (an access key ID and a secret access key) used to authenticate programmatically against AWS APIs. Each IAM user can have up to two active keys at a time.
The check evaluates the AccessKeyLastUsed data that AWS tracks for every key. If a key is in the Active state but its last-used date is older than 90 days, it gets flagged. Keys that have never been used and were created more than 90 days ago are also caught, since they represent the same risk with even less justification for existing.
Note: AWS records the last-used timestamp at roughly four-hour granularity and only for service-level activity. The data goes back to 2015. A key created before then with no recent activity will show no last-used date, which the check treats as unused.
Why it matters
A stale access key is a credential with no owner paying attention to it. That combination drives most of the real-world risk.
Forgotten keys leak from everywhere
Long-lived keys end up committed to Git repositories, baked into container images, dropped in CI environment variables, stored in plaintext config files, and pasted into Slack. Tools like GitGuardian and AWS's own automated scanning find thousands of leaked AWS keys in public repos every month. If the key is still active, a leaked secret is a working credential.
Nobody notices the breach
The dangerous part of an unused key is not that it exists, it is that no one is looking at it. When an attacker picks up a leaked key for a service you actively use, you might spot unusual activity. When they pick up a key tied to a decommissioned workload, there is no baseline of normal behavior, no owner expecting to see traffic, and no alarm when usage suddenly appears.
Warning: Access keys do not expire on their own. A key issued in 2019 is just as valid today as the day it was created unless you explicitly rotate or remove it. Time does not reduce the risk, it increases it.
It is a compliance finding
CIS AWS Foundations Benchmark explicitly requires that credentials unused for 90 days or more be disabled or removed. PCI DSS, SOC 2, and most internal security policies carry similar rules. Leaving stale keys around is an easy audit failure that auditors look for first because it is trivial to check.
How to fix it
The safe path is always: identify, deactivate, observe, then delete. Deactivating a key is reversible and instantly tells you whether anything still depends on it. Deleting it is not reversible.
Step 1: Find which keys are stale
Generate a credential report, which includes last-used data for every user in the account:
aws iam generate-credential-report
aws iam get-credential-report \
--query 'Content' --output text | base64 -d > credential-report.csv
To inspect a specific user's keys directly:
aws iam list-access-keys --user-name alice
aws iam get-access-key-last-used --access-key-id AKIAEXAMPLE12345
The second command returns a LastUsedDate along with the region and service the key last touched. That context helps you figure out what the key was for before you decide what to do with it.
Step 2: Deactivate the key
Switch the key to inactive rather than deleting it outright:
aws iam update-access-key \
--user-name alice \
--access-key-id AKIAEXAMPLE12345 \
--status Inactive
An inactive key cannot be used to authenticate, but it still exists and can be reactivated in seconds if you discover a job actually needed it.
Tip: Wait a week or two after deactivating before you delete. Some workloads run monthly or quarterly. If nothing complains during that window, you can delete with confidence. Watch CloudTrail for AccessDenied errors referencing the key during this period.
Step 3: Delete the key
Danger: Deleting an access key is permanent. If a live workload still uses it, that workload will start failing immediately and you cannot recover the secret. Confirm the key is truly unused before running this.
aws iam delete-access-key \
--user-name alice \
--access-key-id AKIAEXAMPLE12345
Doing it in the console
- Open the IAM console and go to Users.
- Select the user, then open the Security credentials tab.
- Under Access keys, check the Last used column.
- Use the Actions menu to set the key to Deactivate, then later Delete.
If the key is still needed: rotate it
Sometimes a key is genuinely in use but the workload simply runs infrequently. In that case, rotate rather than remove. Create a new key, update the consumer, then deactivate and delete the old one:
# Create the replacement
aws iam create-access-key --user-name alice
# Update your application/secrets manager with the new key, then:
aws iam update-access-key --user-name alice \
--access-key-id OLD_KEY_ID --status Inactive
# After confirming the new key works:
aws iam delete-access-key --user-name alice \
--access-key-id OLD_KEY_ID
How to prevent it from happening again
The real fix is reducing how many long-lived keys exist in the first place, then automating cleanup of the ones that remain.
Prefer roles over long-lived keys
Most use cases for access keys have a better alternative:
- EC2, ECS, Lambda: attach an IAM role. The service gets temporary, auto-rotating credentials and you never manage a static key.
- Human users: use IAM Identity Center (formerly AWS SSO) with short-lived session credentials instead of issuing personal access keys.
- CI/CD pipelines: use OIDC federation. GitHub Actions, GitLab, and others can assume an AWS role without any stored secret.
Here is a GitHub Actions OIDC role assumption that replaces stored keys entirely:
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-deploy
aws-region: us-east-1
Enforce a maximum key age with policy-as-code
Block long-lived keys before they are created or catch them in review. A Service Control Policy can prevent IAM users from being created at all in member accounts, pushing everyone toward federated access:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAccessKeyCreation",
"Effect": "Deny",
"Action": "iam:CreateAccessKey",
"Resource": "*"
}
]
}
Warning: Apply a deny-all SCP like this only after you have migrated existing workloads to roles. Attaching it while services still depend on access keys will break them.
Automate the cleanup
You can wire up an EventBridge scheduled rule that triggers a Lambda to scan credential reports and deactivate keys past your age threshold. A simpler starting point is a scheduled script that runs the credential report and opens a ticket for any key over 90 days. The point is to make stale keys impossible to ignore rather than relying on someone remembering.
Tip: Lensix runs this check continuously and surfaces stale keys with their last-used dates and owning users, so you do not have to parse credential report CSVs by hand or build the scanning logic yourself.
Best practices
- Treat 90 days as a ceiling, not a target. Rotate active keys on a 30 to 90 day cycle, and remove keys the moment a workload is decommissioned.
- One key per workload. Sharing a single key across multiple services makes rotation painful and incident response harder, because you cannot tell which consumer leaked it.
- Store secrets in a secrets manager. Keep keys out of code, config files, and environment variables baked into images. Use AWS Secrets Manager or Parameter Store with rotation enabled.
- Tag keys with an owner. Use IAM user tags or naming conventions so every key maps to a team. Orphaned keys with no owner are the ones that linger.
- Monitor key usage with CloudTrail. Alert on a previously dormant key suddenly being used, which is a strong indicator of credential compromise.
- Audit regularly. Review the IAM credential report monthly. The report's last-used columns make stale keys obvious at a glance.
Access keys are convenient, and that convenience is exactly why they pile up. Each one is a small, permanent liability. The accounts with the cleanest security posture are the ones that have almost no static keys at all, leaning on roles and federation instead. Start by clearing out what is unused today, then change how new credentials get issued so you are not back here in 90 days.

