CloudFormerとCloudFormationで既存リソースもIaCしたい

CloudFormerとCloudFormationで既存リソースもIaCしたい

December 15, 2019
Cloud
AWS, CloudFormation, CloudFormer, Infrastructure as code

この記事はAWS Advent Calendar 2019 9 日目の記事です。

2019/12/15 追記

https://dev.classmethod.jp/cloud/aws/former2/

Q: 永遠にベータの CloudFormer はいつアップデートされるんですか?
A: よく聞いてくれたね笑 残念ながら CloudFormar がこれからアップデートされることはありません。サードパーティ製の Former2 という Web ベースのサービスがあるのですが、これが非常によくできているのでそれを使ってください。

だそうです:cry:この記事の価値はなくなりました… というわけで、既存リソースのインポートは紹介されてるFormer2だったり、最後の余談でも触れてる、Terraform, Pulumi あたりを使うのが今後は良いのかと思います。

マネジメントコンソールが仕様書

という状況ありませんか?

新規機能を開発でテスト/検証のために、検証で動かしてる環境のリソースを自分用に立ち上げたいとかはよくある話だと思います。

その際に構築用のスクリプトやコード、それがなくてもせめてドキュメントなどが整備されていれば良いのですが ドキュメント/スクリプトがまともに更新されてなかったり、そもそもなかったりしたら悲惨です。

AWS マネジメントコンソールで動いてるリソースを別ウィンドウで開いて、それを参考にぽちぽちリソースを新規作成しないといけません。

辛いので、少なくとも自分が環境を構築するときは、なるべく CloudFormation テンプレートを残すようにしています。 Infrastructure as Code をチームで統一とかできればいいんですが、なかなか難しいのかなと思います。

CloudFormation に慣れるのは時間かかる

AWS での IaC としては CloudFormation が一般的だと思うのですが1 CloudFormation は学習コストがそこそこ高いと個人的には思っていまして

例えば、CloudFormation の仕様(Parameter や、Import/Export など)を理解して 1 つのサービスの CloudFormation テンプレートが書けたからと言って、他サービスの CloudFormation テンプレートが書けるかと言ったら、そうではなくて結局サービス毎の仕様を把握しないと作れないと思っています。 (VPC+EC2 の CloudFormation テンプレートが書けるようになっても、Lambda+DynamoDB のテンプレートはまた学習しないと書けなかったり)

あとマネジメントコンソールがよしなにやってくれてるところを、意識して設定しないといけなかったりも大変ですよね

  • security group の自分自身の ingress を許可とか
  • ある設定を有効にしたら、設定しなくていいフィールドを隠したり、追加入力必要なフィールドを表示してくれたり

自分も書き慣れたサービス以外のテンプレートを書くときは、一度マネジメントコンソールで作成/動作確認した後に、ドキュメント見ながら CloudFormation テンプレートにおこすって感じにしています。


今自分が関わらせてもらっている案件では、インフラ/コーダーという感じで別れてはいるのですが、厳密ではないのでコーダーの方も AWS リソースを作ったりします。 そういう方にも毎回 CloudFormation テンプレート書いてくださいっていうのはなかなか酷なのかなとも思います。

そういうのもあり、 マネジメントコンソールで作ったリソースを CloudFormation テンプレート化できないかなとかはぼんやり考えていました。

CloudFormation の新機能が出た

既存リソースのインポートが 2019/11 にリリースされました

ただ、これは自分が期待していた機能とは少し違いました。

CloudFormation の機能おさらい

CloudFormation の用語説明も兼ねて、少し動作の流れを確認しておきましょう

例えば、すでに存在している VPC 上に Subnet, Instance, InternetGateway, VPC Endpoint を 1 つづつ作成するとします。

これを CloudFormation でやる場合の流れは まず作成したいリソースを記載したテンプレートを用意します(汎用的にする場合はリソースが属する VPC はパラメータで指定できるようにするでしょう) そして、このテンプレートでスタックを作成すると、該当のリソースが作成され、これらがスタックで管理されることになります。

image.png

今回のアップデート機能

今回追加された機能を図で書くと以下のようになります。

image.png

今回の新機能は、既存リソースをスタックとマッピングさせて、以後スタックで管理できるようにするというものです。 これはとても良いアップデートなのですが、依然として管理したいリソースをテンプレートに書き起こす作業はユーザーが実施しないといけません。

これでは、最初にあげた課題(テンプレートを書くのが大変)が解決されていません。

CloudFormer で既存リソースをテンプレートに

自分が期待していたのは、以下のような機能でした。 image.png

この既存リソースからスタック化したいリソースを選ぶとテンプレートが生成されるを実現する方法はないのでしょうか?

調べていたら以下のようなドキュメントを見つけました。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-using-cloudformer.html

CloudFormer は、アカウントに既に存在する AWS リソースから AWS CloudFormation テンプレートを作成するテンプレート作成用のベータツールです。アカウントで実行中のサポートされている AWS リソースを選択すると、CloudFormer は Amazon S3 バケットにテンプレートを作成します。

まさしく期待していたツールです! 実際にこの CloudFormer と CloudFormation の新機能を使って、上記の図のようなフローができるかを確かめてみようと思います。

CloudFormer を立ち上げる

まずは CloudFormer を用意しましょう。CloudFormer は Web アプリのようです。 これをデプロイするための CloudFormation テンプレートが用意されているので、こちらでスタックを作成します

特にテンプレートを用意する必要はなく、CloudFormation のスタック作成からサンプルテンプレートを使用で CloudFormer テンプレートを見つけれます。これを選んで次へ行きます image.png

パラメータで, Web アプリへアクセスする際の認証情報を入れておきます。 また VPCSelection で Web アプリのインスタンスを立ち上げる VPC を新しく立てるか、default VPC を使うか選択できます。ここでは新しく VPC を作成します。 image.png

あとは、そのままスタックを作成してください。

image.png

作成完了になったら、スタックの出力に web アプリの URL が表示されます。 (オレオレ証明書が使われているので、Advanced から、リスク承知でのアクセスで入りましょう) (自分の環境では Chrome だとアクセスできなかったので Safari でアクセスしました)

アクセスしたら UserName と Password を聞かれるので、CloudFormation のパラメーターで指定したのを入力して Log In します

既存リソースのマニフェストを出力する

無事アクセスできたら以下のような画面が表示されます。

image.png

Region は Tokyo でいいので、このまま Create Template を押して進めていきます。

image.png

このようにリソースのまとまりごとに、テンプレートに追加したいリソースにチェックをしていきます。 (id だけで、Name とか見れないのがちょっと辛いですね)

これで、上記の図のようにある VPC に属するSubnet, InternetGateway, EC2 Instance, VPC Endpointをそれぞれテンプレート化しようとしたのですが、VPC Endpoint は対応していませんでした:cry:ので、それ以外の 3 つをテンプレートにしてみます。

チェックをつけていって、最後までいくと以下のような確認画面になります。

image.png

出力先の s3 バケットと、ファイル名を調整してSave Templateを押すと S3 テンプレートの json ファイルを吐き出します。

見やすいように yaml に変換したのが以下になります(id とかは適当に変えてます) ちゃんとテンプレートになってますね。

AWSTemplateFormatVersion: '2010-09-09'
Description: Created by CloudFormer
Resources:
  subnet0aaaaaaaaaaaa:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 172.29.0.0/24
      AvailabilityZone: ap-northeast-1a
      VpcId: vpc-aaaaaaaaaaaa
      Tags:
      - Key: Name
        Value: exist-subnet
  igw0aaaaaaaaaaaaaa:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: exist-igw
  instancei0aaaaaaaaaaaa:
    Type: AWS::EC2::Instance
    Properties:
      DisableApiTermination: 'false'
      InstanceInitiatedShutdownBehavior: stop
      ImageId: ami-068a6cefc24c301d2
      InstanceType: t2.micro
      KeyName: aaaaaaaaaaaa
      Monitoring: 'false'
      Tags:
      - Key: Name
        Value: tmp-cloudformer-instance
      NetworkInterfaces:
      - DeleteOnTermination: 'true'
        Description: Primary network interface
        DeviceIndex: 0
        SubnetId:
          Ref: subnet0aaaaaaaaaaaa
        PrivateIpAddresses:
        - PrivateIpAddress: 172.29.0.73
          Primary: 'true'
        GroupSet:
        - Ref: sglaunchwizard2
  sglaunchwizard2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: launch-wizard-2 created 2019-12-09T02:10:44.089+09:00
      VpcId: vpc-0aaaaaaaaaaaa
  gw1:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: vpc-0aaaaaaaaaaaa
      InternetGatewayId:
        Ref: igw0aaaaaaaaaaaaaa
  ingress1:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId:
        Ref: sglaunchwizard2
      IpProtocol: tcp
      FromPort: '22'
      ToPort: '22'
      CidrIp: 0.0.0.0/0
  egress1:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      GroupId:
        Ref: sglaunchwizard2
      IpProtocol: "-1"
      CidrIp: 0.0.0.0/0

既存リソースのテンプレートからスタックを作成する

次に今回の CloudFormation の新機能を使って、出力されたテンプレートからスタックを作成してみます。

CloudFormation の新規スタック作成から、既存のリソースを使用(リソースをインポート)を選びます。 image.png

テンプレートの指定で、先ほど CloudFormer が出力したテンプレートのパスを指定します image.png

次に進もうとしたら、インポートに対応していないというエラーが出てしまいました。 image.png 現状インポートに対応しているリソースは以下だけのようです https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/resource-import-supported-resources.html

対応していないリソースを削除してみます。

  sglaunchwizard2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: launch-wizard-2 created 2019-12-09T02:10:44.089+09:00
      VpcId: vpc-0aaaaaaaaaaaa
-  gw1:
-    Type: AWS::EC2::VPCGatewayAttachment
-    Properties:
-      VpcId: vpc-0aaaaaaaaaaaa
-      InternetGatewayId:
-        Ref: igw0aaaaaaaaaaaaaa
-  ingress1:
-    Type: AWS::EC2::SecurityGroupIngress
-    Properties:
-      GroupId:
-        Ref: sglaunchwizard2
-      IpProtocol: tcp
-      FromPort: '22'
-      ToPort: '22'
-      CidrIp: 0.0.0.0/0
-  egress1:
-    Type: AWS::EC2::SecurityGroupEgress
-    Properties:
-      GroupId:
-        Ref: sglaunchwizard2
-      IpProtocol: "-1"
-      CidrIp: 0.0.0.0/0

これでエラーが出なくなりました。

次にリソースの識別という作業が入ります。今回追加する Resource がそれぞれ対応する id を入力しないといけません。

subnet0aaaaaaaaaaaa:
  Type: AWS::EC2::Subnet
  Properties:
    CidrBlock: 172.29.0.0/24
    AvailabilityZone: ap-northeast-1a
    VpcId: vpc-aaaaaaaaaaaa
    Tags:
      - Key: Name
        Value: exist-subnet

これと結びつく subnet の subnet-id を入力しろって感じです。 (ここが自動でうまいことやってくれるのかなと期待してたのですが。。)

これで行けるかと思ったら、またエラーが出ました。 The following resources to import [...] must have DeletionPolicy attribute specified in the template. 各リソースに DeletionPolicy を指定しないといけないようです。これも追加します。

AWSTemplateFormatVersion: '2010-09-09'
Description: Created by CloudFormer
Resources:
  subnet0aaaaaaaaaaaa:
    Type: AWS::EC2::Subnet
+   DeletionPolicy: Delete
    Properties:
      CidrBlock: 172.29.0.0/24
      AvailabilityZone: ap-northeast-1a
      VpcId: vpc-aaaaaaaaaaaa
      Tags:
      - Key: Name
        Value: exist-subnet
  igw0aaaaaaaaaaaaaa:
    Type: AWS::EC2::InternetGateway
+   DeletionPolicy: Delete
    Properties:
      Tags:
      - Key: Name
        Value: exist-igw
  instancei0aaaaaaaaaaaa:
    Type: AWS::EC2::Instance
+   DeletionPolicy: Delete
    Properties:
      DisableApiTermination: 'false'
      InstanceInitiatedShutdownBehavior: stop
      ImageId: ami-068a6cefc24c301d2
      InstanceType: t2.micro
      KeyName: aaaaaaaaaaaa
      Monitoring: 'false'
      Tags:
      - Key: Name
        Value: tmp-cloudformer-instance
      NetworkInterfaces:
      - DeleteOnTermination: 'true'
        Description: Primary network interface
        DeviceIndex: 0
        SubnetId:
          Ref: subnet0aaaaaaaaaaaa
        PrivateIpAddresses:
        - PrivateIpAddress: 172.29.0.73
          Primary: 'true'
        GroupSet:
        - Ref: sglaunchwizard2
  sglaunchwizard2:
    Type: AWS::EC2::SecurityGroup
+   DeletionPolicy: Delete
    Properties:
      GroupDescription: launch-wizard-2 created 2019-12-09T02:10:44.089+09:00
      VpcId: vpc-0aaaaaaaaaaaa

これで再度やるとスタックが作成できて、既存リソースとの紐付けもできました

スタックを削除してみる

スタックを削除すると、ちゃんと既存リソースも削除されました。 (ただ、今回はAWS::EC2::VPCGatewayAttachmentを対応していないリソースということでテンプレートから外してしまったため、InternetGateway が VPC にアタッチされたままで依存関係が残ってしまい、InternetGateway が残ってしまいました)

image.png

まとめ

image.png

CloudFormer を使って既存リソースのテンプレート化、そしてそのテンプレートをインポートすることで既存リソースのスタック化が実施できた。 ただ、課題はたくさんありそうです。

課題

CloudFormer/CloudFormation Import はまだすべてのリソースに対応していない

今回の例でも VPC Endpoint ありませんでしたし、対応しているリソースは現状少ないですね。(これは CloudFormation Import 機能も同様)

CloudFormer では CloudFormation らしいテンプレート作成が難しそう

上で出力されるテンプレート例を上げましたが、テンプレート内の Resource しか作成されませんでした。 そのため Parameter や Export など使った CloudFormation らしいテンプレートにしようとすると、もう少し手を入れないといけないかなという感じです。

CloudFormation のインポートは手動で対応するリソースを選ばないといけない

CloudFormation のインポート機能については、リソースの識別を手動でやらないといけませんでした。 ここでは CloudFormer が出力するようなテンプレート(Resouce しかない)ではなく、CloudFormation らしいテンプレート(パラメーターあるような..classmethod さんの記事がわかりやすい)が求められてるように感じました。

この辺りもパラメーターによって一意に既存リソースが決定できたら自動で入力されるようにして欲しいですね(あるインスタンスリソースにパラメータで subnet を渡すとして、subnet-bbb に属するインスタンスが 1 台しかなかったら、自動でそのリソースと識別してくれるような)


CloudFormer、CloudFormation どっちも今後まだまだアップデートされると思うので、今後も期待です (もうアップデートはされないそうです。記事の先頭に記載の通り)

余談

Pulumi とか、Terraform だと Import 機能を公式で提供しているようですね

まだ完全にサポートしているというわけではなさそうですが、こういった他のツールも試してみたいところです。


  1. IaC であれば、他にも Terraform, Pulumi などありますが、自分が CloudFormation しか触ったことないのと、公式がサービスとして提供というのもあるので AWS での IaC としては CloudFormation が強いのかなというのもあり、この記事では CloudFormation=IaC としています:bow: ↩︎