こんにちは。ディベロップメントサービス3課の千本松です。
以前の記事で、CDK Migrateを使用してAWS CloudFormation(以下、CloudFormation)からAWS CDK(以下、CDK)への基本的な移行を行いました。
今回は、実際の運用で重要となる外部パラメータの管理について解説します。
前回と同じAmazon EC2(以下、EC2)スタックを題材に、実際の検証結果を交えながら解説します。
前回の記事はこちら:AWS CloudFormationスタックをAWS CDKで管理!CDK Migrateを使ってみた - サーバーワークスエンジニアブログ
CDK Migrateとは
CDK Migrateは、CloudFormationのスタックやテンプレートをCDKコードに変換するための機能であり、CDK内のコマンドとして提供されています
ただし、現状実験的な機能として提供されており、将来的に破壊的な変更が加わる可能性がある点に注意が必要です。(2025年7月時点)
The cdk migrate command is experimental and may have breaking changes in the future.
現在のコードの課題点
CDK Migrateで変換されたコードを確認して、課題点を確認していきます。
変換前のCloudFormationテンプレートのポイント
元のCloudFormationテンプレートでは、環境ごとに異なる設定を行うために、パラメータを使用していました。
Parameters: EnvironmentName: Type: String Default: Dev
※ 元になるCloudFormationテンプレート全文は、前回の記事で紹介しています。
変換後のCDKコードの確認
CDK Migrateコマンドで生成されたコードを確認してみましょう。
app.py(メインアプリケーション)
#!/usr/bin/env python3 import os import aws_cdk as cdk from dev_ec2_stack.dev_ec2_stack_stack import DevEc2StackStack app = cdk.App() DevEc2StackStack(app, "Dev-EC2-Stack") app.synth()
dev_ec2_stack_stack.py(スタック定義)
from aws_cdk import Stack import aws_cdk as cdk import aws_cdk.aws_ec2 as ec2 from constructs import Construct class DevEc2StackStack(Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # パラメータの定義 props = { 'environmentName': kwargs.get('environmentName', 'Dev'), } # セキュリティグループの定義 securityGroup = ec2.CfnSecurityGroup(self, 'SecurityGroup', group_description = 'Security group for EC2 instance', vpc_id = cdk.Fn.import_value(f"""{props['environmentName']}-VPC-ID"""), tags = [ { 'key': 'Name', 'value': f"""{props['environmentName']}-EC2-SecurityGroup""", }, ], ) # EC2インスタンスの定義 ec2Instance = ec2.CfnInstance(self, 'EC2Instance', image_id = 'ami-xxxxxxxxxxxxxxxxx', instance_type = 't3.micro', subnet_id = cdk.Fn.import_value(f"""{props['environmentName']}-PublicSubnet1-ID"""), security_group_ids = [ securityGroup.ref, ], tags = [ { 'key': 'Name', 'value': f"""{props['environmentName']}-EC2-Instance""", }, { 'key': 'Environment', 'value': props['environmentName'], }, ], ) # 出力値の定義 self.instance_id = ec2Instance.ref cdk.CfnOutput(self, 'CfnOutputInstanceId', key = 'InstanceId', description = 'EC2 Instance ID', export_name = f"""{props['environmentName']}-EC2-Instance-ID""", value = str(self.instance_id), )
課題点
以下の行で環境名をパラメータとして受け取っていますが、現状ではデフォルト値が設定されているだけで、実際の環境ごとに異なる設定を適用することができません。
また、異なる環境を設定するためには、コードを直接編集する必要があります。
# パラメータの定義 props = { 'environmentName': kwargs.get('environmentName', 'Dev'), }
コンテキスト値を使用した解決策
CDKでは、アプリが外部からパラメータを受け取るために、コンテキスト値を使用することができます。
このコンテキスト値を使用して、環境ごとに異なるパラメータを指定できるように変更していきます。
app.pyの修正
コンテキスト値を使用して外部からパラメータを受け取れるようにapp.py(メインアプリケーション)を修正します:
#!/usr/bin/env python3 import os import aws_cdk as cdk from dev_ec2_stack.dev_ec2_stack_stack import DevEc2StackStack app = cdk.App() ########################################## # コンテキスト値から環境名を取得するように変更 environment_name = app.node.try_get_context("environmentName") if not environment_name: raise ValueError("environmentName context value is required. Use: cdk deploy -c environmentName=Dev") ########################################## ########################################## # スタック名を環境名に基づいて設定 # 取得した環境名をスタックに渡すように変更 DevEc2StackStack(app, f"{environment_name}-EC2-Stack", environment_name=environment_name) ########################################## app.synth()
dev_ec2_stack_stack.py の修正
スタックがパラメータを引数として受け取れるように修正します:
from aws_cdk import Stack import aws_cdk as cdk import aws_cdk.aws_ec2 as ec2 from constructs import Construct class DevEc2StackStack(Stack): # =========================================== # コンストラクタの引数に environment_name を追加 def __init__(self, scope: Construct, construct_id: str, environment_name: str, **kwargs): # =========================================== super().__init__(scope, construct_id, **kwargs) # VPCの参照を取得 vpc_id = cdk.Fn.import_value(f"{environment_name}-VPC-ID") subnet_id = cdk.Fn.import_value(f"{environment_name}-PublicSubnet1-ID") # セキュリティグループの作成 security_group = ec2.CfnSecurityGroup( self, 'SecurityGroup', group_description='Security group for EC2 instance', vpc_id=vpc_id, tags=[{ 'key': 'Name', 'value': f"{environment_name}-EC2-SecurityGroup", }] ) # EC2インスタンスの作成 ec2_instance = ec2.CfnInstance( self, 'EC2Instance', image_id='ami-xxxxxxxxxxxxxxxxx', instance_type='t3.micro', subnet_id=subnet_id, security_group_ids=[security_group.ref], tags=[ { 'key': 'Name', 'value': f"{environment_name}-EC2-Instance", }, { 'key': 'Environment', 'value': environment_name, } ] ) # 出力値の定義 cdk.CfnOutput( self, 'CfnOutputInstanceId', key='InstanceId', description='EC2 Instance ID', export_name=f"{environment_name}-EC2-Instance-ID", value=ec2_instance.ref )
contextの使い方
以下のようにコマンドラインからコンテキスト値を指定してデプロイできます。
# 開発環境へのデプロイ(変更セット確認) cdk deploy -c environmentName=Dev --no-execute # 開発環境へのデプロイ cdk deploy -c environmentName=Dev # 本番環境へのデプロイ cdk deploy -c environmentName=Prod
もしくは、cdk.json
ファイルにデフォルトの環境名を設定することもできます。
{ "app": "python app.py", "context": { "environmentName": "Dev" } }
まとめ
CDK Migrateを使用して生成されたコードを、環境ごとに異なるパラメータに対応させる方法を解説しました。
コンテキスト値を利用することで、パラメータのハードコーディングを解消し、環境ごとに異なる設定を簡単に適用できるようになりました。
最後までお読みいただきありがとうございました!
本ブログが誰かの参考になれば幸いです。