This check flags any IAM user with the AdministratorAccess policy attached directly or through a group. That level of access turns a single leaked key into a full account takeover, so swap blanket admin for scoped, role-based permissions and require MFA on the rare humans who still need it.
Granting full admin access is the path of least resistance. A user can't do something, you attach AdministratorAccess, the ticket closes, everyone moves on. The problem is that this policy grants "Action": "*" on "Resource": "*", which means the user can do anything to anything in the account, including deleting logs, creating new admin users, and exfiltrating data from every service.
The Lensix User Has Full Admin Access check (account_adminaccess) looks for IAM users with the AWS-managed AdministratorAccess policy attached either directly to the user or inherited from a group they belong to.
What this check detects
The check inspects every IAM user in the account and flags those that resolve to AdministratorAccess through any of these paths:
- Direct attachment — the managed policy is attached straight to the user.
- Group inheritance — the user belongs to a group that has the policy attached.
The policy itself is short, which is exactly why it is so dangerous:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}
Note: This check focuses on IAM users, which are long-lived identities with static credentials. Roles assumed by services or federated identities are a separate concern, but a human user with permanent keys and full admin is the highest-risk combination.
Why it matters
An IAM user with admin access combines two things attackers love: broad blast radius and, usually, static credentials. Here is how that plays out in practice.
One leaked key compromises everything
IAM users typically carry access keys that get baked into scripts, CI pipelines, local ~/.aws/credentials files, and the occasional committed .env. When one of those keys leaks, the attacker inherits whatever the user can do. If that is AdministratorAccess, they can create backdoor users, spin up crypto-mining fleets, disable CloudTrail, and empty your S3 buckets before you finish your coffee.
Public key leaks are not rare. Bots scrape GitHub commits within seconds of a push, and exposed AWS keys are abused almost immediately.
No meaningful audit boundary
When everyone is an admin, your CloudTrail logs lose value. You cannot tell whether a DeleteBucket call was a routine cleanup or the start of a breach, because every user has the rights to do anything. Scoped permissions give you a tripwire: an action that falls outside a user's normal grant is an immediate signal.
Insider risk and accidents
Not every incident is malicious. A tired engineer running a terraform destroy against the wrong workspace can wipe production if their user has admin rights. Least privilege is as much about protecting people from mistakes as it is about stopping attackers.
Danger: A single compromised admin user can disable security logging, delete backups, and lock you out by rotating the root credentials path. Treat every admin user as a critical asset and assume its credentials will eventually leak.
How to fix it
The goal is to remove AdministratorAccess from users and replace it with the narrowest set of permissions each identity actually needs. Work through the following steps.
1. Find every user with admin access
List users with the policy attached directly:
aws iam list-entities-for-policy \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess \
--entity-filter User
And the groups that carry it, so you can check their membership:
# Groups with AdministratorAccess attached
aws iam list-entities-for-policy \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess \
--entity-filter Group
# Members of a flagged group
aws iam get-group --group-name Admins \
--query 'Users[].UserName'
2. Understand what the user actually does
Before stripping access, check what the user has used recently. IAM Access Analyzer can generate a policy from the user's CloudTrail history, which gives you a realistic starting point instead of guessing.
aws accessanalyzer start-policy-generation \
--policy-generation-details '{"principalArn":"arn:aws:iam::123456789012:user/jordan"}' \
--cloud-trail-details file://cloudtrail-details.json
Note: Policy generation reads up to 90 days of CloudTrail data. If a user runs quarterly tasks, generated policies may miss those actions, so review the output rather than applying it blindly.
3. Replace admin with a scoped policy
Create a policy that grants only what the user needs. For an engineer who manages a specific application's compute and storage, that might look like:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AppCompute",
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:StartInstances",
"ec2:StopInstances"
],
"Resource": "*",
"Condition": {
"StringEquals": { "aws:ResourceTag/team": "payments" }
}
},
{
"Sid": "AppBucket",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::payments-app-data",
"arn:aws:s3:::payments-app-data/*"
]
}
]
}
4. Detach the admin policy
Warning: Detaching admin access can break running automation if the user is also a service principal. Confirm the user is human and that nothing depends on its keys before you detach. Test the scoped policy first.
Once the replacement policy is attached and verified, remove the admin grant:
# Detach from a user
aws iam detach-user-policy \
--user-name jordan \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
# Or detach from the group, which affects all members
aws iam detach-group-policy \
--group-name Admins \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
5. Prefer roles over admin users for the few who need it
Some people genuinely need broad access occasionally. Instead of standing admin on a user, give them a role they assume on demand, ideally through AWS IAM Identity Center (formerly SSO), with MFA enforced and a short session duration.
# Assume an admin role with a 1-hour session
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/BreakGlassAdmin \
--role-session-name jordan-incident-response \
--duration-seconds 3600
This way the broad permission is temporary, logged with a clear session name, and gated behind MFA rather than a static key.
How to prevent it from happening again
Manual cleanup only holds until the next "just make it work" ticket. Bake the rule into your platform.
Block admin attachment with an SCP
A Service Control Policy at the organization level can deny attaching AdministratorAccess to users entirely, while still allowing it on dedicated break-glass roles.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAdminToUsers",
"Effect": "Deny",
"Action": [
"iam:AttachUserPolicy",
"iam:AttachGroupPolicy"
],
"Resource": "*",
"Condition": {
"ArnEquals": {
"iam:PolicyARN": "arn:aws:iam::aws:policy/AdministratorAccess"
}
}
}
]
}
Gate it in CI with policy-as-code
If you provision IAM through Terraform, catch admin attachments before they merge. A Conftest/OPA rule does the job:
# rego: fail if AdministratorAccess is attached to a user or group
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_iam_user_policy_attachment"
contains(resource.change.after.policy_arn, "AdministratorAccess")
msg := sprintf("AdministratorAccess attached to user %v", [resource.address])
}
Wire conftest test plan.json into your pipeline so the build fails on a violation.
Tip: Pair the CI gate with continuous detection in Lensix. The pipeline stops new admin grants from being merged, and the account_adminaccess check catches anything created out-of-band through the console or a forgotten script.
Best practices
- Default to roles, not users. Human access through IAM Identity Center and service access through assumed roles both eliminate the static keys that make admin users so risky.
- Right-size with Access Analyzer. Use generated policies and the last-accessed data (
aws iam get-service-last-accessed-details) to trim permissions over time. - Enforce MFA on any remaining privileged user. Require it with a condition key and verify it is active, not just configured.
- Keep a single break-glass path. One well-monitored admin role with alerting on every assumption beats a dozen admin users nobody tracks.
- Rotate and audit access keys. If a user must keep keys, rotate them on a schedule and alert on keys older than 90 days.
- Review group membership regularly. Admin granted via a group is easy to forget. Audit who sits in privileged groups at least quarterly.
Removing blanket admin access is rarely glamorous work, but it shrinks your blast radius more than almost any other single change. Start with the users who hold static keys, replace their access with scoped policies or assumable roles, and put a gate in place so the problem does not creep back in.

