AWS CDKメモ
基本
cdk --version: 2.30
CDK:typescript
Lambda:Python3.9
cdk workshopをベースにして拡張する。
初期化
- プロジェクトのディレクトリを作成し、initを実行する
- ディレクトリ名が
cdk-workshop
ならスタック名がCdkWorkshop
になる -
lib/cdk-workshop-stack.ts
にcdkの本体を記述する
- ディレクトリ名が
cdk init app --language typescript
cdk.StackかStackか
この2つの書き方はimportの仕方の違いだけで、同じ。記述が混ざっている場合があるので注意
import * as cdk from 'aws-cdk-lib';
export class CdkWorkshopStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class CdkWorkshopStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
Lambda基本
const hello = new lambda.Function(this, 'HelloHandler', {
runtime: lambda.Runtime.PYTHON_3_9,
code: lambda.Code.fromAsset('lambda'),
handler: 'my_lambda.handler',
environment: {'SNS_ARN': myTopic.topicArn,},
retryAttempts: 0,
timeout: cdk.Duration.seconds(60),
});
- ts内では
hello
でアクセス - LogicalIDは
HelloHandler
にハッシュを追加したものになる -
handler: my_lambda.handler
の内容を、lambda/my_lambda.py
に記載する-
lambda/
はlib/
と同じ階層
-
- ロールを指定しない場合は自動で作成されるが、自分で設定する場合は
myRole
を作ってrole: myRole
で設定する -
environment: {Key:Value}
でLambdaの環境変数を設定する。ここでは myTopicをいうSNSトピックのArnにアクセスしている - リトライ0に
- タイムアウトの設定 Durationを個別にインポートしてもよい
ポリシー追加
- ポリシーをhelloのRoleに追加する
- ポリシーを作るとき、hello function arnを参照するとCircular dependencyエラーになってしまう TODO
const snsTopicPolicy = new iam.PolicyStatement({
actions: ['sns:*'],
// resources: [hello.functionArn],
resources: ['*'],
});
hello.addToRolePolicy(snsTopicPolicy);
マネージドポリシーを追加するとき
// hello.addToRoleManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaBasicExecutionRole"));
// myRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaBasicExecutionRole"));
Roleを自分で作ってLambdaに設定する
const myRole = new iam.Role(this, 'My Role', {
assumedBy: new iam.ServicePrincipal('sns.amazonaws.com'),
});
lambda.Function
のプロパティ設定に
handler: 'my_lambda.handler',
role: myRole,
のように追加する(handler:
と同じ列)
タイムアウト。デフォルトは3秒?設定するならcdk.Durationを使う。
timeout: cdk.Duration.seconds(3)
${accountID}とかpseudo parametersを使いたい
- ScopedAwsをインポート
-
const { accountId, region } = new ScopedAws(this);
で定義 -
${region}:${accountId}
などを使うときは、バッククオートで囲む
import { ScopedAws } from 'aws-cdk-lib';
export class CdkStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const { accountId, region } = new ScopedAws(this);
const lambdaPolicy = new iam.PolicyStatement({
actions: ['*'],
resources: [`arn:aws:cloudformation:${region}:${accountId}:stack/Cdk*/*`],
});
環境変数を.envファイルから読んでCDKで使う
- ローカルの環境変数から読むこともできるし、.envから読むこともできる。ただし、ローカル環境変数の場合は、cdk synthで読んでくれない。
- .gitignore に .env を追加することで、環境変数を git 管理外にする
-
dotenvをインストール
npm install dotenv --save
- .envファイルをプロジェクトのルートに作り、環境変数を記載する
SNS_ARN=arn:aws:snsxxxxxxxxxxxxxxxxxxxxx
呼び出し方
import * as dotenv from 'dotenv';
dotenv.config();
を記述してから
process.env.SNS_ARN
で呼び出す。
SNSトピックを作成
lib/cdk-workshop-stack.ts
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions';
const myTopic = new sns.Topic(this, 'MyTopic');
emailサブスクライブ
ハードコードで
myTopic.addSubscription(new subscriptions.EmailSubscription('your@mail'));
Contextを使う
- cdk deploy, cdk synth, で
--context email=your@mail
を追加
const email = this.node.tryGetContext('email');
myTopic.addSubscription(new subscriptions.EmailSubscription(email));
既存のSNSトピックを使う
ハードコード
const topic = sns.Topic.fromTopicArn(this, 'MyTopic', <topic-arn>);
ローカル環境変数を読む
(設定は別記)
const myTopic = sns.Topic.fromTopicArn(this, 'MyTopic', process.env.SNS_ARN ?? '');
const Email = process.env.MAIL ?? '';
const Topic = new sns.CfnTopic(this, 'Topic', {
subscription: [{
endpoint: Email,
protocol: 'email',
}],
});
CDKで、CloudFormationのParameter
CDKでは非推奨になっている。(Contextが推奨)
const emailAddress = new cdk.CfnParameter(this, 'email-param');
と書いておく。
デプロイするときは、
cdk deploy --parameters emailparam=aaaa@example.com
のようにする。
複数のパラメータがあるときはコンマでつなぐ
--parameters emailparam=aaaa@example.com,other_pram=1
LogicalIDではハイフンが削除されてemail-param が emailparamになることに注意。
cdk synthをするとyamlではこのようになる。
Parameters:
emailparam:
Type: String
プロパティ追加できるdoc
プロパティ追加するとsynthはできるけど、deployでエラーになる。
const emailAddress = new cdk.CfnParameter(this, 'email-param', {
type: 'string',
description: 'Some description',
default: 'default@mail'
});
こちらはOK
cdk deploy --parameters emailparam=aaaa@example.com
const email = new CfnParameter(this, 'email-param');
myTopic.addSubscription(new subscriptions.EmailSubscription(email.valueAsString));
Outputで変数確認
helloFnというLambda、emailというcontextがあるとする。
new cdk.CfnOutput(this, 'Output_1', { value: helloFn.functionArn });
new cdk.CfnOutput(this, 'Output_2', { value: email });
cdk synth --context email=aaaa@example.com
とすると、こういうOutputsが出る。
Outputs:
Output1:
Value:
Fn::GetAtt:
- HelloHandlerXXXXXXXX
- Arn
Output2:
Value: aaaa@example.com
?? ''
(よくわかっていない)
const myTopic = sns.Topic.fromTopicArn(this, 'MyTopic', process.env.SNS_ARN ?? '');
の ?? ''
に関して。これがないとa
Argument of type string | undefined is not assignable to parameter of type string というエラーが出る。
stringが存在しないときstringのところにundefiendが渡されてしまってエラーになる。undefiend のとき空白としている。
Circular dependencyエラーを回避する
CDKでそのとき作る関数のarn を指定するとエラーになるので、これを回避する。
ポリシーを作るときに、その関数だけにresourcesを限定したいとき。
const helloFn = new lambda.Function(this, 'HelloHandler', {
runtime: lambda.Runtime.PYTHON_3_9,
code: lambda.Code.fromAsset('lambda'),
handler: 'my_lambda.handler',
environment: {'SNS_ARN': myTopic.topicArn,},
});
helloFn.role!.addToPrincipalPolicy(new iam.PolicyStatement({
actions: [ 'sns:publish' ],
resources: [ this.formatArn({
service: 'lambda',
resource: 'function',
resourceName: 'HelloHandler',
}) ],
}));
ここに載っていた
既存CDKを開始するとき
- git clone などでダウンロード
- プロジェクトのルートディレクトリに入る
初期化
npm install
cdk bootstrap
Misc memo
- cdk deployを途中で止めるのは?Terminalで止めてもAWS上では続行されているので、AWS上で止めるかCLIでCfnの生成を止められる?
- ディレクトリ上でスタックがすでに生成されているか調べるには??Stack名を探す?TODO
- Cfnコンソールでスタックを消しても同じ??たぶん同じTODO
EventBridegeでLambdaを呼ぶ
- my_lambda handlerを毎分呼ぶ
- rule.addTarget(new targets.LambdaFunction(fn));で設定している
- Roleは自動でCDKがやってくれる。
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class Test01Stack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const fn = new lambda.Function(this, 'HelloHandler', {
runtime: lambda.Runtime.PYTHON_3_9,
code: lambda.Code.fromAsset('lambda'),
handler: 'my_lambda.handler',
environment: {'StackName': Test01Stack.name,},
});
const rule = new events.Rule(this, 'Schedule Rule', {
schedule: events.Schedule.cron({ minute: '*', hour: '*' }),
});
rule.addTarget(new targets.LambdaFunction(fn));
}
}
stack 名を変数として使う
const stack = cdk.Stack.of(this);
全てのリソースにtagを入れる
Tags.of(scope).add
でOK。IAM Roleとかにも一括で設定できる。
import { Tags } from 'aws-cdk-lib';
export class CdkStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
Tags.of(scope).add("my_key", "yes");
Tags.of(SCOPE).add() applies a new tag to the given construct and all of its children.
Tags.of(SCOPE).remove() removes a tag from the given construct and any of its children, including tags a child construct may have applied to itself.
一見意味不明なエラー
cdk synth
とかcdk deploy
で --context xxx=xxx
を忘れていたらこのエラー。一見何かわかりませんよね。
/node_modules/constructs/src/construct.ts:536
return id.replace(PATH_SEP_REGEX, '--');
^
TypeError: Cannot read properties of undefined (reading 'replace')
at sanitizeId (xxx/node_modules/constructs/src/construct.ts:536:13)
at Node.tryFindChild (xxx/node_modules/constructs/src/construct.ts:114:27)
at Topic.addSubscription (xxx/node_modules/aws-cdk-lib/aws-sns/lib/topic-base.js:1:1218)
at new CdkApprovalMultiStepsStack (xxx/lib/cdk-approval-multi-steps-stack.ts:20:32)
at Object.<anonymous> (xxx/bin/cdk-approval-multi-steps.ts:7:1)
at Module._compile (node:internal/modules/cjs/loader:1112:14)
at Module.m._compile (xxx/node_modules/ts-node/src/index.ts:1618:23)
at Module._extensions..js (node:internal/modules/cjs/loader:1166:10)
at Object.require.extensions.<computed> [as .ts] (xxx/node_modules/ts-node/src/index.ts:1621:12)
at Module.load (node:internal/modules/cjs/loader:988:32)
Subprocess exited with error 1
Discussion