Create AWS Batch Compute Environments

The aws:batch/computeEnvironment:ComputeEnvironment resource, part of the Pulumi AWS provider, defines the compute capacity that AWS Batch uses to run containerized batch jobs: EC2 instances, Fargate tasks, or EKS pods. This guide focuses on three capabilities: EC2-based compute with instance type selection, Fargate serverless compute, and update policies for job lifecycle control.

Compute environments require IAM service roles, instance profiles for EC2, and VPC networking infrastructure. The examples are intentionally small. Combine them with your own IAM roles, VPC configuration, and job queues.

Run batch jobs on EC2 instances

Most AWS Batch deployments start with EC2-based compute environments, which provide full control over instance types, networking, and placement for containerized workloads.

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

The computeResources block defines the EC2 capacity pool. The type property set to “EC2” tells Batch to provision EC2 instances; instanceTypes specifies which instance families to use. The minVcpus and maxVcpus properties control scaling: Batch scales from zero to 16 vCPUs based on queued jobs. The serviceRole grants Batch permission to manage EC2 resources; the instanceRole (via instanceProfile) grants container instances permission to pull images and write logs. Note the dependsOn relationship: the service role policy attachment must complete before creating the compute environment to avoid deletion race conditions.

Run batch jobs on Fargate serverless compute

Teams that want to avoid managing EC2 instances can use Fargate, which provisions compute capacity automatically without requiring instance type selection or AMI management.

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}

Setting type to “FARGATE” in computeResources eliminates the need for instanceTypes, instanceRole, and minVcpus properties. Fargate provisions tasks on-demand up to maxVcpus. You still specify securityGroupIds and subnets for network placement, but Batch handles all capacity provisioning.

Control job behavior during infrastructure updates

When compute environments need updates, you can control whether running jobs are terminated immediately or allowed to complete within a timeout window.

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 lifecycle during infrastructure changes. Setting terminateJobsOnUpdate to false allows running jobs to complete; jobExecutionTimeoutMinutes sets a 30-minute deadline before forcing termination. Without an update policy, Batch terminates jobs immediately when you modify the compute environment.

Beyond these examples

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

The examples may reference pre-existing infrastructure such as IAM service roles and instance profiles, 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:

  • State management (ENABLED/DISABLED)
  • EKS-based compute environments (eksConfiguration)
  • Allocation strategies beyond BEST_FIT_PROGRESSIVE
  • Launch templates and custom AMIs

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

Common Issues & Errors
Why is my compute environment stuck in DELETING state?
This happens when the IAM policy is destroyed before the compute environment finishes deleting. Set dependsOn to the related aws.iam.RolePolicyAttachment to ensure proper deletion order.
Compute Types & Configuration
What are the available compute environment types?
You can create MANAGED or UNMANAGED compute environments. For managed environments, choose between EC2 (you manage instances) or FARGATE (serverless) compute resource types.
What's the difference between EC2 and Fargate compute types?
EC2 requires instanceRole, instanceTypes, and instance configuration in computeResources. Fargate is serverless and only needs maxVcpus, securityGroupIds, and subnets.
How do I control update behavior for my compute environment?
Configure updatePolicy with jobExecutionTimeoutMinutes (how long to wait for jobs) and terminateJobsOnUpdate (whether to stop running jobs during updates).
IAM & Permissions
What IAM roles do I need for a compute environment?
You need a serviceRole (allows AWS Batch to call other services) for all types. EC2 compute environments also require an instanceRole for the ECS container instances.
Why do I need to use dependsOn with IAM role attachments?
Without dependsOn on the aws.iam.RolePolicyAttachment, Pulumi may destroy the policy before the compute environment finishes deleting, causing it to get stuck in DELETING state.
Resource Lifecycle & State
What properties are immutable after creation?
The name, namePrefix, type, and eksConfiguration properties cannot be changed after creation. Modifying them requires replacing the compute environment.
How do I prevent my compute environment from accepting new jobs?
Set state to DISABLED. The compute environment will stop accepting jobs from queues but won’t terminate running jobs. Set it back to ENABLED to resume.
Can I rename my compute environment after creation?
No, the name property is immutable. Changing it will force replacement of the entire compute environment.
Naming & Identification
How do I name my compute environment?
Use either name (explicit name, up to 128 letters/numbers/underscores) or namePrefix (generates unique name with your prefix). These properties conflict, so choose one.
What are the naming constraints for compute environments?
Names can be up to 128 characters and include uppercase letters, lowercase letters, numbers, and underscores.
How do I import an existing compute environment?
Use pulumi import aws:batch/computeEnvironment:ComputeEnvironment <resource_name> <compute_environment_name> where the last argument is the compute environment’s name.

Using a different cloud?

Explore containers guides for other cloud providers: