You cannot eliminate what you haven't inventoried, and non-human identities — IAM users, roles, service accounts, and their associated access keys — are the category almost every mid-market AWS account has lost track of. Human identity is comparatively easy: it maps to an HRIS record, an SSO login, a name someone recognizes. Non-human identity accumulates silently, created for a one-off integration three years ago, owned by an engineer who's since left, referenced by nothing anyone can currently identify.
Why This Inventory Is Harder Than It Sounds
The naive approach — aws iam list-users and aws iam list-roles — produces a list, not an inventory. An inventory needs three additional dimensions that IAM's basic listing APIs don't provide directly: is the identity actually being used, what does it have access to, and who or what owns it.
# Identities with access keys that haven't authenticated in 90+ days
aws iam generate-credential-report
aws iam get-credential-report --query 'Content' --output text | base64 -d > credential-report.csv
# Then filter for password_last_used and access_key_1_last_used_date
# beyond your staleness threshold — these are candidates for the "orphaned" bucket
The credential report is the single richest built-in source for this — it captures last-used timestamps for both console access and each access key, per IAM user, in one export. Roles need a different check, since roles don't have long-lived credentials in the same sense:
# Roles with no recent AssumeRole activity via IAM Access Analyzer's
# "unused access" findings, or directly via CloudTrail
aws iam get-role --role-name legacy-integration-role \
--query 'Role.RoleLastUsed'
RoleLastUsed gives the last time a role was assumed and from which region — a role with no LastUsedDate at all, or one whose last use predates the current engineering team's tenure, is a strong candidate for either deletion or, at minimum, an ownership investigation before it's touched.
Building the Ownership Layer
Timestamps tell you an identity exists and whether it's active. They don't tell you why it exists or who's accountable for it — and deleting an unowned identity that turns out to be load-bearing for a quarterly batch job that runs once every three months is a worse outcome than leaving a genuinely unused one alone a little longer.
# nhi_ownership_tagger.py — cross-reference IAM identities against tagging and git history
import boto3
import subprocess
iam = boto3.client("iam")
def find_untagged_identities() -> list[dict]:
untagged = []
paginator = iam.get_paginator("list_roles")
for page in paginator.paginate():
for role in page["Roles"]:
tags = iam.list_role_tags(RoleName=role["RoleName"])["Tags"]
tag_keys = {t["Key"] for t in tags}
if "Owner" not in tag_keys or "Purpose" not in tag_keys:
untagged.append({
"role_name": role["RoleName"],
"created": str(role["CreateDate"]),
"last_used": role.get("RoleLastUsed", {}).get("LastUsedDate"),
})
return untagged
For roles created via Terraform, git blame on the resource definition often recovers ownership even when tags are missing — the commit author and the PR context frequently identify the original purpose even years later, which is a useful forensic path before defaulting to "unowned, delete after grace period."
The Inventory Feeds Two Downstream Programs
A completed non-human identity inventory isn't the end state — it's the input to two separate remediation tracks that shouldn't be conflated. Identities confirmed active and load-bearing feed the credential-elimination track: migrating them off long-lived access keys onto federated or short-lived credentials. Identities confirmed inactive or genuinely orphaned feed a straightforward deletion track, after a documented grace period and stakeholder notification in case ownership assumptions were wrong.
Treating every finding as "must eliminate the credential" without first separating active-but-risky from actually-dead identities wastes engineering effort migrating something that should simply be deleted, and risks breaking something load-bearing by deleting what should have been migrated instead.
Frequently Asked Questions
How often should a non-human identity inventory be refreshed?
Quarterly at minimum, aligned with your access review cadence — non-human identity sprawl accumulates at a similar rate to human access sprawl, driven by new integrations and one-off automation scripts, and a stale inventory from a year ago misses everything created since.
What counts as "non-human identity" beyond IAM users and roles?
Service accounts in third-party SaaS tools with API access to your AWS environment, CI/CD pipeline identities, Lambda execution roles, EC2 instance profiles, and cross-account trust relationships all count — the inventory scope should extend beyond IAM's own console to anywhere an automated process authenticates into your environment.
Is a credential report sufficient, or do we need a dedicated NHI tool?
For AWS-only environments with a moderate account count, the built-in credential report plus IAM Access Analyzer's unused access findings covers most of the inventory need without additional tooling cost. Organizations with heavy multi-cloud or SaaS-integrated non-human identity sprawl may see faster time-to-value from a dedicated NHI platform, but it's not a prerequisite for starting.
What's a reasonable staleness threshold for flagging an identity as inactive?
90 days of no authentication activity is a common starting threshold, but it should be calibrated against your actual business cycles — an identity used only for annual tax reporting integrations would be flagged incorrectly at 90 days. Cross-reference against known scheduled/periodic workloads before treating every 90-day-stale finding as abandoned.
What's the risk of deleting an IAM role that turns out to still be needed?
Deleting a role that's actively referenced (by a Lambda function, an EC2 instance profile, a cross-account trust relationship) breaks whatever depends on it, sometimes silently until the next time that dependency is exercised. This is exactly why the ownership-investigation step and a documented grace period before deletion matter more than moving fast on the deletion itself.
Rutagon builds non-human identity inventories and ownership mapping as the first phase of a credential elimination program — before migration or deletion, not instead of it.
Talk to us about your credential elimination pilot → rutagon.com/contact | 907-841-8407 | contact@rutagon.com
Related reading: Secrets Sprawl Remediation Sprint Guide · Eliminating Standing Service Account Passwords · Security Automation Capability
External reference: AWS IAM Documentation — Getting Credential Reports