この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
吉川です。
早速ですが、AppSyncを使う上でよく必要になると思われる設定をCDKで行ってみたので紹介します。
- ログをCloudWatch Logsに出力
- AWS WAFの設定と紐付け
- X-Ray設定
上記を行っていきます。
初期コード
下記のStack定義CDKコードを用意します。Lambda関数をDataSourceとするシンプルなAppSyncAPIを定義しました。
lib/appsync-stack.ts
import { Construct } from 'constructs'
import * as cdk from 'aws-cdk-lib'
import * as appsync from '@aws-cdk/aws-appsync-alpha'
import * as lambdaNodejs from 'aws-cdk-lib/aws-lambda-nodejs'
export class AppSyncStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props)
// AppSyncAPIを作成
const api = new appsync.GraphqlApi(this, 'myAppsyncApi', {
name: 'myAppsyncApi',
schema: appsync.Schema.fromAsset('./schema.graphql'),
authorizationConfig: {
defaultAuthorization: {
authorizationType: appsync.AuthorizationType.API_KEY,
},
},
})
// Lambda関数を作成
const findUserFn = new lambdaNodejs.NodejsFunction(this, 'findUserFn', {
entry: 'lambda-handler/find-user-handler.ts',
})
// Lambda関数をDataSourceとしてAppSyncAPIと紐付ける
const findUserDs = api.addLambdaDataSource('findUserDs', findUserFn)
// schema.graphqlで定義した中のどの操作とマッピングするかを指定
findUserDs.createResolver({
typeName: 'Query',
fieldName: 'findUser',
})
}
}
Stack定義以外のファイルは以下です。
bin/cdk.ts
#!/usr/bin/env node
import 'source-map-support/register'
import * as cdk from 'aws-cdk-lib'
import { AppSyncStack } from '../lib/appsync-stack'
const app = new cdk.App()
new AppSyncStack(app, 'AppSyncStack', {
env: { region: 'ap-northeast-1' },
})
lambda-handler/find-user-handler.ts
import { AppSyncResolverEvent } from 'aws-lambda'
export const handler = async (event: AppSyncResolverEvent<{}, {}>) => {
console.log(JSON.stringify({ event }))
return {
id: '1',
name: 'John Doe',
}
}
schema.graphql
type User {
id: String!
name: String!
}
type Query {
findUser: User!
}
ログをCloudWatch Logsに出力する
まずはAppSyncログをCloudWatch Logsに出力する設定を追加します。
lib/appsync-stack.ts
// ログ出力のためにAppSyncにアタッチするIAM Role作成
const apiLogRole = new iam.Role(this, 'apiLogRole', {
assumedBy: new iam.ServicePrincipal('appsync.amazonaws.com'),
path: '/service-role/',
})
apiLogRole.attachInlinePolicy(
new iam.Policy(this, 'apiLogRolePolicy', {
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
'logs:CreateLogGroup',
'logs:CreateLogStream',
'logs:PutLogEvents',
],
resources: [`arn:aws:logs:${this.region}:${this.account}:*`],
}),
],
})
)
// AppSyncAPIを作成
const api = new appsync.GraphqlApi(this, 'myAppsyncApi', {
name: 'myAppsyncApi',
schema: appsync.Schema.fromAsset('./schema.graphql'),
logConfig: {
role: apiLogRole,
fieldLogLevel: appsync.FieldLogLevel.ERROR,
},
authorizationConfig: {
defaultAuthorization: {
authorizationType: appsync.AuthorizationType.API_KEY,
},
},
})
ログ出力用のIAMロールを作成し、 logConfig
にロールとエラーレベルを渡すことで設定できました。
AWS WAFの設定と紐付け
続いてAWS WAFのWebACLを作成し、それをAppSyncに紐付けます。
[AWS CDK] WAF v2のAWSマネージドルールをCloudFrontに適用する | DevelopersIO
こちらを参考にして
- AWSManagedRulesCommonRuleSet
- AWSManagedRulesAdminProtectionRuleSet
- AWSManagedRulesKnownBadInputsRuleSet
- AWSManagedRulesAmazonIpReputationList
- AWSManagedRulesAnonymousIpList
をルールとして設定しました。
lib/appsync-stack.ts
// AppSync用のWebACLを作成
const apiWebAcl = new wafV2.CfnWebACL(this, 'wafV2WebAcl', {
defaultAction: { allow: {} },
// 'CLOUDFRONT' or 'REGIONAL'
// AppSyncは'REGIONAL'を選択
scope: 'REGIONAL',
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'wafV2WebAclMetric',
},
rules: [
{
name: 'AWSManagedRulesCommonRuleSet',
priority: 1,
statement: {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesCommonRuleSet',
},
},
overrideAction: { none: {} },
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'AWSManagedRulesCommonRuleSet',
},
},
{
name: 'AWSManagedRulesAdminProtectionRuleSet',
priority: 2,
statement: {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesAdminProtectionRuleSet',
},
},
overrideAction: { none: {} },
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'AWSManagedRulesAdminProtectionRuleSet',
},
},
{
name: 'AWSManagedRulesKnownBadInputsRuleSet',
priority: 3,
statement: {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesKnownBadInputsRuleSet',
},
},
overrideAction: { none: {} },
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'AWSManagedRulesKnownBadInputsRuleSet',
},
},
{
name: 'AWSManagedRulesAmazonIpReputationList',
priority: 4,
statement: {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesAmazonIpReputationList',
},
},
overrideAction: { none: {} },
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'AWSManagedRulesAmazonIpReputationList',
},
},
{
name: 'AWSManagedRulesAnonymousIpList',
priority: 5,
statement: {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesAnonymousIpList',
},
},
overrideAction: { none: {} },
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'AWSManagedRulesAnonymousIpList',
},
},
],
})
// AppSyncとWebACLを紐付ける
const webAclAssociation = new wafV2.CfnWebACLAssociation(this, 'webAclAssociation', {
resourceArn: api.arn,
webAclArn: apiWebAcl.attrArn,
})
// 本リソースはAppSyncとWebACLより後に設定されなければならないため、DependsOnに両者を設定
webAclAssociation.addDependsOn(apiWebAcl)
webAclAssociation.addDependsOn(api.node.defaultChild as cdk.CfnResource)
X-Ray設定
最後にX-Rayを有効化します。これは xrayEnabled: true
を指定するだけで実現できます。
lib/appsync-stack.ts
// AppSyncAPIを作成
const api = new appsync.GraphqlApi(this, 'myAppsyncApi', {
name: 'myAppsyncApi',
schema: appsync.Schema.fromAsset('./schema.graphql'),
logConfig: {
role: apiLogRole,
fieldLogLevel: appsync.FieldLogLevel.ERROR,
},
authorizationConfig: {
defaultAuthorization: {
authorizationType: appsync.AuthorizationType.API_KEY,
},
},
xrayEnabled: true,
})
完成コード
lib/appsync-stack.ts
import { Construct } from 'constructs'
import * as cdk from 'aws-cdk-lib'
import * as appsync from '@aws-cdk/aws-appsync-alpha'
import * as lambdaNodejs from 'aws-cdk-lib/aws-lambda-nodejs'
import * as logs from 'aws-cdk-lib/aws-logs'
import * as iam from 'aws-cdk-lib/aws-iam'
import * as wafV2 from 'aws-cdk-lib/aws-wafv2'
export class AppSyncStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props)
// ログ出力のためにAppSyncにアタッチするIAM Role作成
const apiLogRole = new iam.Role(this, 'apiLogRole', {
assumedBy: new iam.ServicePrincipal('appsync.amazonaws.com'),
path: '/service-role/',
})
apiLogRole.attachInlinePolicy(
new iam.Policy(this, 'apiLogRolePolicy', {
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
'logs:CreateLogGroup',
'logs:CreateLogStream',
'logs:PutLogEvents',
],
resources: [`arn:aws:logs:${this.region}:${this.account}:*`],
}),
],
})
)
// AppSyncAPIを作成
const api = new appsync.GraphqlApi(this, 'myAppsyncApi', {
name: 'myAppsyncApi',
schema: appsync.Schema.fromAsset('./schema.graphql'),
logConfig: {
role: apiLogRole,
fieldLogLevel: appsync.FieldLogLevel.ERROR,
},
authorizationConfig: {
defaultAuthorization: {
authorizationType: appsync.AuthorizationType.API_KEY,
},
},
xrayEnabled: true,
})
// AppSync用のCloudWatch LogGroup作成
new logs.LogGroup(this, 'apiLogGroup', {
logGroupName: `/aws/appsync/apis/${api.apiId}`,
})
// AppSync用のWebACLを作成
const apiWebAcl = new wafV2.CfnWebACL(this, 'wafV2WebAcl', {
defaultAction: { allow: {} },
// 'CLOUDFRONT' or 'REGIONAL'
// AppSyncは'REGIONAL'を選択
scope: 'REGIONAL',
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'wafV2WebAclMetric',
},
rules: [
{
name: 'AWSManagedRulesCommonRuleSet',
priority: 1,
statement: {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesCommonRuleSet',
},
},
overrideAction: { none: {} },
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'AWSManagedRulesCommonRuleSet',
},
},
{
name: 'AWSManagedRulesAdminProtectionRuleSet',
priority: 2,
statement: {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesAdminProtectionRuleSet',
},
},
overrideAction: { none: {} },
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'AWSManagedRulesAdminProtectionRuleSet',
},
},
{
name: 'AWSManagedRulesKnownBadInputsRuleSet',
priority: 3,
statement: {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesKnownBadInputsRuleSet',
},
},
overrideAction: { none: {} },
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'AWSManagedRulesKnownBadInputsRuleSet',
},
},
{
name: 'AWSManagedRulesAmazonIpReputationList',
priority: 4,
statement: {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesAmazonIpReputationList',
},
},
overrideAction: { none: {} },
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'AWSManagedRulesAmazonIpReputationList',
},
},
{
name: 'AWSManagedRulesAnonymousIpList',
priority: 5,
statement: {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesAnonymousIpList',
},
},
overrideAction: { none: {} },
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'AWSManagedRulesAnonymousIpList',
},
},
],
})
// AppSyncとWebACLを紐付ける
const webAclAssociation = new wafV2.CfnWebACLAssociation(this, 'webAclAssociation', {
resourceArn: api.arn,
webAclArn: apiWebAcl.attrArn,
})
// 本リソースはAppSyncとWebACLより後に設定されなければならないため、DependsOnに両者を設定
webAclAssociation.addDependsOn(apiWebAcl)
webAclAssociation.addDependsOn(api.node.defaultChild as cdk.CfnResource)
// Lambda関数を作成
const findUserFn = new lambdaNodejs.NodejsFunction(this, 'findUserFn', {
entry: 'lambda-handler/find-user-handler.ts',
})
// Lambda関数をDataSourceとしてAppSyncAPIと紐付ける
const findUserDs = api.addLambdaDataSource('findUserDs', findUserFn)
// schema.graphqlで定義した中のどの操作とマッピングするかを指定
findUserDs.createResolver({
typeName: 'Query',
fieldName: 'findUser',
})
}
}
確認
npx cdk deploy
でデプロイし、作成されたAppSyncリソースの設定をマネジメントコンソールで開くと、
このようにひと通りの設定がオンになっていることを確認できました。