Deploy Image on ECR to AWS App Runner with AWS CDK V2

Page content

AWS App Runner makes it easy to deploy a Web Server as long as it is a Docker container.

What’s more,

  • Automatic redeployment of the ECR image when it is updated (or not automatic, of course).
  • AutoScale and custom domains can also be configured.
  • You can view metrics such as number of accesses, latency, number of instances, etc. without any configuration.
  • Application logs can also be viewed automatically.

It’s all there. It will be very useful when you want to deploy a small web service.

Once you get used to it, it’s not that much trouble to set it up manually. However, since it is likely to be used many times in the future, I will summarize how to write with AWS CDK V2.

Version

  • node: 16.13.1
  • aws-cdk: 2.8.0 (build 8a5eb49)
  • tsc: 4.5.4

How To Write with AWS CDK v2

import { Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";

import { CfnService } from "aws-cdk-lib/aws-apprunner";
import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam";
import { StringParameter } from "aws-cdk-lib/aws-ssm";

// Image URI to deploy
const IMAGE_URI =
  "99999999999.dkr.ecr.ap-northeast-1.amazonaws.com/my-web-app:release";

export class MyWebAppStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    let buildRole = this.createECRRole();
    let taskRole = this.createTaskRole();

    // AWS::AppRunner::Service
    let appService = new CfnService(this, "MyWebApp", {
      serviceName: "MyWebApp",
      sourceConfiguration: {
        authenticationConfiguration: {
          accessRoleArn: buildRole.roleArn, // Role when accessing ECR?
        },
        imageRepository: {
          imageIdentifier: IMAGE_URI,
          imageRepositoryType: "ECR",
          imageConfiguration: {
            port: "8080", // Listen Port
            runtimeEnvironmentVariables: [
              // Runtime Environment Variables
              { name: "PRODUCTION", value: "1" },
            ],
            startCommand: "web", // Commands when launching container execution
          },
        },
        autoDeploymentsEnabled: true, // AutoDeploy when images pushed to ECR
      },
      instanceConfiguration: {
        cpu: "1 vCPU",
        memory: "2 GB",
        instanceRoleArn: taskRole.roleArn,
      },
    });
  }

  createECRRole(): Role {
    let role = new Role(this, "MyWebAppECRRole", {
      assumedBy: new ServicePrincipal("build.apprunner.amazonaws.com"),
    });
    role.addManagedPolicy({
      managedPolicyArn:
        "arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess",
    });
    return role;
  }

  // Create a runtime Role. Define as needed.
  createTaskRole(): Role {
    // Create Role for AppRunner Task
    let taskRole = new Role(this, "MyWebAppTaskRole", {
      assumedBy: new ServicePrincipal("tasks.apprunner.amazonaws.com"),
    });

    // SSM's access rights to SecureString
    let googleJson = StringParameter.fromSecureStringParameterAttributes(
      this,
      "google-json",
      {
        parameterName: "/shared/google.json",
        version: 1,
      }
    );

    // Grant
    googleJson.grantRead(taskRole);

    return taskRole;
  }
}

Reference