AWS CloudFormation IaCジェネレータの使用感と注意点

# IaCジェネレータとは
AWSの既存リソースをもとにCloudFormationのテンプレートを作成してくれるサービスです。
これまで手動構築したAWSリソースをもとにIaCテンプレートを作成するには、サードパーティ製のツールを使う必要がありましたが、この度AWSの公式機能として公開されました。
作った環境をそのまま別アカウントに移行したい時や管理外のリソースをスタックにインポートして管理したい時などには大変便利な機能ですね。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/generate-IaC.html

そんな注目のIaCジェネレータですが、実際に使ってみて個人的に感じたことがあるので残しておきたいと思います。

# IaCジェネレータでテンプレートを生成する

### スキャン対象
今回は手動で作成した以下の構成に対して、IaCジェネレータを使ってテンプレート化を試してみました。

<img width=10%><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/e7f6eec9-2e8e-8648-682c-ffca2ed81b5f.png" width=80%>

構成自体は特に本筋と関係がなく、これくらいの規模で試した感想であることが伝わればと思います。


### 手順
WebコンソールからIaCジェネレータを利用します。


1. WebコンソールからCloudFormationを開く。メニューから「IaCジェネレータ」をクリック。
    ![2024-02-18-18-08-18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/6a4b7980-9bb0-1f0f-7c4b-b25354f188a5.png)


2. 「新しいスキャンを開始」をクリック。クリックすると以下のようにスキャンの進捗バーが表示されます。
    ![2024-02-18-18-09-21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/c580b8e4-83a8-885f-077a-07428eb352d8.png)

3.  今回のリソースでは3分ほどでスキャンが完了。リソース数は163だったようです。
    以下画面で「テンプレートを作成」をクリック。
    ![2024-02-18-18-12-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/7992e6d1-86c6-8ec7-61c3-1fac995cfe15.png)


4. テンプレート名を入力します
    - 「削除ポリシー」「置換ポリシーを更新」は保持（デフォルト）にすると、作成テンプレートの全てのリソースに`DeletionPolicy: "Retain"`,`UpdateReplacePolicy: "Retain"`が付きます。スタックの削除・更新の際にリソースも削除したい場合は削除ポリシー・置換ポリシーを削除にしておきましょう。
    - 入力したら「次へ」をクリックします。
    ![2024-02-18-18-12-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/bce03e37-04fa-5b11-5239-6330cd11193d.png)

5. スキャンされたリソース一覧が表示されます。
    テンプレートに含みたいリソースを選択していきます。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/df8a58c7-e1cf-394a-cc1b-c5e513c2a70d.png)



6. 事前にリソースにタグが付いていれば、タグキーやタグ値で絞り込みが可能です。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/2078e881-fbd8-6caa-7d50-946cc7b3af5c.png)

7. 実際にタグ値(=prd)で絞り込みするとこのようになります。
    選択が完了したら「次へ」をクリックします。
    今回は前述の構成リソースにはタグ値をつけたため、こちらでおおよそ対象リソースの選択はできていると思います。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/4fa74bd8-a375-e086-8dbd-5c71d1206540.png)

    


8. 関連リソース一覧が表示されます。
    - 選択したリソースに関連するリソースをIaC generator側で備忘的に提案してくれます。
    - ここではとりあえず提案されたものを全て選択してしまっています。NATGWにアタッチされたEIPやRouteTable内のRouteリソースなどは筆者がタグ付けを忘れていたもののため、提案が役立ちました。一方でNetworkInterfaceなどは特に不要だったのですが、デフォルトでチェック状態だったためここでは追加してしまいました。
    - 選択が終わったら「次へ」をクリックします。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/641c6a8e-ec08-2f4a-64e1-9d896a2a472a.png)

9. 最後に確認ページが表示されます。見切れていますが右下の「テンプレートを作成」をクリック。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/7b104c3e-3198-3422-7d1b-c4ce33ea7d79.png)

10. 少し待つとテンプレートが表示されます。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/98ef2cc2-e9eb-57c4-ea82-fd38e3c26d42.png)


テンプレートの作成作業は以上で完了です。非常に簡単です。

選択したリソースをそのままCloudFormaitonのスタックとして管理したい場合は「スタックにインポート」を行うとよいでしょう。

ここではアカウント移行を考えてYAMLテンプレートをダウンロードしてファイルの中身を確認していきました。
以下がテンプレートの冒頭です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/04abf317-93bc-6975-0075-17f614bf766d.png)
MetadataとしてTemplateIdが記載されている以外は何の変哲もありません。
Resources下のリソース論理IDには固有のIDが自動で割り振られており、画像のVPCは`EC2VPC00vpc~~~`とリソースタイプっぽい値とユニークIDが論理IDとなっています。


なお、テンプレートは後述の点があるため、ほとんどの場合で多少の手修正が必要になると思われます。



# 注意点
## 出力されるテンプレートが不安定
実際に出力されたテンプレートを見て、個人的に注意が必要だと思った点を挙げておこうと思います。

### 1. リソース間の参照に物理IDが使われることがある
テンプレート内のリソース間参照にはテンプレート内の論理IDを利用してほしい所ですが、物理IDで参照していることがあり、注意が必要です。

例えば、以下はELBリソースの定義箇所ですが、`SecurityGroups`で指定されているセキュリティグループIDが物理IDのハードコードとなっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/238d2a7a-047a-1bf0-ae62-e4d20b9faf43.png)

他にもサブネットの定義箇所でも`VpcId`で指定されているVPCIDが物理IDのハードコードです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/b86a55e3-8771-1c37-016e-1b94abd87b9a.png)


既存リソースを既存アカウントのスタックにインポートし管理する場合は気にならないかもしれませんが、
別アカウントへの移行などで新規環境構築にこのテンプレートを使う場合には物理IDではエラーとなるため、手修正する必要があります。

一方で、論理IDを利用出来ている箇所も当然あります。以下はVPC Endpointの定義箇所ですが、`VpcId`にはRef関数を使って自動生成されたVPCの論理IDを参照できています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/49a5875b-9313-d011-9348-7da3dd715f03.png)

物理IDと論理IDの参照がどう使い分けられているかはわかりませんが、少なくともばらつきがあることは認識しておくとよいでしょう。



### 2. DeletionPolicy、UpdateReplacePolicyのデフォルト設定がRetainである
前述の作成手順でも記載しましたが、WebコンソールのIaCジェネレータでテンプレートを作成する際は`更新置換ポリシー：保持`、`削除ポリシー：保持`がデフォルト設定になっています。
したがって、テンプレートの内部では全てのリソースに対して、以下の画像のように`UpdateReplacePolicy: "Retain"`と`DeletionPolicy: "Retain"`が挿入されることになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/dfbb3bb5-8d8c-92b0-9864-e27b98068880.png)
この設定ではスタックを削除した場合にもリソースがそのまま保持され、スタックを更新した際にもリソースは置換されず新たに作られます。

一方で、CloudFormationを一から手動で書く際に、`UpdateReplacePolicy`と`DeletionPolicy`を指定しなかった場合は、どちらも`Delete`がデフォルトです。


CloudFormationのテンプレート作成に慣れている人がデフォルトで`Delete`設定であることを期待して、WebコンソールのIaCジェネレータでデフォルト設定でテンプレートを作成すると`Retain`となるという、デフォルト設定に対するギャップがあるので注意が必要です。


### 3. Cfnのリソース記述ルールに従わない場合がある
そのままテンプレートを使うとエラーとなるリソース設定がありました。

以下の画像はRouteの定義です。IaCジェネレータで出力されたテンプレートでは、赤線の`GatewayId`と`VpcEndpointId`が指定されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/382c0fad-ce98-989e-5294-0269dd268c24.png)
この`GatewayId`と`VpcEndpointId`は、Routeリソースにはどちらか一方しか定義できません。両方指定しているとスタック作成時にエラーとなります。また、`VpcEndpointId`で指定されているのはインターネットゲートウェイの物理IDであって、VPCエンドポイントのIDではないのでこれも誤りです。このRouteの本来の定義は、VpcEndpointIdが無いものとなる筈です。

他にも同様のエラーがあります。以下はELBのListenerRuleの定義です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/0744b52f-ce6c-59fc-ba38-ca9b022cd9e4.png)
ここではルーティングの設定として`Field: "path-pattern"`が設定されています。そしてパラメータとして`Values`と`PathPatternConfig`が設定されていますが、このテンプレートをそのまま使うとValidationErrorとなります。
なぜなら`Field: "path-pattern"`の場合、設定できるのは`Values`か`PathPatternConfig`のどちらか一方のみだからです。

どちらの例も、設定できない値を設定してしまうというエラーが起きています。これらのエラーは実際にスタック作成時にはValidationErrorとして検出されます。CloudTrailで当該リクエストの箇所を見れば詳細なエラーが確認できるのでデバッグ自体は簡単ですが注意が必要です。


### 4. 不要なリソースを作成することがある
正確な挙動は理解できていないのですが、スタック作成時に`Already Exist`エラーで失敗するリソースがありました。

以下の画像はRouteの定義です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115707/9efa18e7-43f5-071b-003f-4f34fb661aad.png)
前述の`GatewayId`と`VpcEndpointId`の両指定問題もあるのですが、これは一旦おいておきます。
ここのリソースでIaCジェネレータが作成しようとしているのはVPCのIPブロック内を送信先とするlocalへのルート定義です。しかし、このRouteの紐付け先であるRouteTableリソースは、作成時にローカルルートをデフォルトで作成するようで、個別にローカルルートのRouteリソースを設定する必要はありません。以上の理由で、ここでは`Already Exist`エラーが発生してしまったものと思います。

このほかにも状況によって、エラーとならないものの冗長なリソース（リソース内で参照紐付きしているのに、Associationリソースで新たに紐付けさせようとするなど）が存在するようなので、その可能性を考慮しながらリファクタリングするとよいかもしれません。



## IaCジェネレータの利用制限
公式ページに記載の通りですが、IaCジェネレータには以下のクォータが存在します。
> 引用元：[既存のリソースのテンプレートを生成 \- AWS CloudFormation](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/generate-IaC.html?icmpid=docs_cfn_console)

|名前|名前|
|-|-|
|1 回のアカウントスキャンで処理できるリソースの最大数|100000|
|1 日あたりのスキャン数 (リソースが 10,000 未満のアカウントの場合)|3|
|1 日あたりのスキャン数 (リソースが 10,000 以上のアカウントの場合)|1|
|アカウントあたりの同時に生成されるテンプレートの数|5|
|1 回のテンプレート生成で同時にモデル化されるリソースの数|5|
|1 つのテンプレートでモデル化できるリソースの合計数|500|
 


特にスキャン回数については、とりあえず試しに実行してみようと考え無しに使うとすぐ上限に達してしまいます（私のことです）。リソースが少ない小規模システムであっても、1日3回までと厳しめの設定になっているため計画的な利用が必要です。
アカウント移行や定期的なリソース変更の自動検知など、いろいろな使い方が考えられるIaCジェネレータではありますが、この制約は頭に入れたうえで移行計画やスケジュールを検討しましょう。

また、1テンプレートのリソース数は500が上限となっています。おそらくシステム全体を1テンプレートに収めようとすると上限を超えることが多くなると思います。（今回のミニマムな構成では、リファクタリングして最終的に30リソース程度になりました。）
PoCレベルのミニマムな構成を除けば、基本的にはスタックの分割方法を検討する必要があると考えておいた方がよいでしょう。


# 最後に
既に多くの方がやってみた記事をあげておりN番煎じですが、サンプルは多くても困らないだろうという考えで投稿してみました。
IaCジェネレータが便利なことは間違いないのですが、実際に業務で使ことになるとまだまだネットには出てきていない躓き所がありそうです。とはいえ、そのうち改善されていくとは思うのでアプデに期待ですね。
現時点では本記事であげたような制約は認識しておく必要がありますが、手動構築したリソースを公式サービスでテンプレート化することができるのは非常に安心感があり、ありがたい限りです。
現行のリソース状態をテキスト化できるという観点でみれば、流行りのLLMを活用した構成レビューなどにも応用できそうです。今後も使えるところでは積極的に使っていきたいと思います。