はじめに
みなさんこんにちは。Platform 開発チーム SREでサブマネージャーの安達(@adachin0817)です。この記事は、ファインディエンジニア Advent Calendar 2025の18日目の記事です。今回はECS Express Modeをスピーディーに試してみたので、使ってみて分かったメリット・デメリットを中心にまとめていきたいと思います。
ECS Express Modeとは
従来のECSを用いたインフラ構築では、ECSクラスターやタスク定義、サービスだけでなく、ロードバランサー、ターゲットグループ、セキュリティグループ、オートスケーリングなど、多数のリソースを個別に定義・管理する手間がありました。
公式ドキュメントによるとECS Express ModeはAPIを通じて、インフラのセットアップを全て、自動化できるようになりました。これにより、アプリケーション開発に集中できる環境を実現し、Amazon ECSを含む各リソースを設定できるため、必須項目はコンテナイメージのみで、シンプルさとスピードを飛躍的に向上させています。
Terraformではaws_ecs_express_gateway_serviceリソースが提供されているため、こちらを使って一連の流れを実装していきます。
Terraform
terraform.tf
ECS Express Modeは、Terraform AWS Providerのバージョン6.23から利用できるようになります。
terraform { required_version = ">= 1.14.2" required_providers { aws = { source = "hashicorp/aws" version = ">= 6.26.0" } } cloud { organization = "hoge" workspaces { name = "hoge-ecs" } } }
iam.tf
ECS Express Modeでは、主に2つのIAM ロールが必要になります。Task Execution Roleは、コンテナイメージのpullやCloudWatch Logsへの書き込みに使用され、Infrastructure Roleは、ECS Express Modeが各種リソースを作成・管理するために使用されます。
resource "aws_iam_role" "ecs_task_execution_role" { name = "test-service-task-execution-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Service = "ecs-tasks.amazonaws.com" } Action = "sts:AssumeRole" }] }) } resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_policy" { role = aws_iam_role.ecs_task_execution_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" } resource "aws_iam_role" "ecs_infrastructure_role" { name = "test-service-infrastructure-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Sid = "AllowAccessInfrastructureForECSExpressServices" Effect = "Allow" Principal = { Service = "ecs.amazonaws.com" } Action = "sts:AssumeRole" }] }) } resource "aws_iam_role_policy_attachment" "ecs_infrastructure_role_policy" { role = aws_iam_role.ecs_infrastructure_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSInfrastructureRoleforExpressGatewayServices" }
ecs.tf
ECS Express Modeでは、中心となるリソースであるaws_ecs_express_gateway_serviceを通じて各種設定を制御します。ECSクラスター自体は別途定義する必要がありますが、サービス側ではコンテナイメージ指定(今回はNginx)、ログ設定、事前に作成したIAM ロール、ネットワーク、CPU・メモリ、スケーリング設定などをまとめて指定します。
ロードバランサーについては ALBとターゲットグループが自動的に作成・管理され、ACM証明書も自動発行される仕様となっていました。また、セキュリティグループもExpress Mode側で自動生成・管理されるため、Terraform側では差分検知を防ぐために、ignore_changesを指定する必要がありました。
※ネットワークはデフォルトVPCを参照しています。
resource "aws_ecs_cluster" "main" { name = var.cluster_name } resource "aws_ecs_express_gateway_service" "main" { cluster = var.cluster_name service_name = var.service_name primary_container { image = var.container_image container_port = var.container_port command = var.command aws_logs_configuration { log_group = aws_cloudwatch_log_group.ecs_service.name log_stream_prefix = var.service_name } } execution_role_arn = aws_iam_role.ecs_task_execution_role.arn infrastructure_role_arn = aws_iam_role.ecs_infrastructure_role.arn network_configuration { subnets = toset(["subnet-xxxxxx", "subnet-xxxxxx"]) security_groups = toset([aws_security_group.tests_service.id]) } cpu = tostring(var.cpu) memory = tostring(var.memory) scaling_target { min_task_count = var.min_capacity max_task_count = var.max_capacity auto_scaling_metric = "AVERAGE_CPU" auto_scaling_target_value = 60 } health_check_path = var.health_check_path lifecycle { ignore_changes = [ network_configuration[0].security_groups ] } }
variables.tf
variable "cluster_name" { type = string description = "The name of the ECS cluster" default = "hoge-cluster" } variable "service_name" { type = string description = "The name of the service" default = "hoge-service" } variable "log_group" { type = string description = "The name of the CloudWatch log group" default = "/ecs/hoge-service" } variable "container_image" { type = string description = "The container image to use for the service" default = "nginx:latest" } variable "container_port" { type = number description = "The port that the container listens on" default = 80 } variable "command" { type = list(string) description = "The command to run in the container" default = ["nginx", "-g", "daemon off;"] } variable "cpu" { type = number description = "The number of CPU units to allocate" default = 256 } variable "memory" { type = number description = "The amount of memory (in MiB) to allocate" default = 512 } variable "min_capacity" { type = number description = "Minimum number of tasks" default = 1 } variable "max_capacity" { type = number description = "Maximum number of tasks" default = 10 } variable "health_check_path" { type = string description = "The path for health checks" default = "/" }
Deploy

デプロイはECSビルトインデプロイであるカナリアデプロイとなっており、アプリケーションURLにHTTPSでアクセスできるようになりました。
❯❯ curl -I https://xxx.ecs.ap-northeast-1.on.aws HTTP/2 200 server: nginx date: Tue, 16 Dec 2025 02:30:25 GMT content-type: text/html; charset=utf-8 content-length: 14 x-powered-by: Express
削除の挙動と懸念点について
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/express-service-delete-task.html
- The Amazon ECS cluster (if no other services are running)
- The Amazon ECS service, task definition, and any running tasks
- Service security group
- CloudWatch log group
- Metric alarm
- ACM Certificate
- The Application Load Balancer (if no other services are configured), target group, security group, listener, and listener rule
- Amazon EC2 Auto Scaling policy, scalable target
削除の挙動については、Terraformでdestroyを実行すればExpress Mode管理下のリソースがすべて削除される想定でした。しかし、実際にはALBやターゲットグループ、ACMが残存し、その後に手動削除と再apply を行った結果、同一サービス名のECSサービスがDraining 状態のままとなり、再作成できない問題に直面しました。
╷ │ Error: creating ECS (Elastic Container) Express Gateway Service │ │ with aws_ecs_express_gateway_service.main, │ on ecs.tf line 5, in resource "aws_ecs_express_gateway_service" "main": │ 5: resource "aws_ecs_express_gateway_service" "main" { │ │ ID: "hoge-service" │ Cause: operation error ECS: CreateExpressGatewayService, , │ InvalidParameterException: Unable to Start a service that is still Draining."
クラスター名を変更して再度applyし直したところ、applyは完了するものの、ロードバランサーが作成されない状態となりました。その結果、存在しないセキュリティグループを参照したままサービスが起動できず、結果としてサービスがデプロイされないままタイムアウトしない事象が発生しました。この挙動から、Terraformでの削除・再作成は、現時点で課題が残っていると感じています。

まとめ
ECS Express Modeは、App Runnerと比べても、インフラ構築の複雑さを大きく減らし、アプリケーションエンジニアでも素早く環境を立ち上げ、開発に集中できる体験を提供してくれる仕組みだと感じました。一方で、Terraformを前提とした運用ではまだ課題が残る印象もありますが、Platform SRE チームがこれまで整備してきた汎用モジュールの一部を、将来的に置き換えられる可能性も感じています。
今後はAWSサポートとも連携しながら挙動の整理を進め、どのユースケースで安全に使えるのかを見極めつつ、実運用に耐えうる形へ落とし込んでいく予定です。
最後まで、読んでいただきありがとうございました!