Hi,
I'm working on a template that allows to create a user, add it to any groups if need, tag it, add managed policies and in-line policies:
## =================== 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
The problem tho is that there's a race condition when sometimes the user is quickly created, so the in-line policy can be correctly attached, but other times it pops.
I've read about the WaitCondition resource (and WaitConditionHandle), but I can't really find a way to make CF wait and evaluate the AWS::IAM::User resource before going ahead and trying to attach the inline policy.
I'm running out of ideas here, thoughts?