CDK CloudFormation Disassemblerで、CloudFormationのyamlをcdk化してみた
久々の投稿であることには触れずに本題をw
最近CDKを触る機会が多いので、記事を書きやすいトピックを探していたのだが、おあつらえ向けのものがあったので書きます。
こんなものがあります。
「CloudFormationのtemplate jsonを喰わせると、よしなにCDKのコードにして吐き出してくれるよ!」というやつ。 どんな感じに出力されるのか興味があったが、最近ついにこれを触る機会が訪れた。
CDK化する対象はこれ。
1 yamlをjsonに変換
dasmはjson -> CDKに変換するツールなので、このままだと使えない。 yamlをjsonに変換するツールを使ってなんとかjsonにする。
このときこれみたいなオンラインの変換サービスを使ってみると、
!Ref
関数などがことごとくnull
になってしまう。この辺をなんとか頑張ってjson化する。
json化すると(きっと)こうなる。
2 dasmに喰わせる
さっそく喰わせてみる
% npm i -g cdk-dasm % cdk-dasm < cloudformation.template.json > cloudformation-stack.ts
すると、こんなファイルが出来上がる。
// generated by cdk-dasm at 2019-11-16T08:07:46.297Z import { Stack, StackProps, Construct, Fn } from '@aws-cdk/core'; import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); import ec2 = require('@aws-cdk/aws-ec2'); export class MyStack extends Stack { constructor(scope: Construct, id: string, props: StackProps = {}) { super(scope, id, props); new ec2.CfnVPC(this, 'VPC', { cidrBlock: { "Ref": "VpcCIDR" }, enableDnsSupport: true, enableDnsHostnames: true, tags: [ { "key": "Name", "value": { "Ref": "EnvironmentName" } } ], }); new ec2.CfnInternetGateway(this, 'InternetGateway', { tags: [ { "key": "Name", "value": { "Ref": "EnvironmentName" } } ], }); new ec2.CfnVPCGatewayAttachment(this, 'InternetGatewayAttachment', { internetGatewayId: { "Ref": "InternetGateway" }, vpcId: { "Ref": "VPC" }, }); new ec2.CfnSubnet(this, 'PublicSubnet1', { vpcId: { "Ref": "VPC" }, availabilityZone: { "Fn::Select": [ 0, { "fn:getAZs": "" } ] }, cidrBlock: { "Ref": "PublicSubnet1CIDR" }, mapPublicIpOnLaunch: true, tags: [ { "key": "Name", "value": { "Fn::Sub": "${EnvironmentName} Public Subnet (AZ1)" } } ], }); new ec2.CfnSubnet(this, 'PublicSubnet2', { vpcId: { "Ref": "VPC" }, availabilityZone: { "Fn::Select": [ 1, { "Fn::GetAZs": "" } ] }, cidrBlock: { "Ref": "PublicSubnet2CIDR" }, mapPublicIpOnLaunch: true, tags: [ { "key": "Name", "value": { "Fn::Sub": "${EnvironmentName} Public Subnet (AZ1)" } } ], }); new ec2.CfnSubnet(this, 'PrivateSubnet1', { vpcId: { "Ref": "VPC" }, availabilityZone: { "Fn::Select": [ 0, { "Fn::GetAZs": "" } ] }, cidrBlock: { "Ref": "PrivateSubnet1CIDR" }, mapPublicIpOnLaunch: false, tags: [ { "key": "Name", "value": { "Fn::Sub": "${EnvironmentName} Private Subnet (AZ1)" } } ], }); new ec2.CfnSubnet(this, 'PrivateSubnet2', { vpcId: { "Ref": "VPC" }, availabilityZone: { "Fn::Select": [ 1, { "Fn::GetAZs": "" } ] }, cidrBlock: { "Ref": "PrivateSubnet2CIDR" }, mapPublicIpOnLaunch: false, tags: [ { "key": "Name", "value": { "Fn::Sub": "${EnvironmentName} Private Subnet (AZ2)" } } ], }); new ec2.CfnEIP(this, 'NatGateway1EIP', { domain: "vpc", }); new ec2.CfnEIP(this, 'NatGateway2EIP', { domain: "vpc", }); new ec2.CfnNatGateway(this, 'NatGateway1', { allocationId: { "Fn::GetAtt": [ "NatGateway1EIP", "AllocationId" ] }, subnetId: { "Ref": "PublicSubnet1" }, }); new ec2.CfnNatGateway(this, 'NatGateway2', { allocationId: { "Fn::GetAtt": [ "NatGateway2EIP", "AllocationId" ] }, subnetId: { "Ref": "PublicSubnet2" }, }); new ec2.CfnRouteTable(this, 'PublicRouteTable', { vpcId: { "Ref": "VPC" }, tags: [ { "key": "Name", "value": { "Fn::Sub": "${EnvironmentName} Public Routes" } } ], }); new ec2.CfnRoute(this, 'DefaultPublicRoute', { routeTableId: { "Ref": "PublicRouteTable" }, destinationCidrBlock: "0.0.0.0/0", gatewayId: { "Ref": "InternetGateway" }, }); new ec2.CfnSubnetRouteTableAssociation(this, 'PublicSubnet1RouteTableAssociation', { routeTableId: { "Ref": "PublicRouteTable" }, subnetId: { "Ref": "PublicSubnet1" }, }); new ec2.CfnSubnetRouteTableAssociation(this, 'PublicSubnet2RouteTableAssociation', { routeTableId: { "Ref": "PublicRouteTable" }, subnetId: { "Ref": "PublicSubnet2" }, }); new ec2.CfnRouteTable(this, 'PrivateRouteTable1', { vpcId: { "Ref": "VPC" }, tags: [ { "key": "Name", "value": { "Fn::Sub": "${EnvironmentName} Private Routes (AZ1)" } } ], }); new ec2.CfnRoute(this, 'DefaultPrivateRoute1', { routeTableId: { "Ref": "PrivateRouteTable1" }, destinationCidrBlock: "0.0.0.0/0", natGatewayId: { "Ref": "NatGateway1" }, }); new ec2.CfnSubnetRouteTableAssociation(this, 'PrivateSubnet1RouteTableAssociation', { routeTableId: { "Ref": "PrivateRouteTable1" }, subnetId: { "Ref": "PrivateSubnet1" }, }); new ec2.CfnRouteTable(this, 'PrivateRouteTable2', { vpcId: { "Ref": "VPC" }, tags: [ { "key": "Name", "value": { "Fn::Sub": "${EnvironmentName} Private Routes (AZ2)" } } ], }); new ec2.CfnRoute(this, 'DefaultPrivateRoute2', { routeTableId: { "Ref": "PrivateRouteTable2" }, destinationCidrBlock: "0.0.0.0/0", natGatewayId: { "Ref": "NatGateway2" }, }); new ec2.CfnSubnetRouteTableAssociation(this, 'PrivateSubnet2RouteTableAssociation', { routeTableId: { "Ref": "PrivateRouteTable2" }, subnetId: { "Ref": "PrivateSubnet2" }, }); new ec2.CfnSecurityGroup(this, 'NoIngressSecurityGroup', { groupName: "no-ingress-sg", groupDescription: "Security group with no ingress rule", vpcId: { "Ref": "VPC" }, }); } }
うーん、やはりまだ安定番ではないだけのことはあってすごいコードだ・・・。
3 手で修正する
ここからは、cdk deploy
が通る状態になるまで、手で修正するしかない。
まぁ、「どのクラスを使うか」というのは網羅しているので、愚直に直していく。
なお、Parametersは固定値にした。
// generated by cdk-dasm at 2019-11-16T08:07:46.297Z import { Stack, StackProps, Construct, Fn } from "@aws-cdk/core"; import ec2 = require("@aws-cdk/aws-ec2"); export class MyStack extends Stack { constructor(scope: Construct, id: string, props: StackProps = {}) { super(scope, id, props); const environmentName = "development"; const vpcCidr = "10.192.0.0/16"; const publicSubnet1CIDR = "10.192.10.0/24"; const publicSubnet2CIDR = "10.192.11.0/24"; const privateSubnet1CIDR = "10.192.20.0/24"; const privateSubnet2CIDR = "10.192.21.0/24"; const vpc = new ec2.CfnVPC(this, "VPC", { cidrBlock: vpcCidr, enableDnsSupport: true, enableDnsHostnames: true, tags: [ { key: "Name", value: environmentName } ] }); const internetGateway = new ec2.CfnInternetGateway( this, "InternetGateway", { tags: [ { key: "Name", value: environmentName } ] } ); new ec2.CfnVPCGatewayAttachment(this, "InternetGatewayAttachment", { internetGatewayId: internetGateway.ref, vpcId: vpc.ref }).addDependsOn(vpc); const publicSubnet1 = new ec2.CfnSubnet(this, "PublicSubnet1", { vpcId: vpc.ref, availabilityZone: Fn.select(0, Fn.getAzs(props.env!.region)), cidrBlock: publicSubnet1CIDR, mapPublicIpOnLaunch: true, tags: [ { key: "Name", value: `${environmentName} Public Subnet (AZ1)` } ] }); publicSubnet1.addDependsOn(vpc); const publicSubnet2 = new ec2.CfnSubnet(this, "PublicSubnet2", { vpcId: vpc.ref, availabilityZone: Fn.select(1, Fn.getAzs(props.env!.region)), cidrBlock: publicSubnet2CIDR, mapPublicIpOnLaunch: true, tags: [ { key: "Name", value: `${environmentName} Public Subnet (AZ1)` } ] }); publicSubnet2.addDependsOn(vpc); const privateSubnet1 = new ec2.CfnSubnet(this, "PrivateSubnet1", { vpcId: vpc.ref, availabilityZone: Fn.select(0, Fn.getAzs(props.env!.region)), cidrBlock: privateSubnet1CIDR, mapPublicIpOnLaunch: false, tags: [ { key: "Name", value: `${environmentName} Private Subnet (AZ1)` } ] }); privateSubnet1.addDependsOn(vpc); const privateSubnet2 = new ec2.CfnSubnet(this, "PrivateSubnet2", { vpcId: vpc.ref, availabilityZone: Fn.select(1, Fn.getAzs(props.env!.region)), cidrBlock: privateSubnet2CIDR, mapPublicIpOnLaunch: false, tags: [ { key: "Name", value: `${environmentName} Private Subnet (AZ2)` } ] }); privateSubnet2.addDependsOn(vpc); const natGateway1Eip = new ec2.CfnEIP(this, "NatGateway1EIP", { domain: "vpc" }); const natGateway2Eip = new ec2.CfnEIP(this, "NatGateway2EIP", { domain: "vpc" }); const natGateway1 = new ec2.CfnNatGateway(this, "NatGateway1", { allocationId: natGateway1Eip.attrAllocationId, subnetId: publicSubnet1.ref }); natGateway1.addDependsOn(natGateway1Eip); natGateway1.addDependsOn(publicSubnet1); const natGateway2 = new ec2.CfnNatGateway(this, "NatGateway2", { allocationId: natGateway2Eip.attrAllocationId, subnetId: publicSubnet2.ref }); natGateway2.addDependsOn(natGateway2Eip); natGateway2.addDependsOn(publicSubnet2); const publicRouteTable = new ec2.CfnRouteTable(this, "PublicRouteTable", { vpcId: vpc.ref, tags: [ { key: "Name", value: `${environmentName} Public Routes` } ] }); publicRouteTable.addDependsOn(vpc); const defaultPublicRoute = new ec2.CfnRoute(this, "DefaultPublicRoute", { routeTableId: publicRouteTable.ref, destinationCidrBlock: "0.0.0.0/0", gatewayId: internetGateway.ref }); defaultPublicRoute.addDependsOn(publicRouteTable); defaultPublicRoute.addDependsOn(internetGateway); const publicSubnet1RouteTableAssociation = new ec2.CfnSubnetRouteTableAssociation( this, "PublicSubnet1RouteTableAssociation", { routeTableId: publicRouteTable.ref, subnetId: publicSubnet1.ref } ); publicSubnet1RouteTableAssociation.addDependsOn(publicRouteTable); publicSubnet1RouteTableAssociation.addDependsOn(publicSubnet1); const publicSubnet2RouteTableAssociation = new ec2.CfnSubnetRouteTableAssociation( this, "PublicSubnet2RouteTableAssociation", { routeTableId: publicRouteTable.ref, subnetId: publicSubnet2.ref } ); publicSubnet2RouteTableAssociation.addDependsOn(publicRouteTable); publicSubnet2RouteTableAssociation.addDependsOn(publicSubnet2); const privateRouteTable1 = new ec2.CfnRouteTable( this, "PrivateRouteTable1", { vpcId: vpc.ref, tags: [ { key: "Name", value: `${environmentName} Private Routes (AZ1)` } ] } ); privateRouteTable1.addDependsOn(vpc); const defaultPrivateRoute1 = new ec2.CfnRoute( this, "DefaultPrivateRoute1", { routeTableId: privateRouteTable1.ref, destinationCidrBlock: "0.0.0.0/0", natGatewayId: natGateway1.ref } ); defaultPrivateRoute1.addDependsOn(privateRouteTable1); defaultPrivateRoute1.addDependsOn(natGateway1); const privateSubnet1RouteTableAssociation = new ec2.CfnSubnetRouteTableAssociation( this, "PrivateSubnet1RouteTableAssociation", { routeTableId: privateRouteTable1.ref, subnetId: privateSubnet1.ref } ); privateSubnet1RouteTableAssociation.addDependsOn(privateRouteTable1); privateSubnet1RouteTableAssociation.addDependsOn(privateSubnet1); const privateRouteTable2 = new ec2.CfnRouteTable( this, "PrivateRouteTable2", { vpcId: vpc.ref, tags: [ { key: "Name", value: `${environmentName} Private Routes (AZ2)` } ] } ); privateRouteTable2.addDependsOn(vpc); const defaultPrivateRoute2 = new ec2.CfnRoute( this, "DefaultPrivateRoute2", { routeTableId: privateRouteTable2.ref, destinationCidrBlock: "0.0.0.0/0", natGatewayId: natGateway2.ref } ); defaultPrivateRoute2.addDependsOn(privateRouteTable2); defaultPrivateRoute2.addDependsOn(natGateway2); const privateSubnet2RouteTableAssociation = new ec2.CfnSubnetRouteTableAssociation( this, "PrivateSubnet2RouteTableAssociation", { routeTableId: privateRouteTable2.ref, subnetId: privateSubnet2.ref } ); privateSubnet2RouteTableAssociation.addDependsOn(privateRouteTable2); privateSubnet2RouteTableAssociation.addDependsOn(privateSubnet2); new ec2.CfnSecurityGroup(this, "NoIngressSecurityGroup", { groupName: "no-ingress-sg", groupDescription: "Security group with no ingress rule", vpcId: vpc.ref }).addDependsOn(vpc); } }