運用管理画面を作ると、最初は画面やAPIのコードにサービス名、クラスタ名、ARN、URLを書きたくなる。 しかしサービスが1つ増えるたび、構成が1か所変わるたびに、画面の修正とデプロイが必要になる。 構成の事実をTerraformと画面の2か所に持つことになり、いずれ片方だけが古くなる。 本稿では、運用管理画面の置き場所で扱った「独立した管理画面」を、モジュール群の上でどう作るかを解説する。 中心にあるのは、構成の出力を、画面が読む契約にするという考え方である。
1. 課題: 運用画面が構成を知りすぎる
運用画面が見せたいのは、たとえば次のような情報である。
- CDの実行はどこまで進んだか
- 新しいタスク定義は作られたか、デプロイは完了したか
- ターゲットは正常か、利用者はエンドポイントに到達できるか
これらを取得するには、CodeBuildのプロジェクト、ECSのクラスタとサービス、ターゲットグループ、配信のURLといったリソースの識別子が要る。 識別子を画面やAPIのコードに書くと、次のことが起きる。
どれも、構成の事実を2か所に持つことから生じる。
2. 考え方: 構成の出力を画面の契約にする
構成の事実は、それを作ったTerraformが一番正確に知っている。 そこで、Terraformがモジュールの出力から画面に必要な情報を組み立て、実行基盤へ渡す。 運用APIと画面は、その情報の形だけを知り、個々のリソースの名前は知らない。
役割を分けると、それぞれが知るべきことは次のように限られる。
この分け方により、サービスやリソースが増減しても、変更はTerraformの記述だけで済み、画面とAPIは修正しなくてよい。 これはWebサービスを運用し続けるためのTerraform設計の「部品どうしは出力でつなぐ」「ARNは組み立てず、参照する」という規則を、画面まで延ばしたものである。
3. 接続情報を組み立て、Lambdaへ渡す
出力から組み立てる
環境ごとの記述で、サービスごとの接続情報をモジュールの出力から組み立てる。値を手で書かない。
# 概念例: サービスごとの接続情報を、モジュールの出力から組み立てる
|
locals {
|
ops_services = {
|
service_a = {
|
display_name = "サービスA"
|
deploy_type = "container"
|
cluster_arn = module.service_a.ecs_cluster_arn
|
service_arn = module.service_a.ecs_service.arn
|
target_group_arn = module.service_a.target_group_arn
|
cd_project_name = module.service_a_cd.project_name
|
}
|
# サービスを追加するときは、ここに1件足す
|
}
|
} |
画面に出す表示名や、デプロイの方式(コンテナ、配信、関数など)もここで持つ。 どのサービスがどの方式でデプロイされるかは構成の事実であり、画面のコードで判定しない。
どう渡すか
Lambdaへ値を渡す手段は、主に環境変数とシークレットである。
サービスの一覧は、サービスが増えるほど大きくなる。 そのため接続情報の本体はTerraformがJSONにしてシークレットへ置き、Lambdaにはその置き場所だけを環境変数で渡す、という分け方が扱いやすい。
Lambdaからの取得には、AWS Parameters and Secrets Lambda Extensionを使える。 拡張機能はシークレットを既定で300秒キャッシュするため、画面を更新するたびにSecrets Managerを呼ばずに済む。 Terraformで接続情報を変更すると、キャッシュの期限が切れた時点で画面に反映される。 アプリ側で独自のキャッシュを重ねない。期限を二重に管理することになるためである。
形を検証する
APIは、接続情報を読んだ時点で必要な構造があるかを確かめ、足りなければ明示的に失敗する。 形が崩れたときに、画面が空の一覧を「サービスがない」と誤って表示するのを防ぐためである。
4. デプロイの進捗を、方式ごとの段階で見せる
CDの進捗を1つの状態で表すと、「実行中」の中身が分からない。 そこで、デプロイの方式ごとに段階を定義し、段階ごとに状態を持たせる。
画面は、接続情報に書かれたデプロイの方式を見て、表示する段階の並びを選ぶ。 CloudscapeのStepsコンポーネントに段階を並べ、各段階の状態をStatusIndicatorで表す。
この形にしておくと、既存の方式で動くサービスを追加したときは、画面を直さなくても段階の並びがそのまま使われる。 画面の変更が必要になるのは、新しいデプロイの方式を追加するときだけである。
段階の状態は、次の語彙に固定する。
APIが返す状態の値、画面の型、Cloudscapeの表示の3つを一対一で対応させる。 前の記事で述べたとおり、判断は「何色にするか」ではなく「この状態はどれに当たるか」になる。
5. 権限も同じ値から導く
運用APIが読める範囲は、IAMポリシーで絞る。 このとき、ポリシーの対象を接続情報と同じローカル値から導く。
# 概念例: 接続情報と同じ値から、参照権限の対象を導く
|
data "aws_iam_policy_document" "ops_read" {
|
statement {
|
actions = ["ecs:DescribeServices"]
|
resources = [for s in local.ops_services : s.service_arn]
|
}
|
} |
表示対象と権限を別々に列挙すると、サービスを追加したときに片方だけが更新され、画面の一部だけが取得に失敗する。 同じ値から導けば、この食い違いは構造上起きない。 また、権限を環境ごとに別の場所へ書くと、ある環境にだけ追加が漏れる。環境によって差がない権限は、共通の記述に置く。
更新系の操作では、運用APIが自分で操作を実装せず、既存の実行経路を呼ぶ。
呼び出してよい実行先は、Lambda関数に付けたタグで区分し、IAMポリシーの条件で判定する(属性ベースのアクセス制御)。
実行先を追加するときに、ポリシーへARNを列挙し直す必要がない。
操作した人は、認証済みの利用者の識別子を SourceIdentity に設定した一時セッションで実行し、CloudTrailに残す。
6. 取得の失敗と更新の周期
取得に失敗した段階は「確認不能」にする
AWS APIからの取得に失敗したとき、直前に取得できた値で表示を埋めてはいけない。 稼働しているように見えることが、運用画面では最も危険である。 失敗は段階ごとに「確認不能」へ変換し、原因はログに残す。一部の取得が失敗しても、応答全体は落とさない。
更新の周期は利用者が選び、重さに応じて下限を持つ
画面の更新の間隔は、利用者が選べるようにする。 デプロイ中だけ自動で間隔を短くする仕組みは、利用者が「更新しない」を選んだのに通信が続く、という食い違いを生むため採らない。
ただし、1回の表示に必要なAWS APIの呼び出し数は画面によって違う。 全サービスをまとめる画面は呼び出しが多く、1つのサービスの詳細は少ない。 そこで取得の系統ごとに更新間隔の下限を持ち、呼び出しの多い画面を短い間隔で回せないようにする。
7. AIが開発・運用しやすい構造として
AWS Summit Japan 2026 のセッション「AI が開発/運用しやすいクラウド サーバーレスの視点から考える設計原則」(CNS449)では、 AIに読ませる範囲を文書だけでなくアーキテクチャで絞ること、インターフェースだけで判断できる境界を作ること、 AIが自分でフィードバックの循環を回せるようにすることが、設計原則として示された。
本稿の構成は、この考え方と同じ方向を向いている。
課題も残る。状態の語彙はAPIと画面の型に書き写して一致させており、片方だけを変えると食い違う。 語彙を1か所で定義して両方へ生成するなど、機械的に一致を保証する仕組みは今後の改善点である。
8. まとめ
運用画面を「作ったあとも直し続けるもの」にしないためには、画面が構成を知らない状態を作ることが要る。 構成の事実はTerraformに一本化し、画面はその出力を読むだけにする。 そうすれば、サービスやリソースの変化に、画面を修正せずに追従できる。
