Business Problem
IDSentinel Solutions' cloud expansion into AWS required establishing a baseline identity architecture before any workloads were deployed. The Security team mandated that no human or service identity operate with standing administrative access — all access must follow least-privilege principles, and any privilege elevation must occur through time-bound role assumption with a full audit trail captured via CloudTrail.
An internal review confirmed that without enforced role assumption boundaries, a compromised service credential would grant unrestricted access to IAM and other AWS services — a direct violation of IDSentinel's Zero Trust initiative.
Risk
- Service identities with standing admin access create permanent blast radius
- No audit trail for privilege elevation without CloudTrail enforcement
- Compromised long-lived credentials grant unrestricted AWS access
- Non-compliant with IDSentinel's Zero Trust and least-privilege mandate
- Potential compliance exposure under SOC 2 Type II CC6.3 controls
Scope
| Field | Detail |
|---|---|
| Affected identities | Service accounts and programmatic users in AWS |
| Target | Zero standing admin access — all elevation via role assumption |
| Controls implemented | Least-privilege IAM policy, scoped trust policy with ExternalId condition, CloudTrail audit trail |
| Validation | CLI-based role assumption test with deny enforcement confirmation |
Solution Design — 4 Workstreams
IAM Identities
Service user created with programmatic-only access and no inline permissions. All permissions assigned via group membership for consistent policy application.
Least-Privilege Policy
Custom IAM policy with explicit ReadOnly grants and explicit Deny on all IAM write actions — ensuring even a misconfiguration cannot grant write access to the identity plane.
Role Assumption with Trust Boundary
IAM role with scoped trust policy restricting assumption to a single principal with an ExternalId condition — preventing confused deputy attacks.
CloudTrail Audit
Trail enabled across all regions capturing AssumeRole events — tamper-evident audit log for compliance and incident response.

Implementation
-
01
Create IAM Identities
Service user
svc-idsentinel-reportercreated with programmatic-only access (no console login). Added to groupGRP-IAMReporters— policy assigned at group level, not inline, for consistent governance.// 01-iam-user-created
// 02a-user-access-key-created
// 02b-group-membership
-
02
Deploy Least-Privilege Policy
Custom policy
IDSentinel-ReadOnly-Policycreated with explicit Allow on IAM read actions and explicit Deny on all IAM write actions. Attached toGRP-IAMReporters— inherited by all group members. Explicit Deny overrides any conflicting Allow — defense in depth.// 03-policy-attached
// 04-policy-summary
-
03
Create IAM Role with Scoped Trust Policy
Role
Role-IDSentinel-Auditorcreated with trust policy restricting assumption tosvc-idsentinel-reporteronly, enforced by ExternalId conditionIDSentinel-Lab-2026to prevent confused deputy attacks.// 05-role-summary
// 06-trust-policy
-
04
Enable CloudTrail
Trail
IDSentinel-AuditTrailcreated across all regions with management events (Read + Write) logged to S3 bucketidsentinel-cloudtrail-logs.// 07-cloudtrail-active
-
05
Assume Role via AWS CLI
bash — STS Role Assumption# Verify baseline identity (user, not role) aws sts get-caller-identity --profile idsentinel-reporter # Assume the role with ExternalId condition aws sts assume-role \ --role-arn "arn:aws:iam::ACCOUNT_ID:role/Role-IDSentinel-Auditor" \ --role-session-name "IDSentinel-AuditSession-01" \ --external-id "IDSentinel-Lab-2026" \ --profile idsentinel-reporter # Export temporary credentials export AWS_ACCESS_KEY_ID=<AccessKeyId> export AWS_SECRET_ACCESS_KEY=<SecretAccessKey> export AWS_SESSION_TOKEN=<SessionToken> # Confirm identity is now the ROLE, not the user aws sts get-caller-identity
// 08-caller-identity-user
// 09-assumerole-response
// 10-caller-identity-role
-
06
Validate Least-Privilege Enforcement
bash — Read/Write enforcement test# Should SUCCEED — allowed by ReadOnly grant aws iam list-users # Should FAIL — blocked by explicit Deny aws iam create-user --user-name test-deny-check # → AccessDenied confirmed ✅
// 11-list-users-allowed
// 12-create-user-denied
// 13-python-audit-script
-
07
CloudTrail Audit Evidence
AssumeRole event captured in CloudTrail Event History with full session metadata — principal ARN, session name, ExternalId, source IP, and timestamp — providing complete audit trail for SOC 2 CC6.3 compliance documentation.
// 14-cloudtrail-assumerole
// 15-cloudtrail-event-json
Outcome
AccessDenied on iam:CreateUser confirmedImplementation Results
| Metric | Value |
|---|---|
| IAM users created | 1 (programmatic only — no console access) |
| IAM groups created | 1 (GRP-IAMReporters) |
| IAM roles created | 1 (Role-IDSentinel-Auditor) |
| Trust policy scope | Single principal + ExternalId condition |
| Policy type | Custom — explicit Allow + explicit Deny |
| Least-privilege violations tested | 1 (iam:CreateUser → AccessDenied confirmed) |
| Credential type used | Temporary STS credentials (time-bound) |
| Long-lived credentials used | 0 |
| CloudTrail events captured | AssumeRole with full session metadata |
| Permanent admin access granted | 0 |
Files
-
policies/IDSentinel-ReadOnly-Policy.jsonLeast-privilege IAM policy — ReadOnly grant + explicit Deny on all write actions -
diagrams/aws-iam-flow.pngArchitecture and role assumption flow diagram -
screenshots/Evidence across all 7 implementation steps