In this post, I explain what I did to resolve the Template format error: Parameter count 62 is greater than max allowed 60 error from CloudFormation after updating an existing CF template with new parameters.

I recently had to update an existing CloudFormation template which required some additional parameters. Upon performing an update in CloudFormation with the new script I got the Template format error: Parameter count 62 is greater than max allowed 60 error.

I quickly found the offical documentation on limits: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html

The offical documentation suggested workaround/alternative solution is to use mappings. However, 60 template parameters is a lot so the first thing I did was verfiy that all the parameters in this script were actually in use. Unfortunately they were so I couldn’t remove any to get back under the limit.

This meant moving the parameters to mappings.

Mappings:
  CpuUtilizationAlarm:
    CpuAlarmThreshold:
      Development: 80
      UAT: 80
      Stage: 80
      Production: 80

This then meant the values could be looked up in the map using the FindInMap intrinsic function:

  CpuUtilizationHighAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      EvaluationPeriods: !FindInMap [CpuUtilizationAlarm, "CpuAlarmThreshold", !Ref Environment]

This works well but what if you are using these parameter values in the user data script of the launch configuration?

For example, let’s say, for some reason or other, you required the CPU alarm threshold value in the user data script as a variable to be used for something further in the script:

    UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          export CPU_ALARM_THRESHOLD=${CpuAlarmThreshold}

This would no longer work because the CpuAlarmThreshold is no longer a template parameter. So how do you insert the mapping value?

This just requires a subtle change to the way the Sub function is used. The Fn::Sub intrinsic function is required that allows a variable map to be specified like so:

      UserData:
        Fn::Base64:
          Fn::Sub:
            - |
              #!/bin/bash
              export CPU_ALARM_THRESHOLD=${CpuAlarmThresholdValue}
            - {
              CpuAlarmThresholdValue: !FindInMap [CpuUtilizationAlarm, "CpuAlarmThreshold", !Ref Environment]
              }

The FindInMap function retrieves the CpuAlarmThreshold value from the map and assigns it to CpuAlarmThresholdValue which is then inserted into the user data script such that the user data script becomes:

#!/bin/bash
export CPU_ALARM_THRESHOLD=80