An EC2 instance whose instance profile carries administrator rights hands account-wide control to anyone who can run code on that server or reach its metadata service. The credentials are delivered to the instance automatically, refreshed for as long as it runs, and usable by every process on it. This runbook shows how to map every running instance to its instance profile and role, decide which roles are effectively administrator, confirm what they can do with the IAM policy simulator, and replace them without taking the workload down. It covers single accounts and AWS Organizations and is written for cloud security, platform and detection engineers. For how the metadata service delivers these credentials and how to harden it, see the EC2 Instance Metadata Service Hardening Guide.
This is an independent guide from NHI Mgmt Group, based on AWS's public documentation as of November 2026. It is not sponsored or reviewed by AWS. Product features change, so confirm settings against AWS's current documentation.
Key takeaways
- Start from the instances, not the roles.
aws ec2 describe-iam-instance-profile-associationslists which profile each instance holds right now; an admin role that no instance uses is a different, lower priority problem. - One profile, one role. AWS limits an instance profile to a single role, but one role can sit in many profiles, so resolve profile to role with
get-instance-profilebefore judging permissions. - Admin is wider than AdministratorAccess. Treat
"Action": "*",iam:*,iam:PassRoleon"*"with compute creation rights, and IAM write actions as effective administrator, then prove it withsimulate-principal-policy. - Replace the profile, do not edit the role in place. AWS recommends replacing an instance's profile; removing a role from a profile can take up to an hour to take effect.
- Watch for credential use off the instance. GuardDuty findings
InstanceCredentialExfiltration.OutsideAWSand.InsideAWSflag instance credentials used from elsewhere, which is exactly what makes an admin profile dangerous.
When you need this
- Cloud posture reviews: admin instance profiles are among the most common high-impact findings in AWS assessments and are a direct target for NIST SP 800-53 AC-6(5) Privileged Accounts.
- After an application vulnerability: server-side request forgery or remote code execution on an instance turns its role into the attacker's role. In the Capital One breach, role credentials taken from a firewall instance were enough to read large volumes of S3 data.
- Before decommissioning IAM users: teams often moved hard-coded keys onto an instance role and gave it the same broad policy; the migration fixed the key problem but not the privilege.
- Agent and automation hosts: build servers, configuration management masters and AI agent hosts frequently run with admin profiles because they "need to do everything".
What counts as admin rights on an instance role
| Grant on the role | Why it is effectively admin | How to detect | Priority |
|---|---|---|---|
AWS managed AdministratorAccess | Allows all actions on all resources | Attached policy ARN arn:aws:iam::aws:policy/AdministratorAccess | Critical |
Customer managed or inline "Action": "*" on "*" | Same as above, under another name | Statement scan; Security Hub IAM.1 for customer managed policies only | Critical |
iam:* or IAM write actions such as iam:AttachRolePolicy, iam:PutRolePolicy, iam:CreateAccessKey | The role can grant itself or others any permission | Simulator on a fixed IAM action list | Critical |
iam:PassRole on "*" plus ec2:RunInstances or lambda:CreateFunction | Can launch compute with a more powerful role and act through it | Access Analyzer security warning "Pass role with star in resource"; simulator | Critical |
PowerUserAccess | Full access to services other than most IAM and Organizations actions; still reaches all data | Attached policy ARN | High |
Service-wide wildcards such as s3:*, kms:*, secretsmanager:* | Full control of data and keys in that service | Statement scan; Security Hub IAM.21 | High |
Permission boundaries and service control policies can cut any of these down, which is why the simulator step matters: it evaluates SCPs and reports AllowedByOrganizations and AllowedByPermissionsBoundary. A role that looks like admin but is fenced by an SCP is still a finding, just a lower one. The method for finding wildcard statements is covered in How to Find aws iam policies with wildcard actions.
Before you start
| Source | What it gives you | Permission |
|---|---|---|
ec2 describe-iam-instance-profile-associations | InstanceId, IamInstanceProfile.Arn, AssociationId, State per Region | ec2:DescribeIamInstanceProfileAssociations |
iam get-instance-profile, list-instance-profiles | The role inside each profile | iam:GetInstanceProfile, iam:ListInstanceProfiles |
iam get-account-authorization-details --filter Role | Each role's attached and inline policies and its InstanceProfileList | iam:GetAccountAuthorizationDetails |
iam list-entities-for-policy | Every role holding a given managed policy | iam:ListEntitiesForPolicy |
iam simulate-principal-policy | allowed, explicitDeny or implicitDeny per action, including SCP and boundary effects | iam:SimulatePrincipalPolicy |
| GuardDuty | Instance credential exfiltration findings | GuardDuty enabled in each Region |
IAM is global but instance associations are Regional, so loop over every enabled Region. Use a read-only audit role assumed into each account, and keep remediation in a separate role with change approval.
Step-by-step
Steps 1 to 4 are read-only. Step 5 changes what the instance can do but is reversible. Step 6 is a clean-up.
- List instance profile associations in every Region.
Keep theaws ec2 describe-iam-instance-profile-associations --region <region> \ --filters Name=state,Values=associated \ --query "IamInstanceProfileAssociations[].[InstanceId,IamInstanceProfile.Arn,AssociationId]" --output textAssociationId: you need it to replace the profile later. - Resolve each profile to its role.
Profiles created in the console share the role's name, but profiles created by templates or the CLI often do not, so never infer the role from the profile name.aws iam get-instance-profile --instance-profile-name <profile-name> --query "InstanceProfile.Roles[].Arn" - Flag roles that hold admin-class policies. Start with the AWS managed policies, which is quick:
Then useaws iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --entity-filter Role aws iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/PowerUserAccess --entity-filter Role aws iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/IAMFullAccess --entity-filter Roleget-account-authorization-details --filter Roleto scan inline policies (RolePolicyList) and the default versions of attached customer managed policies for the patterns in the table above. Join the result with step 2: the critical list is roles that are both admin-class and attached to a running instance. - Prove effective permissions with the simulator. For each candidate role, simulate a fixed set of sensitive actions:
Readaws iam simulate-principal-policy --policy-source-arn arn:aws:iam::<account-id>:role/<role-name> \ --action-names iam:CreateAccessKey iam:AttachRolePolicy iam:PassRole ec2:RunInstances \ kms:Decrypt secretsmanager:GetSecretValue s3:GetObject organizations:LeaveOrganizationEvalDecisionfor each action and theOrganizationsDecisionDetailandPermissionsBoundaryDecisionDetailfields. AWS notes the simulator can differ from the live environment, and the default resource is"*", so add--resource-arnsfor your most sensitive buckets and keys. Record the owner, the application and the IMDS setting of each instance (HttpTokensindescribe-instancesmetadata options): an admin profile on an instance still allowing IMDSv1 is the worst combination. - Replace the profile with a scoped one. Build a new role from what the workload actually calls. IAM Access Analyzer policy generation drafts a policy from up to 90 days of CloudTrail activity; add
iam:PassRoleby hand if needed, as CloudTrail does not track it. Create the new role and instance profile, then swap it on the running instance without a stop:
The response showsaws iam create-instance-profile --instance-profile-name <app>-scoped aws iam add-role-to-instance-profile --instance-profile-name <app>-scoped --role-name <app>-scoped-role aws ec2 replace-iam-instance-profile-association --association-id <iip-assoc-id> \ --iam-instance-profile Name=<app>-scopedStateasassociating; confirm it reachesassociated. Do this on one instance of an Auto Scaling group or a canary host first, and update the launch template so new instances get the scoped profile. - Cut off old sessions and retire the admin profile. Credentials already delivered from the old role stay valid until they expire. For a suspected compromise, attach a deny on
aws:TokenIssueTimeto the old role (the console's Revoke active sessions option does this with theAWSRevokeOlderSessionsinline policy). Once no instance uses the admin profile, remove the admin policy from the role or delete the profile following How to find unused AWS IAM roles and access keys.
Verify it worked
- Associations: a re-run of step 1 shows the scoped profile on every instance of the application, and the launch template references it.
- Simulator: the sensitive action list returns
implicitDenyorexplicitDenyfor the new role, apart from actions the workload genuinely needs. - Workload health: no rise in
AccessDeniederrors in CloudTrail for the new role's sessions across a full business cycle. - Inventory: the count of running instances with admin-class profiles falls and stays down after new deployments.
Rollback and common errors
| Situation | Fix |
|---|---|
| The workload breaks after the swap | Run replace-iam-instance-profile-association again with the old profile name, using the new AssociationId from describe-iam-instance-profile-associations; then add the missing action to the scoped role |
| Permissions did not change after editing the role inside the profile | AWS documents a delay of up to one hour; replace the profile instead, or disassociate and associate it, or stop and start the instance |
| Old admin credentials still work | They remain valid until expiry; apply the aws:TokenIssueTime deny to the old role |
| New instances come up with the admin profile again | The launch template, Auto Scaling group or infrastructure as code still references it; fix the source |
| Simulator says allowed but calls fail, or the reverse | Resource policies, resource control policies or missing condition context; check MissingContextValues and test live |
Choosing the right approach
For one account, the three CLI calls in steps 1 to 3 in a loop over Regions are enough, and they can run weekly. For an organisation, run the same discovery from a security tooling account through a read-only role in every member account, or use your cloud posture tool's equivalent query, and feed the critical list to the owning teams as tickets. Preventive controls matter more at scale: an SCP or permission boundary that stops workload roles holding IAM write actions, and a policy that blocks iam:PassRole of admin roles to ec2.amazonaws.com, stop the problem returning. See the AWS Organizations and SCPs Guide.
Some hosts genuinely need wide rights, such as an infrastructure deployment runner. Prefer moving that job to a pipeline with short-lived federated credentials. If it must stay on an instance, isolate the host, require IMDSv2 with a hop limit of 1, restrict the role with conditions such as source VPC, and treat it as a privileged asset in your PAM programme. See Cloud Instance Metadata Services Compared Guide.
Where identity fits
An instance role is a non-human identity like a service account in Active Directory: it authenticates without a person, its credentials live on a host many people and processes can touch, and it often outlives the project that created it. The same review questions apply as for service accounts with Domain Admin rights: who owns it, what does it actually use, and what is the smallest grant that keeps the workload running. Record each instance role in your NHI inventory with an owner tag, the application, the instance profile names that contain it, and the date of the last permission review.
| Inventory field | Source | Why it matters |
|---|---|---|
| Instance IDs and Regions using the profile | describe-iam-instance-profile-associations | Blast radius and the hosts to patch first |
| Role and every profile containing it | get-account-authorization-details InstanceProfileList | Shared roles break several workloads when trimmed |
| Admin-class grants found | Policy scan and simulator | Drives priority |
| IMDS setting | describe-instances metadata options | IMDSv1 plus admin is the top priority |
| Owner and exception status | Tags and ticket | Someone accountable for the fix or the accepted risk |
RoleLastUsed | get-role | Unused admin roles can be removed instead of scoped |
Detection and monitoring
- GuardDuty:
UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS(High) fires when credentials created for an instance are used from an external IP address;.InsideAWS(High, or Medium if the calling account is affiliated) fires when they are used from another AWS account. Route both to the on-call team for any instance on your admin list. - CloudTrail: alert on
AssociateIamInstanceProfileandReplaceIamInstanceProfileAssociationevents that attach an admin-class profile, and onAttachRolePolicyorPutRolePolicyagainst roles that sit in instance profiles. - IAM activity from instance roles: any IAM write call made by an instance role session is unusual for most workloads and worth a detection rule.
Common failure patterns
- Checking only AdministratorAccess: inline
"*"policies and IAM write rights give the same power and are missed by a name search. - Editing the shared role: one role can sit in many profiles, so trimming it for one application breaks others; give each workload its own role.
- Fixing the instance, not the template: the next scale-out or redeploy brings the admin profile back.
- Leaving IMDSv1 on admin hosts: request forgery against the application becomes credential theft. The Capital One breach is the reference case.
- Assuming the swap revokes old credentials: credentials already delivered remain valid until they expire unless you deny them by issue time.
Metrics
| Metric | Target |
|---|---|
| Running instances with admin-class instance profiles | Zero outside documented exceptions |
| Instance roles allowed IAM write actions (simulator) | Zero |
| Admin-class profiles on instances allowing IMDSv1 | Zero |
| Launch templates referencing admin-class profiles | Zero |
| Instance credential exfiltration findings triaged within SLA | 100% |
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev 5 | AC-6 Least Privilege; AC-6(5) Privileged Accounts; SI-4 System Monitoring | Restrict admin rights on workloads; monitor their use |
| NIST CSF 2.0 | PR.AA-05; DE.CM-09 | Least privilege permissions; monitoring of runtime environments |
| MITRE ATT&CK | T1552.005 Cloud Instance Metadata API; T1078.004 Cloud Accounts; T1098.003 Additional Cloud Roles | How instance credentials are taken and how admin rights are then abused |
Practitioner checklist
- List associated instance profiles in every Region of every account.
- Resolve each profile to its role; never infer from names.
- Flag AdministratorAccess, PowerUserAccess, IAMFullAccess, inline "*", IAM write and PassRole on "*".
- Confirm effective permissions with simulate-principal-policy, including SCP and boundary effects.
- Check IMDS settings on every admin-profile instance.
- Swap to a scoped profile with replace-iam-instance-profile-association, canary first.
- Fix launch templates and infrastructure as code; revoke old sessions where compromise is possible.
- Route GuardDuty instance credential exfiltration findings for these hosts to on-call.
Standards and references
- AWS: Use instance profiles
- AWS CLI Reference: describe-iam-instance-profile-associations
- AWS CLI Reference: replace-iam-instance-profile-association
- AWS CLI Reference: list-entities-for-policy
- AWS CLI Reference: simulate-principal-policy
- AWS: GuardDuty IAM finding types
- AWS: Revoke IAM role temporary security credentials
Related NHI Mgmt Group resources: EC2 Instance Metadata Service Hardening Guide · How to Find IAM Privilege Escalation Paths in AWS · How to Find aws iam policies with wildcard actions · How to find service accounts with Domain Admin rights