CloudFormerとCloudFormationで既存リソースもIaCしたい
December 15, 2019
この記事は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 はパラメータで指定できるようにするでしょう) そして、このテンプレートでスタックを作成すると、該当のリソースが作成され、これらがスタックで管理されることになります。
今回のアップデート機能
今回追加された機能を図で書くと以下のようになります。
今回の新機能は、既存リソースをスタックとマッピングさせて、以後スタックで管理できるようにするというものです。 これはとても良いアップデートなのですが、依然として管理したいリソースをテンプレートに書き起こす作業はユーザーが実施しないといけません。
これでは、最初にあげた課題(テンプレートを書くのが大変)が解決されていません。
CloudFormer で既存リソースをテンプレートに
自分が期待していたのは、以下のような機能でした。
この既存リソースからスタック化したいリソースを選ぶとテンプレートが生成されるを実現する方法はないのでしょうか?
調べていたら以下のようなドキュメントを見つけました。
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 テンプレートを見つけれます。これを選んで次へ
行きます
パラメータで, Web アプリへアクセスする際の認証情報を入れておきます。 また VPCSelection で Web アプリのインスタンスを立ち上げる VPC を新しく立てるか、default VPC を使うか選択できます。ここでは新しく VPC を作成します。
あとは、そのままスタックを作成してください。
作成完了になったら、スタックの出力に web アプリの URL が表示されます。 (オレオレ証明書が使われているので、Advanced から、リスク承知でのアクセスで入りましょう) (自分の環境では Chrome だとアクセスできなかったので Safari でアクセスしました)
アクセスしたら UserName と Password を聞かれるので、CloudFormation のパラメーターで指定したのを入力して Log In します
既存リソースのマニフェストを出力する
無事アクセスできたら以下のような画面が表示されます。
Region は Tokyo でいいので、このまま Create Template を押して進めていきます。
このようにリソースのまとまりごとに、テンプレートに追加したいリソースにチェックをしていきます。 (id だけで、Name とか見れないのがちょっと辛いですね)
これで、上記の図のようにある VPC に属するSubnet
, InternetGateway
, EC2 Instance
, VPC Endpoint
をそれぞれテンプレート化しようとしたのですが、VPC Endpoint は対応していませんでした:cry:ので、それ以外の 3 つをテンプレートにしてみます。
チェックをつけていって、最後までいくと以下のような確認画面になります。
出力先の 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 の新規スタック作成から、既存のリソースを使用(リソースをインポート)
を選びます。
テンプレートの指定で、先ほど CloudFormer が出力したテンプレートのパスを指定します
次に進もうとしたら、インポートに対応していないというエラーが出てしまいました。 現状インポートに対応しているリソースは以下だけのようです 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 が残ってしまいました)
まとめ
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 機能を公式で提供しているようですね
- https://www.pulumi.com/blog/adopting-existing-cloud-resources-into-pulumi/
- https://www.terraform.io/docs/import/index.html
まだ完全にサポートしているというわけではなさそうですが、こういった他のツールも試してみたいところです。
IaC であれば、他にも Terraform, Pulumi などありますが、自分が CloudFormation しか触ったことないのと、公式がサービスとして提供というのもあるので AWS での IaC としては CloudFormation が強いのかなというのもあり、この記事では CloudFormation=IaC としています:bow: ↩︎