DEV Community

k.goto for AWS Community Builders

Posted on

Build TypeScript Managed App Runner with CDK

Overview

In my last article, I wrote about building an AWS App Runner with the Go language as the Managed Runtime of choice with the AWS CDK for Go.

(By the way, Managed Runtime is a convenient way to trigger (or manually deploy) a push to GitHub without the need for a Dockerfile or ECR.)

So this time, since I'm so excited, I also tried to create an AWS App Runner with TypeScript as the managed runtime of choice, using the AWS CDK for TypeScript.

Also, as of June 2023, the L2 Construct of App Runner for CDK is currently in alpha version, but I tried building it both ways "L1 Construct" and "alpha version of L2 Construct".


Assumptions

This time, the structure, writing style, and resource definitions of the CDK stack are almost the same as those in Go (as much as possible, from the constructor to the parameters).

For this reason, this article will not go into much detail, but for a more detailed explanation, please refer to the above article on the Go version.

It is also interesting to compare how CDK is written differently between TypeScript and Go, so if you are interested, please take a look.


GitHub

All code is available on GitHub.


General Description

I do the following

  • Create AutoScalingConfiguration (scaling configuration) with Custom Resource Lambda.
    • Creating AutoScalingConfiguration with Lambda instead of AwsCustomResource in CDK module, so not only creating AutoScalingConfiguration but also updating and deleting.
  • App Runner instance role creation
  • AppRunner Service creation
    • Create both L1 and L2(alpha).
    • Auto Deploy: On
  • VPC Connector creation
    • Create both L1 and L2(alpha).
  • Get GitHub connection ARN with AWS SDK
    • Prerequisite for pre-creation (error termination if not created)
    • Shell script to create GitHub connection created and included

What's different (from what I made in the Go version)

Define a custom resource Lambda

This uses the TypeScript (Node.js) familiar NodejsFunction of aws-lambda-nodejs.

Therefore, the Lambda package.json is shared with the CDK one to make it a single package. (The package.json for the application to be published in App Runner is created separately.)

    const customResourceLambda = new NodejsFunction(this, "custom", {
      runtime: Runtime.NODEJS_16_X,
      bundling: {
        forceDockerBundling: false,
      },
      timeout: Duration.seconds(900),
      initialPolicy: [
        new PolicyStatement({
          actions: [
            "apprunner:*AutoScalingConfiguration*",
            "apprunner:UpdateService",
            "apprunner:ListOperations",
          ],
          resources: ["*"],
        }),
        new PolicyStatement({
          actions: ["cloudformation:DescribeStacks"],
          resources: [this.stackId],
        }),
      ],
    });
Enter fullscreen mode Exit fullscreen mode

Provider for custom resource Lambda

In Go, I created a custom resource by passing a Lambda Arn as ServiceToken to the CustomResource constructor, but in TypeScript, I created a Provider (of CustomResource) and passed its property serviceToken to the CustomResource constructor. TypeScript creates a Provider (of CustomResource) and passes the serviceToken of that property.

  • CDK code
    const autoScalingConfigurationProvider = new Provider(
      this,
      "AutoScalingConfigurationProvider",
      {
        onEventHandler: customResourceLambda,
      },
    );

    // ...

    const autoScalingConfiguration = new CustomResource(this, "AutoScalingConfiguration", {
      resourceType: "Custom::AutoScalingConfiguration",
      properties: autoScalingConfigurationProperties,
      serviceToken: autoScalingConfigurationProvider.serviceToken,
    });
Enter fullscreen mode Exit fullscreen mode
  • Lambda code
export const handler: CdkCustomResourceHandler = async function (event) {
    // ...
Enter fullscreen mode Exit fullscreen mode

How to handle GitHub connections

In the Go version,

  • When deploying CDK, check if there is a GitHub connection, if not, create it, if there is, use it.
  • If it is created or "PENDING_HANDSHAKE", it waits for a prompt and gives you time to go to the console to press a button, and then press Enter to restart the CDK process.

I had been doing something a bit more elaborate, but this time I thought it was unnecessary to go that far, so I decided to simply make a pre-configuration that says "if it's there, get it, if not, error".

I did not include the GitHub connection in the application stack as a custom resource, because I thought it might be used in the same unit as the application or in a larger unit such as an AWS account.

I also created a shell script to create the GitHub connection, so I ran it and then hit CDK.

#!/bin/bash
set -eu

cd `dirname $0`

PROFILE=""
PROFILE_OPTION=""
REGION="ap-northeast-1"
CONNECTION_NAME=""
PROVIDER_TYPE="GITHUB"

while getopts p:c: OPT; do
    case $OPT in
        p)
            PROFILE="$OPTARG"
            ;;
        c)
            CONNECTION_NAME="$OPTARG"
            ;;
    esac
done

if [ -z "${CONNECTION_NAME}" ]; then
    echo "CONNECTION_NAME (-c) is required"
    exit 1
fi

if [ -n "${PROFILE}" ]; then
    PROFILE_OPTION="--profile ${PROFILE}"
fi

aws apprunner create-connection \
  --connection-name ${CONNECTION_NAME} \
  --provider-type ${PROVIDER_TYPE} \
  ${PROFILE_OPTION}
Enter fullscreen mode Exit fullscreen mode

Build

When using App Runner with Managed Runtime, you must specify the build and start commands.

Build command

This time, since it is TypeScript, I used esbuild to build (transpile).

cd app && yarn install --non-interactive --frozen-lockfile && yarn build
Enter fullscreen mode Exit fullscreen mode

The yarn build is defined as follows in the scripts of package.json.

esbuild ./index.ts --bundle --outfile=./index.js --platform=node --minify --allow-overwrite
Enter fullscreen mode Exit fullscreen mode

Startup commands

Start the transpiled js file with the node command.

node app/index.js
Enter fullscreen mode Exit fullscreen mode

Finally

Since I created a Go version of App Runner's Managed Runtime, I thought it would be interesting to write it in TypeScript as well.

It was a good experience for me to learn the difference between TypeScript and Go CDK.

Top comments (0)