はじめに
2026 年 9 月、Amazon CloudWatch Omni が一般提供(GA)になりました。
ログはロググループ、メトリクスはメトリクス画面、トレースは X-Ray……と、CloudWatch を使っていると画面を行ったり来たりしがちです。
Omni はこれらを 1 つの Web 画面にまとめ、AI アシスタントに質問しながら調べられるようにした新しい体験のようです。
この記事では、AWS CLI で Omni のドメインとスペースを作り、送ったログを SQL と AI の両方で調べるところまでを試します。
最終的な構成はこうなります。
CloudWatch Omni とは
CloudWatch に溜まっているログ・メトリクス・トレースを、AWS コンソールの外にある専用 Web 画面(と VS Code / Cursor / Kiro の拡張機能)から横断して見られるサービスです。CloudWatch の置き換えではなく、既存のロググループやアラームはそのまま動き続けます。
登場人物は 3 つだけです。会 社のオフィスにたとえると分かりやすいかと思います。
| 概念 | たとえ | 中身 |
|---|---|---|
| ドメイン | 会社の受付 | チーム共通のサインイン URL(https://<名前>.cloudwatch-omni.global.app.aws)。IAM か IAM Identity Center で認証 |
| スペース | 作業部屋 | 実際にテレメトリ(ログやトレースなどの観測データ)を見る場所。1 アカウント・1 リージョンに 1 つ |
| CloudWatch Dataset | 部屋の本棚 | ログとトレースをまとめて SQL で引けるようにする置き場。ロググループから転送して使う |
できることのイメージはこんな感じです。
- AI に質問(Omni agent): 組み込みの AI アシスタントがクエリを書いて実行し、データをもとに答えてくれる
- アプリケーションマップ: トレースから、サービス同士の呼び出し関係と、リクエスト数・エラー・所要時間を自動で表示
- AI エージェントの品質評価: LangGraph / CrewAI / Strands などで作ったエージェントの回答を採点。エラーにはならないが内容が間違っている、といった失敗に気づける
- ダッシュボード・アラート: 条件を満たしたら Slack や SNS に通知
対応リージョン
対応リージョンはバージニア北部・オレゴン・アイルランドの 3 つで、東京はまだ対象外です。
2026年10月19日 追記
東京リージョンで利用可能になりました。
https://aws.amazon.com/jp/about-aws/whats-new/2026/10/amazon-cloudwatch-omni-8-regions/
料金
お金がかかるところを図にすると、こうなります(緑は追加料金なし、オレンジは従量課金)。
たとえば月に 10 GB のログ・スパンを取り込んでいれば、クエリのスキャンは 50 GB まで無料という計算です。
- 取り込み・保存: 通常の CloudWatch 料金のまま(Omni 用にデータの 2 つ目のコピーは作られない)
- ログ・トレースのクエリ: 月間のログ・スパン取り込み量の 5 倍までは無料、超えた分は $0.005/GB スキャン(バージニア北部の価格)
- メトリクスのクエリ(PromQL): 通常の CloudWatch 料金($0.01 / 100 万サンプル)
- AI エージェントの評価: Amazon Bedrock AgentCore Evaluations の料金
- ダッシュボード・アラート・アプリケーションマップ: 追加料金なし(クォータ内)。ただしダッシュボードが実行するクエリはスキャン量に数えられる
- 無料トライアル: 有効化時に $1,000 分のクレジット(30 日間)。OpenTelemetry 形式の取り込みが対象で、対象外のアカウントもある
やってみた
リージョンは バージニア北部(us-east-1) を使います。
コマンドは上から順にコピペすれば zsh / bash で動くようにしています(zsh と AWS CLI 2.37.7 で通しで確認)。
全体の流れはこうなります。
手順 0: 変数を用意する
ドメイン名は全 AWS で一意である必要があるので、ランダムな接尾辞を付けます(aws- / amazon- / cloudwatch- / omni- で始まる名前などは予約されていて使えません)。
export AWS_REGION=us-east-1 AWS_PAGER=""
echo "AWS_REGION=$AWS_REGION"
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
echo "ACCOUNT_ID=$ACCOUNT_ID"
SUFFIX=$(openssl rand -hex 3)
echo "SUFFIX=$SUFFIX"
DOMAIN_NAME="leomarokun-omni-${SUFFIX}"
echo "DOMAIN_NAME=$DOMAIN_NAME"
LOG_GROUP="/handson/omni-app"
echo "LOG_GROUP=$LOG_GROUP"
手順 1: スペース用の IAM ロールを作る
スペースの操作やデータアクセスで、CloudWatch Omni が引き受けるロール(コンソールで作ると CloudWatchOmniOperatorRole という名前になるもの)を用意します。
信頼ポリシーは公式ドキュメントの例に合わせて、sts:AssumeRole / sts:TagSession / sts:SetContext の 3 つを許可します。
cat > space-trust.json <<JSON
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "cloudwatch.amazonaws.com" },
"Action": ["sts:AssumeRole", "sts:TagSession", "sts:SetContext"]
}]
}
JSON
aws iam create-role --role-name OmniSpaceAccessRole \
--assume-role-policy-document file://space-trust.json
aws iam attach-role-policy --role-name OmniSpaceAccessRole \
--policy-arn arn:aws:iam::aws:policy/CloudWatchOmniSpaceAccessPolicy
手順 2: ドメインを作る
今回は IAM Identity Center を使わず、IAM だけで認証するドメインにします。
DOMAIN_ID=$(aws cloudwatchomni create-domain \
--name "$DOMAIN_NAME" --identity-providers IAM \
--query domain.domainId --output text)
echo "DOMAIN_ID=$DOMAIN_ID"
aws cloudwatchomni get-domain --domain-id "$DOMAIN_ID" \
--query 'domain.{status:status,url:customEndpointUrls[0]}'
手順 3: スペースを作る
手順 1 のロールを渡してスペースを作ります。
SPACE_ID=$(aws cloudwatchomni create-space \
--name handson-space --domain-id "$DOMAIN_ID" \
--data-access-role-arn "arn:aws:iam::${ACCOUNT_ID}:role/OmniSpaceAccessRole" \
--query space.spaceId --output text)
echo "SPACE_ID=$SPACE_ID"
aws cloudwatchomni get-space --space-id "$SPACE_ID" --query space.status
手順 4: ロググループを Dataset に転送する
ドキュメントによると、「Dataset 連携」(ロググループ → Dataset への転送設定)はスペースとは別のリソースで、コンソールでは一緒に作られますが、CLI の create-space では作られません。
ロググループのログは、この連携を通らないと Omni の検索対象になりません。
転送対象は ロールの logs:IntegrateWithDataset 権限で決まります。
ドキュメントの例は log-group:*(全ロググループ)ですが、何が検索対象に入るかを把握しやすいよう、今回は テスト用ロググループ 1 つだけに絞りました。
cat > dataset-trust.json <<JSON
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "logs.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "aws:SourceAccount": "${ACCOUNT_ID}" },
"ArnLike": { "aws:SourceArn": "arn:aws:observabilityadmin:${AWS_REGION}:${ACCOUNT_ID}:dataset-integration/default" }
}
}]
}
JSON
cat > dataset-policy.json <<JSON
{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow", "Action": "logs:IntegrateWithDataset",
"Resource": ["arn:aws:logs:${AWS_REGION}:${ACCOUNT_ID}:log-group:${LOG_GROUP}"] },
{ "Effect": "Allow", "Action": "cloudwatch:PutRecords", "Resource": "*" }
]
}
JSON
aws iam create-role --role-name OmniDatasetIntegrationRole \
--assume-role-policy-document file://dataset-trust.json
aws iam put-role-policy --role-name OmniDatasetIntegrationRole \
--policy-name forward-handson-logs --policy-document file://dataset-policy.json
DATASET_ARN=$(aws observabilityadmin create-dataset-integration \
--role-arn "arn:aws:iam::${ACCOUNT_ID}:role/OmniDatasetIntegrationRole" \
--query Arn --output text)
echo "DATASET_ARN=$DATASET_ARN"
手順 5: テストログを送る
注文処理アプリの INFO ログと ERROR ログを 1 件ずつ書き込みます。
aws logs create-log-group --log-group-name "$LOG_GROUP"
aws logs create-log-stream --log-group-name "$LOG_GROUP" --log-stream-name app
NOW=$(( $(date +%s) * 1000 ))
cat > events.json <<JSON
[
{ "timestamp": ${NOW}, "message": "{\"level\":\"INFO\",\"msg\":\"order created\",\"orderId\":\"o-001\"}" },
{ "timestamp": $((NOW + 1)), "message": "{\"level\":\"ERROR\",\"msg\":\"payment timeout\",\"orderId\":\"o-002\"}" }
]
JSON
aws logs put-log-events --log-group-name "$LOG_GROUP" \
--log-stream-name app --log-events file://events.json
手順 6: SQL で ERROR ログだけ引く
CLI からのクエリは 「セッション」を作り、その中でクエリを投げて、結果を取りに行く 3 段階です。
ログは Dataset の logs.default テーブルに入ります。
cat > query.sql <<'SQL'
SELECT `@timestamp`, `@message`
FROM "logs.default"
WHERE `@timestamp` BETWEEN NOW() - INTERVAL '1' HOUR AND NOW()
AND `@message` LIKE '%ERROR%'
ORDER BY `@timestamp` DESC
LIMIT 10
SQL
SESSION_ID=$(aws cloudwatchomni start-telemetry-query-session \
--session-name handson --query sessionId --output text)
echo "SESSION_ID=$SESSION_ID"
QUERY_ID=$(aws cloudwatchomni start-telemetry-query \
--session-id "$SESSION_ID" --query-string file://query.sql \
--query queryId --output text)
echo "QUERY_ID=$QUERY_ID"
aws cloudwatchomni get-telemetry-query-results --query-id "$QUERY_ID"
手順 7: Omni の Web 画面を開く
スペースを作った IAM ユーザー(CLI を実行したユーザー)は、自動で Space Admin(スペースの管理者)として登録されます。
ブラウザで AWS コンソールに 別の IAM ユーザー でサインインしている場合は、そのユーザーもスペースに登録しておきます。
CONSOLE_USER_ARN="arn:aws:iam::${ACCOUNT_ID}:user/<コンソールで使う IAM ユーザー名>"
echo "CONSOLE_USER_ARN=$CONSOLE_USER_ARN"
aws cloudwatchomni create-access-grant \
--domain-id "$DOMAIN_ID" --space-id "$SPACE_ID" \
--name console-user-admin \
--principal principalType=IAM_USER,principalId="$CONSOLE_USER_ARN" \
--permission SPACE_ADMIN
登録できたら、ドメインの URL をブラウザで開きます。
echo "https://${DOMAIN_NAME}.cloudwatch-omni.global.app.aws"
サインイン画面には「Sign in with SSO」「Sign in via AWS console」「Try playground」が表示されます。
今回は IAM Identity Center をつないでいないので「Sign in via AWS console」を選び、AWS コンソールのアカウントでサインインします。
ナビゲーションの Explore logs では、手順 6 と同じ SQL を画面から実行できます(Builder / Code の切り替えで Code にすると SQL を直接書けます)。
手順 8: Omni agent に質問してみる
SQL を書かなくても、ホーム画面の入力欄から Omni agent に質問できます。
今回は次のように聞いてみます。
直近1時間で ERROR レベルのログを出したロググループと、その内容を教えて
後片付け
スペースが残っているとドメインは消せないので、スペースから消します。
スペースを消すとダッシュボードなどスペース内のリソースもまとめて削除されます(元のログはロググループ側に残ります)。
aws cloudwatchomni stop-telemetry-query-session --session-id "$SESSION_ID"
aws observabilityadmin delete-dataset-integration --arn "$DATASET_ARN"
aws cloudwatchomni delete-space --space-id "$SPACE_ID"
aws cloudwatchomni delete-domain --domain-id "$DOMAIN_ID"
aws logs delete-log-group --log-group-name "$LOG_GROUP"
aws iam delete-role-policy --role-name OmniDatasetIntegrationRole --policy-name forward-handson-logs
aws iam delete-role --role-name OmniDatasetIntegrationRole
aws iam detach-role-policy --role-name OmniSpaceAccessRole \
--policy-arn arn:aws:iam::aws:policy/CloudWatchOmniSpaceAccessPolicy
aws iam delete-role --role-name OmniSpaceAccessRole
補足: コンソールで作る場合
コンソールなら、公式のセットアップ手順(単一アカウント)のとおり、CloudWatch コンソールの Omni 設定ページから ドメインとスペースを 1 つの流れでまとめて作れます。手順 1・4 の IAM ロールや Dataset 連携も自動で作られます。
ただし、AWS Organizations の管理アカウントでは、コンソールの選択肢が変わります。今回のアカウントは管理アカウントだったため、コンソールでは次のように表示されました。
| ドメインの種類 | 管理アカウントのコンソールでの表示 |
|---|---|
| アカウントレベルドメイン | 「管理アカウントはアカウントレベルのドメインを作成できません」と表示され、選べない |
| 組織レベルドメイン | ドメインは作れるが、「スペースは、組織内の管理者以外のアカウントのみが作成できます」と表示され、管理アカウントにはスペースを作れない |
一方、この記事の CLI の手順は、同じ管理アカウントでも最後まで通りました。コンソールと CLI で挙動が異なるので、管理アカウントでの扱いは今後変わる可能性もありそうです。組織で使うなら、組織向けのセットアップ手順どおり 管理アカウントで組織レベルドメインを作り、スペースはメンバーアカウントに作る 構成が公式の想定のようです。
注意点
- スペースをまたいだクエリはできません。複数アカウントのテレメトリを 1 つのスペースで見たいときは、CloudWatch の centralization rule でスペースのあるアカウントに集約します。
- IAM Identity Center で SSO させる場合、ドメインは Identity Center と同じリージョンに作る必要があります。今回は IAM のみにしたのでこの制約を受けませんでした。






