🐕

AWS CDK と CloudFormation の記述量の違いとメンテナンスコストについて勉強してみた。※ymlに修正しました〜

2023/08/16に公開
5

AWS CDK と CloudFormation の記述量の違いとメンテナンスコスト

はじめに

JAWS-UG CDK支部のイベントのラジオで聞いてCDKについて影響を受けて、CDKの勉強をはじめました。
私はAWSのIaCツールについてAWS CDK と CloudFormation について勉強した中での備忘録です。
※すみませんjsonです。時間あったらymlにします。→ymlに修正しました〜

記述量の違い

今回の実装について※CDKインストール時のsampleを使用

  1. SQSキューを作成する。
  2. SNSトピックを作成する。
  3. そのSNSトピックに、前述のSQSキューをサブスクリプションとして追加する。

AWS CDK (TypeScript)

: SNS トピックと SQS キューを作成し、その SNS トピックからのメッセージを SQS キューに送る機能

import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subs from 'aws-cdk-lib/aws-sns-subscriptions';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import { Construct } from 'constructs';

export class SampleStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props);

        const queue = new sqs.Queue(this, 'SampleQueue', {
            visibilityTimeout: Duration.seconds(300),
        });

        const topic = new sns.Topic(this, 'SampleTopic');

        topic.addSubscription(new subs.SqsSubscription(queue));
    }
}


CloudFormation (YAML)

※CDKからCloudFormation (YAML)のテンプレートを作成

: SNS トピックと SQS キューを作成し、その SNS トピックからのメッセージを SQS キューに送る機能

Resources:
  SampleQueue49AAAEFF:
    Type: "AWS::SQS::Queue"
    Properties:
      VisibilityTimeout: "300"
    UpdateReplacePolicy: "Delete"
    DeletionPolicy: "Delete"
    Metadata:
      aws:cdk:path: "SampleStack/SampleQueue/Resource"

  SampleQueuePolicy6B63351F:
    Type: "AWS::SQS::QueuePolicy"
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Action: "sqs:SendMessage"
            Condition:
              ArnEquals:
                aws:SourceArn:
                  Ref: "SampleTopic5FE9B5DC"
            Effect: "Allow"
            Principal:
              Service: "sns.amazonaws.com"
            Resource:
              Fn::GetAtt:
                - "SampleQueue49AAAEFF"
                - "Arn"
      Queues:
        - Ref: "SampleQueue49AAAEFF"
    Metadata:
      aws:cdk:path: "SampleStack/SampleQueue/Policy/Resource"

  SampleQueueSampleStackSampleTopic95768FB97B34B6F6:
    Type: "AWS::SNS::Subscription"
    Properties:
      Endpoint:
        Fn::GetAtt:
          - "SampleQueue49AAAEFF"
          - "Arn"
      Protocol: "sqs"
      TopicArn:
        Ref: "SampleTopic5FE9B5DC"
    DependsOn:
      - "SampleQueuePolicy6B63351F"
    Metadata:
      aws:cdk:path: "SampleStack/SampleQueue/SampleStackSampleTopic95768FB9/Resource"

  SampleTopic5FE9B5DC:
    Type: "AWS::SNS::Topic"
    Metadata:
      aws:cdk:path: "SampleStack/SampleTopic/Resource"

  CDKMetadata:
    Type: "AWS::CDK::Metadata"
    Properties:
      Analytics: "v2:deflate64:H4sIAAAAAAAA/1WNywrCMBBFv6X7ZKzdue4PaOte2iTCtDWpmUSRkH83DxBkYObew4Hp4HSEtpnexIVc+YYzhNFNYmUJ3QI9CcLFK69Yf9c1lH02G4rPD9YaGenkj34mYXF3aHQ2/vrV7CgyLSHGHAdFxltRfvRGS8xmZNpIBQsdXl0LaZqFELn12uFDwVDvFwwGXTbAAAAA"
    Metadata:
      aws:cdk:path: "SampleStack/CDKMetadata/Default"
    Condition: "CDKMetadataAvailable"

Conditions:
  CDKMetadataAvailable: 
    Fn::Or: 
    - Fn::Or: 
      - Fn::Equals: [ Ref: "AWS::Region", "af-south-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "ap-east-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "ap-northeast-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "ap-northeast-2" ]
      - Fn::Equals: [ Ref: "AWS::Region", "ap-south-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "ap-southeast-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "ap-southeast-2" ]
      - Fn::Equals: [ Ref: "AWS::Region", "ca-central-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "cn-north-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "cn-northwest-1" ]
    - Fn::Or: 
      - Fn::Equals: [ Ref: "AWS::Region", "eu-central-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "eu-north-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "eu-south-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "eu-west-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "eu-west-2" ]
      - Fn::Equals: [ Ref: "AWS::Region", "eu-west-3" ]
      - Fn::Equals: [ Ref: "AWS::Region", "me-south-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "sa-east-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "us-east-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "us-east-2" ]
    - Fn::Or: 
      - Fn::Equals: [ Ref: "AWS::Region", "us-west-1" ]
      - Fn::Equals: [ Ref: "AWS::Region", "us-west-2" ]

Parameters:
  BootstrapVersion:
    Type: "AWS::SSM::Parameter::Value<String>"
    Default: "/cdk-bootstrap/hnb659fds/version"
    Description: "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"

Rules:
  CheckBootstrapVersion:
    Assertions:
      - Assert:
          Fn::Not:
            - Fn::Contains:
              - [ "1", "2", "3", "4", "5" ]
              - Ref: "BootstrapVersion"
        AssertDescription: "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."


もしjsonだったら

{
 "Resources": {
  "SampleQueue49AAAEFF": {
   "Type": "AWS::SQS::Queue",
   "Properties": {
    "VisibilityTimeout": 300
   },
   "UpdateReplacePolicy": "Delete",
   "DeletionPolicy": "Delete",
   "Metadata": {
    "aws:cdk:path": "SampleStack/SampleQueue/Resource"
   }
  },
  "SampleQueuePolicy6B63351F": {
   "Type": "AWS::SQS::QueuePolicy",
   "Properties": {
    "PolicyDocument": {
     "Statement": [
      {
       "Action": "sqs:SendMessage",
       "Condition": {
        "ArnEquals": {
         "aws:SourceArn": {
          "Ref": "SampleTopic5FE9B5DC"
         }
        }
       },
       "Effect": "Allow",
       "Principal": {
        "Service": "sns.amazonaws.com"
       },
       "Resource": {
        "Fn::GetAtt": [
         "SampleQueue49AAAEFF",
         "Arn"
        ]
       }
      }
     ],
     "Version": "2012-10-17"
    },
    "Queues": [
     {
      "Ref": "SampleQueue49AAAEFF"
     }
    ]
   },
   "Metadata": {
    "aws:cdk:path": "SampleStack/SampleQueue/Policy/Resource"
   }
  },
  "SampleQueueSampleStackSampleTopic95768FB97B34B6F6": {
   "Type": "AWS::SNS::Subscription",
   "Properties": {
    "Endpoint": {
     "Fn::GetAtt": [
      "SampleQueue49AAAEFF",
      "Arn"
     ]
    },
    "Protocol": "sqs",
    "TopicArn": {
     "Ref": "SampleTopic5FE9B5DC"
    }
   },
   "DependsOn": [
    "SampleQueuePolicy6B63351F"
   ],
   "Metadata": {
    "aws:cdk:path": "SampleStack/SampleQueue/SampleStackSampleTopic95768FB9/Resource"
   }
  },
  "SampleTopic5FE9B5DC": {
   "Type": "AWS::SNS::Topic",
   "Metadata": {
    "aws:cdk:path": "SampleStack/SampleTopic/Resource"
   }
  },
  "CDKMetadata": {
   "Type": "AWS::CDK::Metadata",
   "Properties": {
    "Analytics": "v2:deflate64:H4sIAAAAAAAA/1WNywrCMBBFv6X7ZKzdue4PaOte2iTCtDWpmUSRkH83DxBkYObew4Hp4HSEtpnexIVc+YYzhNFNYmUJ3QI9CcLFK69Yf9c1lH02G4rPD9YaGenkj34mYXF3aHQ2/vrV7CgyLSHGHAdFxltRfvRGS8xmZNpIBQsdXl0LaZqFELn12uFDwVDvFwwGXTbAAAAA"
   },
   "Metadata": {
    "aws:cdk:path": "SampleStack/CDKMetadata/Default"
   },
   "Condition": "CDKMetadataAvailable"
  }
 },
 "Conditions": {
  "CDKMetadataAvailable": {
   "Fn::Or": [
    {
     "Fn::Or": [
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "af-south-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "ap-east-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "ap-northeast-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "ap-northeast-2"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "ap-south-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "ap-southeast-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "ap-southeast-2"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "ca-central-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "cn-north-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "cn-northwest-1"
       ]
      }
     ]
    },
    {
     "Fn::Or": [
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "eu-central-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "eu-north-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "eu-south-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "eu-west-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "eu-west-2"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "eu-west-3"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "me-south-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "sa-east-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "us-east-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "us-east-2"
       ]
      }
     ]
    },
    {
     "Fn::Or": [
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "us-west-1"
       ]
      },
      {
       "Fn::Equals": [
        {
         "Ref": "AWS::Region"
        },
        "us-west-2"
       ]
      }
     ]
    }
   ]
  }
 },
 "Parameters": {
  "BootstrapVersion": {
   "Type": "AWS::SSM::Parameter::Value<String>",
   "Default": "/cdk-bootstrap/hnb659fds/version",
   "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
  }
 },
 "Rules": {
  "CheckBootstrapVersion": {
   "Assertions": [
    {
     "Assert": {
      "Fn::Not": [
       {
        "Fn::Contains": [
         [
          "1",
          "2",
          "3",
          "4",
          "5"
         ],
         {
          "Ref": "BootstrapVersion"
         }
        ]
       }
      ]
     },
     "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
    }
   ]
  }
 }
}
  • CDK: リソースを増やしていくと、CDKの抽象化と再利用の恩恵により、記述が効率的になります。

  • CloudFormation: 初心者にとっては直感的でシンプルですが、大規模なアーキテクチャや高度な設定を扱う場合、テンプレートが非常に長くなる可能性があります。

メンテナンスに関するメリット・デメリット

AWS CDK

メリット:

  1. 抽象化: AWSリソースに関連する複雑な設定やブートストラッピングを抽象化することで、記述ミスを減少させることができます。
  2. 再利用: 複数のスタックやアプリケーションで同じパターンを使用する場合、カスタムコンストラクトを簡単に再利用できます。
  3. テストの容易性: TypeScript (または他のプログラミング言語) による記述のため、ユニットテストや統合テストを組み込みやすい。

デメリット:

  1. 隠蔽: 高度な抽象化により、何が「裏で」起こっているのかを理解するのが難しくなる場合がある。
  2. 依存関係の管理: NPMや他のパッケージマネージャを使用して依存関係を管理する必要があり、これにより追加のメンテナンスの複雑さが生じることがあります。

CloudFormation

メリット:

  1. 直接性: 何が起こるかがテンプレートに明示的に書かれており、AWSのドキュメントと直接対応しています。
  2. 成熟度: 多くのツール、プラグイン、コミュニティサポートが存在します。

デメリット:

  1. 複雑さ: 大規模なプロジェクトや複雑なリソースの関係を管理する場合、テンプレートが非常に長く、複雑になることがあります。
  2. ロジックの制約: YAMLの表現能力に制約があり、プログラム的なロジックの組み込みが難しい。

参考

実践 AWS CDK - TypeScript でインフラもアプリも! - Silverworks - BOOTH

CloudFormationは、AWS CDKから使うのが正解な気がしてきた - 新しいことにはウェルカム

普段 CloudFormation で書いている人が AWS CDK Workshop をやってみて思うこと | DevelopersIO

AWS CDKの学習方法 ドキュメントや学習コンテンツをまとめてみる | DevelopersIO

JAWS-UG CDK支部さんはTwitterを使っています: 「来週の8/9(水)21:00〜22:00にCDK詳しい方達が集まって質問に答えていきます!良かったらリマインドに登録してください 質問も待ってます! https://t.co/hvsQoFurZS」 / X

Discussion

まっきんとっしゅまっきんとっしゅ

https://www.site24x7.com/ja/tools/json-to-yaml.html
このツールを使うとJSONからyamlに簡単に変換できますよー!

ueue

ありがとうございます。
便利なツールがあるのですね!!助かります。
仕事終わったら、やってみます~!!

ueue

ymlに変換できました〜
めっちゃ便利です!!

ueue

ありがとうございます!
見直してみます〜