CDK-CfnXXX系のLambda作成方法について

### まず初めに

主にjavaとPHPをメインにプログラミングをしている一般人です。
自身初めてCDKを用いて設計・実装しています。(勉強中の為誤っている場合は教えていただきますと幸いです。)
少しでも誰かの役に立てば幸いです。

### 対象者
- AWSを学び始めた人
- CDKを学び始めた人
- CDKを用いてLambdaを利用したい人


### まず最初にCDKとは？
> AWS Cloud Development Kit (AWS CDK) は、使い慣れたプログラミング言語を使用してクラウドアプリケーションリソースを定義するためのオープンソースのソフトウェア開発フレームワークです。

とあるように、JavaScript、TypeScript、Python、Java、C#等で記述することが可能です。
開発環境の複製も簡単にできるというメリットもあります。

[詳しくはこちら](https://aws.amazon.com/jp/cdk/faqs/)


### この記事で説明すること
- CfnFunctionを使用したLambda設定方法
- policyDocumentの設定方法
- roleの設定方法
- security groupの設定方法
- Lambdaのソースコード指定方法で`ZipFile`を使用した方法

### この記事で説明しないこと
- ApiGatewayの作成・設定方法
- VPCの作成・設定方法
- applicationLog作成・設定方法
- Lambdaのソースコード指定方法で`S3 Bucket`, `S3 Key`を使用した方法

### CDKによるLambda作成
最小単位でロールバックができるようにある程度の粒度でスタックを切り分けています。

今回だとLambdaのみ作成します。

CfnXXX系を使用してLambdaを作成します。
(今回は[aws_lambda.Funcntion](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Function.html)よりも[aws_lambda.CfnFunction](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.CfnFunction.html)を使用します。)


```Typescript
import {
    aws_accessanalyzer,
    aws_iam,
    aws_lambda as lambda,
    CfnOutput,
    RemovalPolicy,
    Stack,
    StackProps
} from 'aws-cdk-lib';
import { Env } from './env';


export interface Props extends StackProps {
    readonly stackEnv: Env;
    readonly vpcId: string;
    readonly userPoolId: string;
    readonly userPoolArn: string;
    readonly clientId: string;
    readonly subnetIds: string[];
    readonly userName: string;
}

export class MyLambdaStack extends Stack {

    public readonly myLambda: CfnFunction;
    private readonly removalPolicy: RemovalPolicy;
    private securityGroup: SecurityGroup;
    private lambdaApplicationLog: CfnLogGroup;

    constructor(scope: Construct, id: string, props: Props) {
        super(scope, id, props);

        this.removalPolicy = props.stackEnv == Env.STAGE ? RemovalPolicy.RETAIN : RemovalPolicy.DESTROY

        // policyDocumentを作成
        const lambdaCognitoPolicyDocument = new aws_iam.PolicyDocument({
            statements: [new aws_iam.PolicyStatement({
                // 許可したいアクションを設定してください
                actions: [''],
                // 許可するサービスの情報と連携してください
                resources: [``]
            })]
        });

        // roleを作成
        const myLambdaRole = new aws_iam.Role(this, 'myLambdaRole', {
            roleName: `MyLambdaRole`,
            assumedBy: new aws_iam.ServicePrincipal('lambda.amazonaws.com'),
            // 上記で作成したpolicyDocumentと紐付ける
            inlinePolicies: {
                [`my-lambda-integration-policy`]: lambdaCognitoPolicyDocument
            },
            // 関数実行に必要な設定
            managedPolicies: [
                // 仕様により適切な管理ポリシーを設定 (複数選択可)
                ManagedPolicy.fromAwsManagedPolicyName(''),
                ManagedPolicy.fromAwsManagedPolicyName('')
            ]
        });

        // vpcとのペアリング設定
        const vpc = Vpc.fromLookup(this, "Vpc", {
            vpcId: props.vpcId,
        });

        // security groupを作成
        this. securityGroup = new SecurityGroup(this, 'mySecurityGroup',{
            description: `my lambda sg`,
            securityGroupName: `my-lambda-sg`,
            vpc: vpc
        });

        // Lambda関数とSecurityGroupIds, subnetIdsとのペアリング設定
        const vpcConfig : lambda.CfnFunction.VpcConfigProperty = {
            securityGroupIds: [this.securityGroup.securityGroupId],
            subnetIds: props.subnetIds,
        };

        // Lambda関数の作成
        this.lambda = new lambda.CfnFunction(this, 'MyCfnFunction', {
            // Lambda内で使用する定数を設定する
            environment: {
                variables: {
                    // サンプル： 外部からpropsとして受け取った値をLambdaの実装に受け渡す
                    USER_NAME: props.userName
                }
            },
            code: 
                // zipFileを使用する場合、Lambdaの実装部分を外部ファイルに切り出すことが出来ないため注意が必要　
                zipFile: `
                exports.handler = async (event) => {
                    const response = {
                        statusCode: 200,
                        body: JSON.stringify(process.env.USER_NAME),
                    };
                    return response;
                };
                `
            },
            vpcConfig: vpcConfig,
            role: myLambdaRole.roleArn,
            description: `my-lambda-function`,
            // Runtimeの設定
            runtime: 'nodejs16.x',
            // 今回はデフォルトのハンドラーを指定
            handler: 'index.handler'
        });
        this.lambda.applyRemovalPolicy(this.removalPolicy);

        // 外部参照させたいパラメータをエクスポートする設定
        new CfnOutput(this, 'lambdastackArnOutput', {
            value: this.stackId,
            exportName: `lambdaStackArn`,
        });

        // 外部参照させたいパラメータをエクスポートする設定
        new CfnOutput(this, 'lambdaFunctionArnOutput', {
            value: this.lambda.attrArn,
            exportName: `lambdaFunctionArn`,
        });

    }
}

```

### 作成時に気をつけた点

- CfnFunction -> environment の設定
    - 環境変数を Lambda関数に渡します。
    - `environment`オブジェクトは、key,valueで定義します。
    - AWSのコンソール画面から環境変数の内容がわかるので安心ですね。

- CfnFunction -> code -> zipFile
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html

    - 上記にもあるようにソースコードの指定方法は２パターンあります。
        - `S3Bucket`と`S3Key`を使用する方法 (今回は触れません。)
        - `ZipFile`を使用する方法

    - この記事では後者の`ZipFile`を使用する方法で作成しています。
        - 注意点：
            - 作成したCfnFunction内にインラインとして書き込むことになる
            - ソースコードが見にくい

            
            などなどありますが
            - メリットとしてはCfnからCDKの移行などで設定項目が明示的で移行しやすい
            - S3 Bucket等を用意しなくても安易にLambdaの実装コードを反映できる
            - 上記にならいS3 Bucket等の構築や設定をしなくていいこと
            - Lambdaの実装コードが軽量なものや変更回数が比較的少ないもので一目で把握できる(AuthLambda周り)(これはメリットなのか...)

    CfnLambdaを使ってみた感想ですが、CfnからCDKへの移行などで設定値を明示的に移行できるのはメリットかなとお思いました。