Create AWS Batch Compute Environments

The aws:batch/computeEnvironment:ComputeEnvironment resource, part of the Pulumi AWS provider, defines the compute infrastructure that runs AWS Batch containerized jobs: EC2 instances, Fargate tasks, or EKS pods. This guide focuses on three capabilities: EC2 compute with placement groups, serverless Fargate compute, and update policies for job management.

Compute environments require IAM service roles, instance profiles for EC2, and VPC networking. The resource includes a dependsOn requirement to prevent deletion race conditions with IAM policy attachments. The examples are intentionally small. Combine them with your own IAM roles, VPC infrastructure, and job queues.

Run batch jobs on EC2 instances with placement groups

Batch workloads that need low-latency inter-node communication often use EC2 compute environments with placement groups to ensure instances are physically close together.

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

const ec2AssumeRole = aws.iam.getPolicyDocument({
    statements: [{
        effect: "Allow",
        principals: [{
            type: "Service",
            identifiers: ["ec2.amazonaws.com"],
        }],
        actions: ["sts:AssumeRole"],
    }],
});
const ecsInstanceRole = new aws.iam.Role("ecs_instance_role", {
    name: "ecs_instance_role",
    assumeRolePolicy: ec2AssumeRole.then(ec2AssumeRole => ec2AssumeRole.json),
});
const ecsInstanceRoleRolePolicyAttachment = new aws.iam.RolePolicyAttachment("ecs_instance_role", {
    role: ecsInstanceRole.name,
    policyArn: "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role",
});
const ecsInstanceRoleInstanceProfile = new aws.iam.InstanceProfile("ecs_instance_role", {
    name: "ecs_instance_role",
    role: ecsInstanceRole.name,
});
const batchAssumeRole = aws.iam.getPolicyDocument({
    statements: [{
        effect: "Allow",
        principals: [{
            type: "Service",
            identifiers: ["batch.amazonaws.com"],
        }],
        actions: ["sts:AssumeRole"],
    }],
});
const awsBatchServiceRole = new aws.iam.Role("aws_batch_service_role", {
    name: "aws_batch_service_role",
    assumeRolePolicy: batchAssumeRole.then(batchAssumeRole => batchAssumeRole.json),
});
const awsBatchServiceRoleRolePolicyAttachment = new aws.iam.RolePolicyAttachment("aws_batch_service_role", {
    role: awsBatchServiceRole.name,
    policyArn: "arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole",
});
const sample = new aws.ec2.SecurityGroup("sample", {
    name: "aws_batch_compute_environment_security_group",
    egress: [{
        fromPort: 0,
        toPort: 0,
        protocol: "-1",
        cidrBlocks: ["0.0.0.0/0"],
    }],
});
const sampleVpc = new aws.ec2.Vpc("sample", {cidrBlock: "10.1.0.0/16"});
const sampleSubnet = new aws.ec2.Subnet("sample", {
    vpcId: sampleVpc.id,
    cidrBlock: "10.1.1.0/24",
});
const samplePlacementGroup = new aws.ec2.PlacementGroup("sample", {
    name: "sample",
    strategy: aws.ec2.PlacementStrategy.Cluster,
});
const sampleComputeEnvironment = new aws.batch.ComputeEnvironment("sample", {
    name: "sample",
    computeResources: {
        instanceRole: ecsInstanceRoleInstanceProfile.arn,
        instanceTypes: ["c4.large"],
        maxVcpus: 16,
        minVcpus: 0,
        placementGroup: samplePlacementGroup.name,
        securityGroupIds: [sample.id],
        subnets: [sampleSubnet.id],
        type: "EC2",
    },
    serviceRole: awsBatchServiceRole.arn,
    type: "MANAGED",
}, {
    dependsOn: [awsBatchServiceRoleRolePolicyAttachment],
});
import pulumi
import pulumi_aws as aws

ec2_assume_role = aws.iam.get_policy_document(statements=[{
    "effect": "Allow",
    "principals": [{
        "type": "Service",
        "identifiers": ["ec2.amazonaws.com"],
    }],
    "actions": ["sts:AssumeRole"],
}])
ecs_instance_role = aws.iam.Role("ecs_instance_role",
    name="ecs_instance_role",
    assume_role_policy=ec2_assume_role.json)
ecs_instance_role_role_policy_attachment = aws.iam.RolePolicyAttachment("ecs_instance_role",
    role=ecs_instance_role.name,
    policy_arn="arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role")
ecs_instance_role_instance_profile = aws.iam.InstanceProfile("ecs_instance_role",
    name="ecs_instance_role",
    role=ecs_instance_role.name)
batch_assume_role = aws.iam.get_policy_document(statements=[{
    "effect": "Allow",
    "principals": [{
        "type": "Service",
        "identifiers": ["batch.amazonaws.com"],
    }],
    "actions": ["sts:AssumeRole"],
}])
aws_batch_service_role = aws.iam.Role("aws_batch_service_role",
    name="aws_batch_service_role",
    assume_role_policy=batch_assume_role.json)
aws_batch_service_role_role_policy_attachment = aws.iam.RolePolicyAttachment("aws_batch_service_role",
    role=aws_batch_service_role.name,
    policy_arn="arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole")
sample = aws.ec2.SecurityGroup("sample",
    name="aws_batch_compute_environment_security_group",
    egress=[{
        "from_port": 0,
        "to_port": 0,
        "protocol": "-1",
        "cidr_blocks": ["0.0.0.0/0"],
    }])
sample_vpc = aws.ec2.Vpc("sample", cidr_block="10.1.0.0/16")
sample_subnet = aws.ec2.Subnet("sample",
    vpc_id=sample_vpc.id,
    cidr_block="10.1.1.0/24")
sample_placement_group = aws.ec2.PlacementGroup("sample",
    name="sample",
    strategy=aws.ec2.PlacementStrategy.CLUSTER)
sample_compute_environment = aws.batch.ComputeEnvironment("sample",
    name="sample",
    compute_resources={
        "instance_role": ecs_instance_role_instance_profile.arn,
        "instance_types": ["c4.large"],
        "max_vcpus": 16,
        "min_vcpus": 0,
        "placement_group": sample_placement_group.name,
        "security_group_ids": [sample.id],
        "subnets": [sample_subnet.id],
        "type": "EC2",
    },
    service_role=aws_batch_service_role.arn,
    type="MANAGED",
    opts = pulumi.ResourceOptions(depends_on=[aws_batch_service_role_role_policy_attachment]))
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/batch"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		ec2AssumeRole, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
			Statements: []iam.GetPolicyDocumentStatement{
				{
					Effect: pulumi.StringRef("Allow"),
					Principals: []iam.GetPolicyDocumentStatementPrincipal{
						{
							Type: "Service",
							Identifiers: []string{
								"ec2.amazonaws.com",
							},
						},
					},
					Actions: []string{
						"sts:AssumeRole",
					},
				},
			},
		}, nil)
		if err != nil {
			return err
		}
		ecsInstanceRole, err := iam.NewRole(ctx, "ecs_instance_role", &iam.RoleArgs{
			Name:             pulumi.String("ecs_instance_role"),
			AssumeRolePolicy: pulumi.String(ec2AssumeRole.Json),
		})
		if err != nil {
			return err
		}
		_, err = iam.NewRolePolicyAttachment(ctx, "ecs_instance_role", &iam.RolePolicyAttachmentArgs{
			Role:      ecsInstanceRole.Name,
			PolicyArn: pulumi.String("arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"),
		})
		if err != nil {
			return err
		}
		ecsInstanceRoleInstanceProfile, err := iam.NewInstanceProfile(ctx, "ecs_instance_role", &iam.InstanceProfileArgs{
			Name: pulumi.String("ecs_instance_role"),
			Role: ecsInstanceRole.Name,
		})
		if err != nil {
			return err
		}
		batchAssumeRole, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
			Statements: []iam.GetPolicyDocumentStatement{
				{
					Effect: pulumi.StringRef("Allow"),
					Principals: []iam.GetPolicyDocumentStatementPrincipal{
						{
							Type: "Service",
							Identifiers: []string{
								"batch.amazonaws.com",
							},
						},
					},
					Actions: []string{
						"sts:AssumeRole",
					},
				},
			},
		}, nil)
		if err != nil {
			return err
		}
		awsBatchServiceRole, err := iam.NewRole(ctx, "aws_batch_service_role", &iam.RoleArgs{
			Name:             pulumi.String("aws_batch_service_role"),
			AssumeRolePolicy: pulumi.String(batchAssumeRole.Json),
		})
		if err != nil {
			return err
		}
		awsBatchServiceRoleRolePolicyAttachment, err := iam.NewRolePolicyAttachment(ctx, "aws_batch_service_role", &iam.RolePolicyAttachmentArgs{
			Role:      awsBatchServiceRole.Name,
			PolicyArn: pulumi.String("arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole"),
		})
		if err != nil {
			return err
		}
		sample, err := ec2.NewSecurityGroup(ctx, "sample", &ec2.SecurityGroupArgs{
			Name: pulumi.String("aws_batch_compute_environment_security_group"),
			Egress: ec2.SecurityGroupEgressArray{
				&ec2.SecurityGroupEgressArgs{
					FromPort: pulumi.Int(0),
					ToPort:   pulumi.Int(0),
					Protocol: pulumi.String("-1"),
					CidrBlocks: pulumi.StringArray{
						pulumi.String("0.0.0.0/0"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		sampleVpc, err := ec2.NewVpc(ctx, "sample", &ec2.VpcArgs{
			CidrBlock: pulumi.String("10.1.0.0/16"),
		})
		if err != nil {
			return err
		}
		sampleSubnet, err := ec2.NewSubnet(ctx, "sample", &ec2.SubnetArgs{
			VpcId:     sampleVpc.ID(),
			CidrBlock: pulumi.String("10.1.1.0/24"),
		})
		if err != nil {
			return err
		}
		samplePlacementGroup, err := ec2.NewPlacementGroup(ctx, "sample", &ec2.PlacementGroupArgs{
			Name:     pulumi.String("sample"),
			Strategy: pulumi.String(ec2.PlacementStrategyCluster),
		})
		if err != nil {
			return err
		}
		_, err = batch.NewComputeEnvironment(ctx, "sample", &batch.ComputeEnvironmentArgs{
			Name: pulumi.String("sample"),
			ComputeResources: &batch.ComputeEnvironmentComputeResourcesArgs{
				InstanceRole: ecsInstanceRoleInstanceProfile.Arn,
				InstanceTypes: pulumi.StringArray{
					pulumi.String("c4.large"),
				},
				MaxVcpus:       pulumi.Int(16),
				MinVcpus:       pulumi.Int(0),
				PlacementGroup: samplePlacementGroup.Name,
				SecurityGroupIds: pulumi.StringArray{
					sample.ID(),
				},
				Subnets: pulumi.StringArray{
					sampleSubnet.ID(),
				},
				Type: pulumi.String("EC2"),
			},
			ServiceRole: awsBatchServiceRole.Arn,
			Type:        pulumi.String("MANAGED"),
		}, pulumi.DependsOn([]pulumi.Resource{
			awsBatchServiceRoleRolePolicyAttachment,
		}))
		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 ec2AssumeRole = Aws.Iam.GetPolicyDocument.Invoke(new()
    {
        Statements = new[]
        {
            new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
            {
                Effect = "Allow",
                Principals = new[]
                {
                    new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
                    {
                        Type = "Service",
                        Identifiers = new[]
                        {
                            "ec2.amazonaws.com",
                        },
                    },
                },
                Actions = new[]
                {
                    "sts:AssumeRole",
                },
            },
        },
    });

    var ecsInstanceRole = new Aws.Iam.Role("ecs_instance_role", new()
    {
        Name = "ecs_instance_role",
        AssumeRolePolicy = ec2AssumeRole.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
    });

    var ecsInstanceRoleRolePolicyAttachment = new Aws.Iam.RolePolicyAttachment("ecs_instance_role", new()
    {
        Role = ecsInstanceRole.Name,
        PolicyArn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role",
    });

    var ecsInstanceRoleInstanceProfile = new Aws.Iam.InstanceProfile("ecs_instance_role", new()
    {
        Name = "ecs_instance_role",
        Role = ecsInstanceRole.Name,
    });

    var batchAssumeRole = Aws.Iam.GetPolicyDocument.Invoke(new()
    {
        Statements = new[]
        {
            new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
            {
                Effect = "Allow",
                Principals = new[]
                {
                    new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
                    {
                        Type = "Service",
                        Identifiers = new[]
                        {
                            "batch.amazonaws.com",
                        },
                    },
                },
                Actions = new[]
                {
                    "sts:AssumeRole",
                },
            },
        },
    });

    var awsBatchServiceRole = new Aws.Iam.Role("aws_batch_service_role", new()
    {
        Name = "aws_batch_service_role",
        AssumeRolePolicy = batchAssumeRole.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
    });

    var awsBatchServiceRoleRolePolicyAttachment = new Aws.Iam.RolePolicyAttachment("aws_batch_service_role", new()
    {
        Role = awsBatchServiceRole.Name,
        PolicyArn = "arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole",
    });

    var sample = new Aws.Ec2.SecurityGroup("sample", new()
    {
        Name = "aws_batch_compute_environment_security_group",
        Egress = new[]
        {
            new Aws.Ec2.Inputs.SecurityGroupEgressArgs
            {
                FromPort = 0,
                ToPort = 0,
                Protocol = "-1",
                CidrBlocks = new[]
                {
                    "0.0.0.0/0",
                },
            },
        },
    });

    var sampleVpc = new Aws.Ec2.Vpc("sample", new()
    {
        CidrBlock = "10.1.0.0/16",
    });

    var sampleSubnet = new Aws.Ec2.Subnet("sample", new()
    {
        VpcId = sampleVpc.Id,
        CidrBlock = "10.1.1.0/24",
    });

    var samplePlacementGroup = new Aws.Ec2.PlacementGroup("sample", new()
    {
        Name = "sample",
        Strategy = Aws.Ec2.PlacementStrategy.Cluster,
    });

    var sampleComputeEnvironment = new Aws.Batch.ComputeEnvironment("sample", new()
    {
        Name = "sample",
        ComputeResources = new Aws.Batch.Inputs.ComputeEnvironmentComputeResourcesArgs
        {
            InstanceRole = ecsInstanceRoleInstanceProfile.Arn,
            InstanceTypes = new[]
            {
                "c4.large",
            },
            MaxVcpus = 16,
            MinVcpus = 0,
            PlacementGroup = samplePlacementGroup.Name,
            SecurityGroupIds = new[]
            {
                sample.Id,
            },
            Subnets = new[]
            {
                sampleSubnet.Id,
            },
            Type = "EC2",
        },
        ServiceRole = awsBatchServiceRole.Arn,
        Type = "MANAGED",
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            awsBatchServiceRoleRolePolicyAttachment,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.iam.IamFunctions;
import com.pulumi.aws.iam.inputs.GetPolicyDocumentArgs;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.iam.RolePolicyAttachment;
import com.pulumi.aws.iam.RolePolicyAttachmentArgs;
import com.pulumi.aws.iam.InstanceProfile;
import com.pulumi.aws.iam.InstanceProfileArgs;
import com.pulumi.aws.ec2.SecurityGroup;
import com.pulumi.aws.ec2.SecurityGroupArgs;
import com.pulumi.aws.ec2.inputs.SecurityGroupEgressArgs;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.Subnet;
import com.pulumi.aws.ec2.SubnetArgs;
import com.pulumi.aws.ec2.PlacementGroup;
import com.pulumi.aws.ec2.PlacementGroupArgs;
import com.pulumi.aws.batch.ComputeEnvironment;
import com.pulumi.aws.batch.ComputeEnvironmentArgs;
import com.pulumi.aws.batch.inputs.ComputeEnvironmentComputeResourcesArgs;
import com.pulumi.resources.CustomResourceOptions;
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) {
        final var ec2AssumeRole = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
            .statements(GetPolicyDocumentStatementArgs.builder()
                .effect("Allow")
                .principals(GetPolicyDocumentStatementPrincipalArgs.builder()
                    .type("Service")
                    .identifiers("ec2.amazonaws.com")
                    .build())
                .actions("sts:AssumeRole")
                .build())
            .build());

        var ecsInstanceRole = new Role("ecsInstanceRole", RoleArgs.builder()
            .name("ecs_instance_role")
            .assumeRolePolicy(ec2AssumeRole.json())
            .build());

        var ecsInstanceRoleRolePolicyAttachment = new RolePolicyAttachment("ecsInstanceRoleRolePolicyAttachment", RolePolicyAttachmentArgs.builder()
            .role(ecsInstanceRole.name())
            .policyArn("arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role")
            .build());

        var ecsInstanceRoleInstanceProfile = new InstanceProfile("ecsInstanceRoleInstanceProfile", InstanceProfileArgs.builder()
            .name("ecs_instance_role")
            .role(ecsInstanceRole.name())
            .build());

        final var batchAssumeRole = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
            .statements(GetPolicyDocumentStatementArgs.builder()
                .effect("Allow")
                .principals(GetPolicyDocumentStatementPrincipalArgs.builder()
                    .type("Service")
                    .identifiers("batch.amazonaws.com")
                    .build())
                .actions("sts:AssumeRole")
                .build())
            .build());

        var awsBatchServiceRole = new Role("awsBatchServiceRole", RoleArgs.builder()
            .name("aws_batch_service_role")
            .assumeRolePolicy(batchAssumeRole.json())
            .build());

        var awsBatchServiceRoleRolePolicyAttachment = new RolePolicyAttachment("awsBatchServiceRoleRolePolicyAttachment", RolePolicyAttachmentArgs.builder()
            .role(awsBatchServiceRole.name())
            .policyArn("arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole")
            .build());

        var sample = new SecurityGroup("sample", SecurityGroupArgs.builder()
            .name("aws_batch_compute_environment_security_group")
            .egress(SecurityGroupEgressArgs.builder()
                .fromPort(0)
                .toPort(0)
                .protocol("-1")
                .cidrBlocks("0.0.0.0/0")
                .build())
            .build());

        var sampleVpc = new Vpc("sampleVpc", VpcArgs.builder()
            .cidrBlock("10.1.0.0/16")
            .build());

        var sampleSubnet = new Subnet("sampleSubnet", SubnetArgs.builder()
            .vpcId(sampleVpc.id())
            .cidrBlock("10.1.1.0/24")
            .build());

        var samplePlacementGroup = new PlacementGroup("samplePlacementGroup", PlacementGroupArgs.builder()
            .name("sample")
            .strategy("cluster")
            .build());

        var sampleComputeEnvironment = new ComputeEnvironment("sampleComputeEnvironment", ComputeEnvironmentArgs.builder()
            .name("sample")
            .computeResources(ComputeEnvironmentComputeResourcesArgs.builder()
                .instanceRole(ecsInstanceRoleInstanceProfile.arn())
                .instanceTypes("c4.large")
                .maxVcpus(16)
                .minVcpus(0)
                .placementGroup(samplePlacementGroup.name())
                .securityGroupIds(sample.id())
                .subnets(sampleSubnet.id())
                .type("EC2")
                .build())
            .serviceRole(awsBatchServiceRole.arn())
            .type("MANAGED")
            .build(), CustomResourceOptions.builder()
                .dependsOn(awsBatchServiceRoleRolePolicyAttachment)
                .build());

    }
}
resources:
  ecsInstanceRole:
    type: aws:iam:Role
    name: ecs_instance_role
    properties:
      name: ecs_instance_role
      assumeRolePolicy: ${ec2AssumeRole.json}
  ecsInstanceRoleRolePolicyAttachment:
    type: aws:iam:RolePolicyAttachment
    name: ecs_instance_role
    properties:
      role: ${ecsInstanceRole.name}
      policyArn: arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role
  ecsInstanceRoleInstanceProfile:
    type: aws:iam:InstanceProfile
    name: ecs_instance_role
    properties:
      name: ecs_instance_role
      role: ${ecsInstanceRole.name}
  awsBatchServiceRole:
    type: aws:iam:Role
    name: aws_batch_service_role
    properties:
      name: aws_batch_service_role
      assumeRolePolicy: ${batchAssumeRole.json}
  awsBatchServiceRoleRolePolicyAttachment:
    type: aws:iam:RolePolicyAttachment
    name: aws_batch_service_role
    properties:
      role: ${awsBatchServiceRole.name}
      policyArn: arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole
  sample:
    type: aws:ec2:SecurityGroup
    properties:
      name: aws_batch_compute_environment_security_group
      egress:
        - fromPort: 0
          toPort: 0
          protocol: '-1'
          cidrBlocks:
            - 0.0.0.0/0
  sampleVpc:
    type: aws:ec2:Vpc
    name: sample
    properties:
      cidrBlock: 10.1.0.0/16
  sampleSubnet:
    type: aws:ec2:Subnet
    name: sample
    properties:
      vpcId: ${sampleVpc.id}
      cidrBlock: 10.1.1.0/24
  samplePlacementGroup:
    type: aws:ec2:PlacementGroup
    name: sample
    properties:
      name: sample
      strategy: cluster
  sampleComputeEnvironment:
    type: aws:batch:ComputeEnvironment
    name: sample
    properties:
      name: sample
      computeResources:
        instanceRole: ${ecsInstanceRoleInstanceProfile.arn}
        instanceTypes:
          - c4.large
        maxVcpus: 16
        minVcpus: 0
        placementGroup: ${samplePlacementGroup.name}
        securityGroupIds:
          - ${sample.id}
        subnets:
          - ${sampleSubnet.id}
        type: EC2
      serviceRole: ${awsBatchServiceRole.arn}
      type: MANAGED
    options:
      dependsOn:
        - ${awsBatchServiceRoleRolePolicyAttachment}
variables:
  ec2AssumeRole:
    fn::invoke:
      function: aws:iam:getPolicyDocument
      arguments:
        statements:
          - effect: Allow
            principals:
              - type: Service
                identifiers:
                  - ec2.amazonaws.com
            actions:
              - sts:AssumeRole
  batchAssumeRole:
    fn::invoke:
      function: aws:iam:getPolicyDocument
      arguments:
        statements:
          - effect: Allow
            principals:
              - type: Service
                identifiers:
                  - batch.amazonaws.com
            actions:
              - sts:AssumeRole

At runtime, AWS Batch launches EC2 instances in your VPC and registers them with an ECS cluster. The computeResources block defines the instance types, vCPU scaling limits (minVcpus to maxVcpus), and placement group for cluster networking. The instanceRole grants EC2 instances permission to join the ECS cluster, while serviceRole allows Batch to manage infrastructure on your behalf. The dependsOn ensures IAM policies are attached before Batch attempts to use them, preventing deletion race conditions.

Run serverless batch jobs on Fargate

Teams that want to avoid managing EC2 instances can use Fargate compute environments, which run containers without provisioning or scaling infrastructure.

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

const sample = new aws.batch.ComputeEnvironment("sample", {
    name: "sample",
    computeResources: {
        maxVcpus: 16,
        securityGroupIds: [sampleAwsSecurityGroup.id],
        subnets: [sampleAwsSubnet.id],
        type: "FARGATE",
    },
    serviceRole: awsBatchServiceRoleAwsIamRole.arn,
    type: "MANAGED",
}, {
    dependsOn: [awsBatchServiceRole],
});
import pulumi
import pulumi_aws as aws

sample = aws.batch.ComputeEnvironment("sample",
    name="sample",
    compute_resources={
        "max_vcpus": 16,
        "security_group_ids": [sample_aws_security_group["id"]],
        "subnets": [sample_aws_subnet["id"]],
        "type": "FARGATE",
    },
    service_role=aws_batch_service_role_aws_iam_role["arn"],
    type="MANAGED",
    opts = pulumi.ResourceOptions(depends_on=[aws_batch_service_role]))
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := batch.NewComputeEnvironment(ctx, "sample", &batch.ComputeEnvironmentArgs{
			Name: pulumi.String("sample"),
			ComputeResources: &batch.ComputeEnvironmentComputeResourcesArgs{
				MaxVcpus: pulumi.Int(16),
				SecurityGroupIds: pulumi.StringArray{
					sampleAwsSecurityGroup.Id,
				},
				Subnets: pulumi.StringArray{
					sampleAwsSubnet.Id,
				},
				Type: pulumi.String("FARGATE"),
			},
			ServiceRole: pulumi.Any(awsBatchServiceRoleAwsIamRole.Arn),
			Type:        pulumi.String("MANAGED"),
		}, pulumi.DependsOn([]pulumi.Resource{
			awsBatchServiceRole,
		}))
		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 sample = new Aws.Batch.ComputeEnvironment("sample", new()
    {
        Name = "sample",
        ComputeResources = new Aws.Batch.Inputs.ComputeEnvironmentComputeResourcesArgs
        {
            MaxVcpus = 16,
            SecurityGroupIds = new[]
            {
                sampleAwsSecurityGroup.Id,
            },
            Subnets = new[]
            {
                sampleAwsSubnet.Id,
            },
            Type = "FARGATE",
        },
        ServiceRole = awsBatchServiceRoleAwsIamRole.Arn,
        Type = "MANAGED",
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            awsBatchServiceRole,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.batch.ComputeEnvironment;
import com.pulumi.aws.batch.ComputeEnvironmentArgs;
import com.pulumi.aws.batch.inputs.ComputeEnvironmentComputeResourcesArgs;
import com.pulumi.resources.CustomResourceOptions;
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 sample = new ComputeEnvironment("sample", ComputeEnvironmentArgs.builder()
            .name("sample")
            .computeResources(ComputeEnvironmentComputeResourcesArgs.builder()
                .maxVcpus(16)
                .securityGroupIds(sampleAwsSecurityGroup.id())
                .subnets(sampleAwsSubnet.id())
                .type("FARGATE")
                .build())
            .serviceRole(awsBatchServiceRoleAwsIamRole.arn())
            .type("MANAGED")
            .build(), CustomResourceOptions.builder()
                .dependsOn(awsBatchServiceRole)
                .build());

    }
}
resources:
  sample:
    type: aws:batch:ComputeEnvironment
    properties:
      name: sample
      computeResources:
        maxVcpus: 16
        securityGroupIds:
          - ${sampleAwsSecurityGroup.id}
        subnets:
          - ${sampleAwsSubnet.id}
        type: FARGATE
      serviceRole: ${awsBatchServiceRoleAwsIamRole.arn}
      type: MANAGED
    options:
      dependsOn:
        - ${awsBatchServiceRole}

Fargate compute environments eliminate instance management. Set type to “FARGATE” in computeResources, specify maxVcpus for scaling limits, and provide VPC networking (subnets and security groups). Unlike EC2 environments, Fargate doesn’t require instanceRole or instanceTypes since AWS manages the underlying infrastructure.

Control job behavior during infrastructure updates

Production environments need to control how running jobs are handled when compute resources are updated or replaced.

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

const sample = new aws.batch.ComputeEnvironment("sample", {
    name: "sample",
    computeResources: {
        allocationStrategy: "BEST_FIT_PROGRESSIVE",
        instanceRole: ecsInstance.arn,
        instanceTypes: ["optimal"],
        maxVcpus: 4,
        minVcpus: 0,
        securityGroupIds: [sampleAwsSecurityGroup.id],
        subnets: [sampleAwsSubnet.id],
        type: "EC2",
    },
    updatePolicy: {
        jobExecutionTimeoutMinutes: 30,
        terminateJobsOnUpdate: false,
    },
    type: "MANAGED",
});
import pulumi
import pulumi_aws as aws

sample = aws.batch.ComputeEnvironment("sample",
    name="sample",
    compute_resources={
        "allocation_strategy": "BEST_FIT_PROGRESSIVE",
        "instance_role": ecs_instance["arn"],
        "instance_types": ["optimal"],
        "max_vcpus": 4,
        "min_vcpus": 0,
        "security_group_ids": [sample_aws_security_group["id"]],
        "subnets": [sample_aws_subnet["id"]],
        "type": "EC2",
    },
    update_policy={
        "job_execution_timeout_minutes": 30,
        "terminate_jobs_on_update": False,
    },
    type="MANAGED")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := batch.NewComputeEnvironment(ctx, "sample", &batch.ComputeEnvironmentArgs{
			Name: pulumi.String("sample"),
			ComputeResources: &batch.ComputeEnvironmentComputeResourcesArgs{
				AllocationStrategy: pulumi.String("BEST_FIT_PROGRESSIVE"),
				InstanceRole:       pulumi.Any(ecsInstance.Arn),
				InstanceTypes: pulumi.StringArray{
					pulumi.String("optimal"),
				},
				MaxVcpus: pulumi.Int(4),
				MinVcpus: pulumi.Int(0),
				SecurityGroupIds: pulumi.StringArray{
					sampleAwsSecurityGroup.Id,
				},
				Subnets: pulumi.StringArray{
					sampleAwsSubnet.Id,
				},
				Type: pulumi.String("EC2"),
			},
			UpdatePolicy: &batch.ComputeEnvironmentUpdatePolicyArgs{
				JobExecutionTimeoutMinutes: pulumi.Int(30),
				TerminateJobsOnUpdate:      pulumi.Bool(false),
			},
			Type: pulumi.String("MANAGED"),
		})
		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 sample = new Aws.Batch.ComputeEnvironment("sample", new()
    {
        Name = "sample",
        ComputeResources = new Aws.Batch.Inputs.ComputeEnvironmentComputeResourcesArgs
        {
            AllocationStrategy = "BEST_FIT_PROGRESSIVE",
            InstanceRole = ecsInstance.Arn,
            InstanceTypes = new[]
            {
                "optimal",
            },
            MaxVcpus = 4,
            MinVcpus = 0,
            SecurityGroupIds = new[]
            {
                sampleAwsSecurityGroup.Id,
            },
            Subnets = new[]
            {
                sampleAwsSubnet.Id,
            },
            Type = "EC2",
        },
        UpdatePolicy = new Aws.Batch.Inputs.ComputeEnvironmentUpdatePolicyArgs
        {
            JobExecutionTimeoutMinutes = 30,
            TerminateJobsOnUpdate = false,
        },
        Type = "MANAGED",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.batch.ComputeEnvironment;
import com.pulumi.aws.batch.ComputeEnvironmentArgs;
import com.pulumi.aws.batch.inputs.ComputeEnvironmentComputeResourcesArgs;
import com.pulumi.aws.batch.inputs.ComputeEnvironmentUpdatePolicyArgs;
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 sample = new ComputeEnvironment("sample", ComputeEnvironmentArgs.builder()
            .name("sample")
            .computeResources(ComputeEnvironmentComputeResourcesArgs.builder()
                .allocationStrategy("BEST_FIT_PROGRESSIVE")
                .instanceRole(ecsInstance.arn())
                .instanceTypes("optimal")
                .maxVcpus(4)
                .minVcpus(0)
                .securityGroupIds(sampleAwsSecurityGroup.id())
                .subnets(sampleAwsSubnet.id())
                .type("EC2")
                .build())
            .updatePolicy(ComputeEnvironmentUpdatePolicyArgs.builder()
                .jobExecutionTimeoutMinutes(30)
                .terminateJobsOnUpdate(false)
                .build())
            .type("MANAGED")
            .build());

    }
}
resources:
  sample:
    type: aws:batch:ComputeEnvironment
    properties:
      name: sample
      computeResources:
        allocationStrategy: BEST_FIT_PROGRESSIVE
        instanceRole: ${ecsInstance.arn}
        instanceTypes:
          - optimal
        maxVcpus: 4
        minVcpus: 0
        securityGroupIds:
          - ${sampleAwsSecurityGroup.id}
        subnets:
          - ${sampleAwsSubnet.id}
        type: EC2
      updatePolicy:
        jobExecutionTimeoutMinutes: 30
        terminateJobsOnUpdate: false
      type: MANAGED

The updatePolicy block controls job behavior during infrastructure changes. Set terminateJobsOnUpdate to false to let jobs complete naturally, and use jobExecutionTimeoutMinutes to define how long Batch waits before forcing termination. The allocationStrategy property (here set to “BEST_FIT_PROGRESSIVE”) determines how Batch selects instance types from your list, balancing cost and availability.

Beyond these examples

These snippets focus on specific compute environment features: EC2 and Fargate compute types, placement groups and allocation strategies, and update policies for job management. They’re intentionally minimal rather than full batch processing systems.

The examples may reference pre-existing infrastructure such as IAM roles (service role and instance profile), and VPC subnets and security groups. They focus on configuring the compute environment rather than provisioning everything around it.

To keep things focused, common compute environment patterns are omitted, including:

  • EKS-based compute environments (eksConfiguration)
  • State management (ENABLED/DISABLED)
  • Launch templates and custom AMIs
  • Spot instance configuration

These omissions are intentional: the goal is to illustrate how each compute environment feature is wired, not provide drop-in batch processing modules. See the Batch ComputeEnvironment resource reference for all available configuration options.

Let's create AWS Batch Compute Environments

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Resource Lifecycle & Dependencies
Why does my compute environment get stuck in DELETING state?
This happens when the IAM policy is destroyed before the compute environment finishes deleting. Set dependsOn to reference the aws.iam.RolePolicyAttachment to ensure proper deletion order.
What IAM roles are required for a compute environment?
All compute environments require a serviceRole (ARN of IAM role for AWS Batch). EC2 compute environments also require an instanceRole in the computeResources configuration.
Compute Resource Types
What's the difference between EC2 and FARGATE compute environments?
EC2 compute environments require instanceRole, instanceTypes, and vCPU limits. FARGATE compute environments only need maxVcpus, securityGroupIds, and subnets in the computeResources configuration.
What's the difference between MANAGED and UNMANAGED compute environments?
MANAGED compute environments have AWS Batch handle compute resource provisioning and scaling. UNMANAGED environments require you to manage compute resources yourself. Set this using the type property.
Configuration & Naming
What's the difference between name and namePrefix?
Use name to specify an exact name (up to 128 characters), or namePrefix to generate a unique name with your specified prefix. These properties conflict and cannot be used together.
What properties can't be changed after creation?
The name, namePrefix, type, and eksConfiguration properties are immutable. Changing any of these will force replacement of the compute environment.
State & Updates
How do I temporarily stop a compute environment from accepting jobs?
Set state to DISABLED to prevent the compute environment from accepting new jobs. Set it back to ENABLED (the default) to resume accepting jobs.
How do I control what happens to running jobs during updates?
Configure updatePolicy with jobExecutionTimeoutMinutes (how long to wait for jobs) and terminateJobsOnUpdate (whether to terminate jobs immediately or wait for completion).

Using a different cloud?

Explore containers guides for other cloud providers: