Many startups hire an agency to build their AWS setup and give it full AdministratorAccess. The contract ends. Nobody revokes anything.
Months or years later, that agency, and every person who ever worked there, may still hold working credentials to your production account. Most founders have no idea. If an outside vendor holds the keys to your infrastructure, you only half own it.
Why it happens
Access is easy to grant and awkward to remove. Granting is one click while someone is waiting on you. Removing it needs a deadline, an owner and the confidence that nothing still depends on it.
Handover is the worst moment. People create IAM users, long-lived access keys and cross-account roles to get things working, then never write down what exists. A contractor’s access key may also be sitting in a CI system or on an old laptop.
A forgotten key with admin rights is the easiest way into your account, and nobody is watching it. If something goes wrong, it is also hard to prove who did what.
How to check your own account
Start with a credential report. It lists every IAM user with the age and last use of their password and access keys.
aws iam generate-credential-report
aws iam get-credential-report --query Content --output text | base64 --decode > credential-report.csv
Open the CSV. Look for users whose password_last_used and access_key_1_last_used_date are old or empty, users where mfa_active is false, and keys that have never been rotated. The first row is the root user. Check that its access_key_1_active is false. Root should have MFA and no access keys.
Then look at individual users:
aws iam list-users
aws iam list-access-keys --user-name SOME_USER
aws iam get-access-key-last-used --access-key-id AKIAEXAMPLEKEYID
To see who has full admin rights, list everyone attached to the managed policy:
aws iam list-entities-for-policy \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
Users are only half of it. Agencies often connect through roles that trust another AWS account. Look at each role’s trust policy and check the account IDs in it:
aws iam list-roles \
--query 'Roles[].[RoleName,AssumeRolePolicyDocument.Statement[].Principal.AWS]'
If you see an account ID you do not recognise, find out whose it is. Check when a role was last used with aws iam get-role --role-name NAME --query 'Role.RoleLastUsed'.
In the console, IAM Access Analyzer can do much of this continuously. Open IAM, then Access Analyzer, and create an analyzer of type “Unused access”. It reports unused roles, unused access keys and unused passwords for the window you choose. Unlike the external access analyzer, this one is a paid feature, charged per IAM role and user analysed each month, so check the pricing page first. Under Unused access you can open each finding and see the recommended action. A separate external access analyzer shows resources shared outside your account.
Do not forget IAM Identity Center, if you use it, and any vendor users in your SSO provider.
Fixing it and keeping it fixed
Do not delete first. Deactivate, wait, then delete.
aws iam update-access-key \
--access-key-id AKIAEXAMPLEKEYID \
--user-name SOME_USER --status Inactive
If nothing breaks over a week or two, delete the key, then the user. Before you disable anything that looks important, search CloudTrail for the key or user to see what it has been doing.
After the clean-up:
- Enable MFA on the root user, and remove any root access keys.
- Rotate the keys that legitimate systems still use, and move workloads to IAM roles so there are no long-lived keys at all.
- Replace broad admin access with roles that grant what each person or system needs.
- Give agencies and contractors time-limited access, and put an end date in the contract that includes removing it.
- Review the credential report every quarter.
This is also the first thing I check when I take over an account. If you are not sure who holds access to yours, get in touch and I will walk through it with you.