Skip to content

aws-samples/cloudwatch-custom-widgets-samples

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

CloudWatch Custom Widgets Samples

License AWS Provider

The samples in this project demonstrate several uses of Custom Widgets within a CloudWatch Dashboard.

Table of Contents

Table of Contents
  1. ➤ What are Custom Widgets?
  2. ➤ Why would I use Custom Widgets?
  3. ➤ Sounds great, how do I sign up?
  4. ➤ Even quicker - 2-click samples
  5. ➤ How does a widget work?
  6. ➤ Can the returned HTML contain any HTML tags?
  7. ➤ Is interactivity possible in the returned HTML?
  8. ➤ What can I do with the cwdb-action tag?
  9. ➤ Can I call Custom Widget functions in other accounts?
  10. ➤ Can a Lambda function call a customer's internal services?
  11. ➤ Can access to Custom Widgets be restricted?
  12. ➤ What is the 'Hello world' Custom Widget example?
  13. ➤ Example of returning data from a call to an AWS function?
  14. ➤ Can the user of a Custom Widget customize its behavior?
  15. ➤ Does Custom Widgets refresh and auto-refresh?
  16. ➤ Can Custom Widgets be resized and moved around the dashboard?
  17. ➤ Can Custom Widgets react to the time range of the dashboard?
  18. ➤ Is Lambda the only API that these widgets can call?
  19. ➤ What is the default style of Custom Widget HTML elements?
  20. ➤ Can I customize the style of the HTML elements?
  21. ➤ Can the default styles be disabled?
  22. ➤ Can I use Custom Widgets in my own website?
  23. ➤ Contributing
  24. ➤ License

What are Custom Widgets?

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.

Why would I use Custom Widgets?

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.

Sounds great, how do I sign up?

Option A: Using the CloudWatch Console

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:

create_a_custom_widget_1

create_a_custom_widget_2

Option B: Using this repository directly

  1. Clone the repo
  2. Run the build script: scripts/build-assets
  3. 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
  1. The templates can be deployed with the AWS CloudFormation web console (or the AWS CLI)
  2. Modify them or adjust them to suit your own use case and needs!

Option C: Manually from a CloudWatch Dashboard

Alternatively, add this entry into the widgets array of any CloudWatch Dashboard - go to Actions -> View/edit source:

{
    "type": "custom"
}

Even quicker - 2-click samples

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.

JavaScript samples

Python samples

How does a widget work?

Your CloudWatch Dashboard:

  1. calls the Lambda function containing the widget code. The function is passed any custom parameters defined in the widget
  2. the Lambda function is expected to return a string of HTML
  3. the CloudWatch Dashboard displays the HTML
  4. if the response is JSON it is displayed as formatted JSON

Can the returned HTML contain any HTML tags?

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.

Is interactivity possible in the returned HTML?

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

What can I do with the cwdb-action tag?

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> 

cwdb-action: Definition and usage

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

cwdb-action: Examples

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> 

Can I call Custom Widget functions in other accounts?

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.

Can a Lambda function call a customer's internal services?

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.

Can access to Custom Widgets be restricted?

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.

What is the 'Hello world' Custom Widget example?

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>'

Example of returning data from a call to an AWS function?

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();
};

Can the user of a Custom Widget customize its behavior?

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)

Does Custom Widgets refresh and auto-refresh?

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).

Can Custom Widgets be resized and moved around the dashboard?

Yes. The Custom Widgets can be resized and moved around the dashboards as easily as the existing Text and Metric Graph widgets.

Can Custom Widgets react to the time range of the dashboard?

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.

Is the Custom Widget passed any information by default?

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

Is Lambda the only API that these widgets can call?

Yes.

What is the default style of Custom Widget HTML elements?

The default style of HTML elements such as links and tables will follow the styling of the CloudWatch Console and Dashboard.

Can I customize the style of the HTML elements?

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).

Can the default styles be disabled?

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>

Can I use Custom Widgets in my own website?

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>

Contributing

Contributions are very welcome please checkout out the contributing guidelines.

License

Licensed under the MIT-0 license.