Upgrade to Pro — share decks privately, control downloads, hide ads and more …

第106回 雲勉【オンライン】AWS CDKを用いて爆速PoCを実践してみた

第106回 雲勉【オンライン】AWS CDKを用いて爆速PoCを実践してみた

下記、勉強会での資料です。
https://youtu.be/WuvLkQj3kXU

iret.kumoben

June 30, 2023
Tweet

More Decks by iret.kumoben

Other Decks in Technology

Transcript

  1. 講師⾃⼰紹介 2 ▪ 名前 佐藤 淳 • (所属) クラウドインテグレーション事業部 構築第4セクション

    • (経歴) 2021年 アイレット 中途⼊社 • (アイレット歴) 1.5年 プログラマ歴なし。ずっとインフラエンジニア。CDK歴は2ヶ⽉ちょっと。 2023 Japan AWS All Certifications Engineer • 新しいバイク🏍欲しい。。
  2. アジェンダ 3 ⾃⼰紹介 1. はじめに 2. PoCを実践する上での課題 3. AWS CDKの基礎知識

    4. 実践① EC2 を1台作成してみる 5. 実践② 3層WEBアプリケーションを構築してみる 6. 実践③ ECSを試してみる(L3コンストラクトを利⽤) 7. Tips集 8. 終わりに 質疑応答
  3. 2. PoCを実践する上での課題 7 Q. PoC(Proof of Concept) とは? • 直訳すると概念検証のこと。

    • プロジェクト稼働前の段階で、プロトタイプを検証することで、プロジェクトの⽬的に⾒合った 成果が本当に得られるかを検証する。 → プロジェクト投資前の段階で、コストパフォーマンス等の要件の誤りを事前に防ぐ。 → 実現可能性を検証することで、開発途中でのリスク削減。 例) - サーバレスな構成に移⾏したいけど、パフォーマンスの低下は本当に無いか。 - RDSで新しい機能が追加された。既存のシステムに導⼊できないか。
  4. 2. PoCを実践する上での課題 8 PoC(Proof of Concept) のサイクル 【ポイント】 • ⽬標達成のため、サイクルは短期間で効率よく回す。

    • 試作の段階では迅速に環境構築する。 (プロトタイピングともいう。) ⇩ 試作品をいかに早く迅速に作り、 検証を進めることが重要 本発表では、試作のフェーズにいかに早く構築する⽅ 法について解説します!! 概念/アイディア 試作 検証 フィードバック プロジェクト投資 目標達成 要改善
  5. 2. PoCを実践する上での課題 9 PoC(Proof of Concept) のにおける試作の課題 【やりたいこと】 なるべく時間的コストをかけずに試作をしたい。 【課題】

    • 試作段階のトラブルシューティングはなるべく避けたい。 • 修正を重ねるとコストが嵩む。 • ⼤規模システムには不向き。いくつかのスパンで⼩規模に開発。 【⽅法】 • ⼿動構築 • Infrastructure as Code (IaC)
  6. 2. PoCを実践する上での課題 10 Q. ⼿動でPoCは可能? A. 可能ではある。 ただし!! ⼤変。 •

    1つ1つを⼿作業で実施するため時間が掛かる。 • ⼿動での設定のため、思わぬトラブルも発⽣。 → ⼿動による設定誤り。トラブルシューティング。 • 次のPoCのサイクルにて、環境を複製・再利⽤しやすい状態であるか。 → ⼿動の場合、ほとんど不可能。 概念/アイディア 試作 検証 フィードバック
  7. 2. PoCを実践する上での課題 11 Q. CloudformationやTerraform等のIaCツールを活⽤してPoCは可能? A. ⼿動より、環境の複製・再利⽤は簡易的になる。 ただし!! これも⼤変。。。 •

    TerraformやCfnは基本的に1:1でリソースを定義する必要がある。 例) VPCの場合、vpc, subnet, route_table, igw, nat gateway を1つ1つ要定義。 定義不⾜, 参照エラー, オプションや権限不⾜によるトラブルシュート etc.. • 作成するリソース数によってはコード量が増えることもある。 • テンプレート作成の⼯数が別途必要になる可能性も。 → テンプレート作成が⽬的化してしまうと本末転倒。。。
  8. 2. PoCを実践する上での課題 12 ▪ ⼿動, IaCツールでPoCを実践する際の課題と解決策 ⼿動 • リソースを複製/再利⽤できるよ うにしたい。

    • ⼿動設定による設定不⾜などを 解消したい。 Terraform, Cloudformation • リソースを1:1ではなく、1:nであらか じめ利⽤可能な単位でテンプレートを作 成したい。 • ⾃⾝の得意なプログラミング⾔語で学習 コスト少なくリソースを構築したい。 ⇩ AWS CDKを利⽤
  9. 3. AWS CDK の紹介 14 AWS CDKの特徴 ▪ AWS CDK(Cloud

    Development Kit)とは、AWSが提供するAWSリソースをプロビジョ ニングするためのIaCツール。 ▪ TypeScriptやPythonなどの⾔語を利⽤して、プロビジョニングすることができる。 (この発表では全てTypeScriptで構築) ▪ CDKライブラリでは豊富なメソッド、クラスなどが利⽤可能。 ▪ 後述のコンストラクトを使⽤し、少ないコード量でリソースを作成することができる。
  10. 3. AWS CDK の紹介 15 $ mkdir hello-cdk $ cd

    hello-cdk $ cdk init --language typescript CDKプロジェクトの新規作成 ▪ CDKプロジェクトの作成。 init コマンドを実⾏することでCDKプロジェクトを作成す ることができる。 ▪ initを実⾏した結果、右のファイルがプロジェクトフォルダ 内に作成される。 基本的には⾚枠で囲った.tsファイルを修正することでAWS リソースをプロビジョニングすることができる。
  11. 3. AWS CDK の紹介 16 Resource Resource App Stack Stack

    Resource ▪ Application … CDK Appを呼び出し 複数Stackを定義 ▪ Stack … Cloudformation上でのデプロイ単位 ▪ Resource … AWSリソースを定義 CDKの構成
  12. 3. AWS CDK の紹介 17 ファイルの内容 ▪ bin/hello-cdk.ts アプリケーション(App) を定義し、アプリ内で実⾏するスタックを呼び出す。

    ./libで定義されているスタックを呼び出す。 #!/usr/bin/env node import 'source-map-support/register'; import * as cdk from 'aws-cdk-lib'; import { HelloCdkStack, HelloCdkTestStack } from ‘../lib/hello-cdk-stack’; // ./lib/を参照 const app = new cdk.App(); // Applicationを定義 new HelloCdkStack(app, 'HelloCdkStack', {}); // Stackを定義 new HelloCdkTestStack(app, 'HelloCdkTestStack', {});
  13. 3. AWS CDK の紹介 18 ファイルの内容 ▪ lib/hello-cdk-stack.ts CloudFormationスタックHelloCdkStackを定義。 HelloCdkStack下で、Cfnスタック上で作成するリソースを定義。

    import { Duration, Stack, StackProps } from 'aws-cdk-lib’; import * as sqs from ‘aws-cdk-lib/aws-sqs’; // 使用するAWSサービス分呼び出し import { Construct } from 'constructs'; export class HelloCdkStack extends Stack { // スタックを定義 constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const queue = new sqs.Queue(this, ‘HelloCdkQueue’, { // SQSコンストラクトResource(SQS キュー)を定義 visibilityTimeout: Duration.seconds(300) }); } }
  14. 3. AWS CDK の紹介 19 コンストラクトについて ▪ コンストラクトとは︖ • AWS

    CDK上でのアプリケーションやAWSリソースを定義する基本的な構成要素。 • コンストラクトは1つのAWSリソースから複数のAWSリソースの定義を抽象化して少ないコード量で定 義できる。抽象度が増すと複数リソース(1:n)の作成が可能になる。 ▪ コンストラクトの種類 • L1コンストラクト … CloudFormationで定義可能な単位。Cfnの定義のように1つ1つ作成する。 (頭⽂字がCfnで始まる。) • L2コンストラクト … L1以上に抽象化されたコンストラクト。デフォルト値などが定義されてお り少ないコード量で定義可能。メソッドを使⽤することで追加設定も可。 • L3コンストラクト … L2以上に抽象化されたコンストラクト。aws-ecs-patternsなど。
  15. 3. AWS CDK の紹介 20 どれだけ抽象化することができる︖ ▪ Cloudformatioin Resource部分のみで 160⾏

    ▪ CDK VPCのコンストラクト定義のみで 5⾏ VPCネットワークの作成 ・Pub/Priサブネット × Az数 ・ルートテーブル (ルール追加/アタッチ) ・Nat Gateway 2台
  16. 3. AWS CDK の紹介 21 L1 L2 L3 L1 〜

    L3 コンストラクトのそれぞれのメリット & デメリット 抽象度 低 • リソースを1:1で定義 • 必須パラメータを要定義。 • テンプレート作成時間 … 多。 • 汎⽤性は⾼い。 抽象度 ⾼ • リソースを1:nで定義 • デフォルトの設定値が⼊るため、必要な箇所 のみ定義可能。 • テンプレート作成時間 … 少。 • 抽象度が増すと汎⽤性は低くなる。
  17. 3. AWS CDK の紹介 22 CDKプロジェクトの作成 $ mkdir hello-cdk $

    cd hello-cdk $ cdk init --language typescript $ cdk bootstrap aws://123456789012/ap-northeast-1 aws://123456789012/us-east-1 … CDKプロジェクトのデプロイ # dry run $ cdk synth # deploy $ cdk deploy CDKでよく使うコマンド 説明 bootstrap CDKアプリケーションのデプロイのためのリソースをリージョンに作成 init CDKプロジェクトの作成 list (ls) CDKアプリケーション上のCloudformationスタックの一覧 synth Cloudformationテンプレートの作成 diff 環境上のCloudformationスタックと修正したテンプレートの差分比較 deploy Cloudformationテンプレートの作成 & スタックの作成/更新 destroy Cloudformationスタックの削除
  18. 3. AWS CDK の紹介 24 AWS CDK まとめ ▪ 抽象化されているため、少ないコード量で複数リソースを定義することが可能。

    ▪ 環境の複製、再利⽤も可能。 ▪ 条件分岐などの記述も既存のプログラミング⾔語のスキルで簡単に実装可能。 ⇩ AWS CDKを活⽤することで、試作モデルを迅速に作成することができる
  19. 4. 実践① EC2 を1台作成してみる 29 リソースの定義 ▪ lib/hello-cdk-stack.ts import {

    Duration, Stack, StackProps } from 'aws-cdk-lib’; import * as ec2 from ‘aws-cdk-lib/aws-ec2’; // 使用するAWSサービス分呼び出し import { Construct } from 'constructs'; export class HelloCdkStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const ec2 = new ec2.Instance(this, ‘instance’, { }); : : : } } ← ライブラリをインポート ← ここに作成したいAWSリソースを記述
  20. 4. 実践① EC2 を1台作成してみる 31 // vpc const vpc =

    new ec2.Vpc(this, `pj1-vpc`, { vpcName: `pj1-vpc-01`, ipAddresses: ec2.IpAddresses.cidr("10.0.0.0/16"), natGateways: 0, }); VPCの定義 ▪ lib/hello-cdk-stack.ts コンストラクトVpcの呼び出し。(ec2.Vpc) わずか5⾏でVPC,Public/Privateサブネット,インターネットゲートウェイ,ルートテーブル, ルートテーブ ルの関連づけも定義することができる。 natGatewaysのデフォルト値はAZ数になるので、明⽰的に0と指定する。
  21. 4. 実践① EC2 を1台作成してみる 32 /* ---------- SG(EC2 Web) ----------

    */ const sgEc2Web = new ec2.SecurityGroup(this, `pj1-sg-ec2`, { vpc: vpc, securityGroupName: `pj1-sg-ec2-web-01`, description: "Allow EC2 Web Instance.", allowAllOutbound: true }); /* ---------- ルール追加 ---------- */ sgEc2Web.addIngressRule(ec2.Peer.ipv4("xxx.xxx.xxx.xxx/32"),ec2.Port.tcp(22),"allow ssh"); sgEc2Web.addIngressRule(ec2.Peer.ipv4("xxx.xxx.xxx.xxx/32"),ec2.Port.tcp(80),"allow http"); SecurityGroupの定義 ▪ lib/hello-cdk-stack.ts セキュリティグループの定義も少ないコード量で記述可。 SecurityGroup上で定義可能なメソッドaddIngressRuleによってインバウンドルールを追加。 EC2ライブラリ上で定義可能なクラスPeer, Portからメソッドを呼び出してIPやポートを定義。
  22. 4. 実践① EC2 を1台作成してみる 33 // vpc const ec2Web =

    new ec2.Instance(this, `pj1-ec2`, { vpc: vpc, vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC }, instanceType: new ec2.InstanceType("t3.micro"), machineImage: ec2.MachineImage.latestAmazonLinux2023(), securityGroup: sgEc2Web, instanceName: `pj1-ec2-web-001` }); EC2の定義 ▪ lib/hello-cdk-stack.ts 先程定義したVPCとSGを参照。 インスタンスをパブリックサブネット上に作成したいので列挙型(Enum)SubnetTypeでパブリックサブ ネットを指すメンバPUBLICを呼び出す。
  23. 5.実践② 3層WEBアプリケーションを構築してみる 37 /* ---------- SG(ALB) ---------- */ const sgEc2Alb

    = new ec2.SecurityGroup(this, `pj2-sg-alb`, { vpc, }); sgEc2Alb.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(443)); /* ---------- SG(EC2 Web) ---------- */ const sgEc2Web = new ec2.SecurityGroup(this, `pj2-sg-ec2`, { vpc, }); sgEc2Web.addIngressRule(ec2.Peer.ipv4(`xxx.xxx.xxx.xxx/32`),ec2.Port.tcp(22)); sgEc2Web.addIngressRule(sgEc2Alb, ec2.Port.tcp(80)); /* ---------- SG(RDS) ---------- */ const sgRds = new ec2.SecurityGroup(this, `pj2-sg-rds`, { vpc, }); sgRds.addIngressRule(sgEc2Web, ec2.Port.tcp(3306)); Security Groupの定義 ▪ lib/hello-cdk-stack.ts ALBとRDS⽤のセキュリティグループも追加。SecurityGroup の Construct Props での必須パラメータ はvpcのみのため、最低限の記述でたった⼗数⾏で定義できる。
  24. 5.実践② 3層WEBアプリケーションを構築してみる 38 RDSの定義 ▪ lib/hello-cdk-stack.ts CredentialクラスのfromGeneraredSecretメソッドで、SecretsManagerでパスワードを作成/管理。 インスタンスタイプやストレージ容量で料⾦が変動するため、明⽰的に指定。 const rdsInstance

    = new rds.DatabaseInstance(this, `pj2-rds-mysql`, { vpc: vpc, engine: rds.DatabaseInstanceEngine.MARIADB, instanceType: new ec2.InstanceType("t3.micro"), // db.t3.micro vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED }, storageType: rds.StorageType.GP3, multiAz: true, allocatedStorage: 20, credentials: rds.Credentials.fromGeneratedSecret( 'mariadb’, { secretName: `pj2/rds/mariadb`, } ), securityGroups: [sgRds], });
  25. 5.実践② 3層WEBアプリケーションを構築してみる 39 ALBの定義 ▪ lib/hello-cdk-stack.ts /* ---------- ALB ----------

    */ const alb = new elbv2.ApplicationLoadBalancer(this, `pj2-alb`, { vpc: vpc, internetFacing: true, securityGroup: sgEc2Alb, }); /* ---------- TargetGroup ---------- */ const tg = new elbv2.ApplicationTargetGroup(this,`pj2-tg`, { vpc: vpc, port: 80, targetType: elbv2.TargetType.INSTANCE, targets: [ new elbv2_tg.InstanceIdTarget(ec2Web01.instanceId), new elbv2_tg.InstanceIdTarget(ec2Web02.instanceId), ], }); /* ---------- Listner ---------- */ alb.addListener(`pj2-listener-443`, { port: 443, defaultTargetGroups: [tg], certificates: [cert], });
  26. 5.実践② 3層WEBアプリケーションを構築してみる 40 Https化 ▪ lib/hello-cdk-stack.ts ホストゾーンは作成済みのパブリックホストゾーンを使⽤する。 → 既存のAWSリソースを参照する場合は、fromLookupメソッドを使⽤。 /*

    ---------- R53 HostZone ---------- */ const hostzone = r53.HostedZone.fromLookup(this,`pj2-r53`,{ domainName: `example.com`, }); /* ---------- ACM ---------- */ const cert = new acm.Certificate( this, `pj2-cert`,{ domainName: `www.example.com`, validation: acm.CertificateValidation.fromDns(hostzone), }); /* ---------- R53 Record ---------- */ r53.ARecord(this, `pj2-r53-record`, { zone: hostzone, recordName: `www.example.com`, target: r53.RecordTarget.fromAlias(new r53_tgt.LoadBalancerTarget(alb)), });
  27. 6. 実践③ ECSを試してみる(L3コンストラクトを利⽤) 43 L3コンストラクト(aws-ecs-patterns)でECSの構築 ▪ 今まで呼び出していたコンストラクトは全てL2コンストラクト。 例) ALBの場合、ロードバランサー作成、 ターゲットグループ作成、リスナールールの追加が必要。

    ALB以外のリソースに関しては、L2コンストラクト上では定義はできない。 ▪ L3コンストラクトを利⽤するとどうなるのか︖ ECSサービス、タスク定義、IAMロール、ALB(ロードバランサー、リスナー、ターゲットグループ)を⼀ 括で作成。 ▪ 今回は、L3コンストラクトaws-ecs-patternsのApplicationLoadBalancedFargateServiceを使⽤
  28. 6. 実践③ ECSを試してみる(L3コンストラクトを利⽤) 45 // ecr const ecr_repo = new

    ecr.Repository(this, 'NginxRepository', { repositoryName: `pj2/nginx`, }); // vpc const vpc = new ec2.Vpc(this, 'Vpc', { ipAddresses: ec2.IpAddresses.cidr("10.0.0.0/16"), natGateways: 2, }); // vpc endpoint vpc.addInterfaceEndpoint('dkrInterfaceEndpoint', {service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER,}); vpc.addInterfaceEndpoint('ecrInterfaceEndpoint', {service: ec2.InterfaceVpcEndpointAwsService.ECR,}); vpc.addGatewayEndpoint('s3GatewayEndpoint', {service: ec2.GatewayVpcEndpointAwsService.S3}); 事前準備 ▪ lib/hello-cdk-stack.ts ECSがECRからコンテナイメージを取得するため、必要なエンドポイントを事前に作成。
  29. 6. 実践③ ECSを試してみる(L3コンストラクトを利⽤) 46 // ecs const cluster = new

    ecs.Cluster(this, ‘pj3-ecs’, { vpc: vpc, }); const ecsServiceAlbFargate = new ecs_patterns.ApplicationLoadBalancedFargateService(this, ‘pj3-ecs-patterns’, { cluster: cluster, taskImageOptions: { image: ecs.ContainerImage.fromEcrRepository(ecr_repo), }, taskSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, }, }); ECS(L3コンストラクト) ▪ lib/hello-cdk-stack.ts コンストラクトApplicationLoadBalancedFargateServiceを呼び出し、ALB + ECS(Fargate)を構築 。
  30. 7. Tips集 48 #!/usr/bin/env node import 'source-map-support/register'; import * as

    cdk from 'aws-cdk-lib'; import { HelloCDKStack } from '../lib/hello-cdk-stack'; const app = new cdk.App(); new HelloCDKStack(app, 'CrossRegionStackTokyo', { env: { region: 'ap-northeast-1’}, crossRegionReferences: true, // クロスリージョンでの参照を許可 }); new HelloCDKStack(app, 'CrossRegionStackVirginia', { env: { region: 'us-east-1’,}, crossRegionReferences: true, // クロスリージョンでの参照を許可 }); app.synth(); [Tips1] クロスリージョンでStackを構築したい ▪ bin/hello-cdk.ts env regionを指定。 クロスリージョン間での参照を許可するためcrossRegionReferencesをtrueに設定。
  31. 7. Tips集 49 [Tips2] 外部コンストラクトを利⽤したい ▪ Construct Hubから検索。 https://constructs.dev/ 今回はその中から

    セキュリティ/コンプライアンス レポーティングツールであるcdk-nag を利⽤してみ る。 ▪ cdk-nagとは、AWS CDKと統合し、コンプライアンス違反の有無をデプロイ前にチェックすることがで きるコンストラクト。利⽤できるルールとしては以下ルールを利⽤することができる。 ・AWS Solutions ・HIPAA Security ・NIST 800-53 rev 4 ・NIST 800-53 rev 5 ・PCI DSS 3.2.1
  32. 7. Tips集 50 #!/usr/bin/env node import 'source-map-support/register'; import * as

    cdk from 'aws-cdk-lib'; import { SampleCdkNagStack } from '../lib/sample_cdk_nag-stack'; import { AwsSolutionsChecks } from 'cdk-nag'; const app = new cdk.App(); cdk.Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true })) // cdk-nag コンストラクトの有効化. new SampleCdkNagStack(app, 'SampleCdkNagStack', {}); cdk-nagの有効化 ▪ bin/hello-cdk.ts Aspectsを拡張し、nagコンストラクトである AwsSolutionsChecksを有効にすることでコンプライア ンスチェックが有効になる。
  33. 7. Tips集 51 $ cdk synth [Error at /SampleCdkNagStack/SampleCdkNagQueue/Resource] AwsSolutions-SQS3:

    The SQS queue is not used as a dead-letter queue (DLQ) and does not have a DLQ enabled. Using a DLQ helps maintain the queue flow and avoid losing data by detecting and mitigating failures and service disruptions on time. [Error at /SampleCdkNagStack/SampleCdkNagQueue/Resource] AwsSolutions-SQS4: The SQS queue does not require requests to use SSL. Without HTTPS (TLS), a network-based attacker can eavesdrop on network traffic or manipulate it, using an attack such as man-in-the-middle. Allow only encrypted connections over HTTPS (TLS) using the aws:SecureTransport condition in the queue policy to force requests to use SSL. Found errors ▪ lib/hello-cdk-stack.ts SQSを作成。 nagはsynthesizer実⾏前にチェックが⼊り、コンプライアンスに満たさない設定を出⼒する。 以下はsynth実⾏後、コンプライアンス違反のある設定を出⼒。 const queue = new sqs.Queue(this, 'SampleCdkNagQueue', { visibilityTimeout: cdk.Duration.seconds(300) });
  34. 7. Tips集 52 $ cdk synth [Error at /SampleCdkNagStack/SampleCdkNagQueue/Resource] AwsSolutions-SQS4:

    The SQS queue does not require requests to use SSL. Without HTTPS (TLS), a network-based attacker can eavesdrop on network traffic or manipulate it, using an attack such as man-in-the-middle. Allow only encrypted connections over HTTPS (TLS) using the aws:SecureTransport condition in the queue policy to force requests to use SSL. Found errors ▪ lib/hello-cdk-stack.ts サプレス(抑⽌)を追加 const queue = new sqs.Queue(this, 'SampleCdkNagQueue', { visibilityTimeout: cdk.Duration.seconds(300) }); // サプレス追加 NagSuppressions.addResourceSuppressions(queue, [ { id: "AwsSolutions-SQS3", reason: "no need to create DDL."}, ]); コンプライアンスエラー 「AwsSolutions-SQS3」が、サプレス(抑⽌)されている
  35. 8. 終わりに 54 ▪ 習得済みのプログラミング⾔語で学習コストなくAWS CDKを始めることができる。 ▪ 試作を実施する上での⼿動やTerraformでの課題を、AWS CDKでカバーすることができる。 ▪

    L2, L3コンストラクトを利⽤して、少ないコード量でリソースを作成することができる。 ▪ 豊富なメソッドやクラスを利⽤することで、容易に定義することができる。 ↓ AWS CDKを利⽤することで、快適にPoCを進められるようになった。
  36. 8. 終わりに 55 ▪ Reference • Awesome CDK https://github.com/awesome-cdk •

    AWS Prescriptive Guidance Patterns https://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/patterns/welcome.html • AWS Prescriptive Guidance Patterns https://constructs.dev/ > CDK for Terraformなど • CDK Patterns https://cdkpatterns.com/patterns/ > v1が多い..
  37. 57