paint-brush
An Intro to Dynamic CDK Dashboardsby@theBenForce
2,005 reads
2,005 reads

An Intro to Dynamic CDK Dashboards

by Ben ForceSeptember 25th, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

The CDK uses a four-step process to convert your code into a CloudFormation template: construct, prepare, validate, and synthesize. We'll create a CloudWatch dashboard construct that dynamically adds widgets for every DynamoDB table in the construct's scope. The first property is a reference to the dashboard that will store all of the widgets that you create. It should register itself as an Aspect in its scope and create the dashboard construct. Add a constructor to the class in `lib/dynamoDashboard.ts.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - An Intro to Dynamic CDK Dashboards
Ben Force HackerNoon profile picture


One of the CDK's greatest strengths is the ability to create a library of components to be shared across your company or shared with the world on constructs.dev. But how do you create a construct based on other constructs in the same stack without forcing the developer to call a method for each one? That's exactly what aspects allow you to do.


What are Aspects?


The CDK uses a four-step process to convert your code into a CloudFormation template: construct, prepare, validate, and synthesize. Since aspects are used during the second step, you need to understand how the first two steps are related.


When you run a CDK command, it starts by executing your code. Usually, this is some file in the project's bin/ directory where your App is created. This will create all of the constructs that you've added to your project, keeping only the level 1 or Cfn* constructs for the next step.


The prepare step is where aspects are used. Each scope has a set of aspects registered to it during the construct phase. The CDK travels down the construct tree, and if it finds a registered aspect, it will pass in all of the constructs in the current scope and any child scopes.


Creating the Dashboard Construct


If you don't have the CDK installed, you can find instructions in the getting started guide.


Now that you've seen aspects work behind the scenes let's create one of our own. We'll create a CloudWatch dashboard construct that dynamically adds widgets for every DynamoDB table in the construct's scope. Start by creating a skeleton project by running the following commands.


mkdir dynamo-dashboard
cd dynamo-dashboard
cdk init app --language typescript


Create the Construct

First, create a new construct called DynamoDashboard. Create the class in lib/dynamoDashboard.ts and make sure it implements IAspect.


Next, you'll need to add some properties to store the dashboard's state between calls to its visit method. The first property is a reference to the CloudWatch dashboard that will be updated. The second property will store all of the widgets that you create.


private dashboard: cw.CfnDashboard;
widgets: Array<Widget> = [];


Finally, add a constructor. It should register itself as an Aspect in its scope and create the dashboard construct.


constructor(scope: cdk.Construct, id: string) {
	super(scope, id);
	
	// Register this as an aspect
  cdk.Aspects.of(scope).add(this);

	this.dashboard = new cw.CfnDashboard(this, `Dashboard`, {
    dashboardBody: ""
  });
}


Implementing visit()


Adding a widget to the dashboard requires three steps: find a free spot, add a widget object to the widgets array, and update the dashboard body definition. Use the following method to figure out where the next available spot is. You can change the width/height values, but keep in mind that the dashboard is 24 units wide.


getNextPosition(): {x: number; y: number; width: number; height: number;} {
	let x = 0;
  let y = 0;

  if (this.widgets.length > 0) {
    const lastWidget = this.widgets[this.widgets.length - 1];
    x = lastWidget.x + lastWidget.width;
    y = lastWidget.y;

    if (x >= DASHBOARD_WIDTH - 1) {
      x = 0;
      y += this.props.widgetHeight + 1;
    }
  }

	return {x, y, width: 12, height: 6};
}


Now you can implement the actual visit method. The first thing you need to do is make sure the node is a DynamoDB table. Since every construct passed into the visit method will be level 1, you will be checking if the node is a CfnTable instance.


visit(node: cdk.IConstruct): void {
	if(node instanceof dynamodb.CfnTable) {
	}
}


If the node is a table instance, you can create a new widget and update the dashboard.


visit(node: cdk.IConstruct): void {
  if (node instanceof dynamo.CfnTable) {
    const position = this.getNextPosition();

    this.widgets.push({
		  ...position,
		  type: "metric",
		  properties: {
		    metrics: [
		      [
		        "AWS/DynamoDB",
		        "ConsumedReadCapacityUnits",
		        "TableName",
		        node.ref,
		        { label: "Read (max)" },
		      ],
		      [".", "ConsumedWriteCapacityUnits", ".", ".", { label: "Write (max)" }],
		    ],
		    view: "timeSeries",
		    stacked: false,
		    region: node.stack.region,
		    title: node.logicalId,
		    stat: "Maximum",
		    period: 300,
		    liveData: true,
		  },
		});

    this.dashboard.dashboardBody = JSON.stringify({ widgets: this.widgets });
  }
}


Testing It


Now comes the fun part, it's time to test your component. Go to your project's default stack and create an instance of the component. Next, add a couple of DynamoDB tables. Even though your visit method is looking for CfnTable instances you can use the Table class since it creates a CfnTable instance in its constructor.


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

    new DynamoDashboard(this, `DynamoDashboard`);

    new dynamo.Table(this, `FirstTable`, {
      partitionKey: {
        name: "pk",
        type: dynamo.AttributeType.STRING,
      },
    });

    new dynamo.Table(this, `SecondTable`, {
      partitionKey: {
        name: "pk",
        type: dynamo.AttributeType.STRING,
      },
    });
  }
}


Everything should be good to go at this point; run cdk deploy to send your stack to the cloud. Once it's done deploying, open up CloudWatch and find your dashboard. Now enjoy the fact that you don't have to manually add every table to it!


Conclusion


In this article, you've seen what CDK aspects are and how to use them to dynamically update resources. I haven't seen many other examples of aspects, but hopefully, you'll be inspired to create something awesome and share it!


Also Published At: https://thebenforce.com/post/dynamic-cdk-dashboards

You can find the final source for this tutorial at https://github.com/theBenForce/dynamo-dashboard