<p>It turns out I was doing it wrong due to me being new to CloudFormation.</p>
<p>For anyone else looking into how to attach both managed and inline policies to a user at the moment of user creation, this is how I do it which <em>incidentally</em> is pretty well documented by the AWS folks:</p>
<pre class="lang-yaml prettyprint-override"><code>## =================== VERSION ===================
AWSTemplateFormatVersion: '2010-09-09'

## =================== DESCRIPTION ===================
Description: &gt;-
  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 !@#$%&amp;

## =================== 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:
  myUser:
    Type: 'AWS::IAM::User'
    Properties:
      UserName: !FindInMap [mapUserData, UserName, AWS]
      LoginProfile:
        Password: !Ref paramOneTimePassword 
        PasswordResetRequired: true 
      ManagedPolicyArns: # list of ARNs of IAM managed policies that you want to attach to the user
        - !If [ hasManagedPolicy, !FindInMap [mapManagedPolicies, CustomerManagedPolicies, ARN], !Ref &quot;AWS::NoValue&quot; ]
      Policies:
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Sid: ViewAccountPasswordRequirements
                Action: 'iam:GetAccountPasswordPolicy'
                Resource: &quot;*&quot;
              - Effect: Allow
                Sid: ChangeOwnPassword
                Action:
                  - 'iam:GetUser'
                  - 'iam:ChangePassword'
                Resource: !Join ['', [arn:aws:iam::*:user/,!FindInMap [mapUserData, UserName, AWS]]]
          PolicyName: IAM-ChangeOwnPassword
      Groups:
        - !FindInMap [mapUserData, Groups, Group]
      Tags:
        - Key: name
          Value: !FindInMap [mapUserData, UserName, Real]
        - Key: product
          Value: !FindInMap [mapUserData, Tags, product]

## =================== OUTPUT ===================
Outputs:
  outputArn:
    Description: User ARN
    Value: !GetAtt myUser.Arn
  outputName:
    Description: User name
    Value: !Ref myUser
</code></pre>

CC BY-SA 4.0
2021-07-19T20:37:06.423Z
2021-07-19T20:37:06.423Z
solr
<p>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:</p>
<pre class="lang-yaml prettyprint-override"><code>## =================== VERSION ===================
AWSTemplateFormatVersion: '2010-09-09'

## =================== DESCRIPTION ===================
Description: &gt;-
  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 !@#$%&amp;

## =================== 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: &quot;*&quot;
          - 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 &quot;AWS::NoValue&quot; ]
      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
</code></pre>
<p>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.</p>
<p>I read about using <code>WaitCondition</code> 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.</p>
<p>Thanks</p>

<aws-cloudformation>
How to add a wait condition when creating IAM entities
solr
# How to add a wait condition when creating IAM entities

(Asked by: username_0 on 2021-07-19)

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

**Tags:** aws-cloudformation


---

## 1 answers

### Answer by username_0 on 2021-07-19. Score: 1

It turns out I was doing it wrong due to me being new to CloudFormation.
For anyone else looking into how to attach both managed and inline policies to a user at the moment of user creation, this is how I do it which incidentally is pretty well documented by the AWS folks:
## =================== 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:
  myUser:
    Type: 'AWS::IAM::User'
    Properties:
      UserName: !FindInMap [mapUserData, UserName, AWS]
      LoginProfile:
        Password: !Ref paramOneTimePassword 
        PasswordResetRequired: true 
      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" ]
      Policies:
        - 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]]]
          PolicyName: IAM-ChangeOwnPassword
      Groups:
        - !FindInMap [mapUserData, Groups, Group]
      Tags:
        - Key: name
          Value: !FindInMap [mapUserData, UserName, Real]
        - Key: product
          Value: !FindInMap [mapUserData, Tags, product]

## =================== OUTPUT ===================
Outputs:
  outputArn:
    Description: User ARN
    Value: !GetAtt myUser.Arn
  outputName:
    Description: User name
    Value: !Ref myUser


---

