Trying Out CloudWatch's New Feature "Metrics Centralization" to Aggregate Metrics Across an Organization
This page has been translated by machine translation. View original
Introduction
In June 2026, Metrics Centralization was added to CloudWatch. It is a feature that physically copies metrics within Organizations to a destination account, and lets you run alarms, dashboards, and queries locally from the destination.
Since it is positioned differently from the previously available OAM (Cross-Account Observability), I'll organize the differences in a comparison table.
| OAM (Cross-Account Observability) | Metrics Centralization | |
|---|---|---|
| Mechanism | The monitoring account views source telemetry across accounts | Metrics are physically copied to the destination account |
| Data location | Remains in the source account | Replicated to the destination |
| Alarm creation | Possible from the monitoring account (cross-account reference) | Created in the destination as local metrics |
| Source identification | Filtering by Account ID | Classic: :@aws.account dimension, OTel: @aws.account attribute is added automatically |
| Configuration owner | The source account approves the connection to the sink | The management account specifies everything in bulk with a Centralization Rule |
| Organizations required | Not required (can be configured between individual accounts) | Required |
| Targets | Metrics / logs / traces | Metrics + logs |
| Historical data | Existing data on the source side can be referenced | Only new data after rule creation |
| Cost (metrics) | Within normal CloudWatch API / feature usage charges | The first metric copy is free |
How Centralization Works
You create a Centralization Rule from the management account and specify the target accounts and regions. Source identification information is added to the centralized metrics as follows.
- Custom metrics via PutMetricData (Classic path):
:@aws.account/:@aws.regiondimensions are added automatically - OTel metrics via the OTLP endpoint (OTel path):
@aws.account/@aws.regionattributes are added automatically
Logs can also be centralized with the same Centralization Rule. By adding SourceLogsConfiguration, log groups in the specified accounts and regions are forwarded to the destination.
Limitations
- Only new data after rule creation is targeted (historical data is not forwarded)
- Selective filtering (centralizing only specific metrics) is not supported at this time
Test Environment and Prerequisites
| Account | Role | Account ID |
|---|---|---|
| Management account | Create Centralization Rule, enable Trusted access | 111111111111 |
| Monitored account | EC2 + CWAgent (Classic + OTel in parallel) + Enrichment enabled | 222222222222 |
| Destination account | Metric reference, alarms, dashboard construction | 333333333333 |
- Region: ap-northeast-1 (Tokyo)
- EC2: t4g.small (arm64) / Amazon Linux 2023
- CloudWatch Agent: v1.300069.0
Enabling Trusted Access and Creating the Centralization Rule
Enabling Trusted Access
To use Metrics Centralization, you need to enable CloudWatch Trusted access in the management account.
I enabled it from the Organization tab on the Settings screen of the CloudWatch console.



Creating the Centralization Rule (Metrics + Logs)
Create the rule from the management account using the observabilityadmin CLI.
aws observabilityadmin create-centralization-rule-for-organization \
--rule-name metrics-centralization-202606 \
--rule '{
"Source": {
"Regions": ["ap-northeast-1"],
"Scope": "AccountId = '\''222222222222'\''",
"SourceMetricsConfiguration": {},
"SourceLogsConfiguration": {
"LogGroupSelectionCriteria": "*",
"EncryptedLogGroupStrategy": "SKIP"
}
},
"Destination": {
"Region": "ap-northeast-1",
"Account": "333333333333"
}
}' \
--region ap-northeast-1
{
"RuleArn": "arn:aws:observabilityadmin:ap-northeast-1:111111111111:organization-centralization-rule/metrics-centralization-202606"
}
Configuration points:
SourceMetricsConfiguration: {}must be specified explicitly even as an empty object. If omitted, you get aValidationExceptionScopesyntax:AccountId = '<account-id>'specifies a single account,OrganizationUnitId IN (...)specifies OUs, and*specifies all accountsLogGroupSelectionCriteria: "*"targets all log groupsEncryptedLogGroupStrategy: "SKIP"skips log groups encrypted with a CMK
Verifying Metric Centralization
Here is an overview of the centralization results.
| Path | Verification method | Source identification key | Arrival time |
|---|---|---|---|
| Classic (PutMetricData) | ListMetrics + GetMetricData (Metrics Insights SQL) | :@aws.account / :@aws.region (dimensions) |
About 5 minutes |
| OTel (OTLP) | PromQL API (awscurl) | @aws.account / @aws.region (attributes) |
About 5 minutes |
Classic Path (Metrics Insights)
When I ran list-metrics in the destination account, I could see 8 metrics from the source account with the :@aws.account / :@aws.region dimensions.
aws cloudwatch list-metrics --namespace CWAgent --recently-active PT3H --region ap-northeast-1
| Metric | :@aws.account | :@aws.region |
|---|---|---|
| cpu_usage_idle | 222222222222 | ap-northeast-1 |
| mem_used_percent | 222222222222 | ap-northeast-1 |
| netstat_tcp_established | 222222222222 | ap-northeast-1 |
| disk_used_percent | 222222222222 | ap-northeast-1 |
| ... (8 metrics in total) |
Let's confirm that actual data can also be retrieved with GetMetricData.
aws cloudwatch get-metric-data \
--metric-data-queries '[{
"Id": "cpu_idle",
"Expression": "SELECT AVG(\"cpu_usage_idle\") FROM \"CWAgent\" WHERE \":@aws.account\" = '\''222222222222'\''",
"Period": 60
}]' \
--start-time 2026-06-29T09:50:00Z \
--end-time 2026-06-29T10:10:00Z \
--region ap-northeast-1
{
"Values": [98.43, 99.19, 98.87],
"Timestamps": ["2026-06-29T19:06:00+09:00", "2026-06-29T19:02:00+09:00", "2026-06-29T19:01:00+09:00"]
}
Data points arrived about 5 minutes after rule creation.
OTel Path (PromQL)
On the CWAgent in the monitored account, in addition to the Classic configuration, I applied an additional OTel YAML via append-config, so metrics are also sent to the OTLP endpoint in parallel.
OTel YAML (CWAgent append-config)
receivers:
hostmetrics/cwagent:
collection_interval: 60s
scrapers:
cpu: {}
memory: {}
disk: {}
filesystem: {}
processors:
batch/cwagent: {}
resource/cwagent:
attributes:
- key: service.name
value: "ec2-metrics-source"
action: upsert
exporters:
otlphttp/cwagent:
metrics_endpoint: https://monitoring.ap-northeast-1.amazonaws.com/v1/metrics
auth:
authenticator: sigv4auth/cwagent
extensions:
sigv4auth/cwagent:
region: "ap-northeast-1"
service: "monitoring"
service:
extensions: [sigv4auth/cwagent]
pipelines:
metrics/cwagent:
receivers: [hostmetrics/cwagent]
processors: [resource/cwagent, batch/cwagent]
exporters: [otlphttp/cwagent]
From the CloudWatch Agent on the same EC2 instance, I run the conventional CloudWatch metric sending (PutMetricData, Classic path) and OTLP sending (OTel path) in parallel. I added a /cwagent suffix to component names to avoid conflicts with the existing configuration, and the sigv4auth extension uses the instance profile credentials.
I verify the OTel path metrics with PromQL. Since the AWS CLI has no dedicated subcommand for the CloudWatch PromQL API, I call it directly with awscurl.
awscurl --service monitoring --region ap-northeast-1 \
-X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'query={"__name__"="system.cpu.time", "@aws.account"="222222222222"}' \
"https://monitoring.ap-northeast-1.amazonaws.com/api/v1/query"
From the response, I confirmed that @aws.account / @aws.region are added. The resource attribute @resource.service.name is also propagated to the destination.
{
"metric": {
"@aws.account": "222222222222",
"@aws.region": "ap-northeast-1",
"@resource.service.name": "ec2-metrics-source",
"__name__": "system.cpu.time",
"__type__": "Sum",
"cpu": "cpu0",
"state": "idle"
},
"value": [1782727830.413, "5327538.25"]
}
When specifying labels such as @aws.account or @resource.service.name in CloudWatch PromQL, enclose the label name in double quotes.
{"__name__"="system.cpu.time", "@aws.account"="222222222222", "state"="idle"}
Integration with Vended Metric Enrichment
In this test, AWS vended metrics such as EC2's CPUUtilization could not be seen from the destination without Enrichment. However, when Vended Metric Enrichment is enabled, they are generated as OTel metrics (ExponentialHistogram type) and become targets of Centralization.
The CWAgent hostmetrics checked in the previous section (system.cpu.time, etc.) were explicitly sent via OTLP. The Enrichment metrics covered here are automatically generated by AWS, and differ in that EC2 tags and ARNs are added as attributes.
Propagation of Enrichment Metrics via Centralization
In the test, I enabled Vended Metric Enrichment in both the monitored account and the destination account.
When I ran PromQL in the destination account, I was able to filter the CPUUtilization metric generated by Enrichment based on tags. I specified a particular EC2 instance with @aws.tag.Name.
awscurl --service monitoring --region ap-northeast-1 \
-X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'query=histogram_avg({"CPUUtilization", "@aws.account"="222222222222", "@aws.tag.Name"="ec2-instance-name"})' \
"https://monitoring.ap-northeast-1.amazonaws.com/api/v1/query"
You can apply histogram_avg() and histogram_quantile() to metrics that can be handled as ExponentialHistogram thanks to Enrichment. The same histogram functions cannot be used with Classic PutMetricData metrics.
Verifying Log Centralization
Log Group Arrival and Stream Naming Convention
When log centralization is enabled in the Centralization Rule, log groups with the same names as those in the source account are automatically created in the destination.
aws logs describe-log-streams \
--log-group-name "/aws/ec2/ec2-instance/system" \
--region ap-northeast-1
[
{
"name": "i-0123456789abcdef0/messages-222222222222-ap-northeast-1",
"lastEvent": 1782733095809
}
]
A -{source account ID}-{source region} suffix is automatically added to stream names. From this naming convention, it appears to be designed so that even when same-named log groups from multiple source accounts are centralized, the source can be identified by stream name (this was verified with a single source account).
Searching with filter-log-events
I confirmed that filter pattern searches with filter-log-events work correctly on the centralized logs.
aws logs filter-log-events \
--log-group-name "/aws/vendedlogs/states/resource-monitor" \
--filter-pattern "ExecutionSucceeded" \
--region ap-northeast-1 \
--limit 1
{
"logStreamName": "states/resource-monitor/2026-06-29-11-40/00000000-222222222222-ap-northeast-1",
"message": "{\"type\":\"ExecutionSucceeded\",\"details\":{\"output\":\"{\\\"totalChecks\\\":9,\\\"criticalCount\\\":0,...}\"}}"
}
Step Functions execution logs also arrived at the destination and could be searched with a filter pattern.
Logs Insights
Logs Insights also worked correctly on the centralized logs. However, there was a time lag of about 15 to 20 minutes right after centralization before logs were reflected in query results.
aws logs start-query \
--log-group-name "/aws/ec2/ec2-instance/system" \
--start-time $(date -d '1 hour ago' +%s) \
--end-time $(date +%s) \
--query-string 'fields @timestamp, @message, @logStream | sort @timestamp desc | limit 5' \
--region ap-northeast-1
Since the stream name (with the suffix) is displayed in the @logStream field, you can identify the source account from Logs Insights query results as well.
Setting Up Alarms
Classic Alarm (MetricStat)
I narrowed down to the source account with the :@aws.account dimension and set a threshold on the number of TCP connections.
aws cloudwatch put-metric-alarm \
--alarm-name "centralized-tcp-connections-classic" \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 1 \
--metric-name netstat_tcp_established \
--namespace CWAgent \
--period 60 \
--statistic Average \
--threshold 3 \
--treat-missing-data missing \
--dimensions '[
{"Name":"InstanceId","Value":"i-0123456789abcdef0"},
{"Name":":@aws.account","Value":"222222222222"},
{"Name":":@aws.region","Value":"ap-northeast-1"}
]' \
--region ap-northeast-1
It transitioned to ALARM in about 1 minute.

PromQL Alarm (EvaluationCriteria)
For OTel path metrics, I use a PromQL-based alarm. Since it is configured with EvaluationCriteria.PromQLCriteria, you need to pass JSON with --cli-input-json.
aws cloudwatch put-metric-alarm \
--cli-input-json '{
"AlarmName": "centralized-cpu-idle-promql",
"EvaluationCriteria": {
"PromQLCriteria": {
"Query": "avg(rate({\"__name__\"=\"system.cpu.time\", \"@aws.account\"=\"222222222222\", \"state\"=\"idle\"}[5m])) * 100 > 90",
"PendingPeriod": 60,
"RecoveryPeriod": 60
}
},
"EvaluationInterval": 60
}' \
--region ap-northeast-1
To make testing easy, this alarm is set to go into ALARM when the CPU idle value (rate of the idle state × 100) exceeds 90. When monitoring CPU utilization in actual operation, you need to design the state filtering and aggregation method.
Syntax points for PromQL alarms:
- The threshold condition is included in the query (
> 90). The conventional--threshold/--comparison-operatorcannot be specified PendingPeriod: Number of seconds until transitioning to ALARMRecoveryPeriod: Number of seconds until returning to OK- The initial state is OK (unlike INSUFFICIENT_DATA in Classic)
It transitioned to ALARM in about 3 minutes. The transition time depends on PendingPeriod, the evaluation interval, metric arrival delay, and the PromQL range specification.

Building the Dashboard
Dashboard Definition
aws cloudwatch put-dashboard \
--dashboard-name "centralized-ec2-otel" \
--dashboard-body '{
"widgets": [
{
"type": "chart",
"x": 0, "y": 0, "width": 6, "height": 6,
"properties": {
"view": "line",
"title": "CPU Utilization (Vended Metric / タグ絞り込み)",
"region": "ap-northeast-1",
"data": {
"queries": [{
"id": "A",
"type": "cloudwatch-metrics",
"language": "PromQL",
"query": "histogram_avg({\"CPUUtilization\", \"@aws.account\"=\"222222222222\", \"@aws.tag.Name\"=\"ec2-instance-name\"})"
}]
},
"plotOptions": {
"legend": {"position": "bottom", "show": true},
"xAxis": {"type": "datetime"},
"yAxis": [{"type": "linear"}],
"style": {
"label": {"position": "top", "show": false},
"lineOptions": {"filled": false, "pattern": "solid", "spline": false, "stacked": false, "width": 2}
}
}
}
},
{
"type": "chart",
"x": 6, "y": 0, "width": 6, "height": 6,
"properties": {
"view": "line",
"title": "Memory Usage % (OTel hostmetrics)",
"region": "ap-northeast-1",
"data": {
"queries": [{
"id": "A",
"type": "cloudwatch-metrics",
"language": "PromQL",
"query": "sum({\"__name__\"=\"system.memory.usage\", \"@aws.account\"=\"222222222222\", \"state\"=\"used\"}) / sum({\"__name__\"=\"system.memory.usage\", \"@aws.account\"=\"222222222222\"}) * 100"
}]
},
"plotOptions": {
"legend": {"position": "bottom", "show": true},
"xAxis": {"type": "datetime"},
"yAxis": [{"type": "linear"}],
"style": {
"label": {"position": "top", "show": false},
"lineOptions": {"filled": false, "pattern": "solid", "spline": false, "stacked": false, "width": 2}
}
}
}
}
]
}' \
--region ap-northeast-1

Notes on PromQL dashboards:
- Use
"chart"as the widget type. The conventional"metric"is for Metric Math / Metrics Insights SQL - In this
chartwidget definition,plotOptionswas required. If omitted, the console showed a "Something went wrong" error - When a
chartwidget created from the CLI results in an error, adding the widget from Query Studio in the console and pinning it to the dashboard also worked
Cost
The official Pricing page states "The first copy of centralized metrics is free," so no additional transfer cost is incurred for metric copying to the first destination. Normal CloudWatch charges separately apply to log centralization, query execution, alarms, and so on.
Summary
I confirmed that by using CloudWatch Metrics Centralization, you can centralize metrics and logs within AWS Organizations into a destination account and run alarms, dashboards, and queries from the destination.
In this test, CWAgent Classic path (PutMetricData) metrics were centralized with the :@aws.account / :@aws.region dimensions and could be used from Metrics Insights and regular CloudWatch alarms. On the other hand, OTel path metrics were centralized with the @aws.account / @aws.region attributes and could be referenced from the PromQL API and PromQL alarms.
In addition, by combining it with Vended Metric Enrichment, I was able to handle EC2's CPUUtilization with PromQL, and confirmed filtering by EC2 tags and the use of histogram_avg() on ExponentialHistogram. Because Enrichment adds attributes such as tags and ARNs, it looks useful for attribute-based analysis that is difficult with Classic metrics alone.
For logs as well, I confirmed that they can be centralized with the same Centralization Rule, that same-named log groups are created in the destination, and that log stream names get a suffix with the source account ID and region. Centralized logs can also be searched with filter-log-events and Logs Insights.
Unlike OAM, which views source account telemetry across accounts, Metrics Centralization is characterized by letting you treat the data copied to the destination as local resources. It looks like a strong option when you want to handle monitoring data under Organizations collectively in a destination account.

