AWS CloudFormation IaC ジェネレーターで AWS リソースの棚卸を試してみた

2024-04-02
コミュニティ通信

Author : 織田 繁 (AWS Community Hero)

こんにちは、AWS Community Hero の 織⽥繁 (@OutputSeq) です。
AWS ユーザーコミュニティである JAWS-UG で AWS 初学者向けの JAWS-UG 初⼼者⽀部、AWS アップデートをキャッチアップしたい⽅向けの JAWS-UG 主催 週刊 AWS キャッチアップ を勉強会イベントとして開催しています。

普段の業務では複数のプロジェクトでの AWS インフラストラクチャの作成及び管理を⾏っています。インフラストラクチャの作成には AWS CloudFormation または AWS Cloud Development Kit (CDK) などの IaC ツールを使っています。

職場では IaC ツールが不慣れな⽅もプロジェクトにはいらっしゃいますので、開発環境では調査のために AWS マネジメントコンソールで⼀時的な AWS リソース作成が⾏われることもあります。しかし、作成後に削除をし忘れてしまいコスト増加に繋がる AWS アカウントが発生するリスクが出てきます。

このような課題を解決するために、今回は AWS CloudFormation IaC ジェネレーター を AWS リソースの棚卸ツールとして試した話をします。


IaC ジェネレーターの概要

IaC ジェネレーターは、AWS リソースをスキャンし、その結果から CloudFormation テンプレートを作成することができます。これにより、マネジメントコンソールで作成された AWS リソースを Infrastructure as Code の原則に則って管理することが可能になります。このプロセスにより、⼀貫性があり管理しやすいインフラストラクチャの展開を促進します。

今回はスキャン結果から AWS リソースの棚卸を⾏う方法を紹介します。

IaC ジェネレーターの詳細については、ユーザーガイド、または AWS ブログ をご確認ください。


IaC ジェネレーターでリソースをスキャンする

IaC ジェネレーターを使うために、マネジメントコンソールから、「CloudFormation」、「IaCジェネレーター」の順に開きます。

画像をクリックすると拡大します

スキャンを行います。
スキャン」パネルで、「新しいスキャンを開始」をクリックします。

画像をクリックすると拡大します

スキャン」パネルで、「新しいスキャンを開始」をクリックします。

スキャン時間はリソース量によりますが、1,000 個のリソースで最⼤で 10 分かかることがあります。2024 年 3 月時点での IaC ジェネレーターに関するクォータの⼀覧は以下です。

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

その他、IaC ジェネレーターに関するクォータの⼀覧は ユーザーガイド をご確認ください。


スキャンしたリソースを出力する

スキャン結果をテキストとして使うために、AWS CloudShell を利⽤します。

マネジメントコンソールから、「CloudShell」を開きます。

画像をクリックすると拡大します

AWS CLI でマネージメントコンソールで実⾏したスキャン結果の ID (ResourceScanId) を取得します。

スキャン結果が複数である場合には⼀番上のスキャン ID (ResourceScanId) が最新となりますので、そちらをメモしておいてください。

コマンド

aws cloudformation list-resource-scans | \
jq '.ResourceScanSummaries | sort_by(.EndTime) | reverse'

結果サンプル

[
{
"ResourceScanId": "arn:aws:cloudformation:ap-northeast-
1:123456789012:resourceScan/672e5920-5a46-4c22-aaf7-0f8a53fe8d93",
"Status": "COMPLETE",
"StartTime": "2024-02-26T01:18:04.222000+00:00",
"EndTime": "2024-02-26T01:21:56.859000+00:00",
"PercentageCompleted": 100.0
},
{
"ResourceScanId": "arn:aws:cloudformation:ap-northeast-
1:123456789012:resourceScan/d904d627-8f68-4760-8f31-5c00587a1f3a",
"Status": "COMPLETE",
"StartTime": "2024-02-25T00:10:34.553000+00:00",
"EndTime": "2024-02-25T00:14:49.331000+00:00",
"PercentageCompleted": 100.0
},
… 略 …
]

ResourceScanId には上記結果の ResourceScanId を貼り付けます。

コマンド

ResourceScanId="arn:aws:cloudformation:ap-northeast-
1:123456789012:resourceScan/672e5920-5a46-4c22-aaf7-0f8a53fe8d93"
echo $ResourceScanId

結果サンプル

arn:aws:cloudformation:ap-northeast-1:123456789012:resourceScan/672e5920-5a46-4c22-
aaf7-0f8a53fe8d93

スキャンされた件数を確認します。

コマンド

aws cloudformation describe-resource-scan \
--resource-scan-id $ResourceScanId \
| jq '{ResourcesRead}'

結果サンプル

{
"ResourcesRead": 1470
}

スキャンされた内容をファイル出⼒します。

コマンド

aws cloudformation list-resource-scan-resources \
--resource-scan-id $ResourceScanId \
| jq -r '.Resources[] | "\(.ManagedByStack)|\(.ResourceType)|\
(.ResourceIdentifier)"' > list-resource-scan-resources.txt
wc -l list-resource-scan-resources.txt

ファイル出⼒された結果が、スキャンされた結果と同じであることを確認します。

結果サンプル

1470 list-resource-scan-resources.txt

ファイルのサンプル確認をしてみましょう。10 件サンプルでデータを確認します。

コマンド

head -10 list-resource-scan-resources.txt

結果サンプル

false|AWS::AccessAnalyzer::Analyzer|{"Arn":"arn:aws:access-analyzer:ap-northeast-
1:123456789012:analyzer/ConsoleAnalyzer"}
false|AWS::ApiGateway::Deployment|{"DeploymentId":"hsijel","RestApiId":"kpp0xbs554"}
false|AWS::ApiGateway::Deployment|{"DeploymentId":"pxdq2l","RestApiId":"oauq0h8aoa"}
false|AWS::ApiGateway::Deployment|{"DeploymentId":"ob4gej","RestApiId":"oauq0h8aoa"}
false|AWS::ApiGateway::Deployment|{"DeploymentId":"es85me","RestApiId":"q6y4p727ie"}
true|AWS::ApiGateway::Deployment|{"DeploymentId":"voso3d","RestApiId":"oauq0h8aoa"}
false|AWS::ApiGateway::Deployment|{"DeploymentId":"didtcn","RestApiId":"kpp0xbs554"}
false|AWS::ApiGateway::RestApi|{"RestApiId":"q6y4p727ie"}
true|AWS::ApiGateway::RestApi|{"RestApiId":"oauq0h8aoa"}
false|AWS::ApiGateway::RestApi|{"RestApiId":"kpp0xbs554"}

データは | でカラムを区切っており、それぞれのカラムの意味は以下となります。

カラム位置 カラム名 意味
1 カラム⽬  ManagedByStack true の場合は CloudFormation Stack による管理であることを⽰します。
2 カラム⽬  ResourceType リソースタイプを⽰します。
3 カラム⽬  ResourceIdentifier リソース識別⼦を⽰します。リソースタイプ毎に表⽰する個数は異なります

ファイルを確認する

ファイルの確認を Excel で⾏うためにダウンロードを⾏います。

Cloudshell のコンソールで、「アクション」、「ファイルのダウンロード」の順に開きます。

画像をクリックすると拡大します

/home/cloudshell-user/list-resource-scan-resources.txt を「個別のファイルパス」に⼊⼒して、「ダウンロード」をクリックします。

画像をクリックすると拡大します

条件書式を設定した Excel を準備していますので、こちらの Excel ファイルをダウンロードします。

Excelのフォーマットファイル »

Excel の A2 セルに list-resource-scan-resources.txt を貼り付けます。すると、⾃動的に区切り位置、条件付き書式が働き、画像のように表⽰され、スキャン結果を一覧で確認することができるようになりました。

この結果を元に、リソースの棚卸をすることができます。

画像をクリックすると拡大します


リソース棚卸後のリソース削除時の注意点

リソースの棚卸が終わったので、次に削除対象のピックアップとなります。

リソースの利⽤状況により削除すべき対象は変わるため、削除する対象をここで記載することはできませんが、筆者の環境では以下を削除対象から除外しましたので、ご参考にしてください。

ManagedByStack  ResourceType  ResourceIdentifier  理由
FALSE AWS::CloudFront::CachePolicy {"Id":"2e54312d-136d-493c-8eb9-b001f22f67d2"} AWS マネージドポリシーであり、削除対象外と判断
FALSE AWS::CloudFront::CachePolicy {"Id":"658327ea-f89d-4fab-a63d-7e88639e58f6"}
FALSE AWS::CloudFront::CachePolicy {"Id":"b2884449-e4de-46a7-ac36-70bc7f1ddd6d"}
FALSE AWS::CloudFront::CachePolicy {"Id":"08627262-05a9-4f76-9dedb50ca2e3a84f"}
FALSE AWS::CloudFront::CachePolicy {"Id":"4135ea2d-6df8-44a3-9df3-4b5a84be39ad"}
FALSE AWS::CloudFront::OriginRequestPolicy {"Id":"59781a5b-3903-41f3-afcbaf62929ccde1"} AWS マネージドポリシーであり、削除対象外と判断 
FALSE AWS::CloudFront::OriginRequestPolicy {"Id":"acba4595-bd28-49b8-b9fe-13317c0390fa"}
FALSE AWS::CloudFront::OriginRequestPolicy {"Id":"216adef6-5c7f-47e4-b989-5492eafa07d3"}
FALSE AWS::CloudFront::OriginRequestPolicy {"Id":"33f36d7e-f396-46d9-90e0-52428a34d9dc"}
FALSE AWS::CloudFront::OriginRequestPolicy {"Id":"88a5eaf4-2fd4-4709-b370-b4c650ea3fcf"}
FALSE AWS::CloudFront::OriginRequestPolicy {"Id":"775133bc-15f2-49f9-abeaafb2e0bf67d2"}
FALSE AWS::CloudFront::OriginRequestPolicy {"Id":"b689b0a8-53d0-40ab-baf2-68738e2966ac"}
FALSE AWS::ElastiCache::User {"UserId":"default"} default のデータであり、削除対象外と判断
FALSE AWS::Events::EventBus {"Name":"default"} default のデータであり、削除対象外と判断 default のデータであり、削除対象外と判断
FALSE AWS::MemoryDB::ACL {"ACLName":"open-access"}
FALSE AWS::MemoryDB::ParameterGroup {"ParameterGroupName":"default.memorydbredis7"} default のデータであり、削除対象外と判断
FALSE AWS::MemoryDB::ParameterGroup {"ParameterGroupName":"default.memorydbredis7.search.preview"}
FALSE AWS::MemoryDB::ParameterGroup {"ParameterGroupName":"default.memorydbredis6"}
FALSE AWS::MemoryDB::User {"UserName":"default"} default のデータであり、削除対象外と判断
FALSE AWS::XRay::Group {"GroupARN":"arn:aws:xray:ap-northeast-1 123456789012:group/Default"} default のデータであり、削除対象外と判断
FALSE AWS::RDS::OptionGroup {"OptionGroupName":"default:mysql-8-0"} default のデータであり、削除対象外と判断
FALSE AWS::RDS::OptionGroup {"OptionGroupName":"default:aurora-mysql-5-7"}
FALSE AWS::RDS::OptionGroup {"OptionGroupName":"default:postgres-14"}
FALSE AWS::RDS::OptionGroup {"OptionGroupName":"default:aurora-mysql-8-0"}
FALSE AWS::RDS::OptionGroup {"OptionGroupName":"default:mysql-5-7"}
FALSE AWS::RDS::OptionGroup

{"OptionGroupName":"default:sqlserver-web-

15-00"}

FALSE AWS::RDS::OptionGroup {"OptionGroupName":"default:aurorapostgresql-14"}
FALSE AWS::RDS::OptionGroup {"OptionGroupName":"default:neptune-1-0"}

さいごに

今回は、IaC ジェネレーター を AWS リソースの棚卸のツールとして試してみた経験を紹介させていただきました。

実際に私のプロジェクトでは棚卸を実施し、AWSリソースの⼀覧化を通じて、プロジェクト内で完全に把握されていなかったリソースの存在を明らかにすることが出来ました。また、⼀覧をベースにプロジェクト内のコミュニケーションを⾏うことで、チームメンバー間の認識の齟齬を解消することに繋がりましたし、AWS リソースを整理することで "コスト削減" や、"セキュリティ強化"、"運⽤効率の向上" にも繋げることが出来たと考えています。

この記事にご興味を持たれた⽅は、AWS リソースの整理を通じて、プロジェクトチームの連携強化・プロジェクトの効率改善にご活用いただけますと幸いです。


builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

織田 繁 (AWS samurai / AWS Community Hero)

普段は AWS インフラ構築・運用保守・教育を実施しています。育児休暇中で子供を寝かし付けた後にこの記事を書いています。コミュニティ活動としては JAWS-UG 初心者支部 運営メンバーとして楽しんでいます。

Twitter: @OutputSeq / GitHub: @shigeru-oda / zenn.dev: @shigeru_oda

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する