AWS CDK 100 Drill Exercises #002: IAM Basics —— Users, Roles, and Secure Password Management
Source: Dev.to
Introduction
This is the second exercise in the AWS CDK 100 Drill Exercises series.
For more about the series, see this introduction article.
After learning S3 fundamentals in the first exercise, we now dive into AWS Identity and Access Management (IAM). IAM is the foundation of AWS security, controlling who can access your resources and what they can do with them.
Why IAM After S3?
- Security Foundation – IAM is essential for securing all AWS resources.
- Real‑World Necessity – Every AWS deployment requires proper access management.
- CDK Integration – Understanding how CDK generates IAM policies and roles.
- Best Practices – Learning secure patterns from the start prevents future vulnerabilities.
What You’ll Learn
- How CDK creates IAM users, groups, and roles.
- Secure password management with AWS Secrets Manager.
- The difference between managed policies and inline policies.
- Switch‑role implementation with MFA requirements.
- CloudFormation’s dynamic secret resolution.
- IAM security best practices.
📁 Code repository: all examples are available on GitHub.
Architecture Overview
We’ll implement six different patterns across four constructs.
Construct 1: Basic User (CDKDefaultUser)
- Pattern 1 – Minimal IAM user configuration.
Construct 2: Password Management User (IAMUserWithPassword)
- Pattern 2A – Hard‑coded password (⚠️ not recommended).
- Pattern 2B – Secure password management with Secrets Manager (✅ recommended).
- Pattern 3A – AWS managed policy attachment.
- Pattern 3B – Inline policy attachment.
Construct 3: Group Management User (IamUserGroup)
- Pattern 4 – Group‑based permission management.
Construct 4: Switch Role User (SwitchRoleUser)
- Pattern 5 – MFA‑required role assumption.
Prerequisites
- AWS CLI v2 installed and configured.
- Node.js 20+.
- AWS CDK CLI (
npm install -g aws-cdk). - Basic TypeScript knowledge.
- AWS account (Free Tier works).
- Understanding of IAM concepts (users, roles, policies).
Project Directory Structure
iam-basics/
├── bin/
│ └── iam-basics.ts # Application entry point
├── lib/
│ ├── stacks/
│ │ └── iam-basics-stack.ts # Main stack definition
│ └── constructs/
│ ├── iam-user-with-password.ts # Patterns 2‑3
│ ├── iam-user-with-group.ts # Pattern 4
│ └── iam-user-with-switch-role.ts # Pattern 5
├── test/
│ ├── compliance/
│ │ └── cdk-nag.test.ts # Testing (explained later)
│ ├── snapshot/
│ │ └── snapshot.test.ts # Testing (explained later)
│ └── unit/
│ └── iam-basics.test.ts # Testing (explained later)
├── cdk.json
├── package.json
└── tsconfig.json
Pattern 1: Understanding CDK Default User
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as iam from 'aws-cdk-lib/aws-iam';
export class IamBasicsStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Minimal IAM user configuration
const cdkDefaultUser = new iam.User(this, 'CDKDefaultUser', {});
}
}
Generated CloudFormation
{
"Resources": {
"CDKDefaultUserF7AAA71A": {
"Type": "AWS::IAM::User",
"Metadata": {
"aws:cdk:path": "Dev/DrillexercisesIamBasics/CDKDefaultUser/Resource"
}
}
}
}
Default Configuration Details
- User name – Auto‑generated by AWS.
- Password – None; console access disabled by default.
- Policies – No permissions (principle of least privilege).
- Access keys – None; programmatic access disabled.
Until you explicitly grant permissions, this user cannot access anything.
Pattern 2A: User with Hard‑coded Password (⚠️ Not Recommended)
⚠️ This pattern demonstrates why hard‑coding passwords is dangerous in production.
const userWithPassword = new iam.User(this, 'PasswordUser', {
password: cdk.SecretValue.unsafePlainText('InitialPassword123!'),
passwordResetRequired: true,
});
Generated CloudFormation
{
"UserWithPasswordPasswordUserA5E8EDB8": {
"Type": "AWS::IAM::User",
"Properties": {
"LoginProfile": {
"Password": "InitialPassword123!",
"PasswordResetRequired": true
}
}
}
}
Why This Is Dangerous
- Password in source code – Visible in version control.
- CloudFormation template – Password exposed in the console and logs.
- No encryption – Stored in plain text.
- Audit trail – Difficult to track password changes.
Never use this pattern in production.
Pattern 2B: User with Secrets Manager (✅ Recommended)
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as cdk from 'aws-cdk-lib';
const userName = 'SecretsPasswordUser';
// Create the secret with an auto‑generated password
const userSecret = new secretsmanager.Secret(this, 'UserSecret', {
generateSecretString: {
secretStringTemplate: JSON.stringify({ username: userName }),
generateStringKey: 'password',
excludePunctuation: true,
passwordLength: 16,
requireEachIncludedType: true,
},
});
// Create the IAM user with the password from Secrets Manager
const user = new iam.User(this, 'SecretsPasswordUser', {
userName: userName,
password: userSecret.secretValueFromJson('password'),
passwordResetRequired: true,
});
// Attach the AWS‑managed policy that allows the user to change their own password
user.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName('IAMUserChangePassword')
);
This approach stores the password securely in Secrets Manager, avoids hard‑coding secrets, and enables rotation if desired.