Refactor repeated parameters in cloud formation template?

1.4k views Asked by At

I'm looking for a way to refactor repeated value imports in my Cloud Formation template.

I have the following template which configures a simple app:

Parameters:
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access into the server
    Type: AWS::EC2::KeyPair::KeyName
  S3StackName:
    Description: Name of S3 Stack
    Type: String

Resources:
  EC2Instance:
    Type: AWS::EC2::Instance
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            yum:
              httpd: []
              php: []
          files:
            /var/www/html/index.html:
              source:
                Fn::Sub:
                  - https://s3.amazonaws.com/${bucketName}/index.html
                  - bucketName:
                      Fn::ImportValue:
                        !Sub "${S3StackName}-s3Bucket"
            /var/www/html/styles.css:
              source:
                Fn::Sub:
                  - https://s3.amazonaws.com/${bucketName}/styles.css
                  - bucketName:
                      Fn::ImportValue:
                        !Sub "${S3StackName}-s3Bucket"
            /var/www/html/script.js:
              source:
                Fn::Sub:
                  - https://s3.amazonaws.com/${bucketName}/script.js
                  - bucketName:
                      Fn::ImportValue:
                        !Sub "${S3StackName}-s3Bucket"
          services:
            sysvinit:
              httpd:
                enabled: true
                ensureRunning: true
      AWS::CloudFormation::Authentication:
        S3AccessCreds:
          type: S3
          roleName: !Ref EC2InstanceRole
          buckets:
            -
              Fn::ImportValue:
                  !Sub "${S3StackName}-s3Bucket"
    Properties:
      IamInstanceProfile: !Ref EC2InstanceProfile
      InstanceType: t2.micro
      ImageId: ami-1853ac65
      SecurityGroupIds:
        - !Ref MySecurityGroup
      KeyName: !Ref KeyName
      UserData:
        'Fn::Base64':
          !Sub |
            #!/bin/bash -xe
            # Ensure AWS CFN Bootstrap is the latest
            yum install -y aws-cfn-bootstrap
            # Install the files and packages from the metadata
            /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource EC2Instance  --region ${AWS::Region}

  MySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Open Ports 22 and 80
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '22'
        ToPort: '22'
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 0.0.0.0/0

Outputs:
  Website:
    Description: The Public DNS for the EC2 Instance
    Value: !Sub 'http://${EC2Instance.PublicDnsName}'

You'll notice that there's quite a bit of repetition, particularly importing a value that has been exported from an already existing stack, e.g:

Fn::Sub:
  - https://s3.amazonaws.com/${bucketName}/index.html
  - bucketName:
      Fn::ImportValue:
        !Sub "${S3StackName}-s3Bucket"

This pattern is used a grand total of 4 times in the template that I posted above. I want to simplify this some I'm not repeating the same block of YAML over and over again.

My first thought was adding a value to the Metadata section of the template, but that didn't work, as the resources section cannot !Ref from the metadata section.

How can I reduce the amount of repeated YAML in this template?

2

There are 2 answers

4
Abhinav Kaushal Keshari On

You can use Parameters:

Example:

Parameters:
  FunctionRepeat:
    Fn::Sub:
      - https://s3.amazonaws.com/${bucketName}/index.html
      - bucketName:
          Fn::ImportValue:
            !Sub "${S3StackName}-s3Bucket"

Then you can reuse this block wherever you like.

Example:

files:
  /var/www/html/index.html:
    source:
      Ref: FunctionRepeat
  /var/www/html/styles.css:
    source:
      Ref: FunctionRepeat
  /var/www/html/script.js:
    source:
      Ref: FunctionRepeat

For more information you can go to:

0
bwest On

You should be able to achieve this with a CloudFormation Macro. This blog post gives a good overview of macros. You can define a macro that calls a simple lambda function and transforms the template, so you can do lots of interesting things with macros. Here are some examples on GitHub.

Another option to investigate is cfndsl, a domain specific language that makes some things like parameters and templates a bit easier.