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.
Thanks