BioErrorLog Tech Blog

試行錯誤の記録

AWS CDKでStackをネストにする | NestedStack

AWS CDKでStackをネスト (入れ子) にしてデプロイする方法を記します。


はじめに

おはよう。@bioerrorlogです。

AWS CDKでStackをネスト(入れ子)にしようとしたところ、単純に入れ子にしたのでは上手くデプロイできないことに気が付きました。

Stackを入れ子にするにはひと工夫必要だったので、そのやり方の備忘録を残します。


なお、本記事で使用するコードは以下のGitHubにも置いてあります。
github.com


作業環境

今回の作業環境は以下の通りです。

CDKバージョン:

$ cdk --version
1.31.0 (build 8f3ac79)


言語はPythonを使用しました。
Pythonバージョン:

$ python --version
Python 3.6.10


Amazon LiunxのCloud9で作業を行いました。

$ cat /etc/system-release
Amazon Linux AMI release 2018.03


CDKでStackをネストにする

問題: Stackが入れ子にできない

さて、今回の問題はStackが入れ子にできない、という点にあります。

例えば、以下のように単純に入れ子状のStackを作ったとします。

from aws_cdk import core
import aws_cdk.aws_s3 as s3


class ParentStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        
        ChildStack01(self, "ChildStack01")
        ChildStack02(self, "ChildStack02")
        
        
class ChildStack01(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        
        s3.Bucket(self, "ChildBucket01", bucket_name="child-bucket-01")
        
        
class ChildStack02(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        s3.Bucket(self, "ChildBucket02", bucket_name="child-bucket-02")


app = core.App()
ParentStack(app, "ParentStack")
app.synth()


このCDKコードを絵に描き表すと、次のような親子関係になっています。 f:id:BioErrorLog:20200408083931p:plain
ここで期待する動作は、親Stackである ParentStack をデプロイすることで、子Stackたちも同時にデプロイされる、というものです。

しかし、この状態で親Stackをデプロイしても、子StackならびにS3リソースはデプロイされませんでした。


まず、CDKコマンドから cdk synthcdk deploy を行うと、一見うまくいっているように見えます。

$ cdk synth
Successfully synthesized to /home/ec2-user/environment/cdk-test/cdk.out
Supply a stack id (ParentStack, ParentStackChildStack01796333D2, ParentStackChildStack0287B3CF12) to display its template.

$ cdk deploy ParentStack
ParentStack: deploying...
ParentStack: creating CloudFormation changeset...
 0/2 | 11:49:11 PM | CREATE_IN_PROGRESS   | AWS::CDK::Metadata | CDKMetadata 
 0/2 | 11:49:13 PM | CREATE_IN_PROGRESS   | AWS::CDK::Metadata | CDKMetadata Resource creation Initiated
 1/2 | 11:49:13 PM | CREATE_COMPLETE      | AWS::CDK::Metadata | CDKMetadata 
 2/2 | 11:49:15 PM | CREATE_COMPLETE      | AWS::CloudFormation::Stack | ParentStack 

   ParentStack


しかし、作成されたCloudFormation Stack ParentStack の中身を見てみると、リソースが何もデプロイされていないことが分かります。

f:id:BioErrorLog:20200408100042p:plain
CloudFormationコンソールから、ParentStackが作成されていることがわかる
f:id:BioErrorLog:20200408091043p:plain
デプロイされたリソースはメタデータのみで、それ以外は空っぽだった

このように、単純にStackを入れ子状にしても、期待する動作はしてくれないことが判明しました。


解決策: NestedStackを使う

この問題についての解決策は、思ったより単純でした。

子Stackに core.Stack を継承させる代わりに、aws_cloudformation NestedStack を継承させれば良いのです。

必要な変更は、以下の2点です。

  • import aws_cdk.aws_cloudformation as cfn を追加
  • 子Stackの継承を core.Stack から cfn.NestedStack に変更

※CDKのcloudformationパッケージをまだインストールしていない場合は、
pip install aws-cdk.aws-cloudformation でインストールします。

先ほどのCDKコードを書きなおすと、次のようになります。

from aws_cdk import core
import aws_cdk.aws_s3 as s3
import aws_cdk.aws_cloudformation as cfn # cloudformationのインポート


class ParentStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        
        ChildStack01(self, "ChildStack01")
        ChildStack02(self, "ChildStack02")
        
        
class ChildStack01(cfn.NestedStack): # NestedStackの継承

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        
        s3.Bucket(self, "ChildBucket01", bucket_name="child-bucket-01")
        
        
class ChildStack02(cfn.NestedStack): # NestedStackの継承

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        s3.Bucket(self, "ChildBucket02", bucket_name="child-bucket-02")


app = core.App()
ParentStack(app, "ParentStack")
app.synth()


それでは、書き直したCDKコードを cdk deploy でデプロイし、再びCloudFormationコンソールを確認します。

f:id:BioErrorLog:20200408100209p:plain

するとこのように、親Stackに加えて、子StackがNested Stackとしてデプロイされました。

これら二つの子Stackは、親Stackのリソースとしてデプロイされていることがわかります。

f:id:BioErrorLog:20200408123351p:plain
子Stackは、親Stackからデプロイされている

このようにして、CDK Stackを入れ子にしてデプロイすることに成功しました。

f:id:BioErrorLog:20200408083931p:plain


おわりに

今回は、CDK Stackを入れ子にする方法を記しました。

シンプルに解決できたので、この NestedStack はなかなかに有用だと感じました。

CDKのことを知れば知るほど、その便利さを噛みしめています。

[関連記事]

www.bioerrorlog.work

www.bioerrorlog.work


参考

Nested stack support · Issue #239 · aws/aws-cdk · GitHub

NestedStack — AWS Cloud Development Kit 1.94.1 documentation

class NestedStack (construct) · AWS CDK