Skip to content

ECS Fargate

What This Example Shows

This example uses an ECS Fargate service behind an Application Load Balancer to show larger component boundaries.

It demonstrates:

  • splitting infrastructure concerns into composite components
  • passing refs between components
  • using TypeScript values for environment shape
  • using HCL body text for complex ECS and ALB blocks

Key TSX Snippet

tsx
const azCount = 2;
const vpcCidr = "172.17.0.0/16";
const appImage = "adongy/hostname-docker:latest";
const appPort = 3000;
const appCount = 2;
const fargateCpu = 256;
const fargateMemory = 512;

const azRef = useRef();
const vpcRef = useRef();
const igwRef = useRef();
const publicSubnetRefs = Array.from({ length: azCount }, () => useRef());
const privateSubnetRefs = Array.from({ length: azCount }, () => useRef());
const lbSgRef = useRef();
const albRef = useRef();
const tgRef = useRef();
const listenerRef = useRef();

<Data type="aws_availability_zones" label="available" ref={azRef} />
<Resource type="aws_vpc" label="main" ref={vpcRef} cidr_block={vpcCidr} />

<Network
  vpcRef={vpcRef}
  azRef={azRef}
  igwRef={igwRef}
  publicSubnetRefs={publicSubnetRefs}
  privateSubnetRefs={privateSubnetRefs}
  azCount={azCount}
  vpcCidr={vpcCidr}
/>

<Alb
  vpcRef={vpcRef}
  lbSgRef={lbSgRef}
  albRef={albRef}
  publicSubnetRefs={publicSubnetRefs}
/>

<AlbListener
  albRef={albRef}
  vpcRef={vpcRef}
  tgRef={tgRef}
  listenerRef={listenerRef}
/>

<EcsService
  vpcRef={vpcRef}
  lbSgRef={lbSgRef}
  tgRef={tgRef}
  listenerRef={listenerRef}
  privateSubnetRefs={privateSubnetRefs}
  appImage={appImage}
  appPort={appPort}
  appCount={appCount}
  fargateCpu={fargateCpu}
  fargateMemory={fargateMemory}
/>

ECS Service Snippet

tsx
const taskSgRef = useRef();
const clusterRef = useRef();
const taskDefRef = useRef();

<Resource type="aws_ecs_service" label="main">
  {`
    name            = "tf-ecs-service"
    cluster         = ${clusterRef.id}
    task_definition = ${taskDefRef.arn}
    desired_count   = ${appCount}
    launch_type     = "FARGATE"

    network_configuration {
      security_groups = [${taskSgRef.id}]
      subnets         = [${privateSubnetRefs.map((r) => r.id).join(", ")}]
    }

    load_balancer {
      target_group_arn = ${tgRef.id}
      container_name   = "app"
      container_port   = ${appPort}
    }

    depends_on = [${listenerRef.__dependsOnValue}]
  `}
</Resource>

Generated HCL Snippet

hcl
resource "aws_alb_target_group" "app" {
  name        = "tf-ecs-chat"
  port        = 80
  protocol    = "HTTP"
  vpc_id      = aws_vpc.main.id
  target_type = "ip"
}

resource "aws_alb_listener" "front_end" {
  load_balancer_arn = aws_alb.main.id
  port              = "80"
  protocol          = "HTTP"

  default_action {
    target_group_arn = aws_alb_target_group.app.id
    type             = "forward"
  }
}

resource "aws_ecs_cluster" "main" {
  name = "tf-ecs-cluster"
}

resource "aws_ecs_task_definition" "app" {
  family                   = "app"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = 256
  memory                   = 512

  container_definitions = jsonencode([
    {
      cpu          = 256
      image        = "adongy/hostname-docker:latest"
      memory       = 512
      name         = "app"
      networkMode  = "awsvpc"
      portMappings = [
        {
          containerPort = 3000
          hostPort      = 3000
        }
      ]
    }
  ])
}

resource "aws_ecs_service" "main" {
  name            = "tf-ecs-service"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.app.arn
  desired_count   = 2
  launch_type     = "FARGATE"

  network_configuration {
    security_groups = [aws_security_group.ecs_tasks.id]
    subnets         = [aws_subnet.private_0.id, aws_subnet.private_1.id]
  }

  load_balancer {
    target_group_arn = aws_alb_target_group.app.id
    container_name   = "app"
    container_port   = 3000
  }

  depends_on = [aws_alb_listener.front_end]
}

Takeaway

The TSX source groups network, load balancer, listener, and service resources into separate components. The generated HCL remains a flat Terraform configuration with normal resource references.