📖

AWS入門 - CloudFormation 使ってみる

2021/11/14に公開

AWSを使う必要が出てきたので、色々と試してみる。
ここでは、CloudFormation を試す。

AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/Welcome.html

AWSのリソース構成をモデル化し、それを元にリソースを構築してくれるやつ。
いわゆる、IaCなやつ。

Templates

テンプレートは、どのようなリソース構成にするのかを記述したもの。
YAMLかJSONで記述できる。

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "A sample template",
  "Resources" : {
    "MyEC2Instance" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "ImageId" : "ami-0ff8a91507f77f867",
        "InstanceType" : "t2.micro",
        "KeyName" : "testkey",
        "BlockDeviceMappings" : [
          {
            "DeviceName" : "/dev/sdm",
            "Ebs" : {
              "VolumeType" : "io1",
              "Iops" : 200,
              "DeleteOnTermination" : false,
              "VolumeSize" : 20
            }
          }
        ]
      }
    }
  }
}

Stacks

スタックは、AWSリソースを管理するための単位。
スタックには複数のリソースが紐付いていて、スタックを作成・更新・削除することで、リソースも作成・更新・削除できる。
スタックはテンプレートを元に作られる。

Change sets

変更セットは、スタックの変更内容。
変更セットをスタックに適用するとどうなるかを事前確認できる。

まとめるとこんな感じ。

スタックの更新

スタックを更新する方法(=スタックに紐付いているリソースの更新)は、2種類あるらしい。

  • 直接更新
  • 変更セットによる更新

変更セットによる更新の場合は、更新内容を実際に反映する前に確認できる。

CloudFormation Designer

図を描くようにテンプレートを作成できるツール。
D&Dでリソースを配置し、特定のプロパティは矢印をつなげて依存関係を指定できたりする。

テキストエディタでテンプレートを書いていると具体的にどのような構成になっているのか把握しづらいが、Designer上であればそういった課題が解消される。

Former2

https://former2.com/

既に構築済みのリソースを元にテンプレートとして出力できるツール。

プロトタイプ的にコンソール画面をポチポチしてリソースを構築し、Former2でテンプレートとして出力すると、0からテンプレートを書くよりも楽な気がする。

ただし、AWS公式のツールではないので、おまけ程度に使う感じかもしれない。

CloudFormationでEC2を立ち上げてみる

実際にCloudFormationを使って環境を構築してみる。

テンプレート作成

CloudFormation Designer を使ってテンプレートを作成してみる。

VPC・サブネット・EC2インスタンスと順番に配置し、依存関係の矢印をつなげたり、プロパティを直接指定したりしていく。

いちおう上の方にバリデーションするボタンはあるが、YAMLやJSONとして正しいか を確認できるぐらいで、各プロパティや構成が正しいかは全く確認できない。
別途、テンプレートをバリデーションするツールを探したほうが良さそうな感じがする。

AWSTemplateFormatVersion: 2010-09-09
Metadata:
  'AWS::CloudFormation::Designer':
    9fab3b90-1d19-4661-8864-e4df1a422b20:
      size:
        width: 390
        height: 370
      position:
        x: 230
        'y': 130
      z: 0
      embeds:
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - eb941550-fda8-473a-8e1a-85796b4a8fd8
    5932e6f4-ca41-4c64-918e-02a45c97fcf0:
      size:
        width: 100
        height: 120
      position:
        x: 260
        'y': 180
      z: 1
      parent: 9fab3b90-1d19-4661-8864-e4df1a422b20
      embeds:
        - 00a6e911-9e65-4f17-9c68-5d0283368cf7
      iscontainedinside:
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
    00a6e911-9e65-4f17-9c68-5d0283368cf7:
      size:
        width: 60
        height: 60
      position:
        x: 280
        'y': 220
      z: 2
      parent: 5932e6f4-ca41-4c64-918e-02a45c97fcf0
      embeds: []
      isassociatedwith:
        - eb941550-fda8-473a-8e1a-85796b4a8fd8
      iscontainedinside:
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
        - 5932e6f4-ca41-4c64-918e-02a45c97fcf0
    44c9d192-fbd9-4c04-ac0c-6848dc43fd80:
      size:
        width: 60
        height: 60
      position:
        x: 660
        'y': 130
      z: 0
      embeds: []
    dbaa5783-b2b2-4a5d-9357-7fd2d55a8d74:
      source:
        id: 9fab3b90-1d19-4661-8864-e4df1a422b20
      target:
        id: 44c9d192-fbd9-4c04-ac0c-6848dc43fd80
      z: 0
    c206bd08-4348-4325-9f65-2d4320851fa1:
      size:
        width: 100
        height: 120
      position:
        x: 410
        'y': 180
      z: 1
      parent: 9fab3b90-1d19-4661-8864-e4df1a422b20
      embeds:
        - 57901186-5650-4cb5-83c6-12227c4ad128
    18d0301a-cfa9-4816-be9c-64d81d369c88:
      source:
        id: c206bd08-4348-4325-9f65-2d4320851fa1
      target:
        id: 5932e6f4-ca41-4c64-918e-02a45c97fcf0
      z: 1
    57901186-5650-4cb5-83c6-12227c4ad128:
      size:
        width: 60
        height: 60
      position:
        x: 430
        'y': 220
      z: 2
      parent: c206bd08-4348-4325-9f65-2d4320851fa1
      embeds: []
      isassociatedwith:
        - 44c9d192-fbd9-4c04-ac0c-6848dc43fd80
      iscontainedinside:
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
        - c206bd08-4348-4325-9f65-2d4320851fa1
    eb941550-fda8-473a-8e1a-85796b4a8fd8:
      size:
        width: 60
        height: 60
      position:
        x: 280
        'y': 340
      z: 1
      parent: 9fab3b90-1d19-4661-8864-e4df1a422b20
      embeds: []
      iscontainedinside:
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
        - 9fab3b90-1d19-4661-8864-e4df1a422b20
    e79b0b90-5f6f-42a7-bf20-fb2569a19b3c:
      source:
        id: c206bd08-4348-4325-9f65-2d4320851fa1
        selector: 'g:nth-child(1) g:nth-child(5) g:nth-child(3) circle:nth-child(1)     '
        port: 'AWS::DependencyLink-*'
      target:
        id: 9fab3b90-1d19-4661-8864-e4df1a422b20
      z: 4
Resources:
  EC2VPCG3QQG7:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC
    Metadata:
      'AWS::CloudFormation::Designer':
        id: dbaa5783-b2b2-4a5d-9357-7fd2d55a8d74
  EC2SRTA38T7L:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      RouteTableId: !Ref RouteTable
      SubnetId: !Ref Subnet
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 18d0301a-cfa9-4816-be9c-64d81d369c88
  SecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: Allow SSH access
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - CidrIp: 0.0.0.0/0
          FromPort: 22
          IpProtocol: tcp
          ToPort: 22
    Metadata:
      'AWS::CloudFormation::Designer':
        id: eb941550-fda8-473a-8e1a-85796b4a8fd8
  EC2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      ImageId: "ami-0e60b6d05dc38ff11"
      InstanceType: "t2.micro"
      KeyName: "kp-1114"
      NetworkInterfaces:
        - 
          DeviceIndex: "0"
          SubnetId: !Ref Subnet
          AssociatePublicIpAddress: "true"
          GroupSet:
            - !Ref SecurityGroup
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 00a6e911-9e65-4f17-9c68-5d0283368cf7
  Route:
    Type: 'AWS::EC2::Route'
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      RouteTableId: !Ref RouteTable
      GatewayId: !Ref InternetGateway
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 57901186-5650-4cb5-83c6-12227c4ad128
  Subnet:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.0.0/24
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 5932e6f4-ca41-4c64-918e-02a45c97fcf0
  RouteTable:
    Type: 'AWS::EC2::RouteTable'
    Properties:
        VpcId: !Ref VPC
    Metadata:
      'AWS::CloudFormation::Designer':
        id: c206bd08-4348-4325-9f65-2d4320851fa1
  InternetGateway:
    Type: 'AWS::EC2::InternetGateway'
    Properties: {}
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 44c9d192-fbd9-4c04-ac0c-6848dc43fd80
  VPC:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
        - Key: Name
          Value: vpc-d1114
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 9fab3b90-1d19-4661-8864-e4df1a422b20

スタック作成

失敗するとこんな感じになり、途中まで作成されたリソースは全てロールバックすることもできる。
間違ったプロパティ名を指定してもスタック作成には認識できず、スタック作成あとのリソース作成中にエラーになって初めて気づける。

エラーが出ているので、修正して再度スタックを作成してみる。
スタック名の重複はNGな仕様っぽいので、エラーが出たスタックは削除しておく。

成功するとこんな感じ。
スタックから作成された各リソースがわかる。

EC2インスタンスへと問題なくSSHできることも確認できた。

$ ssh -i kp-1114.pem ec2-user@x.x.x.x

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
-bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory

スタック更新(直接更新)

スタックの更新も試してみる。
変更内容は何でも良いので、とりあえずタグを追加してみる。

...
  EC2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      ...
      Tags:
        - Key: Name
          Value: ec2-name-1
...

テンプレート差し替えとして、スタックの更新を進めてみる。

細かい部分は見れないが、大まかな更新内容が事前に確認できる。

更新が完了した。

タグも反映されている。

スタック更新(変更セットによる更新)

変更セットによる更新も試してみる。
最初に更新したテンプレートを指定できる点は先程と同じ感じ。

変更セットが作成され、先程と同じく大まかに更新内容が確認できる。

"JSON changes"を見てみると、どのリソースのどのプロパティが更新されるのか確認できた。
ただし、視認性は良くないので、これを人間に分かりやすい形でUIに出してほしい。

[
  {
    "resourceChange": {
      "logicalResourceId": "EC2Instance",
      "action": "Modify",
      "physicalResourceId": "i-086b9877b80852834",
      "resourceType": "AWS::EC2::Instance",
      "replacement": "False",
      "moduleInfo": null,
      "details": [
        {
          "target": {
            "name": null,
            "requiresRecreation": "Never",
            "attribute": "Tags"
          },
          "causingEntity": null,
          "evaluation": "Static",
          "changeSource": "DirectModification"
        }
      ],
      "changeSetId": null,
      "scope": [
        "Tags"
      ]
    },
    "hookInvocationCount": null,
    "type": "Resource"
  }
]

この変更セットを実行すると、実際にスタック(リソース)が更新される。

スタック削除

最後に、不要になったスタック(リソース)を削除してみる。
スタックを削除すると、スタックに紐付いている各リソースが削除される。
削除されたら困るやつにはロックをかけられるっぽいが、それは別途調べてみる。

各リソースが削除され、コンソール画面上から来ていることが確認できる。
スタック自体は物理削除されず、削除済みのスタックとして後から内容を確認することもできる。

まとめ

コードとしてリソースを管理できるようになるのは良い。
既に構築済みの場合はちょっと手間がかかりそうだが、テンプレートと既存リソースを紐付ける仕組みもあるっぽいので何とかなりそう。

ただ、テンプレートを書くハードルが結構高そうではある。
CloudFormation Disgner で簡単に作れるかと思いきや、普通にリソース作成に失敗する。
そのままだと、必須プロパティが未指定だったり、指定してるがエラーになったりする場合がある。
なので、細かいプロパティは直接テンプレートをいじる必要がある。

スタックを作成するまでエラーに気づけないので、ある程度細かいバリデーションを行ってくれるツールがあると非常に助かる感じがする。

Discussion