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, volumes, and runtime platform. This guide focuses on four capabilities: container definitions with port mappings, Fargate and Windows runtime configuration, EFS volume mounting, and App Mesh proxy integration.

Task definitions reference IAM roles, EFS file systems, and App Mesh configuration that must exist separately. They’re used by ECS services to launch tasks. The examples are intentionally small. Combine them with your own IAM roles, networking, and service definitions.

Define containers with port mappings and volumes

Most ECS deployments start by defining containers that run your application code, specifying resource requirements and network ports.

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 containerDefinitions property accepts a JSON document describing each container: its image, CPU and memory allocation, and port mappings. The portMappings array exposes container ports to the host or load balancer. 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 use Fargate to avoid managing EC2 instances while specifying the exact OS and architecture.

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 set networkMode to “awsvpc” and specify cpu and memory values from Fargate’s supported combinations. The runtimePlatform block defines the operating system family and CPU architecture. Fargate manages the underlying infrastructure, so you don’t configure placement constraints or host volumes.

Mount EFS file systems for persistent storage

Applications that need shared persistent storage across tasks or availability zones can mount EFS file systems directly into containers.

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 connects a volume to an EFS file system by ID. The transitEncryption property secures data in transit, and authorizationConfig enables IAM-based access control through an EFS access point. Containers reference the volume by name in their mount points.

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 wires App Mesh into your task. The containerName identifies which container acts as the Envoy proxy, and the properties map configures port routing: AppPorts lists application ports, ProxyIngressPort and ProxyEgressPort define where the proxy listens. App Mesh intercepts traffic between containers and external services.

Beyond these examples

These snippets focus on specific task definition features: container definitions and resource allocation, volume mounting (EFS, Docker, FSx), Fargate and Windows runtime configuration, and App Mesh proxy integration. They’re intentionally minimal rather than full ECS deployments.

The examples may reference pre-existing infrastructure such as ECS clusters, IAM execution and task roles, EFS file systems and access points, App Mesh virtual nodes and service mesh configuration, and VPC subnets and security groups for awsvpc mode. They focus on configuring the task definition rather than provisioning everything around it.

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

  • IAM role configuration (executionRoleArn, taskRoleArn)
  • Network mode selection and VPC configuration
  • Logging configuration (awslogs driver)
  • Health checks and container dependencies
  • Secrets management and environment variables
  • Ephemeral storage allocation

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

Configuration & Immutability
Why does changing my task definition recreate the resource?
Most properties are immutable, including containerDefinitions, family, networkMode, cpu, memory, volumes, and others. Changing these forces resource replacement.
How do I handle task definitions modified outside Pulumi?
Set trackLatest to true to track the latest ACTIVE revision on AWS instead of the one stored in state. Default is false.
How do I retain old revisions when replacing a task definition?
Set skipDestroy to true to retain the old revision when the resource is destroyed or replaced. Default is false.
Container Definitions & JSON
Why am I getting JSON parsing errors in containerDefinitions?
Container definition JSON values with quotes require specific escaping. Use \" when directly in JSON (e.g., "value": "I \"love\" quotes"), or \\\" in variables (e.g., value = "I \\\"love\\\" quotes").
How do I define multiple containers in a task?
Use JSON.stringify with an array of container definitions, each specifying name, image, cpu, memory, essential, and other properties like portMappings.
IAM Roles & Permissions
What's the difference between executionRoleArn and taskRoleArn?
executionRoleArn is for the ECS container agent and Docker daemon to pull images and write logs. taskRoleArn allows your container tasks to make calls to other AWS services.
Fargate & Launch Types
What are the requirements for running on Fargate?
Set requiresCompatibilities to FARGATE, networkMode to awsvpc, and specify both cpu (in units) and memory (in MiB). These fields are required for Fargate.
How do I run Windows containers on Fargate?
Set requiresCompatibilities to FARGATE, networkMode to awsvpc, and configure runtimePlatform with operatingSystemFamily (e.g., WINDOWS_SERVER_2019_CORE) and cpuArchitecture (e.g., X86_64).
Networking & Fault Injection
What network modes are available?
Valid values are awsvpc, bridge, host, and none. The networkMode property is required.
Why can't I enable fault injection on my task?
Fault injection only works with awsvpc or host network modes and isn’t available on Windows. Set enableFaultInjection to true and ensure your networkMode is compatible.
Storage & Volumes
What volume types can I use with ECS tasks?
Three types are available: dockerVolumeConfiguration (for Docker volumes and NFS), efsVolumeConfiguration (for Amazon EFS with optional encryption), and fsxWindowsFileServerVolumeConfiguration (for FSX Windows File Server).
What's the difference between arn and arnWithoutRevision?
arn includes the full ARN with the revision number. arnWithoutRevision omits the revision and always refers to the latest ACTIVE revision, useful when you want the most recent version.

Using a different cloud?

Explore containers guides for other cloud providers: