本記事は
IaCウィーク
6日目の記事です。
⚙️
5日目
▶▶ 本記事 ▶▶
7日目
💻
はじめに
こんにちは。藤本です!
AWS環境を構築するとき、「コンソールで作った設定をコード化して再利用できたら…」と思ったことはありませんか?
AWS CloudFormationやAWS CDKを使ってInfrastructure as Code(IaC)を実現するには、テンプレートを手書きする方法以外に、IaCジェネレーターを使ってテンプレートを作成する方法があります。
今回は、IaCジェネレーターを使って、マネジメントコンソールで手動構築したリソースからAWS CloudFormationテンプレートを自動生成してみます。
サービス概要
IaCジェネレーター (Infrastructure as Code ジェネレーター) とは、AWSマネジメントコンソール上で構築したリソース構成をもとに、自動的にCloudFormationテンプレートを生成する機能のことです。 これにより、GUI操作で構築した環境をIaC(Infrastructure as Code)としてコード化し、他の環境・アカウント・リージョンへ簡単に再現できるようになります。 CloudFormationテンプレートはYAMLまたはJSON形式で生成されます。 IaCジェネレーターは、CloudFormationの拡張機能であるため、CloudFormationのコンソールからアクセスできます。

料金
IaCジェネレーターの利用自体は無料で利用できます。 ただし利用の際には以下のクォーターに注意する必要があります。
| 項目 | クォータ |
|---|---|
| 1 回のスキャンで処理できるリソースの最大数 | 100,000 |
| 1 日あたりのスキャン数 (スキャンするリソースの数が 10,000 未満の場合) | 10 |
| 1 日あたりのスキャン数 (スキャンするリソースの数が 10,000 以上の場合) | 1 |
| アカウントあたりの同時に生成されるテンプレートの数 | 5 |
| 1 回のテンプレート生成で同時にモデル化されるリソースの数 | 5 |
| 1 つのテンプレートでモデル化できるリソースの合計数 | 500 |
| 生成されたテンプレートのアカウントあたりの最大数 | 1,000 |
使い方
IaCジェネレーターでは、以下の手順でリソースのテンプレート化を行います。
- リソースのスキャン
- テンプレートの作成
- リソースのインポート
1. リソースのスキャン
リソースのスキャンでは、既存の AWS リソースを解析し、テンプレート化(コード化)に必要な情報を収集します。 スキャンの範囲を設定する「フルスキャン」と「部分スキャン」の2つのモードがあります。 「フルスキャン」では、現在のアカウント・リージョン内のすべてのリソースタイプを対象にスキャンを行います。対象を限定せず、アカウント・リージョン内にある全リソースをIaC化の候補にすることができるため、手動構築された複数サービス/複数リソースを一括でコード化したい場合に適しています。また、展開されているリソースの内訳も確認できるようになるため、リソースの棚卸しに利用することが出来ます。

「部分スキャン」では、スキャン対象のリソースタイプを最大100個まであらかじめ指定して、その限定範囲のみをスキャンします。 一部のリソースタイプは未対応です。対応しているリソースに関しては、リソースタイプのサポートからご確認ください。 なお、スキャン結果は常に1件のみ保持され、部分スキャンを実行するたびに前回の結果が上書きされます。スキャン対象を絞り込む特性上、テンプレート化したいリソースが対象外の場合は、生成時に漏れが生じる可能性がありますのでご注意ください。スキャン結果の有効期限は30日です。

2. テンプレートの作成
スキャンが完了したら、その結果をもとにCloudFormationテンプレートの作成を行います。 テンプレートに含めたいリソースを検索・選択し、「テンプレートを作成」を実行します。 テンプレート作成時には、選択したリソースだけでなく、そのリソースの動作に必要な関連リソースも自動的に検出され、テンプレートに含まれます。 例えば、EC2インスタンスを選択した場合は、VPC、サブネット、セキュリティグループ、IAMロール、EBSボリューム、Elastic IP などが関連リソースとして自動的に追跡・追加されます。

また、既存のCloudFormationスタックのテンプレートを開始点(ベース)として利用し、スキャンされたリソースや関連リソースをそのテンプレートに追加することも可能です。
3. リソースのインポート
最後に作成されたテンプレートからスタックの作成を行います。 テンプレートをスタックにインポートすることで簡単にデプロイも行うことができます。

また、作成されたCloudFormationテンプレートからAWS CDKのテンプレートも同時に作成してくれます。

IaCジェネレーターを実際に使ってみる
検証の前提
今回はLambda+S3+Amazon EventBridgeのサーバレス構成をマネジメントコンソールで作成し、IaCジェネレーターによりテンプレート化を行います。 この構成では、EventBridgeのスケジュールルールがLambda関数を定期実行し、関数は処理結果をS3バケットにJSONとして保存します。 このとき、Lambdaは実行ロールでS3書き込みとCloudWatch Logs出力を行い、EventBridgeからLambdaの呼び出し許可が設定されています。
- マネジメントコンソールで作成するリソース
- S3 バケット
- Lambda関数
- Lambda関数IAM ロール
- EventBridge ルール

1. リソースのスキャン
「フルスキャン」を行い、リソースの情報を取得します。
2. テンプレートの作成
スキャン結果からテンプレートを作成します。 マネジメントコンソールで作成したリソースS3バケット、Lambda関数、EventBridgeを選択します。

関連リソースとして、LambdaとEventBridgeのデプロイに必要なリソースであるIAMロールとそのロールにアタッチされるマネージドポリシーが表示されていることが分かります。

結果
以下がIaCジェネレーターによって作成されたCloudFormationテンプレートになります。
CloudFormationテンプレート
Metadata: AWSToolsMetrics: IaC_Generator: "****" Parameters: LambdaFunctionDemoIacGenWriterFunctionCodeImageUrioxajI: NoEcho: "true" Type: "String" Description: "URI of a [container image](https://docs.aws.amazon.com/lambda/latest/dg/lambda-images.html) in the Amazon ECR registry." LambdaFunctionDemoIacGenWriterFunctionCodeSourceKMSKeyArn0xWRU: NoEcho: "true" Type: "String" Description: "The ARN of the KMSlong (KMS) customer managed key that's used to encrypt your function's .zip deployment package. If you don't provide a customer managed key, Lambda uses an [owned key](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#aws-owned-cmk)." LambdaFunctionDemoIacGenWriterFunctionCodeS3ObjectVersionRGhKZ: NoEcho: "true" Type: "String" Description: "For versioned objects, the version of the deployment package object to use." LambdaFunctionDemoIacGenWriterFunctionCodeS3Bucket9QMOR: NoEcho: "true" Type: "String" Description: "An Amazon S3 bucket in the same AWS-Region as your function. The bucket can be in a different AWS-account." LambdaFunctionDemoIacGenWriterFunctionCodeZipFileOqGox: NoEcho: "true" Type: "String" Description: "(Node.js and Python) The source code of your Lambda function. If you include your function source inline with this parameter, CFN places it in a file named ``index`` and zips it to create a [deployment package](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-package.html). This zip file cannot exceed 4MB. For the ``Handler`` property, the first part of the handler identifier must be ``index``. For example, ``index.handler``. When you specify source code inline for a Node.js function, the ``index`` file that CFN creates uses the extension ``.js``. This means that LAM treats the file as a CommonJS module. ES modules aren't supported for inline functions. For JSON, you must escape quotes and special characters such as newline (``\\n``) with a backslash. If you specify a function that interacts with an AWS CloudFormation custom resource, you don't have to write your own functions to send responses to the custom resource that invoked the function. AWS CloudFormation provides a response module ([cfn-response](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-lambda-function-code-cfnresponsemodule.html)) that simplifies sending responses. See [Using Lambda with CloudFormation](https://docs.aws.amazon.com/lambda/latest/dg/services-cloudformation.html) for details." LambdaFunctionDemoIacGenWriterFunctionCodeS3KeyLsSsp: NoEcho: "true" Type: "String" Description: "The Amazon S3 key of the deployment package." Resources: IAMManagedPolicyPolicyserviceroleAmazonEventBridgeSchedulerExecutionPolicyf09a9287c1d842d3840a4f354697bb0c: UpdateReplacePolicy: "Retain" Type: "AWS::IAM::ManagedPolicy" DeletionPolicy: "Retain" Properties: ManagedPolicyName: "****" Path: "/service-role/" Description: "" Groups: [] PolicyDocument: Version: "2012-10-17" Statement: - Resource: - "****" - "****" Action: - "lambda:InvokeFunction" Effect: "Allow" Roles: - "****" Users: [] IAMRoleAmazonEventBridgeSchedulerLAMBDA7c6840abd6: UpdateReplacePolicy: "Retain" Type: "AWS::IAM::Role" DeletionPolicy: "Retain" Properties: Path: "/service-role/" ManagedPolicyArns: - Ref: "IAMManagedPolicyPolicyserviceroleAmazonEventBridgeSchedulerExecutionPolicyf09a9287c1d842d3840a4f354697bb0c" MaxSessionDuration: 3600 RoleName: "****" AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Condition: StringEquals: aws:SourceAccount: "****" Action: "sts:AssumeRole" Effect: "Allow" Principal: Service: "scheduler.amazonaws.com" SchedulerScheduleDemoiacgenschedule: UpdateReplacePolicy: "Retain" Type: "AWS::Scheduler::Schedule" DeletionPolicy: "Retain" Properties: GroupName: "default" ScheduleExpression: "rate(5 hours)" Target: Arn: Fn::GetAtt: - "LambdaFunctionDemoIacGenWriterFunction" - "Arn" RetryPolicy: MaximumEventAgeInSeconds: 86400 MaximumRetryAttempts: 0 RoleArn: Fn::GetAtt: - "IAMRoleAmazonEventBridgeSchedulerLAMBDA7c6840abd6" - "Arn" Description: "demo-iacgen-schedule" State: "ENABLED" FlexibleTimeWindow: Mode: "OFF" ScheduleExpressionTimezone: "Asia/Tokyo" Name: "demo-iacgen-schedule" LambdaFunctionDemoIacGenWriterFunction: UpdateReplacePolicy: "Retain" Type: "AWS::Lambda::Function" DeletionPolicy: "Retain" Properties: MemorySize: 128 Description: "" TracingConfig: Mode: "PassThrough" Timeout: 3 RuntimeManagementConfig: UpdateRuntimeOn: "Auto" Handler: "lambda_function.lambda_handler" Code: SourceKMSKeyArn: Ref: "LambdaFunctionDemoIacGenWriterFunctionCodeSourceKMSKeyArn0xWRU" S3ObjectVersion: Ref: "LambdaFunctionDemoIacGenWriterFunctionCodeS3ObjectVersionRGhKZ" S3Bucket: Ref: "LambdaFunctionDemoIacGenWriterFunctionCodeS3Bucket9QMOR" ZipFile: Ref: "LambdaFunctionDemoIacGenWriterFunctionCodeZipFileOqGox" ImageUri: Ref: "LambdaFunctionDemoIacGenWriterFunctionCodeImageUrioxajI" S3Key: Ref: "LambdaFunctionDemoIacGenWriterFunctionCodeS3KeyLsSsp" Role: Fn::GetAtt: - "IAMRoleDemoIacGenWriterFunctionrole00gmy0x4" - "Arn" FileSystemConfigs: [] FunctionName: "****" Runtime: "python3.13" PackageType: "Zip" LoggingConfig: LogFormat: "Text" LogGroup: "****" RecursiveLoop: "Terminate" EphemeralStorage: Size: 512 Architectures: - "x86_64" IAMManagedPolicyPolicyserviceroleAWSLambdaBasicExecutionRoleba45dd15786f4f9097c96ebc6d12cce6: UpdateReplacePolicy: "Retain" Type: "AWS::IAM::ManagedPolicy" DeletionPolicy: "Retain" Properties: ManagedPolicyName: "****" Path: "/service-role/" Description: "" Groups: [] PolicyDocument: Version: "2012-10-17" Statement: - Resource: "****" Action: "logs:CreateLogGroup" Effect: "Allow" - Resource: - "****" Action: - "logs:CreateLogStream" - "logs:PutLogEvents" Effect: "Allow" Roles: - "****" Users: [] S3BucketDemoiacgenbucket20251110: UpdateReplacePolicy: "Retain" Type: "AWS::S3::Bucket" DeletionPolicy: "Retain" Properties: PublicAccessBlockConfiguration: RestrictPublicBuckets: true IgnorePublicAcls: true BlockPublicPolicy: true BlockPublicAcls: true BucketName: "****" OwnershipControls: Rules: - ObjectOwnership: "BucketOwnerEnforced" BucketEncryption: ServerSideEncryptionConfiguration: - BucketKeyEnabled: true ServerSideEncryptionByDefault: SSEAlgorithm: "AES256" IAMRoleDemoIacGenWriterFunctionrole00gmy0x4: UpdateReplacePolicy: "Retain" Type: "AWS::IAM::Role" DeletionPolicy: "Retain" Properties: Path: "/service-role/" ManagedPolicyArns: - Ref: "IAMManagedPolicyPolicyserviceroleAWSLambdaBasicExecutionRoleba45dd15786f4f9097c96ebc6d12cce6" MaxSessionDuration: 3600 RoleName: "****" Policies: - PolicyDocument: Version: "2012-10-17" Statement: - Resource: "****" Action: - "s3:PutObject" Effect: "Allow" Sid: "AllowPutToDemoBucket" PolicyName: "AllowPutToDemoBucket" AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Action: "sts:AssumeRole" Effect: "Allow" Principal: Service: "lambda.amazonaws.com"
スキャン結果から作成したいリソースとその関連リソースをテンプレート化することができました。 CloudFormationテンプレートから、以下のリソースが作成されていることが分かります。
- EventBridgeScheduler
- EventBridgeSchedulerのIAMロールに用いるポリシー
- EventBridgeSchedulerのIAMロール
- Lambda関数
- Lambda関数のIAMロールに用いるポリシー
- Lambda関数のIAMロール
- S3バケット
また、Canvasで各リソースの依存関係を視覚的に確認することもできます。

注意点
「部分スキャン」だと関連リソースが表示されないときがある
「部分スキャン」は、開始前に指定したリソースタイプのみを主に走査します。
そのため、AWS::S3::Bucket、AWS::Lambda::Function、AWS::Scheduler::Scheduleの3種類だけを指定した場合、AWS::IAM::Role、AWS::IAM::ManagedPolicy、AWS::IAM::Policyがスコープに含まれず、参照は解析されてもテンプレート内で生成すべきと判断されにくく、関連リソースとして表示されないことがあります。
つまり、スキャン範囲が適切でないと関連リソースは検出されません。
「部分スキャン」時は、漏れを防ぐためにIAMなど必要なリソースタイプを事前に範囲へ追加するなど、スコープの確認が必要になります。
アカウント内の展開リソースが少ない場合は、「フルスキャン」の利用を検討するのもよいと思います。
Retainが自動で設定される
IaCジェネレーターによって定義される各リソースにDeletionPolicyとUpdateReplacePolicyがRetainが設定されます。
DeletionPolicyはスタックを削除したり、テンプレートからそのリソースが削除された場合でも、実体のリソースは残す設定となります。また、UpdateReplacePolicyは更新によって置換が必要になったとき、新リソースを作成後、旧リソースは削除せず保持する設定となります。
これらのポリシーをRetainと設定すると、スタックの削除・更新時にリソースが残留しやすく、運用でゴミが溜まる原因となります。
検証用途以外はRetainを外すように修正するとよいと思います。
作成したテンプレートに手動修正が必要になることがある
IaCジェネレーターはテンプレートを自動生成してくれますが、そのまま本番投入できるとは限りません。
たとえば、Lambda の実行ロール名IAMRoleDemoIacGenWriterFunctionrole00gmy0x4のように、論理ID/物理名に乱数サフィックスが付いて可読性・視認性が下がり、社内の命名規則から外れてしまうことがあります。
また、Outputsやタグ、説明(Description)は自動で記載されないため、運用の観点から、ジェネレーターでテンプレート作成した後、手動で追記する必要があります。
このため、IaCジェネレーターの出力は雛形として扱い、命名の整理や不要パラメータの削減、ポリシー・Retain・タグ方針の反映など、必要な手動調整を適宜行う必要があります。
Parametersが冗長である
Parametersでは、Lambdaのデプロイ方法に対応した複数の入力を受け取れるようにパラメータ化されています。
...CodeImageUri...:コンテナイメージでデプロイする際のECRイメージURI...CodeSourceKMSKeyArn...:Zipパッケージの暗号化に使うカスタマー管理 KMS キーARN...CodeS3Bucket / CodeS3Key / CodeS3ObjectVersion...:S3上のZipを使う場合のバケット/キー/バージョン...CodeZipFile...:インラインZip(小さな関数向け、4MB 以内)
Parameters: LambdaFunctionDemoIacGenWriterFunctionCodeImageUrioxajI: NoEcho: "true" Type: "String" Description: "URI of a [container image](https://docs.aws.amazon.com/lambda/latest/dg/lambda-images.html) in the Amazon ECR registry." LambdaFunctionDemoIacGenWriterFunctionCodeSourceKMSKeyArn0xWRU: NoEcho: "true" Type: "String" Description: "The ARN of the KMSlong (KMS) customer managed key that's used to encrypt your function's .zip deployment package. If you don't provide a customer managed key, Lambda uses an [owned key](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#aws-owned-cmk)." LambdaFunctionDemoIacGenWriterFunctionCodeS3ObjectVersionRGhKZ: NoEcho: "true" Type: "String" Description: "For versioned objects, the version of the deployment package object to use." LambdaFunctionDemoIacGenWriterFunctionCodeS3Bucket9QMOR: NoEcho: "true" Type: "String" Description: "An Amazon S3 bucket in the same AWS-Region as your function. The bucket can be in a different AWS-account." LambdaFunctionDemoIacGenWriterFunctionCodeZipFileOqGox: NoEcho: "true" Type: "String" Description: "(Node.js and Python) The source code of your Lambda function. If you include your function source inline with this parameter, CFN places it in a file named ``index`` and zips it to create a [deployment package](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-package.html). This zip file cannot exceed 4MB. For the ``Handler`` property, the first part of the handler identifier must be ``index``. For example, ``index.handler``. When you specify source code inline for a Node.js function, the ``index`` file that CFN creates uses the extension ``.js``. This means that LAM treats the file as a CommonJS module. ES modules aren't supported for inline functions. For JSON, you must escape quotes and special characters such as newline (``\\n``) with a backslash. If you specify a function that interacts with an AWS CloudFormation custom resource, you don't have to write your own functions to send responses to the custom resource that invoked the function. AWS CloudFormation provides a response module ([cfn-response](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-lambda-function-code-cfnresponsemodule.html)) that simplifies sending responses. See [Using Lambda with CloudFormation](https://docs.aws.amazon.com/lambda/latest/dg/services-cloudformation.html) for details." LambdaFunctionDemoIacGenWriterFunctionCodeS3KeyLsSsp: NoEcho: "true" Type: "String" Description: "The Amazon S3 key of the deployment package."
これは、可読性が下がり、実際にどのパラメータに設定が必要であるか、またどのようなデプロイ方法を取るか不明確であるため、テンプレートとしては不十分です。 デプロイ方法を明確にしたうえで、不必要なパラメータは削除するようにしましょう。
終わりに
今回は、コンソールで作成した構成を IaC ジェネレーターで手早くテンプレート化してみました。
使ってみると想像以上に手軽で、CloudFormationに不慣れな方でもテンプレート作成の入口になると思います。また、生成されたテンプレートに後からパラメータ整理や細かな設定を加えれば、自分たちの運用に合う形へ仕上げられます。さらに、AWSリソースのパラメータ洗い出しや設定値の微調整をテンプレートに反映する際にも便利です。対象リソースが一覧で見えるのもうれしい点で、構成の把握やリソース棚卸しにも役立ちます。
IaCジェネレーター を活用して、無理なくテンプレート化を進めていきましょう!!