Trying Out CloudWatch's New Feature "Metrics Centralization" to Aggregate Metrics Across an Organization

Trying Out CloudWatch's New Feature "Metrics Centralization" to Aggregate Metrics Across an Organization

Tested setting up CloudWatch's new Metrics Centralization ("Centralization rule management" in the console) in an AWS Organizations environment to aggregate metrics and logs across accounts. Covers PromQL alarms on the OTel path and tag-based filtering.
2026.06.30

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.

https://aws.amazon.com/jp/about-aws/whats-new/2026/06/amazon-cross-account-metrics-centralization/

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

https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Cross-Account-Methods.html

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.region dimensions are added automatically
  • OTel metrics via the OTLP endpoint (OTel path): @aws.account / @aws.region attributes 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.

Turn on trusted access

Turn on trusted access 2

Turn on trusted access 3

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 a ValidationException
  • Scope syntax: AccountId = '<account-id>' specifies a single account, OrganizationUnitId IN (...) specifies OUs, and * specifies all accounts
  • LogGroupSelectionCriteria: "*" targets all log groups
  • EncryptedLogGroupStrategy: "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.

centralized-tcp-connections-classic

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-operator cannot be specified
  • PendingPeriod: Number of seconds until transitioning to ALARM
  • RecoveryPeriod: 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.

centralized-cpu-idle-promql

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

Centralized dashboard sample

Notes on PromQL dashboards:

  • Use "chart" as the widget type. The conventional "metric" is for Metric Math / Metrics Insights SQL
  • In this chart widget definition, plotOptions was required. If omitted, the console showed a "Something went wrong" error
  • When a chart widget 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.

https://aws.amazon.com/cloudwatch/pricing/

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.

https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatchMetrics_Centralization.html


そのマルチアカウント運用、気合いで支えていませんか

Organizations や Control Tower で土台は作れても、アカウントもポリシーも増えるほど、運用は「詳しい一人」に寄りかかっていく。属人化が限界を迎える前に、組織として回す仕組み=CCoEへ。5,600社の支援から得た立ち上げの型を、無料資料にまとめました。

CCoE総合支援

組織で回す仕組みの資料をもらう

Share this article

AWSのお困り事はクラスメソッドへ