Prerequisites
- A deployed Sawmills Collector. Use v1.1120.0 or later for the documented drop/retry behavior and drop counters.
- A pipeline with a destination that accepts metrics.
- AWS permissions to configure S3, SQS, Firehose, CloudWatch Metric Streams, and their IAM roles.
- Outbound connectivity from the collector to S3, SQS, and the AWS identity service used by its role, such as STS for IRSA.
123456789012, Region us-west-2, bucket example-cloudwatch-metrics, prefix cwmetrics/, and queue example-cloudwatch-metrics. Replace these consistently with your resources. This walkthrough keeps the resources in one account and Region.
Step 1: Create the bucket and queue
- Create an S3 bucket for the metrics, with a dedicated object prefix such as
cwmetrics/. - Create a standard SQS queue in the same Region as the bucket. Record its URL and ARN.
- Use S3-managed encryption (SSE-S3) and SQS-managed encryption (SSE-SQS) for this example. If you use customer-managed KMS keys, also configure the permissions described below.
Step 2: Allow S3 to notify SQS
Add this statement to the queue’s access policy, substituting your queue ARN, bucket name, and bucket owner’s account ID. Preserve any other statements the queue needs.- Event types: all object creation events (
s3:ObjectCreated:*), including multipart upload completion. - Prefix:
cwmetrics/. - Suffix: leave empty; Firehose object keys do not need a
.jsonextension. - Destination: the SQS queue from Step 1.
kms:GenerateDataKey and kms:Decrypt. See AWS permissions for notification destinations.
Step 3: Grant the collector access
Attach the following IAM policy to the role used by the collector pods that run this source. This role is separate from the roles CloudWatch and Firehose use to deliver metrics.kms:Decrypt on the applicable keys and allow the role in the key policies. Firehose also needs the permissions required by your bucket’s encryption configuration. The example policy above assumes SSE-S3 and SSE-SQS.
Step 4: Create the Firehose stream
Create an Amazon Data Firehose delivery stream with these settings:
Allow Firehose to create a service role with access to write to the bucket, or select an existing role with the equivalent permissions. Keep any error-output prefix outside
cwmetrics/ so error files do not enter the source. Firehose buffers records before writing objects, so account for its buffering interval when checking delivery.
Step 5: Create the CloudWatch Metric Stream
In CloudWatch → Metrics → Streams, create a metric stream with:- Custom setup with Firehose: select the delivery stream from Step 4.
- Output format: explicitly select JSON.
- Metrics: select the namespaces and metrics you want to send.
- Service role: create or select a role that allows CloudWatch to write to this Firehose stream.
The source expects CloudWatch Metric Streams
JSON,
with one JSON object per line. Keep this format through Firehose and S3; do
not convert it to OTLP, Parquet, or a JSON array.
Step 6: Add the source in Sawmills
Open your pipeline, add a source, and select CloudWatch Metrics. Set:
Connect a destination that accepts metrics, then deploy the pipeline to the collector.
Step 7: Verify delivery and configure retention
- Confirm that the CloudWatch Metric Stream is running and includes metrics currently being emitted.
- After Firehose’s buffering interval, check for new S3 objects under
cwmetrics/. Inspect a sample object’s contents for newline-delimited CloudWatch JSON. - Check SQS monitoring for incoming and deleted messages. A low queue depth can be normal when the collector consumes quickly; it does not prove successful ingestion.
- Check collector logs for
Processing new S3 object, authentication/download errors, and downstream errors. Monitor the drop counters. - Verify recent metrics at the pipeline’s destination, including their expected namespace and dimensions. Allow for CloudWatch delivery, Firehose buffering, and collector processing.
- Add an S3 lifecycle expiration rule scoped to
cwmetrics/. Choose retention long enough to cover backlog and retries. The collector never deletes S3 objects, and lifecycle expiration does not wait for ingestion.