Create AWS ECS Task Definitions

The aws:ecs/taskDefinition:TaskDefinition resource, part of the Pulumi AWS provider, defines an ECS task definition revision: container specifications, resource requirements, storage volumes, and runtime configuration. This guide focuses on four capabilities: container definitions with port mappings, Fargate deployment with Windows containers, EFS file system mounting, and App Mesh proxy configuration.

Task definitions reference IAM roles, EFS file systems, and container images that must exist separately. The examples are intentionally small. Combine them with your own IAM roles, networking, and storage infrastructure.

Define containers with port mappings and volumes

Most ECS deployments start by defining containers that run your application code, specifying images, resource allocations, and communication patterns.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const service = new aws.ecs.TaskDefinition("service", {
    family: "service",
    containerDefinitions: JSON.stringify([
        {
            name: "first",
            image: "service-first",
            cpu: 10,
            memory: 512,
            essential: true,
            portMappings: [{
                containerPort: 80,
                hostPort: 80,
            }],
        },
        {
            name: "second",
            image: "service-second",
            cpu: 10,
            memory: 256,
            essential: true,
            portMappings: [{
                containerPort: 443,
                hostPort: 443,
            }],
        },
    ]),
    volumes: [{
        name: "service-storage",
        hostPath: "/ecs/service-storage",
    }],
    placementConstraints: [{
        type: "memberOf",
        expression: "attribute:ecs.availability-zone in [us-west-2a, us-west-2b]",
    }],
});
import pulumi
import json
import pulumi_aws as aws

service = aws.ecs.TaskDefinition("service",
    family="service",
    container_definitions=json.dumps([
        {
            "name": "first",
            "image": "service-first",
            "cpu": 10,
            "memory": 512,
            "essential": True,
            "portMappings": [{
                "containerPort": 80,
                "hostPort": 80,
            }],
        },
        {
            "name": "second",
            "image": "service-second",
            "cpu": 10,
            "memory": 256,
            "essential": True,
            "portMappings": [{
                "containerPort": 443,
                "hostPort": 443,
            }],
        },
    ]),
    volumes=[{
        "name": "service-storage",
        "host_path": "/ecs/service-storage",
    }],
    placement_constraints=[{
        "type": "memberOf",
        "expression": "attribute:ecs.availability-zone in [us-west-2a, us-west-2b]",
    }])
package main

import (
	"encoding/json"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ecs"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		tmpJSON0, err := json.Marshal([]interface{}{
			map[string]interface{}{
				"name":      "first",
				"image":     "service-first",
				"cpu":       10,
				"memory":    512,
				"essential": true,
				"portMappings": []map[string]interface{}{
					map[string]interface{}{
						"containerPort": 80,
						"hostPort":      80,
					},
				},
			},
			map[string]interface{}{
				"name":      "second",
				"image":     "service-second",
				"cpu":       10,
				"memory":    256,
				"essential": true,
				"portMappings": []map[string]interface{}{
					map[string]interface{}{
						"containerPort": 443,
						"hostPort":      443,
					},
				},
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		_, err = ecs.NewTaskDefinition(ctx, "service", &ecs.TaskDefinitionArgs{
			Family:               pulumi.String("service"),
			ContainerDefinitions: pulumi.String(json0),
			Volumes: ecs.TaskDefinitionVolumeArray{
				&ecs.TaskDefinitionVolumeArgs{
					Name:     pulumi.String("service-storage"),
					HostPath: pulumi.String("/ecs/service-storage"),
				},
			},
			PlacementConstraints: ecs.TaskDefinitionPlacementConstraintArray{
				&ecs.TaskDefinitionPlacementConstraintArgs{
					Type:       pulumi.String("memberOf"),
					Expression: pulumi.String("attribute:ecs.availability-zone in [us-west-2a, us-west-2b]"),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var service = new Aws.Ecs.TaskDefinition("service", new()
    {
        Family = "service",
        ContainerDefinitions = JsonSerializer.Serialize(new[]
        {
            new Dictionary<string, object?>
            {
                ["name"] = "first",
                ["image"] = "service-first",
                ["cpu"] = 10,
                ["memory"] = 512,
                ["essential"] = true,
                ["portMappings"] = new[]
                {
                    new Dictionary<string, object?>
                    {
                        ["containerPort"] = 80,
                        ["hostPort"] = 80,
                    },
                },
            },
            new Dictionary<string, object?>
            {
                ["name"] = "second",
                ["image"] = "service-second",
                ["cpu"] = 10,
                ["memory"] = 256,
                ["essential"] = true,
                ["portMappings"] = new[]
                {
                    new Dictionary<string, object?>
                    {
                        ["containerPort"] = 443,
                        ["hostPort"] = 443,
                    },
                },
            },
        }),
        Volumes = new[]
        {
            new Aws.Ecs.Inputs.TaskDefinitionVolumeArgs
            {
                Name = "service-storage",
                HostPath = "/ecs/service-storage",
            },
        },
        PlacementConstraints = new[]
        {
            new Aws.Ecs.Inputs.TaskDefinitionPlacementConstraintArgs
            {
                Type = "memberOf",
                Expression = "attribute:ecs.availability-zone in [us-west-2a, us-west-2b]",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ecs.TaskDefinition;
import com.pulumi.aws.ecs.TaskDefinitionArgs;
import com.pulumi.aws.ecs.inputs.TaskDefinitionVolumeArgs;
import com.pulumi.aws.ecs.inputs.TaskDefinitionPlacementConstraintArgs;
import static com.pulumi.codegen.internal.Serialization.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var service = new TaskDefinition("service", TaskDefinitionArgs.builder()
            .family("service")
            .containerDefinitions(serializeJson(
                jsonArray(
                    jsonObject(
                        jsonProperty("name", "first"),
                        jsonProperty("image", "service-first"),
                        jsonProperty("cpu", 10),
                        jsonProperty("memory", 512),
                        jsonProperty("essential", true),
                        jsonProperty("portMappings", jsonArray(jsonObject(
                            jsonProperty("containerPort", 80),
                            jsonProperty("hostPort", 80)
                        )))
                    ), 
                    jsonObject(
                        jsonProperty("name", "second"),
                        jsonProperty("image", "service-second"),
                        jsonProperty("cpu", 10),
                        jsonProperty("memory", 256),
                        jsonProperty("essential", true),
                        jsonProperty("portMappings", jsonArray(jsonObject(
                            jsonProperty("containerPort", 443),
                            jsonProperty("hostPort", 443)
                        )))
                    )
                )))
            .volumes(TaskDefinitionVolumeArgs.builder()
                .name("service-storage")
                .hostPath("/ecs/service-storage")
                .build())
            .placementConstraints(TaskDefinitionPlacementConstraintArgs.builder()
                .type("memberOf")
                .expression("attribute:ecs.availability-zone in [us-west-2a, us-west-2b]")
                .build())
            .build());

    }
}
resources:
  service:
    type: aws:ecs:TaskDefinition
    properties:
      family: service
      containerDefinitions:
        fn::toJSON:
          - name: first
            image: service-first
            cpu: 10
            memory: 512
            essential: true
            portMappings:
              - containerPort: 80
                hostPort: 80
          - name: second
            image: service-second
            cpu: 10
            memory: 256
            essential: true
            portMappings:
              - containerPort: 443
                hostPort: 443
      volumes:
        - name: service-storage
          hostPath: /ecs/service-storage
      placementConstraints:
        - type: memberOf
          expression: attribute:ecs.availability-zone in [us-west-2a, us-west-2b]

The family property names your task definition. The containerDefinitions property is a JSON document that specifies container images, CPU and memory allocations, and port mappings. Port mappings connect container ports to host ports, enabling external access. The volumes array defines storage that containers can mount, and placementConstraints control which EC2 instances can run the task.

Run Windows containers on Fargate

Teams running Windows workloads can deploy containers to Fargate without managing EC2 instances.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const test = new aws.ecs.TaskDefinition("test", {
    family: "test",
    requiresCompatibilities: ["FARGATE"],
    networkMode: "awsvpc",
    cpu: "1024",
    memory: "2048",
    containerDefinitions: `[
  {
    "name": "iis",
    "image": "mcr.microsoft.com/windows/servercore/iis",
    "cpu": 1024,
    "memory": 2048,
    "essential": true
  }
]
`,
    runtimePlatform: {
        operatingSystemFamily: "WINDOWS_SERVER_2019_CORE",
        cpuArchitecture: "X86_64",
    },
});
import pulumi
import pulumi_aws as aws

test = aws.ecs.TaskDefinition("test",
    family="test",
    requires_compatibilities=["FARGATE"],
    network_mode="awsvpc",
    cpu="1024",
    memory="2048",
    container_definitions="""[
  {
    "name": "iis",
    "image": "mcr.microsoft.com/windows/servercore/iis",
    "cpu": 1024,
    "memory": 2048,
    "essential": true
  }
]
""",
    runtime_platform={
        "operating_system_family": "WINDOWS_SERVER_2019_CORE",
        "cpu_architecture": "X86_64",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ecs"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := ecs.NewTaskDefinition(ctx, "test", &ecs.TaskDefinitionArgs{
			Family: pulumi.String("test"),
			RequiresCompatibilities: pulumi.StringArray{
				pulumi.String("FARGATE"),
			},
			NetworkMode: pulumi.String("awsvpc"),
			Cpu:         pulumi.String("1024"),
			Memory:      pulumi.String("2048"),
			ContainerDefinitions: pulumi.String(`[
  {
    "name": "iis",
    "image": "mcr.microsoft.com/windows/servercore/iis",
    "cpu": 1024,
    "memory": 2048,
    "essential": true
  }
]
`),
			RuntimePlatform: &ecs.TaskDefinitionRuntimePlatformArgs{
				OperatingSystemFamily: pulumi.String("WINDOWS_SERVER_2019_CORE"),
				CpuArchitecture:       pulumi.String("X86_64"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var test = new Aws.Ecs.TaskDefinition("test", new()
    {
        Family = "test",
        RequiresCompatibilities = new[]
        {
            "FARGATE",
        },
        NetworkMode = "awsvpc",
        Cpu = "1024",
        Memory = "2048",
        ContainerDefinitions = @"[
  {
    ""name"": ""iis"",
    ""image"": ""mcr.microsoft.com/windows/servercore/iis"",
    ""cpu"": 1024,
    ""memory"": 2048,
    ""essential"": true
  }
]
",
        RuntimePlatform = new Aws.Ecs.Inputs.TaskDefinitionRuntimePlatformArgs
        {
            OperatingSystemFamily = "WINDOWS_SERVER_2019_CORE",
            CpuArchitecture = "X86_64",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ecs.TaskDefinition;
import com.pulumi.aws.ecs.TaskDefinitionArgs;
import com.pulumi.aws.ecs.inputs.TaskDefinitionRuntimePlatformArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var test = new TaskDefinition("test", TaskDefinitionArgs.builder()
            .family("test")
            .requiresCompatibilities("FARGATE")
            .networkMode("awsvpc")
            .cpu("1024")
            .memory("2048")
            .containerDefinitions("""
[
  {
    "name": "iis",
    "image": "mcr.microsoft.com/windows/servercore/iis",
    "cpu": 1024,
    "memory": 2048,
    "essential": true
  }
]
            """)
            .runtimePlatform(TaskDefinitionRuntimePlatformArgs.builder()
                .operatingSystemFamily("WINDOWS_SERVER_2019_CORE")
                .cpuArchitecture("X86_64")
                .build())
            .build());

    }
}
resources:
  test:
    type: aws:ecs:TaskDefinition
    properties:
      family: test
      requiresCompatibilities:
        - FARGATE
      networkMode: awsvpc
      cpu: 1024
      memory: 2048
      containerDefinitions: |
        [
          {
            "name": "iis",
            "image": "mcr.microsoft.com/windows/servercore/iis",
            "cpu": 1024,
            "memory": 2048,
            "essential": true
          }
        ]        
      runtimePlatform:
        operatingSystemFamily: WINDOWS_SERVER_2019_CORE
        cpuArchitecture: X86_64

When requiresCompatibilities includes FARGATE, you must specify networkMode as awsvpc and provide explicit cpu and memory values. The runtimePlatform block defines the operating system and architecture; here, operatingSystemFamily specifies Windows Server 2019 Core. Fargate handles infrastructure provisioning automatically.

Mount EFS file systems for persistent storage

Applications needing persistent storage across task restarts or shared storage between tasks can mount EFS file systems.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as std from "@pulumi/std";

const service = new aws.ecs.TaskDefinition("service", {
    family: "service",
    containerDefinitions: std.file({
        input: "task-definitions/service.json",
    }).then(invoke => invoke.result),
    volumes: [{
        name: "service-storage",
        efsVolumeConfiguration: {
            fileSystemId: fs.id,
            rootDirectory: "/opt/data",
            transitEncryption: "ENABLED",
            transitEncryptionPort: 2999,
            authorizationConfig: {
                accessPointId: test.id,
                iam: "ENABLED",
            },
        },
    }],
});
import pulumi
import pulumi_aws as aws
import pulumi_std as std

service = aws.ecs.TaskDefinition("service",
    family="service",
    container_definitions=std.file(input="task-definitions/service.json").result,
    volumes=[{
        "name": "service-storage",
        "efs_volume_configuration": {
            "file_system_id": fs["id"],
            "root_directory": "/opt/data",
            "transit_encryption": "ENABLED",
            "transit_encryption_port": 2999,
            "authorization_config": {
                "access_point_id": test["id"],
                "iam": "ENABLED",
            },
        },
    }])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ecs"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		invokeFile, err := std.File(ctx, &std.FileArgs{
			Input: "task-definitions/service.json",
		}, nil)
		if err != nil {
			return err
		}
		_, err = ecs.NewTaskDefinition(ctx, "service", &ecs.TaskDefinitionArgs{
			Family:               pulumi.String("service"),
			ContainerDefinitions: pulumi.String(invokeFile.Result),
			Volumes: ecs.TaskDefinitionVolumeArray{
				&ecs.TaskDefinitionVolumeArgs{
					Name: pulumi.String("service-storage"),
					EfsVolumeConfiguration: &ecs.TaskDefinitionVolumeEfsVolumeConfigurationArgs{
						FileSystemId:          pulumi.Any(fs.Id),
						RootDirectory:         pulumi.String("/opt/data"),
						TransitEncryption:     pulumi.String("ENABLED"),
						TransitEncryptionPort: pulumi.Int(2999),
						AuthorizationConfig: &ecs.TaskDefinitionVolumeEfsVolumeConfigurationAuthorizationConfigArgs{
							AccessPointId: pulumi.Any(test.Id),
							Iam:           pulumi.String("ENABLED"),
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var service = new Aws.Ecs.TaskDefinition("service", new()
    {
        Family = "service",
        ContainerDefinitions = Std.File.Invoke(new()
        {
            Input = "task-definitions/service.json",
        }).Apply(invoke => invoke.Result),
        Volumes = new[]
        {
            new Aws.Ecs.Inputs.TaskDefinitionVolumeArgs
            {
                Name = "service-storage",
                EfsVolumeConfiguration = new Aws.Ecs.Inputs.TaskDefinitionVolumeEfsVolumeConfigurationArgs
                {
                    FileSystemId = fs.Id,
                    RootDirectory = "/opt/data",
                    TransitEncryption = "ENABLED",
                    TransitEncryptionPort = 2999,
                    AuthorizationConfig = new Aws.Ecs.Inputs.TaskDefinitionVolumeEfsVolumeConfigurationAuthorizationConfigArgs
                    {
                        AccessPointId = test.Id,
                        Iam = "ENABLED",
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ecs.TaskDefinition;
import com.pulumi.aws.ecs.TaskDefinitionArgs;
import com.pulumi.aws.ecs.inputs.TaskDefinitionVolumeArgs;
import com.pulumi.aws.ecs.inputs.TaskDefinitionVolumeEfsVolumeConfigurationArgs;
import com.pulumi.aws.ecs.inputs.TaskDefinitionVolumeEfsVolumeConfigurationAuthorizationConfigArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.FileArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var service = new TaskDefinition("service", TaskDefinitionArgs.builder()
            .family("service")
            .containerDefinitions(StdFunctions.file(FileArgs.builder()
                .input("task-definitions/service.json")
                .build()).result())
            .volumes(TaskDefinitionVolumeArgs.builder()
                .name("service-storage")
                .efsVolumeConfiguration(TaskDefinitionVolumeEfsVolumeConfigurationArgs.builder()
                    .fileSystemId(fs.id())
                    .rootDirectory("/opt/data")
                    .transitEncryption("ENABLED")
                    .transitEncryptionPort(2999)
                    .authorizationConfig(TaskDefinitionVolumeEfsVolumeConfigurationAuthorizationConfigArgs.builder()
                        .accessPointId(test.id())
                        .iam("ENABLED")
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  service:
    type: aws:ecs:TaskDefinition
    properties:
      family: service
      containerDefinitions:
        fn::invoke:
          function: std:file
          arguments:
            input: task-definitions/service.json
          return: result
      volumes:
        - name: service-storage
          efsVolumeConfiguration:
            fileSystemId: ${fs.id}
            rootDirectory: /opt/data
            transitEncryption: ENABLED
            transitEncryptionPort: 2999
            authorizationConfig:
              accessPointId: ${test.id}
              iam: ENABLED

The efsVolumeConfiguration block within volumes connects to an EFS file system by fileSystemId. The transitEncryption property enables encryption in transit, and authorizationConfig specifies an access point and IAM-based authentication. Containers reference the volume by name to mount it at specific paths.

Route traffic through App Mesh service mesh

Service mesh architectures use sidecar proxies to handle service-to-service communication and observability without changing application code.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as std from "@pulumi/std";

const service = new aws.ecs.TaskDefinition("service", {
    family: "service",
    containerDefinitions: std.file({
        input: "task-definitions/service.json",
    }).then(invoke => invoke.result),
    proxyConfiguration: {
        type: "APPMESH",
        containerName: "applicationContainerName",
        properties: {
            AppPorts: "8080",
            EgressIgnoredIPs: "169.254.170.2,169.254.169.254",
            IgnoredUID: "1337",
            ProxyEgressPort: "15001",
            ProxyIngressPort: "15000",
        },
    },
});
import pulumi
import pulumi_aws as aws
import pulumi_std as std

service = aws.ecs.TaskDefinition("service",
    family="service",
    container_definitions=std.file(input="task-definitions/service.json").result,
    proxy_configuration={
        "type": "APPMESH",
        "container_name": "applicationContainerName",
        "properties": {
            "AppPorts": "8080",
            "EgressIgnoredIPs": "169.254.170.2,169.254.169.254",
            "IgnoredUID": "1337",
            "ProxyEgressPort": "15001",
            "ProxyIngressPort": "15000",
        },
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ecs"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		invokeFile, err := std.File(ctx, &std.FileArgs{
			Input: "task-definitions/service.json",
		}, nil)
		if err != nil {
			return err
		}
		_, err = ecs.NewTaskDefinition(ctx, "service", &ecs.TaskDefinitionArgs{
			Family:               pulumi.String("service"),
			ContainerDefinitions: pulumi.String(invokeFile.Result),
			ProxyConfiguration: &ecs.TaskDefinitionProxyConfigurationArgs{
				Type:          pulumi.String("APPMESH"),
				ContainerName: pulumi.String("applicationContainerName"),
				Properties: pulumi.StringMap{
					"AppPorts":         pulumi.String("8080"),
					"EgressIgnoredIPs": pulumi.String("169.254.170.2,169.254.169.254"),
					"IgnoredUID":       pulumi.String("1337"),
					"ProxyEgressPort":  pulumi.String("15001"),
					"ProxyIngressPort": pulumi.String("15000"),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var service = new Aws.Ecs.TaskDefinition("service", new()
    {
        Family = "service",
        ContainerDefinitions = Std.File.Invoke(new()
        {
            Input = "task-definitions/service.json",
        }).Apply(invoke => invoke.Result),
        ProxyConfiguration = new Aws.Ecs.Inputs.TaskDefinitionProxyConfigurationArgs
        {
            Type = "APPMESH",
            ContainerName = "applicationContainerName",
            Properties = 
            {
                { "AppPorts", "8080" },
                { "EgressIgnoredIPs", "169.254.170.2,169.254.169.254" },
                { "IgnoredUID", "1337" },
                { "ProxyEgressPort", "15001" },
                { "ProxyIngressPort", "15000" },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ecs.TaskDefinition;
import com.pulumi.aws.ecs.TaskDefinitionArgs;
import com.pulumi.aws.ecs.inputs.TaskDefinitionProxyConfigurationArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.FileArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var service = new TaskDefinition("service", TaskDefinitionArgs.builder()
            .family("service")
            .containerDefinitions(StdFunctions.file(FileArgs.builder()
                .input("task-definitions/service.json")
                .build()).result())
            .proxyConfiguration(TaskDefinitionProxyConfigurationArgs.builder()
                .type("APPMESH")
                .containerName("applicationContainerName")
                .properties(Map.ofEntries(
                    Map.entry("AppPorts", "8080"),
                    Map.entry("EgressIgnoredIPs", "169.254.170.2,169.254.169.254"),
                    Map.entry("IgnoredUID", "1337"),
                    Map.entry("ProxyEgressPort", "15001"),
                    Map.entry("ProxyIngressPort", "15000")
                ))
                .build())
            .build());

    }
}
resources:
  service:
    type: aws:ecs:TaskDefinition
    properties:
      family: service
      containerDefinitions:
        fn::invoke:
          function: std:file
          arguments:
            input: task-definitions/service.json
          return: result
      proxyConfiguration:
        type: APPMESH
        containerName: applicationContainerName
        properties:
          AppPorts: '8080'
          EgressIgnoredIPs: 169.254.170.2,169.254.169.254
          IgnoredUID: '1337'
          ProxyEgressPort: 15001
          ProxyIngressPort: 15000

The proxyConfiguration block enables App Mesh integration. The containerName identifies which container acts as the proxy (typically Envoy). The properties map configures proxy behavior: AppPorts lists application ports, ProxyIngressPort and ProxyEgressPort define proxy listening ports, and EgressIgnoredIPs excludes metadata endpoints from proxying.

Beyond these examples

These snippets focus on specific task definition features: container definitions and resource allocation, persistent storage (EFS, FSx, Docker volumes), Fargate and Windows runtime platforms, and App Mesh service mesh integration. They’re intentionally minimal rather than full ECS deployments.

The examples often reference pre-existing infrastructure such as IAM execution and task roles, EFS file systems and access points, App Mesh virtual nodes and services, and container images in ECR or public registries. They focus on task definition configuration rather than provisioning the surrounding infrastructure.

To keep things focused, common task definition patterns are omitted, including:

  • IAM role configuration (executionRoleArn, taskRoleArn)
  • Network mode selection (awsvpc, bridge, host)
  • Logging configuration (awslogs driver)
  • Secrets management and environment variables
  • Health checks and container dependencies
  • Task placement strategies and constraints

These omissions are intentional: the goal is to illustrate how each task definition feature is wired, not provide drop-in ECS modules. See the ECS TaskDefinition resource reference for all available configuration options.

Let's create AWS ECS Task Definitions

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Container Definitions & JSON
How do I format and escape quotes in containerDefinitions?
Use JSON.stringify() for objects or an inline JSON string. For quotes in JSON values (like environment variables), escape as \" in direct JSON (e.g., "value": "I \"love\" quotes"). For variable values, escape as \\\" in the variable, then reference in JSON.
Can I use both JSON.stringify and inline JSON strings?
Yes. Examples show JSON.stringify([{...}]) for objects and inline strings like "[{\"cpu\": 10, ...}]". Both are valid.
Fargate & Launch Types
What's required to run tasks on Fargate?
Set requiresCompatibilities to ["FARGATE"], networkMode to "awsvpc", and specify cpu and memory values. Both cpu and memory are required for Fargate.
What network modes are available?
Valid values are none, bridge, awsvpc, and host. The networkMode property is required and immutable.
Can I use fault injection with any network mode?
No. Fault injection only works with awsvpc or host network modes and isn’t available on Windows.
IAM Roles & Permissions
What's the difference between executionRoleArn and taskRoleArn?
executionRoleArn is for the ECS container agent and Docker daemon (pulls images, writes logs). taskRoleArn is for your task containers to make AWS service calls.
Storage & Volumes
How do I attach EFS volumes to my task?
Configure volumes with efsVolumeConfiguration including fileSystemId, rootDirectory, and optionally transitEncryption and authorizationConfig.
How do I use FSx Windows File Server volumes?
Configure volumes with fsxWindowsFileServerVolumeConfiguration including fileSystemId, rootDirectory, and authorizationConfig with credentialsParameter and domain.
How do I configure Docker volumes with NFS?
Use dockerVolumeConfiguration with driver set to "local" and driverOpts specifying NFS parameters like type, device, and mount options.
Immutability & Lifecycle
What properties trigger task definition replacement?
Most properties are immutable: containerDefinitions, family, networkMode, cpu, memory, executionRoleArn, taskRoleArn, volumes, requiresCompatibilities, runtimePlatform, placementConstraints, proxyConfiguration, ipcMode, pidMode, ephemeralStorage, and enableFaultInjection. Only region, skipDestroy, tags, and trackLatest are mutable.
How do I handle task definitions modified outside Pulumi?
Set trackLatest to true to track the latest ACTIVE task definition on AWS instead of the one in state. Default is false.
How do I prevent old task definition revisions from being deleted?
Set skipDestroy to true to retain old revisions when the resource is destroyed or replaced. Default is false.
What's the difference between arn and arnWithoutRevision?
arn includes both family and revision (e.g., family:123). arnWithoutRevision omits the revision and uses the latest ACTIVE revision when referenced.
How many placement constraints can I configure?
Maximum of 10 placementConstraints are allowed per task definition.

Using a different cloud?

Explore containers guides for other cloud providers: