This CloudFormation Template creates an IAM user and attaches both a custom policy (for S3 access) and an AWS-managed policy (AmazonEC2FullAccess). It also generates AWS access keys for the user.
CloudFormation IAM Management #
CloudFormation Template #
Project Folder #
# Create a CloudFormation project folder
TF_PROJECT_NAME=aws-cloudformation-iam
mkdir $TF_PROJECT_NAME && cd $TF_PROJECT_NAME
Template #
- cloudformation.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "IAM User and Policy Management"
Parameters:
# IAM user
IAMUserName:
Type: String
Description: "IAM user name"
Default: "example-user"
# IAM custom policy
IAMCustomPolicyName:
Type: String
Description: "The S3 bucket to grant access"
Default: "example-policy"
# S3 Bucket name for IAM custom policy
S3BucketName:
Type: String
Description: "The S3 bucket to grant access"
Default: "jkw-example-bucket"
Resources:
IAMUser:
Type: AWS::IAM::User
Properties:
UserName: !Ref IAMUserName
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/AmazonEC2FullAccess"
IAMCustomUserPolicy:
Type: AWS::IAM::UserPolicy
Properties:
PolicyName: !Sub "${IAMUserName}-${IAMCustomPolicyName}"
UserName: !Ref IAMUser
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "s3:ListBucket"
Resource: !Sub "arn:aws:s3:::${S3BucketName}"
- Effect: Allow
Action:
- "s3:GetObject"
- "s3:PutObject"
- "s3:DeleteObject"
Resource: !Sub "arn:aws:s3:::${S3BucketName}/*"
TerraformS3AccessKey:
Type: AWS::IAM::AccessKey
Properties:
UserName: !Ref IAMUser
Outputs:
AccessKeyId:
Description: "Access Key"
Value: !Ref TerraformS3AccessKey
Export:
Name: !Sub "${IAMUserName}AccessKey"
SecretAccessKey:
Description: "Secret Access Key"
Value: !GetAtt TerraformS3AccessKey.SecretAccessKey
Export:
Name: !Sub "${IAMUserName}SecretAccessKey"
Deploy CloudFormation Stack #
Validate Template #
# Validate the CloudFormation template
aws cloudformation validate-template --template-body file://cloudformation.yml
# Shell output:
{
"Parameters": [
{
"ParameterKey": "S3BucketName",
"DefaultValue": "jkw-example-bucket",
"NoEcho": false,
"Description": "The S3 bucket to grant access"
},
{
"ParameterKey": "IAMUserName",
"DefaultValue": "example-user",
"NoEcho": false,
"Description": "IAM user name"
},
{
"ParameterKey": "IAMCustomPolicyName",
"DefaultValue": "example-policy",
"NoEcho": false,
"Description": "The S3 bucket to grant access"
}
],
"Description": "IAM User and Policy Management",
"Capabilities": [
"CAPABILITY_NAMED_IAM"
],
"CapabilitiesReason": "The following resource(s) require capabilities: [AWS::IAM::AccessKey, AWS::IAM::User]"
}
Deploy Template / Stack #
# Deploy a stack from the template: Default region
aws cloudformation create-stack --stack-name iam-stack \
--template-body file://cloudformation.yml \
--capabilities CAPABILITY_NAMED_IAM
# Shell output:
{
"StackId": "arn:aws:cloudformation:us-east-1:012345678912:stack/iam-stack/42befe00-defd-11ef-8e6c-12ab64dd2afb"
}
--capabilities CAPABILITY_NAMED_IAM
Is required when the CloudFormation template creates or modifies IAM resources for security reasons.
Verify the Deployment / Output Access Keys #
Wait till the “StackStatus” changes to “CREATE_COMPLETE”:
# Monitor the deployment
aws cloudformation describe-stacks --stack-name iam-stack
# Shell output:
{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-east-1:012345678912:stack/iam-stack/42befe00-defd-11ef-8e6c-12ab64dd2afb",
"StackName": "iam-stack",
"Description": "IAM User and Policy Management",
"Parameters": [
{
"ParameterKey": "S3BucketName",
"ParameterValue": "jkw-example-bucket"
},
{
"ParameterKey": "IAMUserName",
"ParameterValue": "example-user"
},
{
"ParameterKey": "IAMCustomPolicyName",
"ParameterValue": "example-policy"
}
],
"CreationTime": "2025-01-30T11:28:00.011000+00:00",
"RollbackConfiguration": {},
"StackStatus": "CREATE_COMPLETE",
"DisableRollback": false,
"NotificationARNs": [],
"Capabilities": [
"CAPABILITY_NAMED_IAM"
],
"Outputs": [
{
"OutputKey": "AccessKeyId",
"OutputValue": "AKIARCHUALINUI4UGXI7",
"Description": "Access Key",
"ExportName": "example-userAccessKey"
},
{
"OutputKey": "SecretAccessKey",
"OutputValue": "hJxO+BecJ6HGsohBCEGlPgFWnFQjOubm5v4UOC9d",
"Description": "Secret Access Key",
"ExportName": "example-userSecretAccessKey"
}
],
"Tags": [],
"EnableTerminationProtection": false,
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
}
]
}
Verify Stack with Management Console #

Verify IAM user via AWS CLI #
# List policies that are attached to the IAM user
aws iam list-attached-user-policies --user-name example-user
# Shell output:
{
"AttachedPolicies": [
{
"PolicyName": "AmazonEC2FullAccess",
"PolicyArn": "arn:aws:iam::aws:policy/AmazonEC2FullAccess"
}
]
}
# List inline (directly attached to the user) policies, that are attached to the IAM user
aws iam list-user-policies --user-name example-user
# Shell output:
{
"PolicyNames": [
"example-user-example-policy"
]
}
Cleanup #
Delete CloudFormation Stack #
# Delete the stack: Default region
aws cloudformation delete-stack --stack-name iam-stack