📝

CDK で Amplify アプリに WAF をアタッチしてみた

に公開

前提

  • CDK 実行環境は Cloud9
  • CDK の言語は TypeScript
  • CodeCommit リポジトリは作成済み
    • index.html のみプッシュ済み

事前準備

# TypeScript をインストール
$ mkdir -p ~/.npm-global
$ npm config set prefix '~/.npm-global'
$ echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
$ source ~/.bashrc
$ npm install -g typescript
$ mkdir amplify-waf-cdk
$ cd amplify-waf-cdk
$ cdk init app --language typescript

# 既存のスタックファイルを削除
$ rm lib/amplify-waf-cdk-stack.ts

# 新しいスタックファイルを作成
$ touch lib/amplify-stack.ts
$ touch lib/waf-stack.ts

コード

以下のようにコードを定義しました。

lib/amplify-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as amplify from 'aws-cdk-lib/aws-amplify';
import * as customResources from 'aws-cdk-lib/custom-resources';

export class AmplifyStack extends cdk.Stack {
  public readonly appArn: string;

  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const amplifyRole = new iam.Role(this, 'AmplifyServiceRole', {
      assumedBy: new iam.ServicePrincipal('amplify.amazonaws.com'),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess-Amplify')
      ]
    });

    const amplifyApp = new amplify.CfnApp(this, 'AmplifyApp', {
      name: 'cdk-app',
      repository: 'https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/test',
      iamServiceRole: amplifyRole.roleArn,
      enableBranchAutoDeletion: false
    });

    this.appArn = amplifyApp.attrArn;

    const amplifyBranch = new amplify.CfnBranch(this, 'AmplifyBranch', {
      appId: amplifyApp.attrAppId,
      branchName: 'main',
      stage: 'PRODUCTION',
      enablePullRequestPreview: false,
      enableAutoBuild: true,
      enablePerformanceMode: false
    });

    amplifyBranch.addDependency(amplifyApp);

    const deployTrigger = new customResources.AwsCustomResource(this, 'AmplifyDeployTrigger', {
      onCreate: {
        service: 'Amplify',
        action: 'startJob',
        parameters: {
          appId: amplifyApp.attrAppId,
          branchName: 'main',
          jobType: 'RELEASE'
        },
        physicalResourceId: customResources.PhysicalResourceId.of('AmplifyDeployTrigger'),
      },
      policy: customResources.AwsCustomResourcePolicy.fromSdkCalls({
        resources: customResources.AwsCustomResourcePolicy.ANY_RESOURCE
      })
    });
    
    deployTrigger.node.addDependency(amplifyBranch);
  }
}
lib/waf-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as wafv2 from 'aws-cdk-lib/aws-wafv2';

interface WafStackProps extends cdk.StackProps {
  appArn: string;
}

export class WafStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: WafStackProps) {
    super(scope, id, props);

    // WAF Web ACL を作成
    const webAcl = new wafv2.CfnWebACL(this, 'WebACL', {
      defaultAction: { allow: {} },
      scope: 'CLOUDFRONT',
      rules: [
      ],
      visibilityConfig: {
        cloudWatchMetricsEnabled: true,
        metricName: 'amplify-waf-metric',
        sampledRequestsEnabled: true,
      },
    });

    new wafv2.CfnWebACLAssociation(this, 'WebACLAssociation', {
      resourceArn: props.appArn,  // AmplifyStack から渡された appArn
      webAclArn: webAcl.attrArn,
    });
  }
}
lib/waf-stack.ts
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { AmplifyStack } from '../lib/amplify-stack';
import { WafStack } from '../lib/waf-stack';

const app = new cdk.App();

const amplifyStack = new AmplifyStack(app, 'AmplifyStack', {
  env: { 
    region: 'ap-northeast-1',
    account: process.env.CDK_DEFAULT_ACCOUNT 
  },
});

new WafStack(app, 'WafStack', {
  env: { 
    region: 'us-east-1',
    account: process.env.CDK_DEFAULT_ACCOUNT 
  },
  crossRegionReferences: true,
  appArn: amplifyStack.appArn,
});

デプロイ

$ cdk synth

# 東京リージョン (ap-northeast-1) をブートストラップ
cdk bootstrap aws://$(aws sts get-caller-identity --query Account --output text)/ap-northeast-1

# バージニア北部リージョン (us-east-1) をブートストラップ
cdk bootstrap aws://$(aws sts get-caller-identity --query Account --output text)/us-east-1

# デプロイ
$ cdk deploy

WAF のアタッチを確認

Amplify コンソールから対象のアプリに WAF がアタッチされていることを確認できれば OK です。

また、WAF コンソールからも対象のアプリに WAF がアタッチされていることを確認できます。

まとめ

今回は CDK で Amplify アプリに WAF をアタッチしてみました。
どなたかの参考になれば幸いです。

参考資料

Discussion