LaunchTemplateをCloudFormationで便利に使う小ワザ

LaunchTemplate(起動テンプレート)、使ってますか?

同じようなEC2をぽこぽこ立ち上げたい時に使える便利なやつです。
LaunchTemplateを使用してAutoScalingグループを作成することもできます。

起動テンプレートを使用して Auto Scaling グループを作成する - Amazon EC2 Auto Scaling

バージョン管理ができることも特徴の一つです。

起動テンプレートのバージョンの管理 - Amazon Elastic Compute Cloud

しかし、LaunchTemplateはバージョン管理ができる故に、CloudFormationで取り扱う時に注意が必要です。

CloudFormationでLaunchTemplateを作る

例えば、こんなCFnテンプレートでLaunchTemplateを作ってみます。

---
AWSTemplateFormatVersion: '2010-09-09'

Parameters:
  Ec2ImageId:
    Type: AWS::EC2::Image::Id
  SecurityGroupId:
    Type: AWS::EC2::SecurityGroup::Id

Resources:
  SampleLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: sample-launchtemplate
      LaunchTemplateData:
        BlockDeviceMappings:
          - Ebs:
              VolumeSize: 8
              VolumeType: gp2
            DeviceName: /dev/xvda
        SecurityGroupIds:
          - !Ref SecurityGroupId
        EbsOptimized: false
        ImageId: !Ref Ec2ImageId
        InstanceType: t3.micro
        Monitoring:
          Enabled: true
        UserData:
          Fn::Base64: |
            #!/bin/bash
            yum -y update

            # setup httpd
            yum install -y httpd
            systemctl start httpd.service
            systemctl enable httpd.service

ここまでは何も問題無いです。

やっぱりもう少しボリュームサイズを増やしたいってことで、ボリュームサイズを変えてCFnスタックを更新してみます。どうなるでしょう?

そうすると、バージョンが2になります。
CFnで更新した場合でも、LaunchTemplate自体を更新するわけではありません。
バージョンを積み上げていく挙動をします。

CloudFormationでAutoScalingGroupを作る

次にLaunchTemplateを使用したAutoScalingGroupをCFnテンプレートで作ってみます。
LaunchTemplateはバージョンが存在するので、バージョン番号を指定する必要があります。

これがマネジメントコンソールであれば、「デフォルト」「最新」といった選択肢があります。

しかし、CFnの場合はドキュメントに記載されている通り、$Latest, $Defaultを指定することができません。マジカヨ。

Amazon EC2 Auto Scaling AutoScalingGroup LaunchTemplateSpecification - AWS CloudFormation

と、いうことはこんな風にLaunchTemplateのバージョン番号をべた書きして構築しろということでしょうか。

  SampleAutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      AutoScalingGroupName: sample-ag
      DesiredCapacity: '1'
      LaunchTemplate:
        LaunchTemplateId: !Ref SampleLaunchTemplate
        Version: 1

ちょっと便利な解決方法

バージョンが上がるたびに、CFnテンプレートのバージョン番号を修正するとかだるいことはやっていられません。
実はこれを解決する方法があります。

LaunchTemplateのドキュメントを見ると、Fn::GetAtt組み込み関数を使うと最新バージョンが取得できると記載されています。
個人的にはAutoScalingのドキュメントに、ここへのリンクを張って欲しいくらいの重要な情報です。

Fn::GetAtt - AWS CloudFormation

AWS::EC2::LaunchTemplate - AWS CloudFormation

なので、こういうCFnテンプレートを作成すれば、

---
AWSTemplateFormatVersion: '2010-09-09'

Parameters:
  Ec2ImageId:
    Type: AWS::EC2::Image::Id
  SecurityGroupId:
    Type: AWS::EC2::SecurityGroup::Id

Resources:
  SampleLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: sample-launchtemplate
      LaunchTemplateData:
        BlockDeviceMappings:
          - Ebs:
              VolumeSize: 12
              VolumeType: gp2
            DeviceName: /dev/xvda
        SecurityGroupIds:
          - !Ref SecurityGroupId
        EbsOptimized: false
        ImageId: !Ref Ec2ImageId
        InstanceType: t3.micro
        Monitoring:
          Enabled: true
        UserData:
          Fn::Base64: |
            #!/bin/bash
            yum -y update

            # setup httpd
            yum install -y httpd
            systemctl start httpd.service
            systemctl enable httpd.service

Outputs:
  LatestNumber:
    Value: !GetAtt SampleLaunchTemplate.LatestVersionNumber

!GetAttを使ってLaunchTemplateの最新バージョンが取得できます。

したがって、こういうCFnテンプレートを作れば、CFnで更新した最新のLaunchTemplateを参照するAutoScalingGroupを作ることができます。

---
AWSTemplateFormatVersion: '2010-09-09'

Parameters:
  Ec2ImageId:
    Type: AWS::EC2::Image::Id
  SecurityGroupId:
    Type: AWS::EC2::SecurityGroup::Id
  SubnetIdA:
    Type: AWS::EC2::Subnet::Id
  SubnetIdC:
    Type: AWS::EC2::Subnet::Id

Resources:
  SampleLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: sample-launchtemplate
      LaunchTemplateData:
        BlockDeviceMappings:
          - Ebs:
              VolumeSize: 12
              VolumeType: gp2
            DeviceName: /dev/xvda
        SecurityGroupIds:
          - !Ref SecurityGroupId
        EbsOptimized: false
        ImageId: !Ref Ec2ImageId
        InstanceType: t3.micro
        Monitoring:
          Enabled: true
        UserData:
          Fn::Base64: |
            #!/bin/bash
            yum -y update

            # setup httpd
            yum install -y httpd
            systemctl start httpd.service
            systemctl enable httpd.service
  SampleAutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      AutoScalingGroupName: sample-ag
      DesiredCapacity: '1'
      LaunchTemplate:
        LaunchTemplateId: !Ref SampleLaunchTemplate
        Version: !GetAtt SampleLaunchTemplate.LatestVersionNumber
      MaxSize: '1'
      MinSize: '0'
      Tags:
        - Key: Name
          Value: sample-ag
          PropagateAtLaunch: true
      VPCZoneIdentifier:
        - !Ref SubnetIdA
        - !Ref SubnetIdC