# Combining CloudWatch ## Across Accounts and Regions
Andraž Brodnik — brodul
2026-09-24
--- ## Follow along
Scan for the repo — slides, Terraform, and docs
--- ## What is CloudWatch? A family of sub-services, not one thing: - **Metrics**, **Logs**, **Alarms**, **Dashboards** - EventBridge, Synthetics, RUM, Contributor Insights, … Cross-account support differs **per sub-service**. **This talk: Metrics only** (and not `GetMetricData`). --- ## Two boundaries - **Account** — billing, security, blast radius. Invisible to others by default. - **Region** — physical isolation. CloudWatch is **regional**: a metric in `us-east-1` doesn't exist in `eu-central-1`. **N accounts × M regions = fragmented visibility** --- ## The demo setup 3 real AWS accounts, 2 regions, 1 EC2 instance each: | Account | Region | Role | Instance | |---|---|---|---| | `account_a` | us-east-1 | monitoring account | `i-01c26eedadfd81489` | | `account_b` | us-east-1 | source, same region | `i-0d8eb892991961cac` | | `account_c` | us-west-2 | source, different region | `i-0471d5a973730b195` | `account_c` sits in another region **on purpose**. --- ## Approach 1: OAM **Observability Access Manager** — AWS's recommended default - **Sink** — monitoring account, one per region - **Link** — each source account, one per region - Metrics, logs, traces — the richest picture **Catch**: sinks/links can't cross regions. --- ## Approach 1: OAM, diagrammed ```mermaid flowchart LR subgraph rA["Region: us-east-1"] subgraph accA["account_a (monitoring)"] sink["OAM Sink"] end subgraph accB["account_b (source)"] linkB["OAM Link"] end linkB -->|metrics, logs, traces| sink end subgraph rB["Region: us-west-2"] subgraph accC["account_c (source)"] linkC["OAM Link — no sink here!"] end end linkC -.->|"cannot cross region"| sink style linkC stroke-dasharray: 5 5,stroke:#f66 ``` --- ## Approach 1, live Dashboard **"1: OAM Cross-Account View"** [open in Grafana ↗](https://boldiguana716.grafana.net/public-dashboards/a2f03ab4a1c1487e95903fb0fa7c9a23) - `account_b` shows up on `account_a`'s dashboard via the link - `account_c` is **missing** — different region --- ## Approach 2: Cross-account, cross-Region console Older IAM-role mechanism (`CloudWatch-CrossAccountSharingRole`) - ✅ Metrics + dashboards, **automatic cross-region** - ❌ No logs, view-only alarms Cross-region for free — but **confusing to set up**. --- ## Approach 2: console, diagrammed ```mermaid flowchart LR subgraph mon["Monitoring account"] role1["ServiceRoleFor
CloudWatchCrossAccountV2"] console["CloudWatch Console
(merged view)"] end subgraph src1["Source account, region A"] share1["CloudWatch-
CrossAccountSharingRole"] end subgraph src2["Source account, region B"] share2["CloudWatch-
CrossAccountSharingRole"] end role1 -->|sts:AssumeRole| share1 role1 -->|sts:AssumeRole| share2 share1 -->|"metrics + dashboards
(view only)"| console share2 -->|"metrics + dashboards
(view only)"| console ``` --- ## Approach 2, live — or rather, not No Grafana dashboard **on purpose**: - The merged view only renders **inside the AWS Console** - No API for it — nothing a third-party tool can call - In the console: all 3 accounts, both regions ✅ --- ## Approach 3: Export / stream it out Metric Streams → Kinesis Firehose → S3 or a third-party sink - **True consolidation** of AWS service metrics (`AWS/EC2`, …) into one store - Long retention, external tools (Grafana, Datadog, …) - One stream per account/region, shared destination --- ## Approach 3: export, diagrammed ```mermaid flowchart LR subgraph accA["account_a"] msA["Metric Stream"] --> fhA["Firehose"] end subgraph accB["account_b"] msB["Metric Stream"] --> fhB["Firehose"] end subgraph accC["account_c"] msC["Metric Stream"] --> fhC["Firehose"] end fhA -->|OTLP over HTTP| grafana[("Grafana Cloud
Prometheus / Mimir")] fhB -->|OTLP over HTTP| grafana fhC -->|OTLP over HTTP| grafana grafana --> dash["One dashboard
plain PromQL"] ``` --- ## Approach 3, live Dashboard **"3: Metric Streams (OTLP)"** [open in Grafana ↗](https://boldiguana716.grafana.net/public-dashboards/de83085e81794d9e81cc16a334236671) - All 3 accounts, both regions, **one PromQL query** - No CloudWatch data source at all --- ## Approach 4: Centralization Newest built-in option (June 2026) — **copies** metrics into one account + region - ✅ Custom metrics: `PutMetricData`, EMF, OTLP - ❌ AWS service metrics (`AWS/EC2`, RDS, …) — not supported - ✅ Cross-account **and** cross-region; alarms, PromQL, dashboards on the copy - ⚠️ Needs AWS Organizations · all metrics or nothing · no history First copy free. --- ## Which one, when? | Need | Pick | |---|---| | Metrics + logs + traces | OAM | | Cross-region view in the AWS Console | Console feature | | One store in a tool you already run | Metric Streams | | Custom metrics in one account, org-wide | Centralization | They compose — mix and match. --- ## They compose — AWS does it too **Database Insights** (RDS / Aurora) cross-account, cross-region needs **both**: - **OAM** — the telemetry, set up **in every region** - **Console feature** — the cross-region reach, set up **once** (global) Fleet view: up to **3 regions** at a time, read-only. --- ## What it costs | Approach | Cost | Why | |---|---|---| | 1: OAM | **$0** | query-time read | | 2: Console | **$0** | query-time read | | 3: Metric Streams | **~$21/month** | write-time: every update re-emitted | Demo: 582 metrics, 235,616 updates/day at $0.003 / 1,000. Filtering to `AWS/EC2` would cut it **5-10x**. --- ## Conclusion - **Start with OAM** — easy: 3 resources, free, metrics + logs + traces - **Console feature** — cross-region, but confusing and hard to set up - **Stream out** if you already run the infra for it (Grafana, Datadog, …) - **Centralization** for custom metrics, org-wide — not AWS service metrics - **Region** is the real boundary, not the account --- ## Take it home
Terraform for all 3 approaches, docs, and this deck. Questions?