IP制限がかかっているサーバーにLambdaからアクセスする必要がある場合など、LambdaのIP固定化が必要な際にAWS CDK v2でどのように実装するかを記載します。
構成
VPCを作成し、その中でLambdaとEIPを紐付けてIPを固定化し、インターネットにアクセスする構成です。
Lambdaの中身はTypescriptで実装し、axiosを利用してAPIを実行します。
環境
cdk --version
2.21.1 (build a6ee543)
node_modules/.bin/tsc --v
Version 4.8.3
完成系
import { Duration, Stack, StackProps } from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as nodejs from "aws-cdk-lib/aws-lambda-nodejs";
import { Construct } from "constructs";
export class VpcLambdaStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
// VPCの作成
const vpc = new ec2.Vpc(this, "vpc", {
maxAzs: 2,
subnetConfiguration: [
{
cidrMask: 24,
name: "PublicSubnet",
subnetType: ec2.SubnetType.PUBLIC,
},
{
cidrMask: 24,
name: "PrivateSubnet",
subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
},
],
});
// セキュリティグループの作成
const securityGroup = new ec2.SecurityGroup(this, "SecurityGroup", {
vpc: vpc,
allowAllOutbound: true,
});
// Lambdaの作成
new nodejs.NodejsFunction(this, "VpcLambdaFunction", {
entry: "src/api-test/handler/handler.ts",
runtime: lambda.Runtime.NODEJS_16_X,
timeout: Duration.seconds(10),
vpc: vpc,
securityGroups: [securityGroup],
vpcSubnets: vpc.selectSubnets({
subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
}),
allowPublicSubnet: true,
});
}
}
VPC
まずは基盤となるVPCを作成します(公式リファレンスはこちら)。
このVPCを作成する際にサブネット
やEIP
などの関連するリソースも一緒に作成できます。
ここではデフォルトのリソース以外に、オプションを利用することで2つのアベイラビリティゾーン
に対してパプリックサブネット
とプライベートサブネット
をそれぞれ作成しています。
const vpc = new ec2.Vpc(this, "vpc", {
maxAzs: 2,
subnetConfiguration: [
{
cidrMask: 24,
name: "PublicSubnet",
subnetType: ec2.SubnetType.PUBLIC,
},
{
cidrMask: 24,
name: "PrivateSubnet",
subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
},
],
});
Security Group
次にLambdaに紐づけるセキュリティーグループを作成します(公式リファレンスはこちら)。
セキュリティーグループのvpc
には先ほど作成したものを指定します。
// セキュリティグループの作成
const securityGroup = new ec2.SecurityGroup(this, "SecurityGroup", {
vpc: vpc,
allowAllOutbound: true,
});
Lambda
最後にVPCに配置するLambdaを作成します(公式リファレンスはこちら)。
securityGroups
には先ほど作成したものを、vpcSubnets
にはvpcのselectSubnets
メソッドによってsubnetTypeがPRIVATE_WITH_NAT
になっているものを渡します。
Lambda関数はインターネットにアクセスするため、allowPublicSubnet
をtrue
にする設定も必要です。
// Lambdaの作成
new nodejs.NodejsFunction(this, "VpcLambdaFunction", {
entry: "src/api-test/handler/handler.ts",
runtime: lambda.Runtime.NODEJS_16_X,
timeout: Duration.seconds(10),
vpc: vpc,
securityGroups: [securityGroup],
vpcSubnets: vpc.selectSubnets({
subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
}),
allowPublicSubnet: true,
});
}
固定化されたIPの確認
作成されたEIP
はAWSコンソールのEC2 -> ネットワーク&セキュリティ -> Elastic IP
から確認できます。
今回はmaxAzs
を2に指定しているため、EIP
も2つ作成されます。アクセス制限のあるサーバーなどにアクセスする場合はこちらのIPアドレスに対する許可が必要です。
※注意:デフォルトでは、すべてのAWSアカウントで1リージョンあたり5つのElastic IPアドレスまでしか作成できない制限があります。参考:Elastic IP アドレス
まとめ
LambdaのIPを固定化させるにはVPCを細かく設定しないといけないと思いきや、必要なリソースはVPCが良い感じに作成してくれるため、意外と簡単に実装が可能なことがわかりました。
ただし、1アカウントに作成できるEIPは5個までの制限があるため、複数環境を利用する場合は環境毎にアカウントを分けるなどの対応が必要かと思います。
IP制限のあるアクセスは何かと多いと思うので、この記事が参考になれば幸いです。
参考資料
【AWS/Lambda】lambdaに固定IPをつける/VPCに所属させる
AWS CDKを使ってLambda 関数URL(Function URLs)を設定してみた。(L1 Constractで)
おまけ
Lambdaの中身の実装
Lambdaの中身はTypeScriptで実装し、axiosを利用してAPIを実行します。
以下のサンプルでは簡単なPOSTリクエストを送信できます。
import { APIGatewayProxyEvent } from "aws-lambda";
import axios from "axios";
import * as log from "lambda-log";
/**
* apiを実行するLambda
* @param event
* @returns
*/
export const handler = async (event: APIGatewayProxyEvent) => {
log.info("event row data", { event });
const endpoint = "https://example.com/";
const headers = {
"Content-Type": "application/json",
};
const body = {
id: "XXXXXXXXXX",
};
// APIを実行
try {
return await axios
.post(endpoint, body, {
headers: headers,
})
.then((response) => {
log.info("raw response data", { data: response.data });
})
.catch(async (reason) => {
log.error("Executing api failed.", { reason });
throw reason;
});
} catch (e) {
log.error(`Error executing api, reason=${(e as Error).message}`);
}
};