aws-cloudformation-iam

AWS CloudFormation IAM Security

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "aws-cloudformation-iam" with this command: npx skills add giuseppe-trisciuoglio/developer-kit/giuseppe-trisciuoglio-developer-kit-aws-cloudformation-iam

AWS CloudFormation IAM Security

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.

When to Use

Use this skill when:

  • Creating new IAM users with CloudFormation

  • Configuring IAM roles for AWS services

  • Defining inline policies and managed policies

  • Implementing cross-account access with STS

  • Creating permission boundaries

  • Organizing templates with Parameters, Outputs, Mappings, Conditions

  • Implementing cross-stack references for IAM resources

  • Configuring IAM Identity Center (SSO)

  • Managing service control policies (SCP)

CloudFormation Template Structure

Standard Format Base Template

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 Best Practices

AWS-Specific Parameter Types

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 reference

Parameter Constraints

Parameters: 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 days

SSM Parameter References per Policy ARNs

Parameters: 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

Outputs and Cross-Stack References

Export/Import Patterns for IAM

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 TargetBucket

Nested Stacks for IAM Modularity

AWSTemplateFormatVersion: 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 Environment

IAM Users

User with Access Keys

AWSTemplateFormatVersion: 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 UserSecret

User with Console Password

Resources: 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 InitialPassword

User with Permissions Boundary

AWSTemplateFormatVersion: 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/ReadOnlyAccess

IAM Roles

Role for Lambda Execution

AWSTemplateFormatVersion: 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: RANGE

Role for ECS Tasks

AWSTemplateFormatVersion: 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 TopicArn

Role for Cross-Account Access

AWSTemplateFormatVersion: 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"

Role for API Gateway with Cognito

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 LambdaFunctionArn

Role for EKS Pods

AWSTemplateFormatVersion: 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/CloudWatchLogsReadOnly

Role for CodeBuild

AWSTemplateFormatVersion: 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: "*"

Role for Step Functions

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: ""

IAM Policies

Inline Policy for S3 Access

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/*"

Custom Managed Policy

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: ""

Policy with Conditions

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 - status

Permission Boundaries

Permission Boundary for Developers

AWSTemplateFormatVersion: 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/ReadOnlyAccess

IAM Identity Center (SSO)

SSO Permission Set

AWSTemplateFormatVersion: 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_ACCOUNT

SSO Custom Policy with Conditions

AWSTemplateFormatVersion: 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}/" ] } ] }

Service Control Policies (SCP)

SCP for Organizational Units

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" ] } } ] }

Conditions and Transform

Conditions for Environment-Specific Roles

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: 43200

Transform for Policy Reuse

AWSTemplateFormatVersion: 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: INFO

Best Practices

Security

  • Always apply least privilege: start with minimal permissions and add only what's needed

  • Use IAM Access Analyzer to identify excessive permissions

  • Implement permission boundaries to limit maximum permissions

  • Use condition keys for granular restrictions (aws:SourceIp, aws:ResourceTag, aws:RequestedRegion)

  • Enable MFA for all IAM users

  • Rotate access keys regularly (max 90 days)

  • Use roles instead of long-term credentials where possible

  • Implement external ID for cross-account trust

  • Use service-linked roles for AWS services

Template Organization

  • Separate IAM into dedicated stacks for clear ownership

  • Use nested stacks for modularity

  • Export only necessary values for cross-stack references

  • Always document intent with Description fields

  • Use Parameters for environment-specific configuration

  • Implement conditions to manage variants without duplication

  • Tag all IAM resources for tracking and cost allocation

Monitoring

  • Enable CloudTrail for audit of all IAM actions

  • Configure S3 Object Lock for critical policy files

  • Use IAM Access Analyzer for periodic reports

  • Implement SCP for organizational guardrails

  • Create alarms for unauthorized access

Compliance

  • Implement separation of duties policies

  • Use AWS managed policies for standard compliance

  • Document all deviations from standards

  • Maintain audit trail for at least 90 days

  • Apply encryption at rest and in transit

CloudFormation Stack Management Best Practices

Stack Policies

Stack 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: "*"

Termination Protection

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: true

CLI 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-protection

Drift Detection

Detect 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-stack

Change Sets

Preview 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-changeset

Related Resources

  • IAM Documentation

  • AWS CloudFormation User Guide

  • IAM Best Practices

  • IAM Access Analyzer

Additional Files

For complete details on IAM resources and their properties, see:

  • REFERENCE.md - Detailed reference guide for all CloudFormation IAM resources

  • EXAMPLES.md - Complete production-ready examples

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

spring-boot-security-jwt

No summary provided by upstream source.

Repository SourceNeeds Review
Security

unit-test-security-authorization

No summary provided by upstream source.

Repository SourceNeeds Review
Security

typescript-security-review

No summary provided by upstream source.

Repository SourceNeeds Review
Security

aws-cloudformation-security

No summary provided by upstream source.

Repository SourceNeeds Review