The first time I audited an AWS account for a client, I found 40 IAM users with AdministratorAccess attached — for a team of 12 people. Nobody could tell me why half of them existed. That kind of sprawl is incredibly common, and it’s exactly what IAM done right is supposed to prevent.
Identity and Access Management (IAM) is one of those areas that’s easy to set up quickly and badly, but hard to fix once an organization has grown around the bad setup. Here’s how I approach it in a DevSecOps context, where humans, services, and pipelines all need access simultaneously.
What Makes IAM Different in DevSecOps
In a traditional IT setup, IAM is mostly about managing human user access. In DevSecOps, you’re managing access for:
- Human developers and operators
- CI/CD pipelines
- Containers and orchestration platforms (like Kubernetes)
- Service-to-service communication
- Third-party integrations
Each of these identity types needs a different access pattern, and treating them all the same is where most IAM problems start.
flowchart TD
A[IAM Root Policy] --> B[Human Users]
A --> C[Service Accounts]
A --> D[CI/CD Pipelines]
A --> E[Applications/Workloads]
B --> F[Role-Based Access + MFA]
C --> G[Scoped, Short-Lived Credentials]
D --> H[Pipeline-Specific Roles]
E --> I[Workload Identity / Instance Roles]
Principle 1: Least Privilege, Always
This is the foundation of every good IAM setup. Every identity — human or machine — should have exactly the permissions it needs to do its job, and nothing more. I never grant broad access “just in case” it might be needed later.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-app-bucket/uploads/*"
}
]
}
This policy grants read-only access to a single bucket path — not full S3 access, not account-wide access. Scoping policies this tightly takes more upfront effort, but it dramatically limits what’s exposed if a credential is ever compromised.
Principle 2: Use Roles Instead of Long-Lived Credentials
Wherever possible, I avoid long-lived access keys entirely and use roles that issue temporary, automatically expiring credentials instead. In AWS, this means using IAM roles with STS (Security Token Service) rather than static access keys attached to a user.
# Example: assuming a role for temporary credentials
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/DeploymentRole \
--role-session-name ci-pipeline-session
This is especially important for CI/CD pipelines, which historically have been one of the worst offenders for storing static, never-rotated credentials directly in pipeline configuration.
Principle 3: Enforce MFA for Human Access
Every human account with any meaningful level of access should require multi-factor authentication, full stop. This is one of the cheapest, most effective controls available, and I don’t make exceptions for it, even for “low-risk” accounts, because low-risk accounts are frequently the ones attackers pivot through.
Principle 4: Separate Duties Between Environments
I never let the same credentials or roles span both production and non-production environments. A developer with access to staging shouldn’t automatically have the same access in production. This containment limits how far a compromised credential can spread.
Principle 5: Regularly Audit and Prune Access
Access accumulates over time — new roles get created for one-off tasks and never removed, employees change teams but retain old permissions, and service accounts outlive the services they were created for. I schedule quarterly IAM audits specifically to find and remove:
- Unused roles and users
- Overly permissive policies (anything with wildcard
*actions or resources) - Credentials that haven’t been used in 90+ days
- Duplicate or overlapping service accounts
Most cloud providers have built-in tools for this — AWS IAM Access Analyzer, for example, flags unused permissions automatically.
Principle 6: Centralize Identity Where Possible
Rather than managing separate credentials across every tool and platform, I push toward centralized identity providers (SSO via Okta, Azure AD, or similar) wherever feasible. This gives me one place to enforce policy, revoke access, and audit activity — instead of chasing down access across a dozen disconnected systems.
IAM and Credential Management Go Hand in Hand
IAM defines who can access what, but the credentials themselves still need to be protected and rotated. I treat these as two sides of the same coin — see Secure Credential Management in DevSecOps and Secret Rotation Best Practices for the credential-handling side of this equation.
Common IAM Mistakes I See Often
- Using root or admin credentials for routine automated tasks
- Granting wildcard (
*) permissions out of convenience - Never revoking access after employee offboarding
- Sharing a single service account across multiple unrelated applications
- No MFA enforcement on privileged human accounts
- Treating IAM as a one-time setup rather than an ongoing process
Best Practices Checklist
| Area | Recommendation |
|---|---|
| Human access | MFA enforced, role-based, SSO where possible |
| Machine access | Short-lived, role-based credentials, no static keys |
| Policy scope | Least privilege, resource-specific |
| Environment separation | No shared roles between prod and non-prod |
| Auditing | Quarterly reviews, automated unused-access detection |
| Offboarding | Immediate revocation, no delayed cleanup |
FAQs
What’s the difference between IAM roles and IAM users? Users are typically tied to a specific person or application with directly attached credentials. Roles are assumed temporarily and issue short-lived credentials, which makes them a safer default for both humans and services.
How often should I audit IAM permissions? I recommend at least quarterly, with automated tools flagging unused permissions continuously in between formal audits.
Should service accounts have MFA too? MFA doesn’t apply the same way to machine identities, but the equivalent control is short-lived, scoped credentials combined with strict monitoring of usage patterns.
Is least privilege really practical for fast-moving teams? Yes, though it takes more upfront policy design. The alternative — broad access “for convenience” — creates far more risk than the time saved, especially once a team scales beyond a handful of people.
Conclusion
Good IAM isn’t a one-time configuration step — it’s an ongoing discipline of granting the least access necessary, using short-lived credentials over static ones, enforcing MFA, separating environments, and auditing regularly. Get this foundation right, and it becomes one of the strongest defenses your organization has, because even if a credential does leak, tightly scoped IAM policies dramatically limit what an attacker can actually do with it.