22

Imagine you have a aws resource such as

  Resources:
    IdentityPool:
      Type: "AWS::Cognito::IdentityPool"
      Properties:
        IdentityPoolName: ${self:custom.appName}_${self:provider.stage}_identity
        CognitoIdentityProviders:
          - ClientId:
              Ref: UserPoolClient

The Ref for "AWS::Cognito::IdentityPool" returns the id of this resource. Now lets say I want to reference that id in a multiline string. I've tried

Outputs:  
  AmplifyConfig:
    Description: key/values to be passed to Amplify.configure(config);
    Value: |
      {
        'aws_cognito_identity_pool_id': ${Ref: IdentityPool}, ##<------ Error
        'aws_sign_in_enabled': 'enable',
        'aws_user_pools_mfa_type': 'OFF',
      }

I've also tried to use Fn:Sub but without luck.

   AmplifyConfig:
      Description: key/values to be passed to Amplify.configure(config);
      Value: 
        Fn::Sub 
          - |
            {
              'aws_cognito_identity_pool_id': '${Var1Name}',
              'aws_sign_in_enabled': 'enable',
            }
          - Var1Name:
              Ref: IdentityPool

Any way to do this?

honkskillet
  • 3,007
  • 6
  • 31
  • 47

4 Answers4

61

Using a pipe symbol | in YAML turns all of the following indented lines into a multi-line string.

A pipe, combined with !Sub will let you use:

  • your resources Ref return value easily like ${YourResource}
  • their Fn::GetAtt return values with just a period ${YourResource.TheAttribute}
  • any Pseudo Parameter just as is like ${AWS:region}

As easy as !Sub |, jumping to the next line and adding proper indentation. Example:

Resources:
  YourUserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      UserPoolName: blabla

Outputs:
  AmplifyConfig:
    Description: key/values to be passed to Amplify.configure(config);
    Value: !Sub |
      {
        'aws_cognito_identity_pool_id': '${YourUserPool}',
        'aws_sign_in_enabled': 'enable',
        'aws_user_pools_mfa_type': 'OFF',
      }
  AdvancedUsage:
    Description: use Pseudo Parameters and/or resources attributes
    Value: !Sub |
      {
        'aws_region': '${AWS::Region}',
        'user_pool_arn': '${YourUserPool.Arn}',
      }
Clorichel
  • 1,940
  • 1
  • 13
  • 24
  • How do you do the same with JSON? – San Oct 26 '18 at 15:26
  • @San you can use the [CloudFormation designer](https://console.aws.amazon.com/cloudformation/designer/home) to convert YAML and JSON, see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/working-with-templates-cfn-designer-json-editor.html#w2ab1c17c17c13c17c15 : just have to paste the template snippet above then switch the "Choose template language" input radio from YAML to JSON – Clorichel Oct 27 '18 at 13:47
  • 1
    What if you neet to use !FindInMap? I know I can do it using !Sub ['${var}', { var: !FindInMap [...] }] when '${var}' is only 1 line long, but when it is multiline? – Matias Haeussler May 26 '21 at 16:17
6

I found out how to do this using Join

AmplifyConfig:
  Description: key/values to be passed to Amplify.configure(config);
  Value:
    Fn::Join:
      - ''
      - - "{"
        - "\n  'aws_cognito_identity_pool_id':"
        - Ref : IdentityPool
        - "\n  'aws_user_pools_id':"
        - Ref : UserPool
        - "\n  'aws_user_pools_web_client_id':"
        - Ref : UserPoolClient
        - ",\n  'aws_cognito_region': '${self:provider.region}'"
        - ",\n  'aws_sign_in_enabled': 'enable'"
        - ",\n  'aws_user_pools': 'enable'"
        - ",\n  'aws_user_pools_mfa_type': 'OFF'"
        - "\n}"

This works but it's kinda ugly. I'm going to leave this answer unaccepted for a while to see if anyone can show how to do this with Fn::Sub.

honkskillet
  • 3,007
  • 6
  • 31
  • 47
3

Using YAML you could compose this simply:

Outputs:  
  AmplifyConfig:
    Description: key/values to be passed to Amplify.configure(config);
    Value: !Sub '
      {
        "aws_cognito_identity_pool_id": "${IdentityPool}",
        "aws_sign_in_enabled": "enable",
        "aws_user_pools_mfa_type": "OFF",
      }'
Chad Elias
  • 637
  • 6
  • 7
2

Leaving this here as I encountered a Base64 encoding error when doing something similar and this question came up when searching for a solution.

In my case I was a using multi line string + !Sub to populate UserData and receiving the following error in AWS Cloudformation.

Error:

Invalid BASE64 encoding of user data. (Service: AmazonEC2; Status Code: 400; Error Code: InvalidUserData.Malformed; Request ID: *; Proxy: null)

Solution:

Can be solved by combining two built in Cloudformation functions; Fn::Base64 and !Sub:

UserData: 
    Fn::Base64: !Sub | 
    #!/bin/bash
    echo ${SomeVar}
squareben
  • 31
  • 5