AWS再入門2018 上級者でもたまに忘れる!? AWS CloudFormation 小ネタ編

スタートプラン

こんにちは。池田です。札幌では八重桜がちらほら咲き始めてきました。道産子にもようやく屋外焼肉シーズンがやってまいりました。
※ ハマりポイントというよりは「これ忘れることあるよねー」という意図でしたので、タイトルと本文の一部を追記・変更しました。

はじめに

早速ですが問題です。マネジメントコンソールにてテスト用の新規VPNとサブネット2つを作成し、下記の(某所からコピーしてちょっとカスタマイズした)AWS CloudFormationテンプレート(225行)を実行したところ、図のようなエラーにより失敗してしまいました。その原因と、最も簡単に解決するためのテンプレート修正方法を見つけられるでしょうか。制限時間は5分です。ではスクロールし過ぎのネタバレに気をつけてご覧ください。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
AWSTemplateFormatVersion: 2010-09-09
Description: >-
  Test Template.
Parameters:
  VpcId:
    Type: 'AWS::EC2::VPC::Id'
    Description: VpcId of your existing Virtual Private Cloud (VPC)
  Subnets:
    Type: 'List<AWS::EC2::Subnet::Id>'
    Description: The list of SubnetIds in your Virtual Private Cloud (VPC)
  InstanceType:
    Description: WebServer EC2 instance type
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.nano
      - t2.micro
      - t2.small
    ConstraintDescription: must be a valid EC2 instance type.
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instances
    Type: 'AWS::EC2::KeyPair::KeyName'
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
  SSHLocation:
    Description: The IP address range that can be used to SSH to the EC2 instances
    Type: String
    MinLength: '9'
    MaxLength: '18'
    Default: 0.0.0.0/0
    AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
Mappings:
  AWSInstanceType2Arch:
    t2.nano:
      Arch: HVM64
    t2.micro:
      Arch: HVM64
    t2.small:
      Arch: HVM64
 
  AWSInstanceType2NATArch:
    t2.nano:
      Arch: NATHVM64
    t2.micro:
      Arch: NATHVM64
    t2.small:
      Arch: NATHVM64
 
  AWSRegionArch2AMI:
    ap-northeast-1:
      PV64: ami-3e42b65f
      HVM64: ami-ceafcba8
      HVMG2: ami-edfd658b
 
Resources:
  WebServerGroup:
    Type: 'AWS::AutoScaling::AutoScalingGroup'
    Properties:
      VPCZoneIdentifier: !Ref Subnets
      LaunchConfigurationName: !Ref LaunchConfig
      MinSize: '2'
      MaxSize: '2'
      TargetGroupARNs:
        - !Ref ALBTargetGroup
    CreationPolicy:
      ResourceSignal:
        Timeout: PT15M
    UpdatePolicy:
      AutoScalingRollingUpdate:
        MinInstancesInService: '1'
        MaxBatchSize: '1'
        PauseTime: PT15M
        WaitOnResourceSignals: 'true'
  LaunchConfig:
    Type: 'AWS::AutoScaling::LaunchConfiguration'
    Metadata:
      Comment: Install a simple application
      'AWS::CloudFormation::Init':
        config:
          packages:
            yum:
              httpd: []
          files:
            /var/www/html/index.html:
              content: !Join
                - |+
 
                - - >-
                    <h3>You have successfully launched the AWS
                    CloudFormation test.</h3>
              mode: '000644'
              owner: root
              group: root
            /etc/cfn/cfn-hup.conf:
              content: !Join
                - ''
                - - |
                    [main]
                  - stack=
                  - !Ref 'AWS::StackId'
                  - |+
 
                  - region=
                  - !Ref 'AWS::Region'
                  - |+
 
              mode: '000400'
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Join
                - ''
                - - |
                    [cfn-auto-reloader-hook]
                  - |
                    triggers=post.update
                  - >
                    path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init
                  - 'action=/opt/aws/bin/cfn-init -v '
                  - '         --stack '
                  - !Ref 'AWS::StackName'
                  - '         --resource LaunchConfig '
                  - '         --region '
                  - !Ref 'AWS::Region'
                  - |+
 
                  - |
                    runas=root
              mode: '000400'
              owner: root
              group: root
          services:
            sysvinit:
              httpd:
                enabled: 'true'
                ensureRunning: 'true'
              cfn-hup:
                enabled: 'true'
                ensureRunning: 'true'
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf
    Properties:
      ImageId: !FindInMap
        - AWSRegionArch2AMI
        - !Ref 'AWS::Region'
        - !FindInMap
          - AWSInstanceType2Arch
          - !Ref InstanceType
          - Arch
      SecurityGroups:
        - !Ref InstanceSecurityGroup
      InstanceType: !Ref InstanceType
      KeyName: !Ref KeyName
      UserData: !Base64
        'Fn::Join':
          - ''
          - - |
              #!/bin/bash -xe
            - |
              yum update -y aws-cfn-bootstrap
            - '/opt/aws/bin/cfn-init -v '
            - '         --stack '
            - !Ref 'AWS::StackName'
            - '         --resource LaunchConfig '
            - '         --region '
            - !Ref 'AWS::Region'
            - |+
 
            - '/opt/aws/bin/cfn-signal -e $? '
            - '         --stack '
            - !Ref 'AWS::StackName'
            - '         --resource WebServerGroup '
            - '         --region '
            - !Ref 'AWS::Region'
            - |+
 
  ApplicationLoadBalancer:
    Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
    Properties:
      Subnets: !Ref Subnets
      SecurityGroups:
        - !Ref InstanceSecurityGroup
  ALBListener:
    Type: 'AWS::ElasticLoadBalancingV2::Listener'
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref ALBTargetGroup
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: '80'
      Protocol: HTTP
  ALBTargetGroup:
    Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
    Properties:
      HealthCheckIntervalSeconds: 30
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 3
      Port: 80
      Protocol: HTTP
      UnhealthyThresholdCount: 5
      VpcId: !Ref VpcId
  InstanceSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: Enable SSH access and HTTP access on the inbound port
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '80'
          ToPort: '80'
          CidrIp: !Ref SSHLocation
        - IpProtocol: tcp
          FromPort: '22'
          ToPort: '22'
          CidrIp: !Ref SSHLocation
      VpcId: !Ref VpcId
Outputs:
  URL:
    Description: URL of the website
    Value: !Join
      - ''
      - - 'http://'
        - !GetAtt
          - ApplicationLoadBalancer
          - DNSName

正解

原因はわかりましたでしょうか。実は初心者に限らずとも、つい見落としがちなミスが原因でした。

143
144
145
146
147
148
149
150
151
Properties:
  AssociatePublicIpAddress: true
  ImageId: !FindInMap
    - AWSRegionArch2AMI
    - !Ref 'AWS::Region'
    - !FindInMap
      - AWSInstanceType2Arch
      - !Ref InstanceType
      - Arch

AssociatePublicIpAddress
VPC の Amazon EC2 インスタンスで、Auto Scaling グループのインスタンスがパブリック IP アドレスを受け取るかどうかを示します。true の場合には、Auto Scaling の各インスタンスが一意のパブリック IP を受け取ります。
AWS::AutoScaling::LaunchConfiguration

そうです。CloudFormationにより作成されたEC2インスタンスにパブリックIPアドレスが割り当てられていないことで、yumコマンドが完了せず、インスタンス作成のsuccessシグナルが送られないままタイムアウトが発生し、ロールバックされていたのでした。

まとめ

慣れたつもりでヒョイヒョイっとコピペして使うと、こういううっかりミスに遭遇して(マネジメントコンソールに表示される簡易エラー表示からは連想できずに)ハマってしまうことがあるよね。新規作成したVPC側の設定忘れはよくやらかすわー。などという話題がオフィスで出たのでご紹介でした。
※ 他にもサブネットを作成するときにPublic IPの自動付与をしておく、NAT経由で外部との通信が可能となるようにするなど、複数の解消方法はありますが、もっとも簡単な(テンプレートに1行追記するだけで済む)方法を今回の正解とさせていただきました。

スタートプラン