AWSCDK::Synthetics

26 types

Amazon CloudWatch Synthetics Construct Library

Amazon CloudWatch Synthetics allow you to monitor your application by generating synthetic traffic. The traffic is produced by a canary: a configurable script that runs on a schedule. You configure the canary script to follow the same routes and perform the same actions as a user, which allows you to continually verify your user experience even when you don't have any traffic on your applications.

Canary

To illustrate how to use a canary, assume your application defines the following endpoint:

% curl "https://api.example.com/user/books/topbook/"
The Hitchhikers Guide to the Galaxy

The below code defines a canary that will hit the books/topbook endpoint every 5 minutes:

canary = AWSCDK::Synthetics::Canary.new(self, "MyCanary", {
    schedule: AWSCDK::Synthetics::Schedule.rate(AWSCDK::Duration.minutes(5)),
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_asset(path.join(__dirname, "canary")),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2,
    environment_variables: {
        stage: "prod",
    },
})

The following is an example of an index.js file which exports the handler function:

const synthetics = require('Synthetics');
const log = require('SyntheticsLogger');

const pageLoadBlueprint = async function () {
  // Configure the stage of the API using environment variables
  const url = `https://api.example.com/${process.env.stage}/user/books/topbook/`;

  const page = await synthetics.getPage();
  const response = await page.goto(url, {
    waitUntil: 'domcontentloaded',
    timeout: 30000,
  });
  // Wait for page to render. Increase or decrease wait time based on endpoint being monitored.
  await page.waitFor(15000);
  // This will take a screenshot that will be included in test output artifacts.
  await synthetics.takeScreenshot('loaded', 'loaded');
  const pageTitle = await page.title();
  log.info('Page title: ' + pageTitle);
  if (response.status() !== 200) {
    throw 'Failed to load page!';
  }
};

exports.handler = async () => {
  return await pageLoadBlueprint();
};

Note: The function must be called handler.

The canary will automatically produce a CloudWatch Dashboard:

UI Screenshot

The Canary code will be executed in a lambda function created by Synthetics on your behalf. The Lambda function includes a custom runtime provided by Synthetics. The provided runtime includes a variety of handy tools such as Puppeteer (for nodejs based one) and Chromium.

To learn more about Synthetics capabilities, check out the docs.

Canary Schedule

You can specify the schedule on which a canary runs by providing a Schedule object to the schedule property.

Configure a run rate of up to 60 minutes with Schedule.rate:

schedule = AWSCDK::Synthetics::Schedule.rate(AWSCDK::Duration.minutes(5))

You can also specify a cron expression with Schedule.cron:

schedule = AWSCDK::Synthetics::Schedule.cron({
    hour: "0,8,16",
})

If you want the canary to run just once upon deployment, you can use Schedule.once().

Automatic Retries

You can configure the canary to automatically retry failed runs by using the max_retries property.

This is only supported on the following runtimes or newer: Runtime.SYNTHETICS_NODEJS_PUPPETEER_10_0, Runtime.SYNTHETICS_NODEJS_PLAYWRIGHT_2_0, Runtime.SYNTHETICS_PYTHON_SELENIUM_5_1.

Max retries can be set between 0 and 2. Canaries which time out after 10 minutes are automatically limited to one retry.

For more information, see Configuring your canary to retry automatically.

canary = AWSCDK::Synthetics::Canary.new(self, "MyCanary", {
    schedule: AWSCDK::Synthetics::Schedule.rate(AWSCDK::Duration.minutes(5)),
    test: AWSCDK::Synthetics::Test.custom({
        handler: "canary.handler",
        code: AWSCDK::Synthetics::Code.from_asset(path.join(__dirname, "canaries")),
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_PYTHON_SELENIUM_5_1,
    max_retries: 2,
})

Active Tracing

You can choose to enable active AWS X-Ray tracing on canaries that use the syn-nodejs-2.0 or later runtime by setting active_tracing to true.

With tracing enabled, traces are sent for all calls made by the canary that use the browser, the AWS SDK, or HTTP or HTTPS modules.

For more information, see Canaries and X-Ray tracing.

canary = AWSCDK::Synthetics::Canary.new(self, "MyCanary", {
    schedule: AWSCDK::Synthetics::Schedule.rate(AWSCDK::Duration.minutes(5)),
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_asset(path.join(__dirname, "canary")),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2,
    active_tracing: true,
})

Memory

You can set the maximum amount of memory the canary can use while running with the memory property.

require 'aws-cdk-lib'


canary = AWSCDK::Synthetics::Canary.new(self, "MyCanary", {
    schedule: AWSCDK::Synthetics::Schedule.rate(AWSCDK::Duration.minutes(5)),
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_asset(path.join(__dirname, "canary")),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2,
    memory: AWSCDK::Size.mebibytes(1024),
})

Timeout

You can set how long the canary is allowed to run before it must stop with the timeout property.

require 'aws-cdk-lib'


canary = AWSCDK::Synthetics::Canary.new(self, "MyCanary", {
    schedule: AWSCDK::Synthetics::Schedule.rate(AWSCDK::Duration.minutes(5)),
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_asset(path.join(__dirname, "canary")),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2,
    timeout: AWSCDK::Duration.seconds(60),
})

Browser Type Configuration

You can configure which browsers your canary uses for testing by specifying the browser_configs property. This allows you to test your application across different browsers to ensure compatibility.

Available browser types:

You can specify up to 2 browser configurations. When multiple browsers are configured, the canary will run tests on each browser sequentially.

canary = AWSCDK::Synthetics::Canary.new(self, "MyCanary", {
    schedule: AWSCDK::Synthetics::Schedule.rate(AWSCDK::Duration.minutes(5)),
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_asset(path.join(__dirname, "canary")),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_9_1,
    browser_configs: [
        AWSCDK::Synthetics::BrowserType::CHROME,
        AWSCDK::Synthetics::BrowserType::FIREFOX,
    ],
})

Note: Firefox support is available for Node.js runtimes (Puppeteer and Playwright) but not for Python Selenium runtimes. When using Firefox, ensure your runtime version supports it.

Deleting underlying resources on canary deletion

When you delete a lambda, the following underlying resources are isolated in your AWS account:

To learn more about these underlying resources, see Synthetics Canaries Deletion.

In the CDK, you can configure your canary to delete the underlying lambda function when the canary is deleted. This can be provisioned by setting provisioned_resource_cleanup to true.

canary = AWSCDK::Synthetics::Canary.new(self, "Canary", {
    test: AWSCDK::Synthetics::Test.custom({
        handler: "index.handler",
        code: AWSCDK::Synthetics::Code.from_inline("/* Synthetics handler code"),
    }),
    provisioned_resource_cleanup: true,
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2,
})

Note: To properly clean up your canary on deletion, you still have to manually delete other resources like S3 buckets and CloudWatch logs.

Note: The deletion of Lambda resources can also be performed by setting the cleanup argument to Cleanup.LAMBDA. However, this is an outdated argument that uses custom resources and is currently deprecated.

Configuring the Canary Script

To configure the script the canary executes, use the test property. The test property accepts a Test instance that can be initialized by the Test class static methods. Currently, the only implemented method is Test.custom(), which allows you to bring your own code. In the future, other methods will be added. Test.custom() accepts code and handler properties -- both are required by Synthetics to create a lambda function on your behalf.

The synthetics.Code class exposes static methods to bundle your code artifacts:

Using the Code class static initializers:

# To supply the code from a S3 bucket:
require 'aws-cdk-lib'
# To supply the code inline:
AWSCDK::Synthetics::Canary.new(self, "Inline Canary", {
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_inline("/* Synthetics handler code */"),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2,
})

# To supply the code from your local filesystem:
AWSCDK::Synthetics::Canary.new(self, "Asset Canary", {
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_asset(path.join(__dirname, "canary")),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2,
})
bucket = AWSCDK::S3::Bucket.new(self, "Code Bucket")
AWSCDK::Synthetics::Canary.new(self, "Bucket Canary", {
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_bucket(bucket, "canary.zip"),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2,
})

Note: Synthetics have a specified folder structure for canaries. For Node with puppeteer scripts supplied via code.fromAsset() or code.fromBucket(), the canary resource requires the following folder structure for runtime versions older than syn-nodejs-puppeteer-11.0:

canary/
├── nodejs/
   ├── node_modules/
        ├── <filename>.js

For puppeteer based runtime versions newer than or equal to syn-nodejs-puppeteer-11.0, nodjs/node_modules is not necessary but supported.

Both

canary/
├── nodejs/
   ├── node_modules/
        ├── <filename>.js

And

canary/
 ├── <filename>.js

are supported.

For Node with playwright scripts supplied via code.fromAsset() or code.fromBucket(), the canary resource requires the following folder structure:

canary/
├── <filename>.js,.mjs,.cjs
├─some/dir/path
             ├── <filename>.js,.mjs,.cjs

If <filename>.js is placed in the canary directory, the handler should be specified as filename.handler. However, if it is placed in the some/dir/path directory, the handler should be specified as some/dir/path/filename.handler. For more information, see Synthetics docs.

For Python scripts supplied via code.fromAsset() or code.fromBucket(), the canary resource requires the following folder structure:

canary/
├── python/
    ├── <filename>.py

See Synthetics docs.

Running a canary on a VPC

You can specify what VPC a canary executes in. This can allow for monitoring services that may be internal to a specific VPC. To place a canary within a VPC, you can specify the vpc property with the desired VPC to place then canary in. This will automatically attach the appropriate IAM permissions to attach to the VPC. This will also create a Security Group and attach to the default subnets for the VPC unless specified via vpc_subnets and security_groups.

require 'aws-cdk-lib'

vpc = nil # AWSCDK::EC2::IVPC

AWSCDK::Synthetics::Canary.new(self, "Vpc Canary", {
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_asset(path.join(__dirname, "canary")),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2,
    vpc: vpc,
})

Note: By default, the Synthetics runtime needs access to the S3 and CloudWatch APIs, which will fail in a private subnet without internet access enabled (e.g. an isolated subnnet).

Ensure that the Canary is placed in a VPC either with internet connectivity or with VPC Endpoints for S3 and CloudWatch enabled and configured.

See Synthetics VPC docs.

Alarms

You can configure a CloudWatch Alarm on a canary metric. Metrics are emitted by CloudWatch automatically and can be accessed by the following APIs:

Create an alarm that tracks the canary metric:

require 'aws-cdk-lib'

canary = nil # AWSCDK::Synthetics::Canary

AWSCDK::CloudWatch::Alarm.new(self, "CanaryAlarm", {
    metric: canary.metric_success_percent,
    evaluation_periods: 2,
    threshold: 90,
    comparison_operator: AWSCDK::CloudWatch::ComparisonOperator::LESS_THAN_THRESHOLD,
})

Performing safe canary updates

You can configure a canary to first perform a dry run before applying any updates. The dry_run_and_update property can be used to safely update canaries by validating the changes before they're applied. This feature is supported for canary runtime versions syn-nodejs-puppeteer-10.0+, syn-nodejs-playwright-2.0+, and syn-python-selenium-5.1+.

When dry_run_and_update is set to true, CDK will execute a dry run to validate the changes before applying them to the canary. If the dry run succeeds, the canary will be updated with the changes. If the dry run fails, the CloudFormation deployment will fail with the dry run's failure reason.

canary = AWSCDK::Synthetics::Canary.new(self, "MyCanary", {
    schedule: AWSCDK::Synthetics::Schedule.rate(AWSCDK::Duration.minutes(5)),
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_asset(path.join(__dirname, "canary")),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_PYTHON_SELENIUM_5_1,
    dry_run_and_update: true,
})

For more information, see Performing safe canary updates.

Artifacts

You can pass an S3 bucket to store artifacts from canary runs. If you do not, one will be auto-generated when the canary is created. You may add lifecycle rules to the auto-generated bucket.

canary = AWSCDK::Synthetics::Canary.new(self, "MyCanary", {
    schedule: AWSCDK::Synthetics::Schedule.rate(AWSCDK::Duration.minutes(5)),
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_asset(path.join(__dirname, "canary")),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_6_2,
    artifacts_bucket_lifecycle_rules: [
        {
            expiration: AWSCDK::Duration.days(30),
        },
    ],
})

Canary artifacts are encrypted at rest using an AWS-managed key by default.

You can choose the encryption options SSE-S3 or SSE-KMS by setting the artifact_s3_encryption_mode property.

When you use SSE-KMS, you can also supply your own external KMS key by specifying the kms_key property. If you don't, a KMS key will be automatically created and associated with the canary.

require 'aws-cdk-lib'


key = AWSCDK::KMS::Key.new(self, "myKey")

canary = AWSCDK::Synthetics::Canary.new(self, "MyCanary", {
    schedule: AWSCDK::Synthetics::Schedule.rate(AWSCDK::Duration.minutes(5)),
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_asset(path.join(__dirname, "canary")),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_7_0,
    artifacts_bucket_lifecycle_rules: [
        {
            expiration: AWSCDK::Duration.days(30),
        },
    ],
    artifact_s3_encryption_mode: AWSCDK::Synthetics::ArtifactsEncryptionMode::KMS,
    artifact_s3_kms_key: key,
})

Tag replication

You can configure a canary to replicate its tags to the underlying Lambda function. This is useful when you want the same tags that are applied to the canary to also be applied to the Lambda function that the canary uses.

canary = AWSCDK::Synthetics::Canary.new(self, "MyCanary", {
    schedule: AWSCDK::Synthetics::Schedule.rate(AWSCDK::Duration.minutes(5)),
    test: AWSCDK::Synthetics::Test.custom({
        code: AWSCDK::Synthetics::Code.from_asset(path.join(__dirname, "canary")),
        handler: "index.handler",
    }),
    runtime: AWSCDK::Synthetics::Runtime.SYNTHETICS_NODEJS_PUPPETEER_7_0,
    resources_to_replicate_tags: [
        AWSCDK::Synthetics::ResourceToReplicateTags::LAMBDA_FUNCTION,
    ],
})

When you specify ResourceToReplicateTags.LAMBDA_FUNCTION in the resources_to_replicate_tags property, CloudWatch Synthetics will keep the tags of the canary and the Lambda function synchronized. Any future changes you make to the canary's tags will also be applied to the function.

Groups

You can create groups to associate canaries with each other, including cross-Region canaries. Using groups can help you with managing and automating your canaries, and you can also view aggregated run results and statistics for all canaries in a group.

Groups are global resources. When you create a group, it is replicated across all AWS Regions that support groups, and you can add canaries from any of these Regions to it.

Creating a Group

You can create a group and associate canaries with it:

# First, declare your canaries
canary1 = nil # AWSCDK::Synthetics::ICanary
canary2 = nil # AWSCDK::Synthetics::ICanary


group = AWSCDK::Synthetics::Group.new(self, "MyCanaryGroup", {
    group_name: "production-canaries",
    canaries: [canary1, canary2],
})

Adding Canaries to a Group

You can add canaries to a group after creation:

canary = nil # AWSCDK::Synthetics::ICanary


group = AWSCDK::Synthetics::Group.new(self, "MyCanaryGroup")

# Add canary to group
group.add_canary(canary)

Group Limitations

Importing Existing Groups

You can import existing groups by ARN or name:

# Import by ARN
imported_group_by_arn = AWSCDK::Synthetics::Group.from_group_arn(self, "ImportedGroup", "arn:aws:synthetics:us-east-1:123456789012:group:my-group")

# Import by name
imported_group_by_name = AWSCDK::Synthetics::Group.from_group_name(self, "ImportedGroup", "my-group")

For more information about groups, see the CloudWatch Synthetics Groups documentation.

API Reference

Classes 11

AssetCodeCanary code from an Asset. CanaryDefine a new Canary. CfnCanaryCreates or updates a canary. CfnGroupCreates or updates a group which you can use to associate canaries with each other, includ CodeThe code the canary should execute. GroupDefine a new CloudWatch Synthetics Group. InlineCodeCanary code from an inline string. RuntimeRuntime options for a canary. S3CodeS3 bucket path to the code zip file. ScheduleSchedule for canary runs. TestSpecify a test that the canary should run.

Interfaces 10

ArtifactsBucketLocationOptions for specifying the s3 location that stores the data of each canary run. CanaryPropsProperties for a canary. CfnCanaryPropsProperties for defining a `CfnCanary`. CfnGroupPropsProperties for defining a `CfnGroup`. CodeConfigConfiguration of the code class. CronOptionsOptions to configure a cron expression. CustomTestOptionsProperties for specifying a test. GroupPropsProperties for defining a CloudWatch Synthetics Group. ICanaryRepresents a CloudWatch Synthetics Canary. IGroupRepresents a CloudWatch Synthetics Group.

Enums 5

ArtifactsEncryptionModeEncryption mode for canary artifacts. BrowserTypeBrowser types supported by CloudWatch Synthetics Canary. CleanupDifferent ways to clean up underlying Canary resources when the Canary is deleted. ResourceToReplicateTagsResources that tags applied to a canary should be replicated to. RuntimeFamilyAll known Lambda runtime families.