Cloud Engineering with Containers

Manage Clusters and Deploy Containers with Ease

Pulumi supports managing clusters and their associated infrastructure, whether it is Kubernetes, Amazon ECS, Azure ACI, or Google GKE. Build and deploy application containers to private registies, all in one programming model. Any code, any cloud, any language.

"use strict";
const pulumi = require("@pulumi/pulumi");
const aws = require("@pulumi/aws");
const awsx = require("@pulumi/awsx");

const lb = new awsx.lb.ApplicationLoadBalancer("lb");
const cluster = new aws.ecs.Cluster("cluster");

const service = new awsx.ecs.FargateService("service", {
    cluster: cluster.arn,
    assignPublicIp: true,
    desiredCount: 2,
    taskDefinitionArgs: {
        container: {
            name: "my-service",
            image: "nginx:latest",
            cpu: 128,
            memory: 512,
            essential: true,
            portMappings: [
                {
                    containerPort: 80,
                    targetGroup: lb.defaultTargetGroup,
                },
            ],
        },
    },
});

exports.url = pulumi.interpolate`http://${lb.loadBalancer.dnsName}`;
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";

const lb = new awsx.lb.ApplicationLoadBalancer("lb");
const cluster = new aws.ecs.Cluster("cluster");

const service = new awsx.ecs.FargateService("service", {
    cluster: cluster.arn,
    assignPublicIp: true,
    desiredCount: 2,
    taskDefinitionArgs: {
        container: {
            name: "my-service",
            image: "nginx:latest",
            cpu: 128,
            memory: 512,
            essential: true,
            portMappings: [
                {
                    containerPort: 80,
                    targetGroup: lb.defaultTargetGroup,
                },
            ],
        },
    },
});

export const url = pulumi.interpolate`http://${lb.loadBalancer.dnsName}`;
import pulumi
import pulumi_aws as aws
import pulumi_awsx as awsx

lb = awsx.lb.ApplicationLoadBalancer("lb")
cluster = aws.ecs.Cluster("cluster")

service = awsx.ecs.FargateService("service",
    cluster=cluster.arn,
    assign_public_ip=True,
    desired_count=2,
    task_definition_args=awsx.ecs.FargateServiceTaskDefinitionArgs(
        container=awsx.ecs.TaskDefinitionContainerDefinitionArgs(
            name="my-service",
            image="nginx:latest",
            cpu=128,
            memory=512,
            essential=True,
            port_mappings=[awsx.ecs.TaskDefinitionPortMappingArgs(
                container_port=80,
                target_group=lb.default_target_group,
            )],
        ),
    ))

pulumi.export("url", pulumi.Output.concat("http://", lb.load_balancer.dns_name))
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ecs"
	ecsx "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/ecs"
	"github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/lb"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		lb, err := lb.NewApplicationLoadBalancer(ctx, "lb", nil)
		if err != nil {
			return err
		}

		cluster, err := ecs.NewCluster(ctx, "cluster", nil)
		if err != nil {
			return err
		}

		_, err = ecsx.NewFargateService(ctx, "service", &ecsx.FargateServiceArgs{
			Cluster:        cluster.Arn,
			AssignPublicIp: pulumi.Bool(true),
			DesiredCount:   pulumi.Int(2),
			TaskDefinitionArgs: &ecsx.FargateServiceTaskDefinitionArgs{
				Container: &ecsx.TaskDefinitionContainerDefinitionArgs{
					Name:      pulumi.String("my-service"),
					Image:     pulumi.String("nginx:latest"),
					Cpu:       pulumi.Int(128),
					Memory:    pulumi.Int(512),
					Essential: pulumi.Bool(true),
					PortMappings: ecsx.TaskDefinitionPortMappingArray{
						&ecsx.TaskDefinitionPortMappingArgs{
							ContainerPort: pulumi.Int(80),
							TargetGroup:   lb.DefaultTargetGroup,
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}

		ctx.Export("url", pulumi.Sprintf("http://%s", lb.LoadBalancer.DnsName()))
		return nil
	})
}
using System.Collections.Generic;
using Pulumi;
using Aws = Pulumi.Aws;
using Awsx = Pulumi.Awsx;

return await Deployment.RunAsync(() =>
{
    var lb = new Awsx.Lb.ApplicationLoadBalancer("lb");
    var cluster = new Aws.Ecs.Cluster("cluster");

    var service = new Awsx.Ecs.FargateService("service", new()
    {
        Cluster = cluster.Arn,
        AssignPublicIp = true,
        TaskDefinitionArgs = new Awsx.Ecs.Inputs.FargateServiceTaskDefinitionArgs
        {
            Container = new Awsx.Ecs.Inputs.TaskDefinitionContainerDefinitionArgs
            {
                Name = "my-service",
                Image = "nginx:latest",
                Cpu = 128,
                Memory = 512,
                Essential = true,
                PortMappings = new()
                {
                    new Awsx.Ecs.Inputs.TaskDefinitionPortMappingArgs
                    {
                        ContainerPort = 80,
                        TargetGroup = lb.DefaultTargetGroup,
                    },
                },
            },
        },
    });

    return new Dictionary<string, object?>
    {
        ["url"] = lb.LoadBalancer.Apply(loadBalancer => Output.Format($"http://{loadBalancer.DnsName}")),
    };
});
package myproject;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.awsx.ecr.Repository;
import com.pulumi.awsx.ecr.RepositoryArgs;
import com.pulumi.aws.ecs.Cluster;
import com.pulumi.awsx.lb.ApplicationLoadBalancer;
import com.pulumi.awsx.ecs.FargateService;
import com.pulumi.awsx.ecs.FargateServiceArgs;
import com.pulumi.awsx.ecs.inputs.FargateServiceTaskDefinitionArgs;
import com.pulumi.awsx.ecs.inputs.TaskDefinitionContainerDefinitionArgs;
import com.pulumi.awsx.ecs.inputs.TaskDefinitionPortMappingArgs;

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

    public static void stack(Context ctx) {
        var cluster = new Cluster("cluster");
        var lb = new ApplicationLoadBalancer("lb");

        var service = new FargateService("service", FargateServiceArgs.builder()
            .cluster(cluster.arn())
            .assignPublicIp(true)
            .taskDefinitionArgs(FargateServiceTaskDefinitionArgs.builder()
                .container(TaskDefinitionContainerDefinitionArgs.builder()
                    .name("my-service")
                    .image("nginx:latest")
                    .cpu(128)
                    .memory(512)
                    .essential(true)
                    .portMappings(TaskDefinitionPortMappingArgs.builder()
                        .containerPort(80)
                        .targetGroup(lb.defaultTargetGroup())
                        .build())
                    .build())
                .build())
            .build());

        ctx.export("url", Output.format("http://%s", lb.loadBalancer().applyValue(loadBalancer -> loadBalancer.dnsName())));
    }
}
name: awsx-load-balanced-fargate-nginx-yaml
runtime: yaml
resources:
  lb:
    type: awsx:lb:ApplicationLoadBalancer
  cluster:
    type: aws:ecs:Cluster
  service:
    type: awsx:ecs:FargateService
    properties:
      cluster: ${cluster.arn}
      assignPublicIp: true
      taskDefinitionArgs:
        container:
          name: my-service
          image: "nginx:latest"
          cpu: 128
          memory: 512
          essential: true
          portMappings:
            - containerPort: 80
              targetGroup: ${lb.defaultTargetGroup}
outputs:
  url: http://${lb.loadBalancer.dnsName}

What is Container Management?

Docker, and containers more generally, transformed the way that developers could build, ship, and run applications. As a standardized unit of delivery, containers have all of the libraries, code, and runtime knowledge they need to work, and so can be deployed and scaled into any environment.

For DevOps teams, container management is an important capability whether delivering containers into bespoke environments, or more likely through a managed cloud offering such as AWS Fargate, AWS ECS, and Microsoft ACI, or through Kubernetes - either on-premises or AWS EKS, Microsoft AKS, or Google GKE.

Deploying containers with Pulumi

Container management solutions are broadly split into two options: low-management solutions for ease of deployment, and Kubernetes-based solutions for complete control.

Low Cluster Management

AWS Fargate, and Microsoft ACI are examples of services that enable the deployment of containers without having to manage servers or clusters. This means teams can very easily build container applications and deploy them into production without worrying about infrastructure, but gaining the benefits of seamless scaling.

Kubernetes Management

While running Kubernetes on-premises is a viable, and well-used solution, the major cloud vendors also offer Kubernetes sevices: Amazon EKS, Microsoft AKS, and Google GKE.

Pulumi makes it simple to interact with any of the services available from the major cloud vendors, and with Kubernetes.

Deploy Nginx to AWS Fargate

In this example, Pulumi defines and uses a new Amazon ECS Fargate cluster, and creates a load balanced service running the standard Nginx image from the Docker Hub. The same experience is available on other clouds and Pulumi can pull from any container registry.

"use strict";
const pulumi = require("@pulumi/pulumi");
const aws = require("@pulumi/aws");
const awsx = require("@pulumi/awsx");

const lb = new awsx.lb.ApplicationLoadBalancer("lb");
const cluster = new aws.ecs.Cluster("cluster");

const service = new awsx.ecs.FargateService("service", {
    cluster: cluster.arn,
    assignPublicIp: true,
    desiredCount: 2,
    taskDefinitionArgs: {
        container: {
            name: "my-service",
            image: "nginx:latest",
            cpu: 128,
            memory: 512,
            essential: true,
            portMappings: [
                {
                    containerPort: 80,
                    targetGroup: lb.defaultTargetGroup,
                },
            ],
        },
    },
});

exports.url = pulumi.interpolate`http://${lb.loadBalancer.dnsName}`;
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";

const lb = new awsx.lb.ApplicationLoadBalancer("lb");
const cluster = new aws.ecs.Cluster("cluster");

const service = new awsx.ecs.FargateService("service", {
    cluster: cluster.arn,
    assignPublicIp: true,
    desiredCount: 2,
    taskDefinitionArgs: {
        container: {
            name: "my-service",
            image: "nginx:latest",
            cpu: 128,
            memory: 512,
            essential: true,
            portMappings: [
                {
                    containerPort: 80,
                    targetGroup: lb.defaultTargetGroup,
                },
            ],
        },
    },
});

export const url = pulumi.interpolate`http://${lb.loadBalancer.dnsName}`;
import pulumi
import pulumi_aws as aws
import pulumi_awsx as awsx

lb = awsx.lb.ApplicationLoadBalancer("lb")
cluster = aws.ecs.Cluster("cluster")

service = awsx.ecs.FargateService("service",
    cluster=cluster.arn,
    assign_public_ip=True,
    desired_count=2,
    task_definition_args=awsx.ecs.FargateServiceTaskDefinitionArgs(
        container=awsx.ecs.TaskDefinitionContainerDefinitionArgs(
            name="my-service",
            image="nginx:latest",
            cpu=128,
            memory=512,
            essential=True,
            port_mappings=[awsx.ecs.TaskDefinitionPortMappingArgs(
                container_port=80,
                target_group=lb.default_target_group,
            )],
        ),
    ))

pulumi.export("url", pulumi.Output.concat("http://", lb.load_balancer.dns_name))
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ecs"
	ecsx "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/ecs"
	"github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/lb"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		lb, err := lb.NewApplicationLoadBalancer(ctx, "lb", nil)
		if err != nil {
			return err
		}

		cluster, err := ecs.NewCluster(ctx, "cluster", nil)
		if err != nil {
			return err
		}

		_, err = ecsx.NewFargateService(ctx, "service", &ecsx.FargateServiceArgs{
			Cluster:        cluster.Arn,
			AssignPublicIp: pulumi.Bool(true),
			DesiredCount:   pulumi.Int(2),
			TaskDefinitionArgs: &ecsx.FargateServiceTaskDefinitionArgs{
				Container: &ecsx.TaskDefinitionContainerDefinitionArgs{
					Name:      pulumi.String("my-service"),
					Image:     pulumi.String("nginx:latest"),
					Cpu:       pulumi.Int(128),
					Memory:    pulumi.Int(512),
					Essential: pulumi.Bool(true),
					PortMappings: ecsx.TaskDefinitionPortMappingArray{
						&ecsx.TaskDefinitionPortMappingArgs{
							ContainerPort: pulumi.Int(80),
							TargetGroup:   lb.DefaultTargetGroup,
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}

		ctx.Export("url", pulumi.Sprintf("http://%s", lb.LoadBalancer.DnsName()))
		return nil
	})
}
using System.Collections.Generic;
using Pulumi;
using Aws = Pulumi.Aws;
using Awsx = Pulumi.Awsx;

return await Deployment.RunAsync(() =>
{
    var lb = new Awsx.Lb.ApplicationLoadBalancer("lb");
    var cluster = new Aws.Ecs.Cluster("cluster");

    var service = new Awsx.Ecs.FargateService("service", new()
    {
        Cluster = cluster.Arn,
        AssignPublicIp = true,
        TaskDefinitionArgs = new Awsx.Ecs.Inputs.FargateServiceTaskDefinitionArgs
        {
            Container = new Awsx.Ecs.Inputs.TaskDefinitionContainerDefinitionArgs
            {
                Name = "my-service",
                Image = "nginx:latest",
                Cpu = 128,
                Memory = 512,
                Essential = true,
                PortMappings = new()
                {
                    new Awsx.Ecs.Inputs.TaskDefinitionPortMappingArgs
                    {
                        ContainerPort = 80,
                        TargetGroup = lb.DefaultTargetGroup,
                    },
                },
            },
        },
    });

    return new Dictionary<string, object?>
    {
        ["url"] = lb.LoadBalancer.Apply(loadBalancer => Output.Format($"http://{loadBalancer.DnsName}")),
    };
});
package myproject;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.awsx.ecr.Repository;
import com.pulumi.awsx.ecr.RepositoryArgs;
import com.pulumi.aws.ecs.Cluster;
import com.pulumi.awsx.lb.ApplicationLoadBalancer;
import com.pulumi.awsx.ecs.FargateService;
import com.pulumi.awsx.ecs.FargateServiceArgs;
import com.pulumi.awsx.ecs.inputs.FargateServiceTaskDefinitionArgs;
import com.pulumi.awsx.ecs.inputs.TaskDefinitionContainerDefinitionArgs;
import com.pulumi.awsx.ecs.inputs.TaskDefinitionPortMappingArgs;

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

    public static void stack(Context ctx) {
        var cluster = new Cluster("cluster");
        var lb = new ApplicationLoadBalancer("lb");

        var service = new FargateService("service", FargateServiceArgs.builder()
            .cluster(cluster.arn())
            .assignPublicIp(true)
            .taskDefinitionArgs(FargateServiceTaskDefinitionArgs.builder()
                .container(TaskDefinitionContainerDefinitionArgs.builder()
                    .name("my-service")
                    .image("nginx:latest")
                    .cpu(128)
                    .memory(512)
                    .essential(true)
                    .portMappings(TaskDefinitionPortMappingArgs.builder()
                        .containerPort(80)
                        .targetGroup(lb.defaultTargetGroup())
                        .build())
                    .build())
                .build())
            .build());

        ctx.export("url", Output.format("http://%s", lb.loadBalancer().applyValue(loadBalancer -> loadBalancer.dnsName())));
    }
}
name: awsx-load-balanced-fargate-nginx-yaml
runtime: yaml
resources:
  lb:
    type: awsx:lb:ApplicationLoadBalancer
  cluster:
    type: aws:ecs:Cluster
  service:
    type: awsx:ecs:FargateService
    properties:
      cluster: ${cluster.arn}
      assignPublicIp: true
      taskDefinitionArgs:
        container:
          name: my-service
          image: "nginx:latest"
          cpu: 128
          memory: 512
          essential: true
          portMappings:
            - containerPort: 80
              targetGroup: ${lb.defaultTargetGroup}
outputs:
  url: http://${lb.loadBalancer.dnsName}

Deploying with a custom build

This example builds a container image from a Dockerfile at ./app and deploys it behind a load balancer with ECS Fargate.

"use strict";
const pulumi = require("@pulumi/pulumi");
const aws = require("@pulumi/aws");
const awsx = require("@pulumi/awsx");

const repo = new awsx.ecr.Repository("repo", {
    forceDelete: true,
});

const image = new awsx.ecr.Image("image", {
    repositoryUrl: repo.url,
    context: "./app",
    platform: "linux/amd64",
});

const cluster = new aws.ecs.Cluster("cluster");

const lb = new awsx.lb.ApplicationLoadBalancer("lb");

const service = new awsx.ecs.FargateService("service", {
    cluster: cluster.arn,
    assignPublicIp: true,
    taskDefinitionArgs: {
        container: {
            name: "my-service",
            image: image.imageUri,
            cpu: 128,
            memory: 512,
            essential: true,
            portMappings: [
                {
                    containerPort: 80,
                    targetGroup: lb.defaultTargetGroup,
                },
            ],
        },
    },
});

exports.url = pulumi.interpolate`http://${lb.loadBalancer.dnsName}`;
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";

const repo = new awsx.ecr.Repository("repo", {
    forceDelete: true,
});

const image = new awsx.ecr.Image("image", {
    repositoryUrl: repo.url,
    context: "./app",
    platform: "linux/amd64",
});

const cluster = new aws.ecs.Cluster("cluster");

const lb = new awsx.lb.ApplicationLoadBalancer("lb");

const service = new awsx.ecs.FargateService("service", {
    cluster: cluster.arn,
    assignPublicIp: true,
    taskDefinitionArgs: {
        container: {
            name: "my-service",
            image: image.imageUri,
            cpu: 128,
            memory: 512,
            essential: true,
            portMappings: [
                {
                    containerPort: 80,
                    targetGroup: lb.defaultTargetGroup,
                },
            ],
        },
    },
});

export const url = pulumi.interpolate`http://${lb.loadBalancer.dnsName}`;
import pulumi
import pulumi_aws as aws
import pulumi_awsx as awsx

repository = awsx.ecr.Repository(
    "repository",
    awsx.ecr.RepositoryArgs(
        force_delete=True
    ),
)

image = awsx.ecr.Image(
    "image",
    awsx.ecr.ImageArgs(
        repository_url=repository.url, context="./app", platform="linux/amd64"
    ),
)

cluster = aws.ecs.Cluster("cluster")
lb = awsx.lb.ApplicationLoadBalancer("lb")

service = awsx.ecs.FargateService(
    "service",
    awsx.ecs.FargateServiceArgs(
        cluster=cluster.arn,
        assign_public_ip=True,
        task_definition_args=awsx.ecs.FargateServiceTaskDefinitionArgs(
            container=awsx.ecs.TaskDefinitionContainerDefinitionArgs(
                name="my-service",
                image=image.image_uri,
                cpu=512,
                memory=128,
                essential=True,
                port_mappings=[
                    awsx.ecs.TaskDefinitionPortMappingArgs(
                        container_port=80,
                        target_group=lb.default_target_group,
                    )
                ],
            ),
        ),
    ),
)

pulumi.export("url", pulumi.Output.concat("http://", lb.load_balancer.dns_name))
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ecs"
	"github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/ecr"
	ecsx "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/ecs"
	"github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/lb"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		repository, err := ecr.NewRepository(ctx, "repository", &ecr.RepositoryArgs{
			ForceDelete: pulumi.Bool(true),
		})
		if err != nil {
			return err
		}

		image, err := ecr.NewImage(ctx, "image", &ecr.ImageArgs{
			RepositoryUrl: repository.Url,
			Context:       pulumi.String("./app"),
			Platform:      pulumi.StringPtr("linux/amd64"),
		})
		if err != nil {
			return err
		}

		cluster, err := ecs.NewCluster(ctx, "cluster", nil)
		if err != nil {
			return err
		}

		lb, err := lb.NewApplicationLoadBalancer(ctx, "lb", nil)
		if err != nil {
			return err
		}

		_, err = ecsx.NewFargateService(ctx, "service", &ecsx.FargateServiceArgs{
			Cluster:        cluster.Arn,
			AssignPublicIp: pulumi.Bool(true),
			TaskDefinitionArgs: &ecsx.FargateServiceTaskDefinitionArgs{
				Container: &ecsx.TaskDefinitionContainerDefinitionArgs{
					Name:      pulumi.String("app"),
					Image:     image.ImageUri,
					Cpu:       pulumi.Int(512),
					Memory:    pulumi.Int(128),
					Essential: pulumi.Bool(true),
					PortMappings: ecsx.TaskDefinitionPortMappingArray{
						&ecsx.TaskDefinitionPortMappingArgs{
							ContainerPort: pulumi.Int(80),
							TargetGroup:   lb.DefaultTargetGroup,
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}

		ctx.Export("url", pulumi.Sprintf("http://%s", lb.LoadBalancer.DnsName()))
		return nil
	})
}
using System.Collections.Generic;
using Pulumi;
using Aws = Pulumi.Aws;
using Awsx = Pulumi.Awsx;

return await Deployment.RunAsync(() =>
{
    var repo = new Awsx.Ecr.Repository("repo", new()
    {
        ForceDelete = true,
    });

    var image = new Awsx.Ecr.Image("image", new()
    {
        RepositoryUrl = repo.Url,
        Context = "./app",
        Platform = "linux/amd64",
    });

    var cluster = new Aws.Ecs.Cluster("cluster");

    var lb = new Awsx.Lb.ApplicationLoadBalancer("lb");

    var service = new Awsx.Ecs.FargateService("service", new Awsx.Ecs.FargateServiceArgs
    {
        Cluster = cluster.Arn,
        AssignPublicIp = true,
        TaskDefinitionArgs = new Awsx.Ecs.Inputs.FargateServiceTaskDefinitionArgs
        {
            Container = new Awsx.Ecs.Inputs.TaskDefinitionContainerDefinitionArgs
            {
                Name = "my-service",
                Image = image.ImageUri,
                Cpu = 128,
                Memory = 512,
                Essential = true,
                PortMappings = new[]
                {
                    new Awsx.Ecs.Inputs.TaskDefinitionPortMappingArgs
                    {
                        ContainerPort = 80,
                        TargetGroup = lb.DefaultTargetGroup,
                    },
                },
            },
        },
    });

    return new Dictionary<string, object?>
    {
        ["url"] = lb.LoadBalancer.Apply(loadBalancer => Output.Format($"http://{loadBalancer.DnsName}")),
    };
});
package myproject;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.awsx.ecr.Repository;
import com.pulumi.awsx.ecr.RepositoryArgs;
import com.pulumi.awsx.ecr.Image;
import com.pulumi.awsx.ecr.ImageArgs;
import com.pulumi.aws.ecs.Cluster;
import com.pulumi.awsx.lb.ApplicationLoadBalancer;
import com.pulumi.awsx.ecs.FargateService;
import com.pulumi.awsx.ecs.FargateServiceArgs;
import com.pulumi.awsx.ecs.inputs.FargateServiceTaskDefinitionArgs;
import com.pulumi.awsx.ecs.inputs.TaskDefinitionContainerDefinitionArgs;
import com.pulumi.awsx.ecs.inputs.TaskDefinitionPortMappingArgs;

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

    public static void stack(Context ctx) {
        var repository = new Repository("repository", RepositoryArgs.builder()
            .forceDelete(true)
            .build());

        var image = new Image("image", ImageArgs.builder()
            .repositoryUrl(repository.url())
            .context("./app")
            .platform("linux/amd64")
            .build());

        var cluster = new Cluster("cluster");

        var lb = new ApplicationLoadBalancer("lb");

        var service = new FargateService("service", FargateServiceArgs.builder()
            .cluster(cluster.arn())
            .assignPublicIp(true)
            .taskDefinitionArgs(FargateServiceTaskDefinitionArgs.builder()
                .container(TaskDefinitionContainerDefinitionArgs.builder()
                    .name("my-service")
                    .image(image.imageUri())
                    .cpu(128)
                    .memory(512)
                    .essential(true)
                    .portMappings(TaskDefinitionPortMappingArgs.builder()
                        .containerPort(80)
                        .targetGroup(lb.defaultTargetGroup())
                        .build())
                    .build())
                .build())
            .build());

        ctx.export("url", Output.format("http://%s", lb.loadBalancer().applyValue(loadBalancer -> loadBalancer.dnsName())));
    }
}
name: awsx-load-balanced-fargate-ecr-yaml
runtime: yaml

resources:
  repo:
    type: awsx:ecr:Repository
    properties:
      forceDelete: true

  image:
    type: awsx:ecr:Image
    properties:
      repositoryUrl: ${repo.url}
      context: ./app
      platform: linux/amd64

  cluster:
    type: aws:ecs:Cluster

  lb:
    type: awsx:lb:ApplicationLoadBalancer

  service:
    type: awsx:ecs:FargateService
    properties:
      cluster: ${cluster.arn}
      assignPublicIp: true
      taskDefinitionArgs:
        container:
          name: my-service
          image: ${image.imageUri}
          cpu: 128
          memory: 512
          essential: true
          portMappings:
            - containerPort: 80
              targetGroup: ${lb.defaultTargetGroup}

outputs:
  url: http://${lb.loadBalancer.dnsName}

Creating a Kubernetes cluster

Pulumi can provision Kubernetes clusters — in this example, an AWS EKS cluster — in addition to deploying application-level configuration, using a standard set of languages, abstractions, and tools.

"use strict";
const pulumi = require("@pulumi/pulumi");
const aws = require("@pulumi/aws");
const awsx = require("@pulumi/awsx");
const eks = require("@pulumi/eks");

// Create a VPC for the Kubernetes cluster.
const eksVpc = new awsx.ec2.Vpc("eks-vpc", {
    enableDnsHostnames: true,
    cidrBlock: "10.0.0.0/16",
});

// Create the EKS cluster itself.
const eksCluster = new eks.Cluster("eks-cluster", {
    vpcId: eksVpc.vpcId,
    publicSubnetIds: eksVpc.publicSubnetIds,
    privateSubnetIds: eksVpc.privateSubnetIds,
    instanceType: "t3.medium",
    desiredCapacity: 3,
    minSize: 3,
    maxSize: 6,
});

// Export the cluster's kubeconfig.
exports.kubeconfig = eksCluster.kubeconfig;
import * as pulumi from "@pulumi/pulumi";
import * as awsx from "@pulumi/awsx";
import * as eks from "@pulumi/eks";

// Create a VPC for the Kubernetes cluster.
const eksVpc = new awsx.ec2.Vpc("eks-vpc", {
    enableDnsHostnames: true,
    cidrBlock: "10.0.0.0/16",
});

// Create the EKS cluster itself.
const eksCluster = new eks.Cluster("eks-cluster", {
    vpcId: eksVpc.vpcId,
    publicSubnetIds: eksVpc.publicSubnetIds,
    privateSubnetIds: eksVpc.privateSubnetIds,
    instanceType: "t3.medium",
    desiredCapacity: 3,
    minSize: 3,
    maxSize: 6,
});

// Export the cluster's kubeconfig.
export const kubeconfig = eksCluster.kubeconfig;
import pulumi
import pulumi_awsx as awsx
import pulumi_eks as eks

# Create a VPC for the Kubernetes cluster.
eks_vpc = awsx.ec2.Vpc("eks-vpc", enable_dns_hostnames=True, cidr_block="10.0.0.0/16")

# Create the EKS cluster itself.
eks_cluster = eks.Cluster(
    "eks-cluster",
    vpc_id=eks_vpc.vpc_id,
    public_subnet_ids=eks_vpc.public_subnet_ids,
    private_subnet_ids=eks_vpc.private_subnet_ids,
    instance_type="t3.medium",
    desired_capacity=3,
    min_size=3,
    max_size=6,
)

# Export the cluster's kubeconfig.
pulumi.export("kubeconfig", eks_cluster.kubeconfig)
package main

import (
	"github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/ec2"
	"github.com/pulumi/pulumi-eks/sdk/v2/go/eks"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {

		// Create a VPC for the Kubernetes cluster.
		eksVpc, err := ec2.NewVpc(ctx, "eks-vpc", &ec2.VpcArgs{
			EnableDnsHostnames: pulumi.Bool(true),
			CidrBlock:          pulumi.StringRef("10.0.0.0/16"),
		})
		if err != nil {
			return err
		}

		// Create the EKS cluster itself.
		eksCluster, err := eks.NewCluster(ctx, "eks-cluster", &eks.ClusterArgs{
			VpcId:            eksVpc.VpcId,
			PublicSubnetIds:  eksVpc.PublicSubnetIds,
			PrivateSubnetIds: eksVpc.PrivateSubnetIds,
			InstanceType:     pulumi.String("t3.medium"),
			DesiredCapacity:  pulumi.Int(3),
			MinSize:          pulumi.Int(3),
			MaxSize:          pulumi.Int(6),
		})
		if err != nil {
			return err
		}

		// Export the cluster's kubeconfig.
		ctx.Export("kubeconfig", eksCluster.Kubeconfig)
		return nil
	})
}
using Pulumi;
using Awsx = Pulumi.Awsx;
using Eks = Pulumi.Eks;
using System.Collections.Generic;

return await Deployment.RunAsync(() =>
{
    // Create a VPC for the Kubernetes cluster.
    var eksVpc = new Awsx.Ec2.Vpc("eks-vpc", new()
    {
        EnableDnsHostnames = true,
        CidrBlock = "10.0.0.0/16",
    });

    // Create the EKS cluster itself.
    var eksCluster = new Eks.Cluster("eks-cluster", new()
    {
        VpcId = eksVpc.VpcId,
        PublicSubnetIds = eksVpc.PublicSubnetIds,
        PrivateSubnetIds = eksVpc.PrivateSubnetIds,
        InstanceType = "t3.medium",
        DesiredCapacity = 3,
        MinSize = 3,
        MaxSize = 6,
    });

    // Export the cluster's kubeconfig.
    return new Dictionary<string, object?>
    {
        ["kubeconfig"] = eksCluster.Kubeconfig,
    };
});
package myproject;

import com.pulumi.Pulumi;
import com.pulumi.awsx.ec2.Vpc;
import com.pulumi.awsx.ec2.VpcArgs;
import com.pulumi.eks.Cluster;
import com.pulumi.eks.ClusterArgs;

public class App {
    public static void main(String[] args) {
        Pulumi.run(ctx -> {
            // Create a VPC for the Kubernetes cluster
            var vpc = new Vpc("eks-vpc", VpcArgs.builder()
                .enableDnsHostnames(true)
                .cidrBlock("10.0.0.0/16")
                .build());

            // Create the EKS Cluster
            var cluster = new Cluster("eks-cluster", ClusterArgs.builder()
                .vpcId(vpc.vpcId())
                .instanceType("t3.medium")
                .desiredCapacity(3)
                .minSize(3)
                .maxSize(6)
                .build());

            // Export the cluster's kubeconfig
            ctx.export("kubeconfig", cluster.kubeconfig());
        });
    }
}
name: aws-eks-cluster-yaml
runtime: yaml
description: An example that deploys a Kubernetes cluster on AWS.
resources:
  # Create a VPC for the Kubernetes cluster.
  eks-vpc:
    type: awsx:ec2:Vpc
    properties:
      enableDnsHostnames: true
      cidrBlock: 10.0.0.0/16

  # Create the EKS cluster itself.
  eks-cluster:
    type: eks:Cluster
    properties:
      vpcId: ${eks-vpc.vpcId}
      publicSubnetIds: ${eks-vpc.publicSubnetIds}
      privateSubnetIds: ${eks-vpc.privateSubnetIds}
      instanceType: t3.medium
      desiredCapacity: 3
      minSize: 3
      maxSize: 6

outputs:
  # Export the cluster's kubeconfig.
  kubeconfig: ${eks-cluster.kubeconfig}

Deploy containers to Microsoft ACI

The @pulumi/azure-native library provides fine-grained control of Azure resources. In this example, we deploy a simple linux container to Microsoft ACI, in the West US zone.

import * as containerinstance from "@pulumi/azure-native/containerinstance";
import * as resources from "@pulumi/azure-native/resources";

const resourceGroup = new resources.ResourceGroup("resourcegroup", {
    location: "West US",
});

const imageName = "mcr.microsoft.com/azuredocs/aci-helloworld";
const containerGroup = new containerinstance.ContainerGroup("containerGroup", {
    resourceGroupName: resourceGroup.name,
    osType: "Linux",
    containers: [{
        name: "acilinuxpublicipcontainergroup",
        image: imageName,
        resources: {
            requests: {
                cpu: 1.0,
                memoryInGB: 1.5,
            },
        },
        ports: [{ port: 80 }],
    }],
    ipAddresses: [{
        ports: [{
          port: 80,
          protocol: "Tcp",
        }],
        type: "Public",
    }],
    restartPolicy: "always",
});

Invoke a long-running container as a task

This example shows a container used for executing a long-running task. Here, we use a container to perform a thumbnail extraction on a piece of video uploaded to an S3 bucket.

"use strict";
const aws = require("@pulumi/aws");
const awsx = require("@pulumi/awsx");

// Create a bucket to store videos and thumbnails.
const bucket = new aws.s3.Bucket("bucket", {
    forceDestroy: true,
});

// Create a ECR repository for the thumbnailer container image.
const repo = new awsx.ecr.Repository("repo", {
    forceDelete: true,
});

// Create the thumbnailer container image.
const image = new awsx.ecr.Image("image", {
    repositoryUrl: repo.url,
    context: "./app",
    platform: "linux/amd64",
});

// Create an IAM role to grant the Lambda functions access to Amazon S3.
const role = new aws.iam.Role("role", {
    assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({
        Service: "lambda.amazonaws.com",
    }),
});

const attachment = new aws.iam.RolePolicyAttachment("attachment", {
    role: role.name,
    policyArn: aws.iam.ManagedPolicy.AWSLambdaExecute,
});

// Create a Lambda function to execute an ffmpeg container task in response to video uploads.
bucket.onObjectCreated(
    "video-handler",
    new aws.lambda.Function("thumbnailer", {
        packageType: "Image",
        imageUri: image.imageUri,
        role: role.arn,
        timeout: 900,
    }),
    { filterSuffix: ".mp4" },
);

// Create another Lambda function to log to CloudWatch when new thumbnails are created.
bucket.onObjectCreated(
    "thumbnail-handler",
    new aws.lambda.CallbackFunction("thumbnail-callback", {
        role,
        callback: async bucketArgs => {
            if (bucketArgs.Records) {
                for (const record of bucketArgs.Records) {
                    console.log(`New thumbnail: File ${record.s3.object.key} saved at ${record.eventTime}.`);
                }
            }
        },
    }),
    { filterSuffix: ".jpg" },
);

// Export the bucket name.
exports.bucketName = bucket.id;
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";

// Create a bucket to store videos and thumbnails.
const bucket = new aws.s3.Bucket("bucket", {
    forceDestroy: true,
});

// Create a ECR repository for the thumbnailer container image.
const repo = new awsx.ecr.Repository("repo", {
    forceDelete: true,
});

// Create the thumbnailer container image.
const image = new awsx.ecr.Image("image", {
    repositoryUrl: repo.url,
    context: "./app",
    platform: "linux/amd64",
});

// Create an IAM role to grant the Lambda functions access to Amazon S3.
const role = new aws.iam.Role("role", {
    assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({
        Service: "lambda.amazonaws.com",
    }),
});

const attachment = new aws.iam.RolePolicyAttachment("attachment", {
    role: role.name,
    policyArn: aws.iam.ManagedPolicy.AWSLambdaExecute,
});

// Create a Lambda function to execute an ffmpeg container task in response to video uploads.
bucket.onObjectCreated(
    "video-handler",
    new aws.lambda.Function("thumbnailer", {
        packageType: "Image",
        imageUri: image.imageUri,
        role: role.arn,
        timeout: 900,
    }),
    { filterSuffix: ".mp4" },
);

// Create another Lambda function to log to CloudWatch when new thumbnails are created.
bucket.onObjectCreated(
    "thumbnail-handler",
    new aws.lambda.CallbackFunction<aws.s3.BucketEvent, void>("thumbnail-callback", {
        role,
        callback: async bucketArgs => {
            if (bucketArgs.Records) {
                for (const record of bucketArgs.Records) {
                    console.log(`New thumbnail: File ${record.s3.object.key} saved at ${record.eventTime}.`);
                }
            }
        },
    }),
    { filterSuffix: ".jpg" },
);

// Export the bucket name.
export const bucketName = bucket.id;

How Pulumi Works

Build

  • Code in modern languages
  • Share and reuse patterns
  • Use your favorite IDE and tools

Deploy

  • Preview changes
  • Run pulumi up to deploy
  • Integrate with CI/CD

Manage

  • Audit all changes
  • Manage complex environments
  • Implement policies and controls

Get Started with Pulumi

Use Pulumi's open source SDK to create, deploy, and manage infrastructure on any cloud.

AWS
Azure
Google Cloud
Kubernetes

Need Help?

Need technical help?

Use our Support Portal to get in touch.

Need help getting started?

Send us a note, and someone will reach out to help you.

Want a demo?

Talk with an expert to see how Pulumi fits your use case.

Something else on your mind?

Send us a note.