The situation
A logistics startup built its platform over two years and now has eleven engineers, three contractors, and one AWS account.
Everyone signs in with the same credentials: the email address the account was opened with, and a password in a shared vault that six people know. Multi-factor authentication is not enabled on it. An access key belonging to that same identity is hard-coded into a build script in the main repository, where it has been since the second week of the company.
Two EC2 instances run a nightly job that reads from and writes to an S3 bucket. They authenticate with a second access key, stored in a file on each instance, belonging to an IAM user called automation that carries the AdministratorAccess policy.
The company now has three requirements. The first customer contract needs an attestation that access is individually attributable. A new analytics team of four needs read-only access to two S3 buckets and nothing else. And the finance manager needs to see the bill without being able to touch infrastructure.
Nobody can currently say who performed any action in the account, because every action was performed by the same identity.
What actually matters
Start with what the root user is, because everything else follows from it. The root user is created with the account, signs in with the email address, and carries permissions that cannot be restricted: no policy limits it, and it can close the account, change the address and password it signs in with, and undo any lockout an administrator creates. Lockout recovery is why the root user exists; the unrestricted power is why it should sit unused. An identity nobody can constrain is the wrong tool for a daily task performed by eleven people.
Then attribution, because the audit requirement turns on it. CloudTrail records the identity behind every management API call, so the record is only as useful as the identities are distinct. Eleven people sharing one sign-in produces a log in which every action was taken by the same principal, and no amount of logging configuration recovers who actually did it. Attribution is created at the identity layer; the audit trail only reports it.
Third, the difference between long-lived credentials and temporary ones. An access key is a long-lived credential: it does not expire, it works from anywhere, and it sits in plaintext wherever it is stored. The key in the build script has been in a repository for two years, which means it exists in every clone, every branch, and the history of every fork. A role, by contrast, is assumed and produces credentials that expire and rotate automatically. Where a workload runs on AWS compute, there is no reason for a stored key to exist at all.
Finally, the shape of the permissions themselves. AdministratorAccess on an automation identity that reads and writes one bucket is the opposite of least privilege. If that key leaks, the blast radius is the whole account rather than one bucket. Least privilege means starting from nothing and adding what a task needs, and it is easier to do while designing the four analytics identities than it is to retrofit onto an administrator.
What we’ll filter on
- Every action attributable to one named human or one named workload.
- No long-lived credentials where a temporary one can do the job.
- Permissions scoped to the task, starting from nothing and adding.
- The root user protected and reserved for the tasks that require it.
- Manageable as the company grows past one account, without redoing it.
The landscape
Carry on with the root user. It works, which is why it has lasted two years. It fails every requirement on the list: no attribution, no ability to scope permissions, and an unrestricted identity in a vault that six people can open. It also puts the account itself at risk, since the root user can alter billing settings, change the sign-in email address, and close the account.
IAM users, one per person. Each engineer gets their own identity with its own password and its own MFA device. Actions become attributable, permissions can differ per person, and offboarding is a deletion. Groups collect users so that a policy is attached once rather than eleven times. What this does not solve is the long-lived credential problem for anyone who needs programmatic access, and it does not scale gracefully to a second or third account, where the users would have to be recreated.
IAM groups with customer managed policies. Groups are the unit that policies attach to: an Analytics group with a read-only policy on two buckets, a Billing group with billing access and nothing else, an Engineers group with what the platform work needs. A group is not a principal, so it cannot be named in a resource policy and it cannot be nested, but as a way of applying one policy to many people it is the correct tool.
IAM roles for workloads. An instance profile attaches a role to an EC2 instance, and the SDK on that instance retrieves temporary credentials automatically. Nothing is stored on disk, the credentials are replaced automatically at least five minutes before the old set expires, and the permissions are scoped to what the nightly job does. The same mechanism covers Lambda functions, ECS tasks and anything else running on AWS compute.
IAM Identity Center. Workforce sign-in across one or many accounts, with permission sets defining what an assigned group can do in each account. It connects to an external identity provider, or maintains its own directory, so joiners and leavers are handled where the rest of the company handles them. Sign-in produces temporary credentials rather than long-lived keys, including for the command line. Permission sets require an organization instance, so enabling one from a standalone account creates an AWS Organization with that account as the management account. This is the route that survives a second account.
Federation with an existing identity provider. Where the company already runs an identity provider, users authenticate there and assume a role in AWS. No separate AWS password exists, and access is removed by disabling the account in one place. Identity Center is the managed front end for exactly this.
Evaluation
Side by side
| Option | Attributable per person | No long-lived keys | Scoped permissions | Root protected | Survives more accounts |
|---|---|---|---|---|---|
| Keep using the root user | ✗ | ✗ | ✗ | ✗ | ✗ |
| IAM users, one per person | ✓ | ✗ | ✓ | ✓ | ✗ |
| IAM groups with customer managed policies | ✓ | ✗ | ✓ | ✓ | ✗ |
| IAM roles for the EC2 workload | ✓ | ✓ | ✓ | ✓ | ✓ |
| IAM Identity Center | ✓ | ✓ | ✓ | ✓ | ✓ |
| Federation with an external provider | ✓ | ✓ | ✓ | ✓ | ✓ |
The human rows and the workload rows answer different halves of the problem and both halves have to be answered. Identity Center handles the eleven engineers, the four analysts, the three contractors and the finance manager. A role on the instance profile handles the nightly job. Neither substitutes for the other, and the root user is the row that has to stop being used.
How a request is evaluated
Permissions resolve the same way every time, and the order is worth holding:
| Step | Result |
|---|---|
1. Is there an explicit Deny that matches? |
Denied. Nothing overrides this. |
2. Is there an explicit Allow that matches? |
Allowed, unless step 1 matched. |
| 3. Neither | Denied, by default. Everything starts denied. |
A request with no matching allow is denied, and a deny in any applicable policy ends the evaluation there.
The solution
Secure the root user before anything else, because it is the control with the largest effect and it takes an afternoon. Enable MFA on it. Delete the root access key, which is the credential the build script has been using. Change the password and store it somewhere that requires a deliberate, logged retrieval. Move the account email address to a shared mailbox that more than one person can reach, because account recovery depends on it. Then write down the short list of things that will bring anyone back to it: changing the root email address or password, closing the account, restoring permissions after a lockout, turning on MFA Delete, and a handful of billing actions such as activating IAM access to the Billing console. Changing the Support plan is not on that list, though it is often repeated as though it were: AWS governs the Support Plans console with ordinary IAM permissions.
Set up IAM Identity Center for the people. Create permission sets rather than per-person policies: an administrator set, an engineering set, an analytics set with read-only access to the two buckets, and a billing set. Assign groups to permission sets rather than individuals, so that the analytics team is one assignment and a fifth analyst is one group membership. Sign-in produces temporary credentials, including for the command line, so nobody ends up with a long-lived key on a laptop. Where the company already runs an identity provider, connect it, so that a departing contractor is removed in one place.
The finance manager is a useful test of least privilege. Billing access is a distinct set of permissions, and a permission set granting billing and cost management without any infrastructure permission gives exactly the visibility required and nothing more. That is the shape every other assignment should follow: start from nothing, add the actions the job needs, and do not attach AdministratorAccess because it would be quicker.
Replace the automation user with a role. Create a role with a policy allowing only the S3 actions the nightly job performs on only the bucket it uses, attach it to the instances through an instance profile, and delete the key file from both instances. Then delete the automation user and its access key. The SDK finds the temporary credentials automatically, so the job’s code changes little or not at all, and there is no longer a permanent administrator credential sitting in a file.
The key in the repository has to be treated as compromised rather than as tidied up. Two years of history means it exists in every clone and every fork, so deleting the line does nothing. Delete the key itself, then review CloudTrail for its use, because a key in a public or widely cloned repository has to be assumed to have been found. Going forward, credentials that genuinely cannot be replaced by a role belong in AWS Secrets Manager, which stores them encrypted, controls retrieval through IAM, and rotates them on a schedule.
Finally, make the new arrangement verifiable. CloudTrail was already recording management calls; with distinct identities it now records who made them, which is what the customer attestation needs. Object-level reads and writes in the analytics buckets are data events, and those are not recorded until a trail is configured to capture them. Create an IAM Access Analyzer external access analyzer, in each Region the company uses, to report resources shared with anything outside the account. Run the IAM credential report to confirm there are no remaining users with access keys and no identity without MFA. After a month, add an unused access analyzer: it reports unused roles, unused access keys and unused passwords, and for roles and users that are active, the services and actions they never called. Removing those is how least privilege stays true after the first week.
What’s worth remembering
- The root user cannot be restricted by any policy, so protect it with MFA and remove its access keys. Reserve it for the short list that genuinely requires it: the root email address and password, closing the account, recovering from a lockout, MFA Delete, and activating IAM access to the Billing console. Changing the Support plan is not one of them.
- Attribution is created by distinct identities, not by logging configuration; CloudTrail can only record the principal that made the call.
- A role is assumed and produces temporary, automatically rotated credentials, which is why any workload on AWS compute should carry one instead of a stored access key.
- Policies attach to users, groups and roles; a group is not a principal, cannot be named in a resource policy, and cannot be nested.
- Evaluation is deny by default, an explicit allow grants, and an explicit deny overrides everything.
- IAM Identity Center is the route for workforce sign-in across one or many accounts, with permission sets assigned to groups and temporary credentials rather than long-lived keys.