You've enabled CloudTrail S3 data events. You're streaming them into your SIEM. You have behavioral baselines per IAM role. And then you open an investigation and realize: this "IAM role" is actually used by a Lambda function during weekday nights and a data science team during business hours. The baseline is meaningless — it's averaging two completely different behavioral profiles into one noisy model.
This is the NHI problem in behavioral detection. Non-human identities (NHIs) — Lambda functions, service accounts, CI/CD pipelines, AI agents — operate S3 access patterns that look nothing like human access. If they share an IAM role with humans, or even just share a behavioral baseline, your detection accuracy suffers dramatically.
How to Distinguish NHI from Human in CloudTrail
CloudTrail gives you the signals to classify identity type — you just need to know where to look. The key fields are:
1. userAgent
The User-Agent field in CloudTrail events is the most reliable identity classifier. AWS SDK versions, CLI versions, and console access each produce distinct user-agent strings:
# Human via AWS Console:
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...aws-console"
# Human via AWS CLI:
userAgent: "aws-cli/2.15.0 Python/3.11.5 Darwin/23.0.0 botocore/2.0.0"
# Lambda function via Python SDK:
userAgent: "python-requests/2.31.0 Botocore/1.32.0"
# Terraform / infrastructure automation:
userAgent: "APN/1.0 HashiCorp/1.0 Terraform/1.7.0..."
# Kubernetes pod via Go SDK:
userAgent: "aws-sdk-go/1.50.0 (go1.21; linux; amd64)"
# GitHub Actions CI/CD:
userAgent: "aws-cli/2.15.0...github-actions"A simple user-agent parser can classify the vast majority of your CloudTrail principals in seconds. Lambda functions running Python will almost always show boto3/botocore. GitHub Actions will show aws-cli from known CI/CD IP ranges. Console access will show the Mozilla/5.0 console string. The exceptions (custom SDK configurations, unusual environments) can be classified by IP or session context.
2. sourceIPAddress
Source IP is a strong secondary classifier:
- AWS service-internal IPs (e.g., Lambda, ECS, EC2 within VPC) — these indicate service-to-service calls
- Known CI/CD IP ranges (GitHub Actions, GitLab, CircleCI all publish their egress IP ranges) — indicates automation
- AWS infrastructure addresses (*.amazonaws.com) — indicates cross-service calls
- Corporate network ranges — indicates human access from managed devices
- Consumer ISP ranges — indicates human access from personal/remote devices
- Unknown/new IPs — highest risk signal, especially for established roles
3. sessionContext
The sessionContext field reveals how the credentials were obtained — which tells you a lot about the identity type:
# Human with MFA (high trust):
"sessionContext": {
"sessionIssuer": { "type": "Role", "principalId": "AROA...", "arn": "arn:aws:iam::...:role/HumanRole" },
"attributes": { "mfaAuthenticated": "true", "creationDate": "2026-03-09T09:14:00Z" }
}
# Lambda assumed role (service identity):
"sessionContext": {
"sessionIssuer": { "type": "Role", "arn": "arn:aws:iam::...:role/my-lambda-role" },
"ec2RoleDelivery": null,
"attributes": { "mfaAuthenticated": "false" }
}
# EC2 instance profile (service-to-service):
"sessionContext": {
"sessionIssuer": { "type": "Role", "arn": "arn:aws:iam::...:role/my-ec2-role" },
"ec2RoleDelivery": "1.0"
}Key signals: mfaAuthenticated: true strongly suggests human access. The principalId (who assumed the role) tells you the source identity — a Lambda execution role will have a Lambda ARN as the issuer, while a human will have a user ARN.
Why This Changes Your Detection Strategy
Once you can classify identity types, your behavioral detection changes fundamentally:
- Separate baselines per identity type — Lambda and human access to the same role are modeled independently, eliminating the averaging problem
- Different anomaly thresholds — service identities have predictable, machine-regular patterns; small deviations are high-signal. Human access has natural variance; larger deviations are needed to fire
- Identity-specific detectors — "new IP for this role" is much more interesting for a Lambda (which should always call from AWS-internal IPs) than for a human (who might travel, use VPN, work from home)
- Cross-assumer detection — alert when a role that was previously only assumed by Lambda functions suddenly gets a human MFA session (possible credential compromise)
- NHI inventory — track which service identities exist, what they access, and flag "orphaned NHIs" whose source service has been decommissioned but the credentials still work

Common Anti-Patterns That Break Identity Visibility
Even with good tooling, these IAM anti-patterns make NHI detection harder:
- Shared roles — one IAM role used by both a Lambda and a human. Fix: one role per identity, named to reflect its consumer
- Service accounts masquerading as users — a "service" that assumes a human's role by having access to long-lived user credentials. Fix: service accounts should assume dedicated service roles, never user roles
- Over-permissioned instance profiles — an EC2 instance with S3 access that's actually used by 12 different applications. Fix: task-level IAM roles via ECS task definitions or pod-level IRSA in EKS
- Long-lived access keys for Lambda — Lambda should use execution roles via STS, not static access keys. Static keys in Lambda code are a critical NHI risk
- Unnamed automation roles — roles named "automation-role" or "service-account" with no indication of the consuming system. Fix: name roles to reflect their source: lambda-rag-pipeline-prod, github-actions-deploy, etc.
Getting Started with NHI Visibility
If you're starting from zero, here's a practical sequence:
- Inventory your IAM roles — use IAM Access Analyzer or AWS Config to list all roles with S3 permissions and when they were last used
- Classify manually first — go through the top 20 most active S3 roles and classify each as HUMAN or SERVICE/LAMBDA/CI_CD by examining the userAgent in recent CloudTrail events
- Build a naming convention — rename roles to reflect their consumer type and owner team. This makes automated classification far more accurate
- Enable Entroptics NHI classification — connect your CloudTrail stream and let the identity classifier run. Review the automated classifications on your first 50 roles to calibrate confidence
- Set separate baselines per identity type — once classification is running, separate baselines are built automatically per type
- Alert on identity type changes — when a role previously only accessed by Lambda suddenly gets a human console session, that's your highest-priority alert