How to Create a Read-Only IAM User for a Junior Developer

Onboarding a junior developer to AWS without granting destructive permissions is one of the most common IAM tasks — and one of the most frequently misconfigured. The wrong policy choice can silently grant write access, while the right one gives full console visibility with zero mutation risk.

TL;DR: Read-Only IAM User Setup

Decision PointRecommended Choice
Policy typeAWS Managed: ReadOnlyAccess
Attachment targetIAM Group (not directly to user)
Console accessEnable with a temporary password + force reset
Programmatic accessOmit unless explicitly required
MFAEnforce via IAM condition on sensitive views

How IAM Read-Only Access Works

AWS managed policies are pre-built permission sets maintained by AWS. The policy ReadOnlyAccess (ARN: arn:aws:iam::aws:policy/ReadOnlyAccess) grants List*, Describe*, Get*, and View* actions across the majority of AWS services. It explicitly excludes any Create*, Put*, Delete*, or Update* actions.

Think of ReadOnlyAccess as a museum visitor badge — the holder can walk every gallery and read every label, but the display cases stay locked.

AWS updates this managed policy as new services launch, so coverage expands automatically without requiring manual policy edits. That said, coverage is not universal — some newer or specialized services may have partial or delayed inclusion. Always verify against the policy's current JSON in the IAM console before assuming a specific service is covered.

graph LR User["IAM User
junior-dev-alice"] -->|member of| Group["IAM Group
ReadOnly-Developers"] Group -->|attached policy| Policy["AWS Managed Policy
ReadOnlyAccess"] Policy -->|allows| Actions["List* / Get*
Describe* / View*"] Policy -->|implicitly denies| Mutations["Create* / Put*
Delete* / Update*"] SCP["SCP
Org-level ceiling"] -->|evaluated first| User
  1. IAM User is created and added to the ReadOnly-Developers group.
  2. The group carries the ReadOnlyAccess managed policy — permissions flow down to the user through group membership.
  3. When the user calls any AWS API (via Console or CLI), IAM evaluates the policy: List*/Describe*/Get* actions are allowed; all mutating actions are implicitly denied.
  4. An optional SCP at the AWS Organizations level can enforce an additional ceiling, but is not required for this pattern.

Step-by-Step: Creating the Read-Only IAM User

Step 1 — Create a Dedicated IAM Group

Attaching policies directly to users creates a maintenance burden the moment you have more than one junior developer. A group lets you revoke or upgrade permissions for the entire cohort in a single operation. Create the group first, then add users to it — this ordering matters because you cannot attach a policy to a user-group relationship that doesn't exist yet.

aws iam create-group \
  --group-name ReadOnly-Developers

Step 2 — Attach the ReadOnlyAccess Managed Policy to the Group

The policy ARN for ReadOnlyAccess is a global AWS-managed resource — it has no region component and no account ID. Using the exact ARN below avoids any ambiguity with similarly named customer-managed policies in your account.

aws iam attach-group-policy \
  --group-name ReadOnly-Developers \
  --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess

Step 3 — Create the IAM User

The --tags flag is optional but strongly recommended in team environments — it lets you filter users by team, cost center, or onboarding date without opening the console.

aws iam create-user \
  --user-name junior-dev-alice \
  --tags Key=Team,Value=Engineering Key=Role,Value=JuniorDev

Step 4 — Add the User to the Group

This single command is what actually grants permissions. Until the user is in the group, the policy has no effect on them — a detail that catches engineers off guard when they test access immediately after user creation.

aws iam add-user-to-group \
  --user-name junior-dev-alice \
  --group-name ReadOnly-Developers

Step 5 — Create a Console Login Profile with a Temporary Password

The --password-reset-required flag forces the user to set their own password on first login. This ensures you never retain knowledge of their credentials after handoff — a least-privilege principle that applies to secrets, not just permissions.

aws iam create-login-profile \
  --user-name junior-dev-alice \
  --password 'TempP@ssw0rd!2024' \
  --password-reset-required

Step 6 — Verify the Effective Permissions

Before handing over credentials, confirm the policy is attached at the group level and inherited by the user. The list-attached-group-policies call is the authoritative check — it shows what the user will actually inherit, not just what's on their user record.

# Confirm policy is on the group
aws iam list-attached-group-policies \
  --group-name ReadOnly-Developers

# Confirm user is in the group
aws iam list-groups-for-user \
  --user-name junior-dev-alice

Optional: Enforce MFA for Console Access

Read-only access still exposes sensitive data — environment variable values in Lambda, database connection strings in RDS parameter groups, secrets metadata in Secrets Manager. Enforcing MFA adds a second factor before any console session becomes active. The inline policy below denies all actions except MFA self-enrollment until the user has authenticated with a device.

🔽 Click to expand — MFA enforcement inline policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyWithoutMFA",
      "Effect": "Deny",
      "NotAction": [
        "iam:CreateVirtualMFADevice",
        "iam:EnableMFADevice",
        "iam:GetUser",
        "iam:ListMFADevices",
        "iam:ListVirtualMFADevices",
        "iam:ResyncMFADevice",
        "sts:GetSessionToken"
      ],
      "Resource": "*",
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      }
    }
  ]
}

Attach this as an inline policy on the ReadOnly-Developers group so it applies to every member automatically:

aws iam put-group-policy \
  --group-name ReadOnly-Developers \
  --policy-name EnforceMFA \
  --policy-document file://enforce-mfa.json

Experience Signal: The ViewOnlyAccess Misdiagnosis

A common onboarding mistake: searching the IAM console for "read only" and selecting ViewOnlyAccess instead of ReadOnlyAccess. The names sound equivalent. They are not.

The symptom surfaces when the junior developer opens the Lambda console and sees function names but no configuration details, or opens S3 and sees bucket names but cannot view bucket policies or object metadata. The misdiagnosis is usually "the console is broken" or "the user needs more permissions." The actual cause: ViewOnlyAccess grants only top-level List* actions — it intentionally omits Get* and Describe* calls that populate resource detail pages.

ReadOnlyAccess is the correct policy for full console visibility. ViewOnlyAccess is appropriate when you want inventory-level awareness without exposing configuration details — a stricter posture suited for auditors, not developers.

The fix is a single policy swap on the group, but the diagnostic cost is the ten minutes spent convincing yourself the console wasn't caching stale permissions.

graph TD Start["Need console access
for junior developer"] --> Q1{"Require full
resource detail?"} Q1 -->|Yes| ReadOnly["Use: ReadOnlyAccess
List* + Get* + Describe*"] Q1 -->|No - inventory only| ViewOnly["Use: ViewOnlyAccess
List* only"] ReadOnly --> Result1["Full resource config
visible in console"] ViewOnly --> Result2["Resource names visible
detail pages restricted"]
  1. ReadOnlyAccess covers List*, Get*, and Describe* — full resource detail visibility in the console.
  2. ViewOnlyAccess covers primarily List* — resource names are visible but configuration detail pages are largely inaccessible.
  3. Neither policy grants any write, create, or delete action.

Depth Signal: Policy Inheritance and Explicit Deny Interaction

One behavioral interaction that surprises engineers: if your AWS account is under AWS Organizations and a Service Control Policy (SCP) contains an explicit Deny for a specific service, that deny overrides the ReadOnlyAccess allow — even for read actions. The user will see an access denied error in the console for that service, and the IAM policy simulator will show the action as allowed, because the simulator does not evaluate SCPs by default.

This ordering dependency — SCP ceiling evaluated before IAM policy floor — means a correctly configured IAM user can still experience access denials that are invisible in the IAM console. If a junior developer reports they cannot view a specific service despite having ReadOnlyAccess, check the effective SCP stack at the account level before assuming the IAM configuration is wrong.

# Check which SCPs are attached to the current account
aws organizations list-policies-for-target \
  --target-id  \
  --filter SERVICE_CONTROL_POLICY

IAM Policy for Auditing This Setup

The administrator performing these steps needs the following minimum permissions. Read/List IAM actions require "Resource": "*" because the IAM Service Authorization Reference specifies that these actions do not support resource-level restrictions.

🔽 Click to expand — administrator policy for user/group management
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ManageReadOnlyGroup",
      "Effect": "Allow",
      "Action": [
        "iam:CreateGroup",
        "iam:AttachGroupPolicy",
        "iam:CreateUser",
        "iam:AddUserToGroup",
        "iam:CreateLoginProfile",
        "iam:PutGroupPolicy",
        "iam:ListAttachedGroupPolicies",
        "iam:ListGroupsForUser",
        "iam:TagUser"
      ],
      "Resource": "*"
    }
  ]
}

Wrap-Up: Read-Only IAM User for Junior Developers

The correct AWS managed policy for full console read-only access is ReadOnlyAccess (ARN: arn:aws:iam::aws:policy/ReadOnlyAccess). Attach it to a group, add the user to the group, enable console login with a forced password reset, and optionally enforce MFA. That sequence covers the full onboarding path without granting any mutation capability.

For accounts under AWS Organizations, verify that no SCP is silently denying read actions for the services your developer needs — the IAM policy simulator will not surface this without explicit SCP evaluation.

Next steps worth reviewing:

Glossary

TermDefinition
AWS Managed PolicyA standalone IAM policy created and maintained by AWS, identified by the arn:aws:iam::aws:policy/ prefix.
ReadOnlyAccessAn AWS managed policy granting List*, Get*, Describe*, and View* actions across most AWS services, with no write permissions.
IAM GroupA collection of IAM users that share the same attached policies; the recommended unit for permission management.
SCP (Service Control Policy)An AWS Organizations policy that sets the maximum permissions boundary for all accounts in an organizational unit; evaluated before IAM policies.
Explicit DenyAn IAM or SCP statement with "Effect": "Deny" that overrides any Allow regardless of policy source or attachment order.

Related Posts

Comments

Popular posts from this blog

EC2 No Internet Access in Custom VPC: Fix Internet Gateway and Route Table

EC2 SSH Connection Timeout: Which Security Group Rules to Check

Difference Between IAM User and IAM Role: Which One Should Your EC2 Use?