AWS CDKで既存のacm証明書とroute53のゾーンを使ってカスタムドメインを当てたapigw&lambdaの作成

# 概要/前提
route53ですでに管理している `example.com` を使って、 `api.example.com` を割り当てたapiを作成する想定

httpapiなので**証明書はapigwと同リージョン**に用意されていること

ディレクトリ構造に合わせてhandlerの設定をしているのでそこはよしなに

アプリのソースはpython、cdkのソースはtsで記述

やってることはだいたい以下

- lambda作成
- route53のゾーン参照
- acmの証明書参照
- apigwのカスタムドメイン登録
- サブドメインのレコードを参照したゾーンに書き込み
- apigw作成 & lambda紐付け

# 環境変数

```:.env
HOSTED_ZONE_ID=xxxxxxxxxxxxxxxxx
HOSTED_ZONE_NAME=example.com
CERT_ARN=arn:aws:acm:[REGION]:[ID]:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
API_DOMAIN_NAME=api.example.com
```


# ディレクトリ構造

```
.
├── .env
├── .envrc
├── ...
└── src/
    ├── app/
    │   ├── pyproject.toml
    │   ├── poetry.lock
    │   └── handlers/
    │       └── hoge.py
    └── infra/
        ├── cdk.json
        ├── package.json
        ├── package-lock.json
        ├── tsconfig.json
        ├── bin/
        └── lib/
            └── hoge-stack.ts
```

# CDKソース

```typescript:lib/hoge-stack.ts
import * as cdk from "@aws-cdk/core";
import { CorsHttpMethod, HttpApi, HttpMethod, DomainName } from "@aws-cdk/aws-apigatewayv2";
import { LambdaProxyIntegration } from "@aws-cdk/aws-apigatewayv2-integrations";
import { Runtime } from "@aws-cdk/aws-lambda";
import { Role, ServicePrincipal, ManagedPolicy } from "@aws-cdk/aws-iam";
import * as path from "path";
import { PythonFunction } from "@aws-cdk/aws-lambda-python";
import { HostedZone, ARecord, RecordTarget } from "@aws-cdk/aws-route53";
import { Certificate } from "@aws-cdk/aws-certificatemanager";
import { ApiGatewayv2DomainProperties } from "@aws-cdk/aws-route53-targets";

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

    const lambdaExecutionRole = new Role(this, "LambdaExecutionRole", {
      roleName: "LambdaExecutionRole",
      assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
      managedPolicies: [ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaBasicExecutionRole")],
    });

    const handler = new PythonFunction(this, "Handler", {
      entry: path.resolve(path.join("", "../app")),
      index: "handlers/hoge.py",
      handler: "handler",
      memorySize: 128,
      runtime: Runtime.PYTHON_3_8,
      timeout: cdk.Duration.seconds(30),
      functionName: "Handler",
      environment: {},
      role: lambdaExecutionRole,
    });

    const integration = new LambdaProxyIntegration({
      handler,
    });

    const hostedZone = HostedZone.fromHostedZoneAttributes(this, "HostedZone", {
      hostedZoneId: process.env.HOSTED_ZONE_ID,
      zoneName: process.env.HOSTED_ZONE_NAME, // ゾーンで管理してるドメイン、e.g.) example.com
    });
    const certificate = Certificate.fromCertificateArn(
      this,
      "Certificate",
      process.env.CERT_ARN // arn:aws:acm:[REGION]:[ID]:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    );

    const domainName = new DomainName(this, "ApiDomainName", {
      domainName: process.env.API_DOMAIN_NAME,
      certificate,
    });

    const aRecord = new ARecord(this, "ARecord", {
      zone: hostedZone,
      recordName: process.env.API_DOMAIN_NAME, // 参照してるゾーンで管理していてこのapiに適用したいドメイン
      target: RecordTarget.fromAlias(
        new ApiGatewayv2DomainProperties(domainName.regionalDomainName, domainName.regionalHostedZoneId)
      ),
    });

    const api = new HttpApi(this, "ApiGateway", {
      defaultDomainMapping: {
        domainName,
      },
      apiName: "HOGEAPI",
      corsPreflight: {
        allowHeaders: ["*"],
        allowOrigins: ["*"],
        allowMethods: [
          CorsHttpMethod.HEAD,
          CorsHttpMethod.OPTIONS,
          CorsHttpMethod.GET,
          CorsHttpMethod.POST,
          CorsHttpMethod.PUT,
          CorsHttpMethod.DELETE,
        ],
      },
    });
    api.addRoutes({
      path: "/hoge",
      methods: [HttpMethod.GET],
      integration,
    });
  }
}

```