Create an IAM user with iam.User, define permissions in an iam.Policy, and attach that policy to the user. This TypeScript example grants permission to list one existing S3 bucket; it does not create a password, access key, or permission to read its objects.
Before choosing a user, check whether the caller can use temporary credentials. AWS recommends federation for people and IAM roles for workloads in its IAM security guidance. Creating a user is appropriate only when that is an explicit requirement.
Set up the project
Use an AWS CDK v2 TypeScript app, Node.js, and credentials for a sandbox account. These examples use aws-cdk-lib 2.272.0 and constructs 10.5.0; preserve your lock file.
mkdir cdk-iam-user cd cdk-iam-user cdk init app --language typescript npm install --save-exact aws-cdk-lib@2.272.0 constructs@10.5.0
Create the user and attach a policy
Replace lib/cdk-iam-user-stack.ts. The class name matches the generated app import.
import { CfnOutput, Stack, StackProps } from "aws-cdk-lib"; import * as iam from "aws-cdk-lib/aws-iam"; import { Construct } from "constructs"; export class CdkIamUserStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const user = new iam.User(this, "BucketLister"); const policy = new iam.Policy(this, "ListOneBucket", { statements: [new iam.PolicyStatement({ actions: ["s3:ListBucket"], resources: ["arn:aws:s3:::YOUR_EXISTING_BUCKET"], })], }); policy.attachToUser(user); new CfnOutput(this, "UserArn", { value: user.userArn }); } }
Replace YOUR_EXISTING_BUCKET with a bucket you intend to allow. In another AWS partition, use its corresponding ARN prefix. Leaving userName unspecified lets CloudFormation generate a physical name, avoiding collisions between repeated exercises.
The bucket ARN scopes s3:ListBucket. An object ARN such as arn:aws:s3:::YOUR_EXISTING_BUCKET/* is a different resource and is used for operations such as s3:GetObject. Attaching this policy does not override an explicit deny, a permissions boundary, or an organization policy.
Inspect and deploy
npx tsc --noEmit npx aws-cdk synth npx aws-cdk diff npx aws-cdk deploy --outputs-file stack-outputs.json
Check the synthesized IAM policy: its action should be s3:ListBucket and its resource should be the bucket you chose. The revision is checked locally, not deployed to an AWS account. If deployment requires bootstrap resources, see CDK bootstrap.
The UserArn output identifies the user. It is not a credential and cannot authenticate an SDK request. Review CDK outputs for retrieving output values. For a workload running on EC2 or Lambda, assign permissions to its execution role instead of distributing user access keys.
Cleanup and limits
Run npx aws-cdk destroy only for this exercise stack and review the deletion proposal. Credentials, group memberships, or policies added outside CloudFormation can prevent user deletion; inspect the error before removing anything. The existing S3 bucket is not owned by this stack.
This example intentionally covers an IAM user and one identity policy. It is not a complete identity lifecycle, federation setup, or cross-account access design. For bucket-side authorization, continue with S3 bucket policies in CDK.
Quick Recap
Does creating an IAM user create access keys?
No. The User construct does not create an access key or a console password unless you separately configure those credentials.
Should applications use IAM users or roles?
AWS recommends temporary credentials through IAM roles for workloads. This example is for a requirement that specifically needs an IAM user; human access usually belongs in federation or IAM Identity Center.
Does s3:ListBucket allow reading files?
No. ListBucket applies to a bucket ARN. Reading object contents requires an object-level permission such as s3:GetObject on the appropriate object ARNs.
Comments
Share your thoughts and questions below