Provides AWS CloudFormation patterns for IAM users, roles, policies, and managed policies. Use when creating IAM resources with CloudFormation, implementing least privilege access, configuring cross-account access, setting up identity centers, managing permissions boundaries, and organizing template structure with Parameters, Outputs, Mappings, Conditions for secure infrastructure deployments.
59
74%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./plugins/developer-kit-aws/skills/aws-cloudformation/aws-cloudformation-iam/SKILL.mdThe canonical home for this skill is giuseppe-trisciuoglio/developer-kit
Create production-ready IAM infrastructure using AWS CloudFormation templates. This skill covers users, roles, policies, managed policies, permission boundaries, and best practices for implementing least privilege access.
Use this skill when:
Follow these steps to create IAM infrastructure with CloudFormation:
For complete examples, see the EXAMPLES.md file.
The following examples demonstrate common IAM patterns:
LambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-lambda-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRoleDynamoDBPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: !Sub "${AWS::StackName}-dynamodb-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
Resource: !GetAtt Table.Arn
Roles:
- !Ref LambdaRoleDeveloperBoundary:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: Permission boundary for developers
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Deny
Action:
- iam:*
Resource: "*"
DeveloperUser:
Type: AWS::IAM::User
Properties:
UserName: !Sub "${AWS::StackName}-developer"
PermissionsBoundary: !Ref DeveloperBoundaryFor complete production-ready examples, see EXAMPLES.md.
AWSTemplateFormatVersion: 2010-09-09
Description: IAM infrastructure with users, roles, and policies
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: User Configuration
Parameters:
- UserName
- UserPermissionsBoundary
- Label:
default: Role Configuration
Parameters:
- RoleName
- AssumeRolePolicyService
Parameters:
UserName:
Type: String
Default: app-user
Description: Name of the IAM user
MinLength: 1
MaxLength: 64
UserPermissionsBoundary:
Type: String
Description: IAM policy ARN for permissions boundary
Default: ""
RoleName:
Type: String
Default: app-execution-role
Description: Name of the IAM role
AssumeRolePolicyService:
Type: String
Default: lambda.amazonaws.com
Description: Service that can assume the role
AllowedValues:
- lambda.amazonaws.com
- ec2.amazonaws.com
- ecs-tasks.amazonaws.com
- eks.amazonaws.com
- states.amazonaws.com
Mappings:
EnvironmentConfig:
dev:
MaxSessionDuration: 3600
PolicyArns: []
staging:
MaxSessionDuration: 7200
PolicyArns:
- arn:aws:iam::aws:policy/ReadOnlyAccess
production:
MaxSessionDuration: 43200
PolicyArns:
- arn:aws:iam::aws:policy/ReadOnlyAccess
- arn:aws:iam::aws:policy/SecurityAudit
Conditions:
HasPermissionsBoundary: !Not [!Equals [!Ref UserPermissionsBoundary, ""]]
IsProduction: !Equals [!Ref Environment, production]
Transform:
- AWS::Serverless-2016-10-31
Resources:
# IAM User
AppUser:
Type: AWS::IAM::User
Properties:
UserName: !Ref UserName
PermissionsBoundary: !If
- HasPermissionsBoundary
- !Ref UserPermissionsBoundary
- !Ref AWS::NoValue
Tags:
- Key: Environment
Value: !Ref Environment
- Key: Project
Value: !Ref ProjectName
Outputs:
UserArn:
Description: ARN of the IAM user
Value: !GetAtt AppUser.Arn
Export:
Name: !Sub "${AWS::StackName}-UserArn"Parameters:
# AWS-specific types for validation
UserArn:
Type: AWS::IAM::User::Arn
Description: IAM user ARN for reference
RoleArn:
Type: AWS::IAM::Role::Arn
Description: IAM role ARN for reference
PolicyArn:
Type: AWS::IAM::Policy::Arn
Description: IAM policy ARN
ManagedPolicyArn:
Type: AWS::IAM::ManagedPolicy::Arn
Description: AWS managed policy ARN
InstanceProfileArn:
Type: AWS::IAM::InstanceProfile::Arn
Description: IAM instance profile ARN
S3BucketPolicy:
Type: AWS::S3::BucketPolicy::Resource
Description: S3 bucket policy referenceParameters:
UserName:
Type: String
Default: app-user
Description: IAM username
MinLength: 1
MaxLength: 64
ConstraintDescription: Must be 1-64 characters
AllowedPattern: "[a-zA-Z0-9+=,.@_-]+"
RoleName:
Type: String
Default: execution-role
Description: IAM role name
MinLength: 1
MaxLength: 64
ConstraintDescription: Must be 1-64 characters
AllowedPattern: "[a-zA-Z0-9+=,.@_-]+"
MaxSessionDuration:
Type: Number
Default: 3600
Description: Maximum session duration in seconds
MinValue: 900
MaxValue: 43200
ConstraintDescription: Must be between 900 and 43200 seconds
AccessKeyRotationFrequency:
Type: Number
Default: 90
Description: Days between access key rotations
MinValue: 1
MaxValue: 365
ConstraintDescription: Must be between 1 and 365 daysParameters:
ReadOnlyPolicyArn:
Type: AWS::SSM::Parameter::Value<String>
Default: /iam/policies/read-only-arn
Description: ARN of the read-only policy from SSM
CustomPolicyDocument:
Type: AWS::SSM::Parameter::Value<String>
Default: /iam/policies/custom-policy-json
Description: Policy document from SSM Parameter Store# Stack A - IAM Core Stack
AWSTemplateFormatVersion: 2010-09-09
Description: Core IAM infrastructure stack
Resources:
# Execution Role for applications
ApplicationExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-execution-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
MaxSessionDuration: 3600
# Role for Cross-Account Access
CrossAccountReadRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-crossaccount-read"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${TargetAccountId}:root"
Action: sts:AssumeRole
Condition:
StringEquals:
sts:Externalid: !Ref ExternalId
Policies:
- PolicyName: ReadOnlyAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectVersion
Resource: !Ref SourceBucketArn
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
Resource: !Ref SourceTableArn
Outputs:
ApplicationExecutionRoleArn:
Description: ARN of the application execution role
Value: !GetAtt ApplicationExecutionRole.Arn
Export:
Name: !Sub "${AWS::StackName}-ExecutionRoleArn"
CrossAccountReadRoleArn:
Description: ARN for cross-account read access
Value: !GetAtt CrossAccountReadRole.Arn
Export:
Name: !Sub "${AWS::StackName}-CrossAccountReadRoleArn"
CrossAccountReadRoleExternalId:
Description: External ID for cross-account role assumption
Value: !Ref ExternalId
Export:
Name: !Sub "${AWS::StackName}-CrossAccountExternalId"# Stack B - Application Stack (imports from IAM Stack)
AWSTemplateFormatVersion: 2010-09-09
Description: Application stack importing IAM roles
Parameters:
IAMStackName:
Type: String
Default: iam-core
Description: Name of the IAM stack
Resources:
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-processor"
Runtime: python3.11
Handler: app.handler
Code:
S3Bucket: !Ref CodeBucket
S3Key: lambda/function.zip
Role: !ImportValue
!Sub "${IAMStackName}-ExecutionRoleArn"
Environment:
Variables:
TARGET_BUCKET: !Ref TargetBucketAWSTemplateFormatVersion: 2010-09-09
Description: Main stack with nested IAM stacks
Resources:
# Nested stack for users
IAMUsersStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/bucket/iam-users.yaml
TimeoutInMinutes: 15
Parameters:
Environment: !Ref Environment
UserNames: !Ref UserNames
# Nested stack for roles
IAMRolesStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/bucket/iam-roles.yaml
TimeoutInMinutes: 15
Parameters:
Environment: !Ref Environment
TrustedServices: !Ref TrustedServices
# Nested stack for policies
IAMPoliciesStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/bucket/iam-policies.yaml
TimeoutInMinutes: 15
Parameters:
Environment: !Ref EnvironmentAWSTemplateFormatVersion: 2010-09-09
Description: IAM user with programmatic access
Resources:
AppUser:
Type: AWS::IAM::User
Properties:
UserName: !Sub "${AWS::StackName}-app-user"
Tags:
- Key: Environment
Value: !Ref Environment
- Key: Project
Value: !Ref ProjectName
UserAccessKey:
Type: AWS::IAM::AccessKey
Properties:
UserName: !Ref AppUser
Status: Active
Serial: 1
UserSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub "${AWS::StackName}/iam-user-credentials"
Description: IAM user access key credentials
SecretString: !Sub |
{
"username": "${AppUser.UserName}",
"access_key": "${UserAccessKey.Ref}",
"secret_key": "{{resolve:secretsmanager:${UserAccessKey.SecretAccessKey}}}"
}
Outputs:
AccessKeyId:
Description: Access Key ID for the user
Value: !Ref UserAccessKey
Export:
Name: !Sub "${AWS::StackName}-AccessKeyId"
SecretArn:
Description: ARN of the secret containing credentials
Value: !Ref UserSecretResources:
ConsoleUser:
Type: AWS::IAM::User
Properties:
UserName: !Sub "${AWS::StackName}-console-user"
UserLoginProfile:
Type: AWS::IAM::UserLoginProfile
Properties:
UserName: !Ref ConsoleUser
Password: !Ref InitialPassword
PasswordResetRequired: true
UserPasswordSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub "${AWS::StackName}/console-password"
Description: Initial console login password
SecretString: !Ref InitialPasswordAWSTemplateFormatVersion: 2010-09-09
Description: IAM user with permissions boundary
Resources:
# Permissions boundary policy
ReadOnlyBoundaryPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: Read-only permissions boundary
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Deny
Action: "*"
Resource: "*"
NotPrincipal:
- !GetAtt ReadOnlyRole.Arn
AppUser:
Type: AWS::IAM::User
Properties:
UserName: !Sub "${AWS::StackName}-restricted-user"
PermissionsBoundary: !Ref ReadOnlyBoundaryPolicy
ManagedPolicyArns:
- arn:aws:iam::aws:policy/ReadOnlyAccessAWSTemplateFormatVersion: 2010-09-09
Description: Lambda execution role with least privilege
Resources:
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-lambda-execution"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
Policies:
- PolicyName: !Sub "${AWS::StackName}-dynamodb-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource: !GetAtt DataTable.Arn
Condition:
StringEquals:
dynamodb:TableName: !Ref TableName
- Effect: Allow
Action:
- dynamodb:DescribeTable
Resource: "*"
- PolicyName: !Sub "${AWS::StackName}-secrets-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
- secretsmanager:DescribeSecret
Resource: !Ref SecretsArn
- PolicyName: !Sub "${AWS::StackName}-kms-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- kms:Decrypt
- kms:DescribeKey
Resource: !Ref KmsKeyArn
DataTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Ref TableName
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: pk
AttributeType: S
- AttributeName: sk
AttributeType: S
KeySchema:
- AttributeName: pk
KeyType: HASH
- AttributeName: sk
KeyType: RANGEAWSTemplateFormatVersion: 2010-09-09
Description: ECS task execution role
Resources:
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-ecs-task-execution"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
Policies:
- PolicyName: !Sub "${AWS::StackName}-ecr-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ecr:GetDownloadUrlForLayer
- ecr:BatchGetImage
- ecr:BatchCheckLayerAvailability
Resource: !Ref EcrRepositoryArn
- Effect: Allow
Action:
- ecr:GetAuthorizationToken
Resource: "*"
- PolicyName: !Sub "${AWS::StackName}-cloudwatch-logs"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
- logs:CreateLogGroup
Resource: !Ref LogGroupArn
ECSTaskRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-ecs-task"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: !Sub "${AWS::StackName}-app-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:ReceiveMessage
- sqs:DeleteMessage
- sqs:GetQueueAttributes
Resource: !GetAtt Queue.Arn
- Effect: Allow
Action:
- sns:Publish
Resource: !Ref TopicArnAWSTemplateFormatVersion: 2010-09-09
Description: Cross-account access role
Parameters:
SourceAccountId:
Type: String
Description: AWS account ID that can assume this role
ExternalId:
Type: String
Description: External ID for trust relationship
Resources:
CrossAccountReadRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-crossaccount-read"
Description: Role for cross-account read access
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${SourceAccountId}:root"
Action: sts:AssumeRole
Condition:
StringEquals:
sts:Externalid: !Ref ExternalId
IpAddress:
aws:SourceIp:
- 10.0.0.0/8
- 172.16.0.0/12
MaxSessionDuration: 7200
Policies:
- PolicyName: !Sub "${AWS::StackName}-s3-read"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectVersion
- s3:ListBucket
Resource:
- !Ref SourceBucketArn
- !Sub "${SourceBucketArn}/*"
- PolicyName: !Sub "${AWS::StackName}-dynamodb-read"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:DescribeTable
Resource:
- !GetAtt SourceTable.Arn
- !Sub "${GetAtt SourceTable.Arn}/index/*"
Outputs:
RoleArn:
Description: ARN of the cross-account role
Value: !GetAtt CrossAccountReadRole.Arn
Export:
Name: !Sub "${AWS::StackName}-CrossAccountRoleArn"AWSTemplateFormatVersion: 2010-09-09
Description: API Gateway execution role for Cognito authorization
Resources:
ApiGatewayCognitoAuthorizerRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-apigw-cognito-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: !Sub "${AWS::StackName}-cognito-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- cognito-idp:DescribeUserPool
- cognito-idp:DescribeUserPoolClient
Resource: !Ref UserPoolArn
ApiGatewayExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-apigw-execution"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs
Policies:
- PolicyName: !Sub "${AWS::StackName}-lambda-invoke"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource: !Ref LambdaFunctionArnAWSTemplateFormatVersion: 2010-09-09
Description: IAM role for EKS pod execution
Resources:
EKSPodRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-eks-pod-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: eks.amazonaws.com
Action: sts:AssumeRole
Condition:
StringEquals:
aws:SourceArn: !Sub "arn:aws:eks:${AWS::Region}:${AWS::AccountId}:cluster/${EKSClusterName}"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/SecretsManagerReadWrite
Policies:
- PolicyName: !Sub "${AWS::StackName}-s3-read"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectVersion
Resource: !Sub "${DataBucketArn}/*"
EKSPodExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-eks-execution"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: eks.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
- arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
- arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
- arn:aws:iam::aws:policy/CloudWatchLogsReadOnlyAWSTemplateFormatVersion: 2010-09-09
Description: IAM role for CodeBuild project
Resources:
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-codebuild-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
- arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess
Policies:
- PolicyName: !Sub "${AWS::StackName}-source-access"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectVersion
Resource:
- !Ref SourceBucketArn
- !Sub "${SourceBucketArn}/*"
- Effect: Allow
Action:
- s3:PutObject
Resource:
- !Ref BuildOutputBucketArn
- !Sub "${BuildOutputBucketArn}/*"
- Effect: Allow
Action:
- codecommit:GitPull
Resource: !Ref CodecommitRepositoryArn
- Effect: Allow
Action:
- codebuild:CreateReportGroup
- codebuild:CreateReport
- codebuild:UpdateReport
- codebuild:BatchPutTestCases
- codebuild:BatchPutCodeCoverages
Resource: "*"AWSTemplateFormatVersion: 2010-09-09
Description: IAM role for Step Functions state machine
Resources:
StepFunctionsExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-stepfunctions-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: states.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: !Sub "${AWS::StackName}-lambda-tasks"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
- lambda:InvokeAsync
Resource:
- !Ref ProcessFunctionArn
- !Ref ValidateFunctionARN
- !Ref NotifyFunctionArn
- PolicyName: !Sub "${AWS::StackName}-dynamodb-tasks"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
- dynamodb:Query
- dynamodb:Scan
Resource:
- !GetAtt WorkflowTable.Arn
- !Sub "${GetAtt WorkflowTable.Arn}/*"
- PolicyName: !Sub "${AWS::StackName}-sns-tasks"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sns:Publish
Resource: !Ref NotificationTopicArn
- PolicyName: !Sub "${AWS::StackName}-events"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- events:PutTargets
- events:PutRule
- events:DescribeRule
Resource: "*"AWSTemplateFormatVersion: 2010-09-09
Description: Role with S3 access policies
Resources:
S3AccessRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-s3-access"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: !Sub "${AWS::StackName}-s3-readonly"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketPolicy
- s3:GetBucketPolicyStatus
Resource:
- !Ref DataBucketArn
- !Sub "${DataBucketArn}/*"
- Effect: Allow
Action:
- s3:ListBucket
Resource: !Ref DataBucketArn
Condition:
StringLike:
s3:prefix:
- ""
- "documents/*"
- "reports/*"
- PolicyName: !Sub "${AWS::StackName}-s3-write"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:PutObject
- s3:PutObjectAcl
- s3:DeleteObject
Resource: !Sub "${DataBucketArn}/processed/*"AWSTemplateFormatVersion: 2010-09-09
Description: Custom managed policy for DynamoDB access
Resources:
DynamoDBFullAccessPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: Full access to specific DynamoDB tables
ManagedPolicyName: !Sub "${AWS::StackName}-dynamodb-full-access"
Groups:
- !Ref AppUserGroup
Roles:
- !Ref AppExecutionRole
Users:
- !Ref AppUser
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- dynamodb:CreateTable
- dynamodb:UpdateTable
- dynamodb:DeleteTable
- dynamodb:DescribeTable
- dynamodb:DescribeTimeToLive
- dynamodb:ListTagsOfResource
Resource: !GetAtt DataTable.Arn
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
- dynamodb:BatchGetItem
- dynamodb:BatchWriteItem
Resource:
- !GetAtt DataTable.Arn
- !Sub "${GetAtt DataTable.Arn}/index/*"
- Effect: Allow
Action:
- dynamodb:DescribeLimits
- dynamodb:ListTables
Resource: "*"AWSTemplateFormatVersion: 2010-09-09
Description: Policy with IP and time-based conditions
Resources:
RestrictedAccessRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-restricted-access"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: !Sub "${AWS::StackName}-conditional-access"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:GetObject
Resource: !Sub "${DataBucketArn}/*"
Condition:
IpAddress:
aws:SourceIp:
- 10.0.0.0/8
- 192.168.1.0/24
StringEquals:
s3:ExistingObjectTag/classification: internal
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:Query
Resource: !GetAtt DataTable.Arn
Condition:
StringEquals:
dynamodb:Select: SPECIFIC_ATTRIBUTES
dynamodb:Attributes:
- id
- name
- statusAWSTemplateFormatVersion: 2010-09-09
Description: Permission boundary for developer roles
Resources:
DeveloperBoundaryPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: Permission boundary for developers - denies production write access
ManagedPolicyName: !Sub "${AWS::StackName}-developer-boundary"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Deny
Action:
- "*"
Resource: "*"
Condition:
StringEquals:
aws:RequestedRegion:
- us-east-1
- us-west-2
StringLike:
aws:ResourceTag/environment:
- production
- prod
- Effect: Deny
Action:
- iam:CreateUser
- iam:DeleteUser
- iam:PutUserPolicy
- iam:AttachUserPolicy
- iam:DetachUserPolicy
Resource: "*"
- Effect: Deny
Action:
- iam:CreateRole
- iam:DeleteRole
- iam:AttachRolePolicy
- iam:DetachRolePolicy
Resource: "*"
- Effect: Allow
Action:
- s3:Get*
- s3:List*
Resource: "*"
- Effect: Allow
Action:
- dynamodb:Get*
- dynamodb:Query
- dynamodb:Scan
Resource: "*"
- Effect: Allow
Action:
- lambda:InvokeFunction
- lambda:GetFunction
Resource: "*"
DeveloperRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-developer"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
AWS: !Ref DeveloperUserArn
Action: sts:AssumeRole
PermissionsBoundary: !Ref DeveloperBoundaryPolicy
ManagedPolicyArns:
- arn:aws:iam::aws:policy/ReadOnlyAccessAWSTemplateFormatVersion: 2010-09-09
Description: IAM Identity Center permission set
Resources:
SSOPermissionSet:
Type: AWS::SSO::PermissionSet
Properties:
InstanceArn: !Ref SSOInstanceArn
PermissionSetName: !Sub "${AWS::StackName}-admin-permissions"
Description: Administrator access permission set
SessionDuration: PT8H
RelayStateType: sso.amazonaws.com
ManagedPolicies:
- arn:aws:iam::aws:policy/AdministratorAccess
InlinePolicy: |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"aws-portal:*Billing",
"aws-portal:*Usage"
],
"Resource": "*"
}
]
}
SSOAssignment:
Type: AWS::SSO::AccountAssignment
Properties:
InstanceArn: !Ref SSOInstanceArn
PermissionSetArn: !Ref SSOPermissionSet
PrincipalId: !Ref SSOGroupId
PrincipalType: GROUP
TargetId: !Ref TargetAWSAccountId
TargetType: AWS_ACCOUNTAWSTemplateFormatVersion: 2010-09-09
Description: SSO permission set with custom policies
Resources:
ReadOnlyPermissionSet:
Type: AWS::SSO::PermissionSet
Properties:
InstanceArn: !Ref SSOInstanceArn
PermissionSetName: !Sub "${AWS::StackName}-read-only"
Description: Read-only access for auditors
SessionDuration: PT4H
ManagedPolicies:
- arn:aws:iam::aws:policy/ReadOnlyAccess
InlinePolicy: |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::${DataBucketArn}",
"arn:aws:s3:::${DataBucketArn}/*"
]
},
{
"Effect": "Deny",
"Action": [
"s3:DeleteObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::${DataBucketArn}/*"
]
}
]
}AWSTemplateFormatVersion: 2010-09-09
Description: Service control policy for production OU
Resources:
ProductionSCP:
Type: AWS::Organizations::Policy
Properties:
Name: !Sub "${AWS::StackName}-production-restrictions"
Description: SCP to restrict actions in production accounts
Type: SERVICE_CONTROL_POLICY
Content:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyDeleteResources",
"Effect": "Deny",
"Action": [
"s3:DeleteBucket",
"dynamodb:DeleteTable",
"rds:DeleteDBInstance",
"lambda:DeleteFunction"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/environment": "production"
}
}
},
{
"Sid": "RequireEncryption",
"Effect": "Deny",
"Action": [
"s3:PutObject",
"dynamodb:PutItem"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "AES256"
}
}
},
{
"Sid": "DenyRootAccess",
"Effect": "Deny",
"Action": [
"iam:CreateAccessKey",
"iam:CreateLoginProfile",
"iam:EnableMFADevice"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:PrincipalTag/role": "breakglass"
}
}
},
{
"Sid": "AllowKnownServices",
"Effect": "Allow",
"Action": "*",
"Resource": "*",
"NotPrincipal": {
"AWS": [
"arn:aws:iam::aws:policy/AdministratorAccess"
]
}
}
]
}AWSTemplateFormatVersion: 2010-09-09
Description: IAM resources with conditional configurations
Parameters:
Environment:
Type: String
Default: dev
AllowedValues:
- dev
- staging
- production
Conditions:
IsProduction: !Equals [!Ref Environment, production]
IsDevelopment: !Equals [!Ref Environment, dev]
CreateAdminRole: !Or [!Equals [!Ref Environment, staging], !Equals [!Ref Environment, production]]
Resources:
# Base role for all environments
BaseExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-base-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
MaxSessionDuration: !FindInMap [EnvironmentConfig, !Ref Environment, MaxSessionDuration]
Policies:
- PolicyName: !Sub "${AWS::StackName}-base-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "*"
# Admin role only for staging and production
AdminRole:
Type: AWS::IAM::Role
Condition: CreateAdminRole
Properties:
RoleName: !Sub "${AWS::StackName}-admin-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
AWS: !Ref AdminUserArn
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/ReadOnlyAccess
- !If
- IsProduction
- arn:aws:iam::aws:policy/ViewBilling
- !Ref AWS::NoValue
Mappings:
EnvironmentConfig:
dev:
MaxSessionDuration: 3600
staging:
MaxSessionDuration: 7200
production:
MaxSessionDuration: 43200AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: Using SAM Transform for IAM patterns
Globals:
Function:
Timeout: 30
Runtime: python3.11
Policies:
- S3ReadPolicy:
BucketName: !Ref DataBucket
- DynamoDBCrudPolicy:
TableName: !Ref DataTable
- SecretsManagerReadWrite:
SecretArn: !Ref AppSecret
Resources:
ProcessorFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-processor"
Handler: app.handler
CodeUri: lambda_function/
Environment:
Variables:
LOG_LEVEL: INFOStack Policies prevent unintended updates to critical resources during stack updates.
Resources:
# Stack Policy to protect IAM roles from updates
IAMStackPolicy:
Type: AWS::CloudFormation::StackPolicy
Properties:
PolicyDocument:
Statement:
- Effect: Deny
Action: Update:Replace
UpdateReplacePolicy: Retain
Resource: "*"
Condition:
StringEquals:
aws:ResourceTag/Protected: "true"
- Effect: Allow
Action: Update:*
Resource: "*"Enable termination protection to prevent accidental stack deletion.
Resources:
ProductionStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/bucket/iam-template.yaml
TerminationProtection: trueCLI commands for termination protection:
# Enable termination protection
aws cloudformation update-termination-protection \
--stack-name my-iam-stack \
--enable-termination-protection
# Disable termination protection
aws cloudformation update-termination-protection \
--stack-name my-iam-stack \
--no-enable-termination-protectionDetect when infrastructure has diverged from the template definition.
# Detect drift on a stack
aws cloudformation detect-drift \
--stack-name my-iam-stack
# Get drift detection status
aws cloudformation describe-stack-drift-detection-status \
--stack-drift-detection-id <detection-id>
# Get drift detection results
aws cloudformation describe-stack-resource-drifts \
--stack-name my-iam-stackPreview and review changes before executing them.
# Create a change set
aws cloudformation create-change-set \
--stack-name my-iam-stack \
--change-set-name my-changeset \
--template-body file://template.yaml \
--capabilities CAPABILITY_IAM
# List change sets
aws cloudformation list-change-sets \
--stack-name my-iam-stack
# Describe change set
aws cloudformation describe-change-set \
--stack-name my-iam-stack \
--change-set-name my-changeset
# Execute change set
aws cloudformation execute-change-set \
--stack-name my-iam-stack \
--change-set-name my-changeset
# Delete change set (if not executing)
aws cloudformation delete-change-set \
--stack-name my-iam-stack \
--change-set-name my-changesetFor complete details on IAM resources and their properties, see:
fernandezbaptiste/developer-kit
810297c
Canonical home
since Mar 15, 2026
Also appears in
since Sep 19, 2026
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.