Skip to content

Intrinsic functions

cfnrain adds some intrinsic functions that CloudFormation does not have. cfnrain resolves them when it packages the template, so CloudFormation never sees them. It also simplifies Fn::Join and Fn::Select when it can.

Function Short form Where it works
Fn::Merge !Merge In module resources
Fn::InsertFile !InsertFile Anywhere
Fn::Invoke !Invoke In a template that has a Modules section

Fn::Merge only runs in modules

In the resources of the parent template, cfnrain leaves Fn::Merge as it is. CloudFormation has no Fn::Merge, so a deploy with Fn::Merge outside a module fails.

Fn::Merge

Fn::Merge merges a list of maps into one map, or a list of lists into one list. For maps, a later key replaces an earlier one. For lists, cfnrain joins the lists and keeps duplicates.

Use it to combine values that come from module properties, such as tags from the parent and tags from the module.

The module:

Parameters:
  X:
    Type: Object
  Y:
    Type: Object
  XX:
    Type: List
  YY:
    Type: List
Resources:
  Bar:
    Type: A::B::C
    Properties:
      Merged:
        Fn::Merge:
        - !Ref X
        - !Ref Y
      Lists:
        Fn::Merge:
        - !Ref XX
        - !Ref YY
        - !Ref YY

The parent template:

Modules:
  Foo:
    Source: merge-module.yaml
    Properties:
      X: 
        A: "BBB"
        B: "c"
      Y:
        B: "CCC"
        C: "D"
      XX:
      - a
      - b
      YY:
      - b
      - c

The packaged template:

Resources:
  FooBar:
    Type: A::B::C
    Properties:
      Merged:
        A: BBB
        B: CCC
        C: D
      Lists:
      - a
      - b
      - b
      - c
      - b
      - c

Fn::InsertFile

Fn::InsertFile <path> inserts the contents of a file as a string. The path is relative to the template. Unlike !Rain::Embed, it keeps the whitespace at the end of the file.

The template:

Resources:
  Foo:
    Type: A::B::C
    Properties:
      Content:
        Fn::InsertFile: insertfile-file.yaml

insertfile-file.yaml:

Test: abc

The packaged template:

Resources:
  Foo:
    Type: A::B::C
    Properties:
      Content: 'Test: abc

        '

Fn::Invoke

Fn::Invoke calls a module like a function. It takes three items:

  1. The name of a module in the Modules section
  2. A map of properties. These replace the properties that the Modules section sets.
  3. The name of a module output

cfnrain evaluates the module with the new properties and inserts the output. A module for Invoke does not need resources. Parameters and Outputs are enough. A common use is a naming module that joins an environment, an application and a resource name.

The naming module, invoke-module.yaml:

Description: This module outputs a consistent resource name

Parameters:

  Env:
    Type: String
    Description: The environment, for example, dev, stage, prod

  App:
    Type: String
    Description: The application name

  Name:
    Type: String
    Description: The name of the resource

  Delimiter:
    Type: String
    Default: "-"

  MaxLength:
    Type: Integer
    Description: If the components exceed this, truncate and append a hash

  IncludeRegion:
    Type: Boolean
    Default: false

  IncludeAccount:
    Type: Boolean
    Default: false

  LabelOrder:
    Type: List<String>
    Default:
      - Name
      - App
      - Env
      - Region
      - Account

Conditions:

  IncludeRegion:
    Fn::Equals:
      - !Ref IncludeRegion
      - true

Resources:

  Why:
    Type: X::Y::Z
    Properties:
      Id:  
        Fn::Join:
          - !Ref Delimiter
          - - !Ref Name
            - !Ref App
            - !Ref Env
            - Fn::If:
              - IncludeRegion
              - !Ref AWS::Region
              - !Ref AWS::NoValue

Outputs:

  Id:
    Value:
      Fn::Join:
        - !Ref Delimiter
        - - !Ref Name
          - !Ref App
          - !Ref Env
          - Fn::If:
            - IncludeRegion
            - !Ref AWS::Region
            - !Ref AWS::NoValue

A module that returns an object, invoke-module-b.yaml:

Parameters:
  X:
    Type: String
  Y: 
    Type: String
  Z:
    Type: String

Outputs:
  Props:
    Value:
      X: !Ref X
      Y: !Ref Y
      Z: !Ref Z

The parent template:

Modules:

  Label:
    Source: invoke-module.yaml
    Properties:
      Env: prod
      App: myapp
      Name: "?"

  HIJProps:
    Source: invoke-module-b.yaml
    Properties:
      X: x
      Y: y
      Z: z


Resources:

  A:
    Type: D::E::F
    Properties:
      Name: 
        Fn::Invoke:
          - Label
          - Name: test
          - Id
      Name2: !Invoke [Label, {Name: test2}, Id]
      WithRegion: !Invoke [Label, {Name: test3, IncludeRegion: true}, Id]

  B: 
    Type: H::I::J
    Properties: 
      Fn::Invoke:
        - HIJProps
        - Y: yy
        - Props

  C:
    Type: H::I::J
    Properties: !Invoke [HIJProps, {Z: zz}, Props]

The packaged template:

Resources:
  A:
    Type: D::E::F
    Properties:
      Name: test-myapp-prod
      Name2: test2-myapp-prod
      WithRegion:
        Fn::Join:
        - '-'
        - - test3
          - myapp
          - prod
          - Ref: AWS::Region
  B:
    Type: H::I::J
    Properties:
      X: x
      Y: yy
      Z: z
  C:
    Type: H::I::J
    Properties:
      X: x
      Y: y
      Z: zz
  LabelWhy:
    Type: X::Y::Z
    Properties:
      Id: ?-myapp-prod

The Label module also has a resource, Why. cfnrain adds it to the template as LabelWhy, with the properties from the Modules section. Fn::Invoke does not add resources.

Fn::Join and Fn::Select

cfnrain also simplifies two CloudFormation functions when it can resolve them while it packages the template:

  • Fn::Join becomes a plain string when every item in the list is a string. This works in the parent template and in modules.
  • Fn::Select becomes the selected item when the list and the index are known.

Fn::Select takes its arguments in the reverse order

cfnrain simplifies Fn::Select only when the list comes first and the index second: Fn::Select: [!Ref Things, 0]. CloudFormation uses the opposite order: Fn::Select: [0, !Ref Things]. If you use the CloudFormation order, cfnrain leaves the function as it is and CloudFormation resolves it. That works. If you use the cfnrain order and cfnrain cannot resolve the list, CloudFormation gets the arguments in the wrong order and the deploy fails. Use the CloudFormation order. See Known issues.

The test for Fn::Select uses the cfnrain order:

Parameters:
  Things:
    Type: List<String>
Resources:
  Foo:
    Type: A::B::C
    Properties:
      Names: !Ref Things
      OneName: 
        Fn::Select:
        - !Ref Things
        - 0
Modules:
  TestSelect:
    Source: select-module.yaml
    Properties:
      Things:
      - foo
      - bar

The packaged template:

Resources:
  TestSelectFoo:
    Type: A::B::C
    Properties:
      Names:
      - foo
      - bar
      OneName: foo