AWS CDKでCloudFormationのテンプレートをTypeScriptから生成しデプロイする

(2019-05-19)

AWS CDK(Cloud Development Kit)はTypeScriptやJavaなどのコードから CloudFormationのテンプレートを生成して差分を確認しデプロイできる公式のツール。まだdeveloper preview。

$ npm i -g aws-cdk
$ cdk --version
0.33.0 (build 50d71bf)

$ mkdir cdk-vpc
$ cd cdk-vpc
$ cdk init app --language=typescript

CloudFormationのリソースと対応するCfnFooや、それを内部で作成する高レベル(L2)のResource ClassFooが実装されている。 ただし、現状CfnFooに対応するResource Classが存在しないものや、複数のリソースを内部で作成するResource Classが存在する。 例えば、ec2.VpcCfnVPCだけではなく、Public/Private Subnet、NATGatewayまでまとめて一般的な構成で作る。Resource Classはまだ変更が多い。

CloudFormationでVPCを作成してLambdaをデプロイしAurora Serverlessを使う - sambaiz-net

型があり補完が効くので通常のテンプレートと比べて書きやすいし、ループしたりすることもできる。

補完

import * as cdk from '@aws-cdk/cdk'
import * as ec2 from '@aws-cdk/aws-ec2'

interface Export {
  vpc: ec2.Vpc
}

export class VPCStack extends cdk.Stack {
  protected deployEnv: string
  export: Export
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props)
    this.deployEnv = this.node.getContext('env')
    const vpc = this.vpc()

    this.export = {
      vpc: vpc
    }
  }

  private vpc() {
    let cidr: string | null
    if (this.deployEnv === 'stg') {
      cidr = '172.32.1.0/24'
    } else if (this.deployEnv === 'prd') {
      cidr = '172.32.2.0/24'
    } else {
      throw new Error(`unknown env ${this.deployEnv}`)
    }
    return new ec2.Vpc(this, `${this.deployEnv}-vpc`, {
      cidr: cidr,
      maxAZs: 2,
      subnetConfiguration: [
        {
          cidrMask: 26,
          subnetType: ec2.SubnetType.Public,
          name: `${this.deployEnv}-vpc-public`
        },
        {
          cidrMask: 26,
          subnetType: ec2.SubnetType.Private,
          name: `${this.deployEnv}-vpc-private`
        }
      ]
    })
  }
}

プロパティを渡せばRefやGetAttになる。Stack間のExportやImportValueも同様で、さらに依存関係を考慮した順序でStackを更新してくれる。 必要であればcdk.Fn.ImportValue()で外部からImportValueしたり、 cdk.CfnOutput()でOutputすることもできる。 Serverless FrameworkなどのCDK外StackからImportする際は、自動で作られたExportはCDK内で依存がなくなると消そうとしてしまうので、 明示的にExportしたものをImportした方が良さそう。

const vpcStackExport = new VPCStack(app, `${deployEnv}VPCStack`).export
new SomeAppStack(app, `${deployEnv}SomeAppStack`, {
    dbSubnetIds: vpcStackExport.vpc.privateSubnets.map(v => v.subnetId)
})

cdk synthでテンプレートを出力して確認できる。

$ npm run build
$ cdk synth -o ./outputs
./outputs/CdkVpcStack.template.yaml

cdk diffで作成/削除されるリソースを見て、

$ cdk diff
Stack CdkVpcStack
Resources
[+] AWS::EC2::VPC test-vpc testvpc8985080E 
[+] AWS::EC2::Subnet test-vpc/test-publicSubnet1/Subnet testvpctestpublicSubnet1SubnetF24995A1 
[+] AWS::EC2::RouteTable test-vpc/test-publicSubnet1/RouteTable testvpctestpublicSubnet1RouteTableF3DE8760 
[+] AWS::EC2::SubnetRouteTableAssociation test-vpc/test-publicSubnet1/RouteTableAssociation testvpctestpublicSubnet1RouteTableAssociation13140377 
[+] AWS::EC2::Route test-vpc/test-publicSubnet1/DefaultRoute testvpctestpublicSubnet1DefaultRoute06B168C8 
[+] AWS::EC2::EIP test-vpc/test-publicSubnet1/EIP testvpctestpublicSubnet1EIP5E036D64 
...

cdk deployでデプロイできる。

$ cdk deploy 
CdkVpcStack: deploying...
CdkVpcStack: creating CloudFormation changeset...
  0/25 | 20:59:21 | CREATE_IN_PROGRESS   | AWS::EC2::EIP                         | test-vpc/test-publicSubnet2/EIP (testvpctestpublicSubnet2EIPCB02013C) 
  0/25 | 20:59:21 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata                    | CDKMetadata 
  0/25 | 20:59:22 | CREATE_IN_PROGRESS   | AWS::EC2::InternetGateway             | test-vpc/IGW (testvpcIGW2C2BA83F) 
  0/25 | 20:59:22 | CREATE_IN_PROGRESS   | AWS::EC2::EIP                         | test-vpc/test-publicSubnet1/EIP (testvpctestpublicSubnet1EIP5E036D64) 
...

CDK/CircleCI/GitHubでAWSリソース管理リポジトリを作る - sambaiz-net