The samples in this project demonstrate several uses of Custom Widgets within a CloudWatch Dashboard.
Table of Contents
- ➤ What are Custom Widgets?
- ➤ Why would I use Custom Widgets?
- ➤ Sounds great, how do I sign up?
- ➤ Even quicker - 2-click samples
- ➤ How does a widget work?
- ➤ Can the returned HTML contain any HTML tags?
- ➤ Is interactivity possible in the returned HTML?
- ➤ What can I do with the cwdb-action tag?
- ➤ Can I call Custom Widget functions in other accounts?
- ➤ Can a Lambda function call a customer's internal services?
- ➤ Can access to Custom Widgets be restricted?
- ➤ What is the 'Hello world' Custom Widget example?
- ➤ Example of returning data from a call to an AWS function?
- ➤ Can the user of a Custom Widget customize its behavior?
- ➤ Does Custom Widgets refresh and auto-refresh?
- ➤ Can Custom Widgets be resized and moved around the dashboard?
- ➤ Can Custom Widgets react to the time range of the dashboard?
- ➤ Is Lambda the only API that these widgets can call?
- ➤ What is the default style of Custom Widget HTML elements?
- ➤ Can I customize the style of the HTML elements?
- ➤ Can the default styles be disabled?
- ➤ Can I use Custom Widgets in my own website?
- ➤ Contributing
- ➤ License
A Custom Widget is a CloudWatch Dashboards widget that can display virtually anything you want. Custom Widgets enables you to add custom visualizations, display information from multiple sources or add custom controls like buttons to take actions directly in a CloudWatch Dashboard. Custom Widgets are powered by custom Lambda functions, enabling complete control over the content, layout, and interactions. You can add custom widgets programmatically using the AWS SDK, CLI and CloudFormation.
Custom Widgets is a simple way to build a custom data view or tool on a dashboard. There's no complicated web framework to learn, it's completely serverless. If you can write code in Lambda and create HTML then you can create a useful custom widget.
The samples are already available in the CloudWatch Console, from a CloudWatch dashboard you can click the Add widget button and then select Custom widget. The samples found within this repository are available for a 1-click quick creation:
- Clone the repo
- Run the build script:
scripts/build-assets
- YAML templates will be generated inside the
build
folder e.g.:
build
└── cfn
├── customWidgetSample1-js.yaml
├── customWidgetSample2-py.yaml
├── customWidgetSample3-js.yaml
├── customWidgetSample4-py.yaml
├── ...
└── customWidgetSampleN-py.yaml
- The templates can be deployed with the AWS CloudFormation web console (or the AWS CLI)
- Modify them or adjust them to suit your own use case and needs!
Alternatively, add this entry into the widgets array of any CloudWatch Dashboard - go to Actions -> View/edit source:
{
"type": "custom"
}
The following links launch a sample Custom Widget into your AWS account, along with a sample dashboard showing how the widget works. To launch:
- if you're not happy with default region of us-east-1 (N. Virginia) switch region at top right of AWS Console
- change the function name if you want to use it as a starter widget of your own
- tick the I acknowledge that AWS CloudFormation might create IAM resources. checkbox
- click orange Create stack button
CloudFormation will create the custom widget Lambda function and sample CloudWatch Dashboard that uses it for you within a minute. Once the stack is created check the Outputs tab for links to the Lambda function code and sample dashboard. Edit the widget code directly in Lambda console and test changes directly in CloudWatch dashboard.
- Hello world: a very simple starter widget
- Custom widget debugger: a helpful debugger widget that displays useful information about the Lambda runtime environment
- Echo: a simple echoer. Test how HTML will appear in a widget without writing a widget
- Run Athena queries: run and edit queries against Amazon Athena
- Call AWS API: call any read-only AWS API and display results as JSON
- EC2 Table: display top EC2 instances by CPU, plus a Reboot button (disabled by default as IAM role created only has read-only permissions needed to read EC2 instances and metric data)
- Fast CloudWatch bitmap graphs: render CloudWatch graphs server-side, for faster display
- CloudWatch metric data as table: display raw CloudWatch metric data in table
- Code Deployments: display CodeDeploy deployments
- Cost explorer report: displays a report on cost of each AWS service for a selected time range
- Display content of external URL: display externally accessible HTML content
- Include text widget from dashboard: displays the first text widget from a specified CloudWatch Dashboard
- Run Logs Insights queries: run and edit queries against CloudWatch Logs Insights
- Display S3 object: display object from S3 bucket in your account
- Simple SVG pie chart: simple example of graphical SVG-based widget
- Hello world: a very simple starter widget
- Custom widget debugger: a helpful debugger widget that displays useful information about the Lambda runtime environment
- Echo: a simple echoer. Test how HTML will appear in a widget without writing a widget
- Call AWS API: call any read-only AWS API and display results as JSON
- Fast CloudWatch bitmap graphs: render CloudWatch graphs server-side, for faster display
- Send dashboard snapshot by email: take a snapshot of the current dashboard and send it to email recipient
- Display content of an external URL: display externally accessible HTML content
- Include text widget from dashboard: displays the first text widget from a specified CloudWatch Dashboard
- RSS Reader: display RSS feeds
- Display S3 object: display object from S3 bucket in your account
- Simple SVG pie chart: simple example of graphical SVG-based widget
- Snapshot dashboard to S3: take a snapshot of the current dashboard and store it in S3
Your CloudWatch Dashboard:
- calls the Lambda function containing the widget code. The function is passed any custom parameters defined in the widget
- the Lambda function is expected to return a string of HTML
- the CloudWatch Dashboard displays the HTML
- if the response is JSON it is displayed as formatted JSON
Almost all HTML tags are supported. CSS styles and SVGs can be used for building sophisticated and graphically rich views.
However, for security reasons JavaScript is not allowed in the returned HTML. If the HTML string from the Lambda function contains any Javascript it will be "cleaned" from the HTML before rendering. Also, the <iframe> tag is forbidden.
Removing Javascript is done to prevent privilege escalation issues, where the writer of the Lambda function can inject code that would run with the possibly higher privileges of the user viewing the widget in the CloudWatch Console.
Yes. Even though JavaScript is not allowed, there are a number of avenues that allow interactivity with the returned HTML:
- Any element in the returned HTML can be tagged with special configuration in a <cwdb-action> tag that trigger display information in popups, ask for confirmation on clicks can call any Lambda function when that element is clicked. This allows for example the definition of buttons that will call any AWS API via a Lambda function. The returned HTML can be set to either replace the existing Lambda widget's content, or display inside a modal.
- HTML can include links that open new consoles, other customer pages or load other dashboards
- HTML can include the 'title' attribute for an element, that gives additional information if the user hovers over that element
- HTML can include CSS selectors such as :hover which can trigger animations or different CSS effects
Example of how to create a button that reboots an EC2 instance via a Lambda function call, displaying the success or failure of the call in a popup:
<a class="btn">Reboot Instance</a>
<cwdb-action action="call" endpoint="arn:aws:lambda:us-east-1:123456:function:rebootInstance" display="popup">
{ "instanceId": "i-342389adbfef" }
</cwdb-action>
The <cwdb-action> element defines a behavior on the previous element. The content of the <cwdb-action> is either HTML to display or JSON of parameters to pass to the call to a Lambda function.
Attributes General definition:
<cwdb-action
action="call|html"
confirmation="message"
display="popup|widget"
endpoint="<lambda ARN>"
event="click|dblclick|mouseenter">
html | params in JSON
</cwdb-action>
Attribute | Value | Description |
---|---|---|
action | call | html | Two actions are supported, call a Lambda function or (default) display html contained within <cwdb-action> |
confirmation | message | Displays a confirmation message that needs to be acknowledged before the action is taken (allowing customer to cancel) |
display | popup | widget | Where should action result be displayed. Can be either in a popup or (default) replace the content of the widget itself |
endpoint | arn of lambda function | The ARN of the Lambda function to call. Required if action is set to call |
event | click | dblclick | mouseenter | The event on the previous element which triggers the action. The mouseenter event can be used only in combination with the html action. The default is click |
A link which displays more information in a popup:
<a>Click me for more info in popup</a>
<cwdb-action display="popup">
<h1>Big title</h1>
More info about <b>something important</b>.
</cwdb-action>
A Next button (primary) which replaces content of widget with call to a Lambda:
<a class="btn btn-primary">Next</a>
<cwdb-action action="call" endpoint="arn:aws:lambda:us-east-1:123456:function:nextPage">
{ "pageNum": 2 }
</cwdb-action>
Yes. In order to help with sharing of functionality between multiple accounts owned by a customer, a Custom Widget function can be defined in one account and called from the dashboards of other accounts, as long as the correct permissions have been defined to allow access from other accounts. Follow CloudWatch Cross-Account Cross-Region setup and make sure you allow the current account to invoke the custom widget Lambda function in the shared account(s).
The CloudWatch Dashboard facilitates this by allowing the customer to pick a Lambda function by pasting a raw ARN to it into the Dashboard definition.
If those services are accessible within an AWS VPC then the Lambda function can run within that VPC, thus allowing access to the customer's internal services/data.
Yes. Normal IAM policies can be applied so that IAM users can be allowlisted or blocklisted for Lambda execute permissions on all or particular Lambda functions.
This allows customers to share a single dashboard with multiple users but lock down the view of particular Lambda widgets to users with higher privileges.
Below is the Javascript code for the Hello world example:
exports.handler = async (event) => {
const name = event.name || 'friend';
return `<h1>Hello ${name}</h1>`;
};
And here is the Python version of Hello world:
def lambda_handler(event, context):
name = event.get('name', 'friend')
return f'<h1>Hello {name}</h1>'
Below is the JavaScript code for displaying the HTML content of any file in an S3 bucket:
const aws = require('aws-sdk');
exports.handler = async (event) => {
const region = event.region || process.env.AWS_REGION;
const params = {
Bucket: event.bucket,
Key: event.key
};
const s3 = new aws.S3({ region });
const result = await s3.getObject(params).promise();
return result.Body.toString();
};
And the Python equivalent:
def lambda_handler(event, context):
region = event.get('region', os.environ['AWS_REGION'])
s3 = boto3.client('s3', region_name=region)
result = s3.get_object(Bucket=event['bucket'], Key=event['key'])
return result['Body'].read().decode('utf-8')
Another one, that can call any AWS API and display the results in pretty-fied JSON:
const aws = require('aws-sdk');
exports.handler = async (event) => {
const service = event.service || 'CloudWatch';
const api = event.api || 'listDashboards';
const region = event.region || process.env.AWS_REGION;
const params = event.params || {};
if (!aws[service]) {
throw `Unknown AWS service ${service}`;
}
const client = new aws[service]({ region });
if (!client[api]) {
throw `Unknown API ${api} for AWS service ${service}`;
}
return await client[api](params).promise();
};
Yes. All Lambda functions for HTML widgets can receive custom parameters from the dashboard, defined by the user on a per-widget basis. It is up to the Lambda function writer to decide what parameters will be accepted.
When creating/modifying a custom widget a customer can:
- select the Lambda function to call from any region in the account via a dropdown
- enter a Version or Alias of the Lambda function to run a specific version of the function
- enter a specific ARN for a Lambda function, which could be in a different account
- list the custom parameters to be sent to the Lambda function, in either JSON or YAML form
- set the title for the widget
- choose when the widget should be updated (i.e. when the Lambda function should be called again). This can be on refresh when the dashboard auto-refreshes and/or when the widget is resized and/or when the dashboard time range is adjusted (including when graphs are zoomed into)
Yes. The refresh button will call all Lambda functions and re-render all Lambda widgets along with the CloudWatch metric graphs. The auto-refresh however will re-render as long as the Refresh option is selected (see previous question).
Yes. The Custom Widgets can be resized and moved around the dashboards as easily as the existing Text and Metric Graph widgets.
Yes. The parameters passed to every call to a Custom Widget function will include the time range of the dashboard.
They will also include the dimensions of the viewable area of the widget box, helping writers of the function design their HTML to fit the size of the Lambda widget's area as they see fit.
Yes. Every call to Lambda includes a parameter called widgetContext which has the following structure/contents:
{
"widgetContext": {
"dashboardName": "Name-of-current-dashboard",
"widgetId": "widget-16",
"accountId": "XXXXXXXXXXXX",
"locale": "en",
"timezone": {
"label": "UTC",
"offsetISO": "+00:00",
"offsetInMinutes": 0
},
"period": 300,
"isAutoPeriod": true,
"timeRange": {
"mode": "relative",
"start": 1512556923228,
"end": 1512600123228,
"relativeStart": 43200000,
"zoom": {
"start": 1627276030434,
"end": 1627282956521
}
},
"theme": "light",
"linkCharts": true,
"title": "Tweets for Amazon website problem",
"forms": {
"all": {}
},
"params": {
"original": "param-to-widget"
},
"width": 588,
"height": 369
}
}
This gives the Lambda developer important widget context information such as:
- current dashboard settings such as time range, zoom range, timezone, period settings, if charts are "linked" together and language setting of the console
- width and height of visible area of the widget
- widget title and unique id
- content of all form fields within the widget – allowing widget to take input and send it to another Lambda call via <cwdb-action> tag
- the calling account id
- the original parameters configured for the widget, so that a button that calls the Lambda function again does not have to copy/paste original parameters
Yes.
The default style of HTML elements such as links and tables will follow the styling of the CloudWatch Console and Dashboard.
Yes. Styles of HTML elements can be set either via inline styles or including a stylesheet within the returned HTML (include <style></style> anywhere within the HTML).
Yes. In order not to force customers to override and fight with the default CSS we also allow the default CSS to be disabled, under control of the returned HTML. Simply include a single HTML element that has a class of cwdb-no-default-styles , e.g.
<span class="cwdb-no-default-styles">I have default styling</span>
Yes. CloudWatch Dashboarding Sharing allows you to display a CloudWatch Dashboard outside the AWS console and embed it into other websites via the iframe tag.
CloudWatch Dashboarding Sharing supports sharing dashboards by:
- share single dashboard with group of email addresses, using login with passwords
- share single dashboard via a public (obscure) URL, login not required
- Share all dashboards in an account and via third-party single sign-on (SSO) provider
So add your Custom Widgets to a dashboard and share it. Once it is shared you will have a URL for the dashboard, in this form:
https://cloudwatch.amazonaws.com/dashboard.html?dashboard=<name>&context=<obscureUniqueString>
This can then be embedded into a website with HTML similar to this:
<iframe src="https://cloudwatch.amazonaws.com/dashboard.html?dashboard=<name>&context=<obscureUniqueString>" width="1200" height="800" frameBorder="0">Loading dashboard...</iframe>
Contributions are very welcome please checkout out the contributing guidelines.
Licensed under the MIT-0 license.