Skip to Content
How-To GuidesAWS CloudWatch Metrics (Cardinal Cloud)

Streaming CloudWatch Metrics to Cardinal Cloud

This guide streams AWS CloudWatch metrics into Cardinal Cloud (SaaS) over HTTPS. A CloudWatch Metric Stream writes to an Amazon Data Firehose stream, and Firehose POSTs each batch to Cardinal’s CloudWatch intake endpoint. Nothing runs in your account except the two AWS-managed streams: no collector, no polling agent, no per-metric GetMetricData charges.

The moving parts are the metric stream, the Firehose stream, and your organization ingest API key. Around them you provision what Firehose itself requires: an S3 bucket that receives only failed batches, two IAM roles, and a CloudWatch Logs group for delivery errors. The setup section covers both the AWS console and Terraform; pick the tab you want.

Which path is this? Cardinal can also read Metric Streams that Firehose delivers to your own S3 bucket, which is the path for self-hosted Cardinal Data Lake and customer-account deployments. That path is described in the blog post Getting CloudWatch Metrics into Cardinal Data Lake . This page covers the HTTP endpoint path: Firehose sends metrics directly to Cardinal, so there is no bucket notification, ingest queue, or prefix mapping to configure, and the only bucket on your side holds failed batches. This is the right choice for Cardinal Cloud.

Prerequisites

🔑
Organization ingest API keyBefore starting, obtain your organization's ingest API key from Cardinal support. This is the key that authenticates telemetry sent to Cardinal; it is not the Query API Key or an admin credential.
☁
AWS permissionsRights to create a Firehose stream, a CloudWatch metric stream, two IAM roles, and one S3 bucket (Firehose's required backup location) in each account and region you want to stream from.
↔
Outbound HTTPSFirehose connects to awsfirehose-cwmetrics.us-east-2.aws.cardinalhq.io on port 443. Firehose is AWS-managed, so no VPC or firewall change is needed.

How it works

SettingValue
Endpoint URLhttps://awsfirehose-cwmetrics.us-east-2.aws.cardinalhq.io
AuthenticationYour organization ingest API key, sent by Firehose as the X-Amz-Firehose-Access-Key header (the Firehose Access key field)
PayloadCloudWatch Metric Streams JSON output format
Content encodingGZIP (recommended) or none
DeliveryAt least once. Firehose retries any non-2xx response for the configured retry duration, then writes the failed batch to your backup bucket

Cardinal identifies your organization from the API key alone. Every metric stream in every AWS account and region that uses that key lands in the same Cardinal organization; the records themselves carry account_id and region, which become the cloud_account_id and cloud_region attributes.

The endpoint is hosted in us-east-2, but it is a plain HTTPS URL. A Firehose stream in any AWS region can deliver to it. Metric Streams are regional, so you create one metric stream and one Firehose stream per region you want to monitor.

Setup

Firehose requires an S3 bucket for HTTP endpoint destinations, used for batches that Cardinal could not accept. Create or pick a bucket before you start; it stays empty in normal operation.

1

Have your organization ingest API key ready

Request the key from Cardinal support if you do not have it yet. It is the same kind of key an OpenTelemetry collector uses to send telemetry to your Cardinal organization.

Treat this key like a password. Anyone holding it can write metrics into your organization. The Firehose console stores it as a secret and never shows it again after creation.

2

Create the Firehose stream

Open the Amazon Data Firehose console in the region whose metrics you want, and click Create Firehose stream.

  1. Source: Direct PUT.
  2. Destination: HTTP Endpoint.
  3. Firehose stream name: cardinal-cwmetrics (any name works; this guide uses it throughout).
  4. Under Destination settings:
    • HTTP endpoint name: Cardinal
    • HTTP endpoint URL: https://awsfirehose-cwmetrics.us-east-2.aws.cardinalhq.io
    • Access key: paste your organization ingest API key
    • Content encoding: GZIP
    • Retry duration: 300 seconds (the default). Keep it at least 60; a short retry window sends transient failures to the backup bucket instead of retrying.
    • Parameters: add key cardinalhq.format with value cwmetrics. This is optional today, since cwmetrics is the default, but it makes the stream’s purpose explicit.
  5. Under Buffer hints:
    • Buffer size: 5 MiB
    • Buffer interval: 60 seconds Firehose flushes when either limit is reached. A 60-second interval gives metrics close to the freshness CloudWatch itself offers; a longer interval sends fewer, larger requests. Keep the buffer size at or below 48 MiB: Firehose base64-encodes records into the request body, which grows it by about a third, and Cardinal rejects bodies over 64 MiB.
  6. Under Backup settings:
    • Source record backup in Amazon S3: Failed data only
    • S3 backup bucket: your backup bucket
    • S3 backup bucket error output prefix: cardinal-cwmetrics-errors/ (optional, keeps failures easy to find)
  7. Under Advanced settings, leave Amazon CloudWatch error logging enabled. Firehose writes Cardinal’s rejection reasons there, which is where you look if nothing arrives.
  8. Let the console create a new IAM role for the stream, or pick an existing one that can write to the backup bucket and to CloudWatch Logs.
  9. Click Create Firehose stream and wait for the status to become Active.
3

Create the CloudWatch metric stream

Open the CloudWatch console in the same region, go to Metrics → Streams, and click Create metric stream.

  1. Metrics to be streamed: choose Select metrics and pick the namespaces you want, such as AWS/EC2, AWS/RDS, AWS/ApplicationELB, AWS/Lambda, AWS/SQS, and AWS/ECS. Choosing All metrics also works; add AWS/Usage and other high-cardinality namespaces to the exclude list to control cost.
  2. Configuration: choose Custom setup with Firehose, then Select your Firehose stream and pick cardinal-cwmetrics.
  3. Service role: let the console create a new role, or pick one that allows firehose:PutRecord and firehose:PutRecordBatch on that stream.
  4. Change output format: choose JSON. This matters: Cardinal accepts the Metric Streams JSON format and rejects the OpenTelemetry 0.7 and 1.0 formats with a 400 that names the problem.
  5. Metric stream name: cardinal-cwmetrics.
  6. Click Create metric stream.

CloudWatch starts streaming within a couple of minutes.

4

Verify delivery

In the Firehose console, open the stream’s Monitoring tab. Within five minutes you should see:

  • Incoming records rising, which means CloudWatch is sending.
  • Delivery to HTTP endpoint success at 1, which means Cardinal is accepting every request.

In Cardinal Cloud, open Explore → Metrics and search for a metric from a namespace you streamed. Names are the CloudWatch namespace and metric name, lowercased, joined with _:

CloudWatchCardinal metric
AWS/EC2 / CPUUtilizationaws_ec2_cpuutilization
AWS/RDS / DatabaseConnectionsaws_rds_databaseconnections
AWS/ApplicationELB / TargetResponseTimeaws_applicationelb_targetresponsetime

Filter by cloud_account_id, cloud_region, aws_namespace, or any CloudWatch dimension such as instanceid.

5

Repeat per region and account

Metric streams are regional. For each additional region, repeat the Firehose and metric stream steps in that region with the same API key. For additional AWS accounts, do the same in each account. Everything lands in one Cardinal organization, separated by the cloud_account_id and cloud_region attributes.

What arrives in Cardinal

Each Metric Streams record is one datapoint carrying {min, max, sum, count} for a period. Cardinal stores those four statistics exactly as rollups, so counts, sums, averages, minimums, and maximums are correct and aggregate correctly across dimensions. CloudWatch does not send the individual samples, so percentiles cannot be recovered from a streamed metric.

Record fieldCardinal
namespace + metric_namemetric name, e.g. aws_elb_latency, type gauge
each dimensions keyattribute, lowercased, e.g. loadbalancername
account_idcloud_account_id
regioncloud_region
namespaceaws_namespace
metric_stream_namemetric_stream_name
unitpassed through verbatim (Seconds, Count, Percent)

Troubleshooting

Firehose reports every rejection in the stream’s CloudWatch Logs group (/aws/kinesisfirehose/<stream name> when created by the console, or the group you configured) and in the Monitoring tab. Cardinal’s response body carries an errorMessage that says why.

SymptomCauseFix
Delivery to HTTP endpoint success is 0 and the error log shows 401 with invalid API keyThe Access key is not a valid organization ingest API key, or the organization is disabledConsole path: re-enter the key under the Firehose stream’s Destination settings. Terraform path: update the Secrets Manager secret’s value; Firehose refreshes its cached secret within 10 minutes. Cardinal accepts a newly issued key within a minute
Error log shows 400 with record is not CloudWatch Metric Streams JSONThe metric stream’s output format is OpenTelemetry 0.7 or 1.0, or the Firehose source is a CloudWatch Logs subscription rather than a metric streamSet the metric stream’s output format to JSON
Error log shows 400 with unsupported cardinalhq.formatThe cardinalhq.format parameter is set to something other than cwmetricsSet it to cwmetrics or remove it
Error log shows 413A single request body exceeded 64 MiB. Base64 encoding makes the body about a third larger than the buffer, so a buffer size near Firehose’s 64 MiB maximum always fails. Firehose treats this as permanent and writes the batch to the backup bucketLower the Buffer size to 48 MiB or less
Objects appearing in the backup bucketCardinal rejected a batch or was unreachable for longer than the retry durationRead the error log for the reason. Backup objects are not re-sent automatically
Incoming records is 0The metric stream is not running, or its role cannot write to FirehoseCheck the metric stream’s status in CloudWatch → Metrics → Streams, and the role’s firehose:PutRecord permission
Delivery succeeds but a namespace is missing in CardinalThe namespace is not in the metric stream’s include list, or is in its exclude listEdit the metric stream’s filters. Changes take effect within minutes
Metrics arrive several minutes lateCloudWatch publishes many service metrics minutes after the fact, and Firehose buffers up to the configured intervalExpected. Lowering the buffer interval below 60 s changes little, because CloudWatch’s own publication delay dominates

Delivery is at least once. If Firehose retries a batch that Cardinal already stored, the retry is deduplicated for about an hour; a much later retry is ingested again, which can double-count that batch’s datapoints for the affected minute.

Cost notes

  • CloudWatch bills per metric update sent to the stream. Include only the namespaces you query, and exclude AWS/Usage.
  • Firehose bills per GiB ingested. GZIP content encoding does not change the Firehose charge (it is billed before compression) but shrinks the transfer.
  • S3 charges for the backup bucket are negligible unless deliveries fail; the lifecycle rule above expires failed batches after 14 days.
  • There is no GetMetricData polling and no agent fleet to run.

Reach out to support@cardinalhq.io for support or to ask questions not answered in our documentation.

Last updated on