I'm working on a template that allows to create a user, tag it, and add it to any groups, add any roles, managed policies and in-line policies if needed:
## =================== VERSION =================== AWSTemplateFormatVersion: '2010-09-09' ## =================== DESCRIPTION =================== Description: >- Creates an IAM User if none is present, optionally manage to which IAM group(s) the user belongs to Optionally embed AWS managed policies, customer managed policies and inline policies in the user ## =================== METADATA =================== Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Set a one-time password Parameters: - paramOneTimePassword ParameterLabels: paramOneTimePassword: default: ' ' ## =================== PARAMETERS =================== Parameters: paramOneTimePassword: Description: The user will have to pick a new password upon first sign in Type: String NoEcho: 'true' # mask the parameter value as asterisks (*****) to prevent it from being displayed in the console, CLI, or API ConstraintDescription: Password must be between 8 and 32 characters, start with lowercase or uppercase letter, and can be alphanumeric with the following special characters !@#$%& ## =================== MAPPINGS =================== Mappings: mapUserData: UserName: AWS: testuser Real: 'Test User' Groups: Group: BackendDev Tags: product: testProduct mapManagedPolicies: CustomerManagedPolicies: ARN: None ## =================== CONDITIONS =================== Conditions: hasManagedPolicy: !Not [!Equals [!FindInMap [mapManagedPolicies, CustomerManagedPolicies, ARN], 'None'] ] ## =================== RESOURCES =================== Resources: myInlinePolicyForChangeOwnPassword: Type: 'AWS::IAM::Policy' Properties: PolicyName: IAM-ChangeOwnPassword PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Sid: ViewAccountPasswordRequirements Action: 'iam:GetAccountPasswordPolicy' Resource: "*" - Effect: Allow Sid: ChangeOwnPassword Action: - 'iam:GetUser' - 'iam:ChangePassword' Resource: !Join ['', [arn:aws:iam::*:user/,!FindInMap [mapUserData, UserName, AWS]]] Users: - !FindInMap [mapUserData, UserName, AWS] myUser: Type: 'AWS::IAM::User' Properties: UserName: !FindInMap [mapUserData, UserName, AWS] LoginProfile: Password: !Ref paramOneTimePassword PasswordResetRequired: true Groups: - !FindInMap [mapUserData, Groups, Group] ManagedPolicyArns: # list of ARNs of IAM managed policies that you want to attach to the user - !If [ hasManagedPolicy, !FindInMap [mapManagedPolicies, CustomerManagedPolicies, ARN], !Ref "AWS::NoValue" ] Tags: - Key: name Value: !FindInMap [mapUserData, UserName, Real] - Key: product Value: !FindInMap [mapUserData, Tags, product] WaitCondition: Type: AWS::CloudFormation::WaitCondition DependsOn: myUser Properties: # Handle: !Ref WaitHandle Timeout: 120 Count: 1 ## =================== OUTPUT =================== Outputs: outputArn: Description: User ARN Value: !GetAtt myUser.Arn outputName: Description: User name Value: !Ref myUser
Thing is that there's a race condition between the moment the user creation is triggered, and the moment CF tries to create/attach the in-line policy to the user: if the user haven't finished creating, the stack deployment will fail.
I read about using
WaitCondition but I'm not really sure how to employ it here with IAM resources. I'd greatly appreciate any feedback that can point me into the right direction.