```package application import ( "fmt" "strings"...
# general
n
Copy code
package application

import (
	"fmt"
	"strings"

	"<http://github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ecs|github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ecs>"
	ecsx "<http://github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/ecs|github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/ecs>"
	lbx "<http://github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/lb|github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/lb>"
	"<http://github.com/pulumi/pulumi/sdk/v3/go/pulumi|github.com/pulumi/pulumi/sdk/v3/go/pulumi>"
	"<http://github.com/pulumi/pulumi/sdk/v3/go/pulumi/config|github.com/pulumi/pulumi/sdk/v3/go/pulumi/config>"
)

func DeployAndRunApplicationImages(ctx *pulumi.Context) error {
	stackRef, err := pulumi.NewStackReference(ctx, fmt.Sprintf("organization/application/%s", ctx.Stack()), &pulumi.StackReferenceArgs{})
	if err != nil {
		return err
	}

	repoURIs := stackRef.GetOutput(pulumi.String("repositoryURIs"))
	repos := repoURIs.ApplyT(func(val any) map[string]string {
		output := make(map[string]string)
		for k, v := range val.(map[string]any) {
			output[k] = v.(string)
		}
		return output
	}).(pulumi.StringMapOutput)

	lbBackend, err := lbx.NewApplicationLoadBalancer(ctx, "application-app-lb-backend", &lbx.ApplicationLoadBalancerArgs{
		Name:                   pulumi.String("application-app-lb-backend"),
		DefaultTargetGroupPort: <http://pulumi.Int|pulumi.Int>(8080),
	})
	if err != nil {
		return err
	}

	lbFrontend, err := lbx.NewApplicationLoadBalancer(ctx, "application-app-lb-frontend", &lbx.ApplicationLoadBalancerArgs{
		Name:                   pulumi.String("application-app-lb-frontend"),
		DefaultTargetGroupPort: <http://pulumi.Int|pulumi.Int>(3000),
	})
	if err != nil {
		return err
	}

	cluster, err := ecs.NewCluster(ctx, "application-app-cluster", &ecs.ClusterArgs{
		Name: pulumi.String("application-app-cluster"),
	})
	if err != nil {
		return err
	}

	imageUrlBackend := pulumi.Sprintf("%s:%s", repos.MapIndex(pulumi.String("ecr-core-backend")), "v0.0.3")
	imageUrlDB := pulumi.Sprintf("%s:%s", repos.MapIndex(pulumi.String("ecr-core-postgres")), "v0.0.2")
	imageUrlFrontend := pulumi.Sprintf("%s:%s", repos.MapIndex(pulumi.String("ecr-core-frontend")), "v0.0.1")
	imageUrlKafka := pulumi.Sprintf("%s:%s", repos.MapIndex(pulumi.String("ecr-core-redpanda")), "v0.0.2")

	c := config.New(ctx, "application")
	pgUser := c.RequireSecret("application_pg_user")
	pgPass := c.RequireSecret("application_pg_pass")
	pgDb := c.RequireSecret("application_pg_db")

	_, err = ecsx.NewFargateService(ctx, "application-app-fargate", &ecsx.FargateServiceArgs{
		Name:           pulumi.String("application-app-fargate"),
		Cluster:        cluster.Arn,
		DesiredCount:   <http://pulumi.Int|pulumi.Int>(1),
		AssignPublicIp: pulumi.Bool(true),
		TaskDefinitionArgs: &ecsx.FargateServiceTaskDefinitionArgs{
			Volumes: ecs.TaskDefinitionVolumeArray{
				ecs.TaskDefinitionVolumeArgs{
					Name: pulumi.String("application-app-volume"),
				},
				// ecs.TaskDefinitionVolumeArgs{
				// 	Name: pulumi.String("application-app-db"),
				// },
			},
			Containers: map[string]ecsx.TaskDefinitionContainerDefinitionArgs{
				"application-app-backend": {
					Name:      pulumi.String("application-app-backend"),
					Image:     imageUrlBackend,
					Cpu:       <http://pulumi.Int|pulumi.Int>(128),
					Memory:    <http://pulumi.Int|pulumi.Int>(512),
					Essential: pulumi.Bool(true),
					// HealthCheck: ecsx.TaskDefinitionHealthCheckArgs{
					// 	Command: pulumi.StringArray{
					// 		pulumi.String("CMD-SHELL"),
					// 		pulumi.String("curl -f <http://127.0.0.1:8080/metrics>"),
					// 	},
					// 	Interval:    <http://pulumi.Int|pulumi.Int>(15),
					// 	Retries:     <http://pulumi.Int|pulumi.Int>(3),
					// 	StartPeriod: <http://pulumi.Int|pulumi.Int>(5),
					// 	Timeout:     <http://pulumi.Int|pulumi.Int>(5),
					// },
					DependsOn: ecsx.TaskDefinitionContainerDependencyArray{
						ecsx.TaskDefinitionContainerDependencyArgs{
							ContainerName: pulumi.String("application-app-db"),
							Condition:     pulumi.String("HEALTHY"),
						},
					},
					Environment: ecsx.TaskDefinitionKeyValuePairArray{
						ecsx.TaskDefinitionKeyValuePairArgs{
							Name:  pulumi.Sprintf("DB"),
							Value: pulumi.Sprintf("<postgres://%s:%s@127.0.0.1:5432/%s?sslmode=disable>", pgUser, pgPass, pgDb),
						},
						ecsx.TaskDefinitionKeyValuePairArgs{
							Name: pulumi.Sprintf("APPLICATION_ALLOWED_ORIGINS"),
							Value: lbBackend.LoadBalancer.DnsName().ApplyT(func(val string) string {
								// Replacing the secret might not be needed.
								modifiedDnsName := strings.Replace(val, "[secret]", "application", 1)
								return fmt.Sprintf("<http://%s:%d>", modifiedDnsName, 3000)
							}).(pulumi.StringOutput),
						},
					},
					PortMappings: ecsx.TaskDefinitionPortMappingArray{
						&ecsx.TaskDefinitionPortMappingArgs{
							ContainerPort: <http://pulumi.Int|pulumi.Int>(8080),
							TargetGroup:   lbBackend.DefaultTargetGroup,
						},
					},
					MountPoints: ecsx.TaskDefinitionMountPointArray{
						&ecsx.TaskDefinitionMountPointArgs{
							SourceVolume:  pulumi.String("application-app-volume"),
							ContainerPath: pulumi.String("/application"),
							ReadOnly:      pulumi.Bool(false),
						},
					},
				},
				"application-app-frontend": {
					Name:      pulumi.String("application-app-frontend"),
					Image:     imageUrlFrontend,
					Cpu:       <http://pulumi.Int|pulumi.Int>(128),
					Memory:    <http://pulumi.Int|pulumi.Int>(512),
					Essential: pulumi.Bool(true),
					// HealthCheck: ecsx.TaskDefinitionHealthCheckArgs{
					// 	Command: pulumi.StringArray{
					// 		pulumi.String("CMD-SHELL"),
					// 		pulumi.String("curl -f <http://127.0.0.1:3000>"),
					// 	},
					// 	Interval:    <http://pulumi.Int|pulumi.Int>(15),
					// 	Retries:     <http://pulumi.Int|pulumi.Int>(3),
					// 	StartPeriod: <http://pulumi.Int|pulumi.Int>(5),
					// 	Timeout:     <http://pulumi.Int|pulumi.Int>(5),
					// },
					DependsOn: ecsx.TaskDefinitionContainerDependencyArray{
						ecsx.TaskDefinitionContainerDependencyArgs{
							ContainerName: pulumi.String("application-app-db"),
							Condition:     pulumi.String("HEALTHY"),
						},
					},
					Environment: ecsx.TaskDefinitionKeyValuePairArray{
						ecsx.TaskDefinitionKeyValuePairArgs{
							Name:  pulumi.String("APPLICATION_BACKEND_URL"),
							Value: pulumi.Sprintf("<http://%s:%d/v1>", lbBackend.LoadBalancer.DnsName(), 8080),
						},
					},
					PortMappings: ecsx.TaskDefinitionPortMappingArray{
						&ecsx.TaskDefinitionPortMappingArgs{
							TargetGroup:   lbFrontend.DefaultTargetGroup,
							ContainerPort: <http://pulumi.Int|pulumi.Int>(3000),
						},
					},
				},
				"application-app-db": {
					Name:      pulumi.String("application-app-db"),
					Image:     imageUrlDB,
					Cpu:       <http://pulumi.Int|pulumi.Int>(1024 * 2),
					Memory:    <http://pulumi.Int|pulumi.Int>(2048),
					Essential: pulumi.Bool(true),
					HealthCheck: &ecsx.TaskDefinitionHealthCheckArgs{
						Command: pulumi.StringArray{
							pulumi.String("CMD-SHELL"),
							pulumi.Sprintf("pg_isready -U %s -d %s -h localhost", pgUser, pgDb),
						},
						Interval:    <http://pulumi.Int|pulumi.Int>(15),
						Retries:     <http://pulumi.Int|pulumi.Int>(3),
						StartPeriod: <http://pulumi.Int|pulumi.Int>(10),
						Timeout:     <http://pulumi.Int|pulumi.Int>(5),
					},
					Environment: ecsx.TaskDefinitionKeyValuePairArray{
						ecsx.TaskDefinitionKeyValuePairArgs{
							Name:  pulumi.String("POSTGRES_USER"),
							Value: pgUser,
						},
						ecsx.TaskDefinitionKeyValuePairArgs{
							Name:  pulumi.String("POSTGRES_PASSWORD"),
							Value: pgPass,
						},
						ecsx.TaskDefinitionKeyValuePairArgs{
							Name:  pulumi.String("POSTGRES_DB"),
							Value: pgDb,
						},
					},
					PortMappings: ecsx.TaskDefinitionPortMappingArray{
						&ecsx.TaskDefinitionPortMappingArgs{
							ContainerPort: <http://pulumi.Int|pulumi.Int>(5432),
						},
					},
					// MountPoints: ecsx.TaskDefinitionMountPointArray{
					// 	&ecsx.TaskDefinitionMountPointArgs{
					// 		SourceVolume:  pulumi.String("application-app-db"),
					// 		ContainerPath: pulumi.String("/var/lib/postgresql"),
					// 		ReadOnly:      pulumi.Bool(false),
					// 	},
					// },
				},
				"application-app-kafka": {
					Name:      pulumi.String("application-app-kafka"),
					Image:     imageUrlKafka,
					Cpu:       <http://pulumi.Int|pulumi.Int>(1024 * 2),
					Memory:    <http://pulumi.Int|pulumi.Int>(2048),
					Essential: pulumi.Bool(true),
					// HealthCheck: &ecsx.TaskDefinitionHealthCheckArgs{
					// 	Command: pulumi.StringArray{
					// 		pulumi.String("CMD-SHELL"),
					// 		pulumi.String("curl -f <http://127.0.0.1:9644/v1/status/ready>"),
					// 	},
					// 	Interval:    <http://pulumi.Int|pulumi.Int>(15),
					// 	Retries:     <http://pulumi.Int|pulumi.Int>(3),
					// 	StartPeriod: <http://pulumi.Int|pulumi.Int>(10),
					// 	Timeout:     <http://pulumi.Int|pulumi.Int>(5),
					// },
					Environment: ecsx.TaskDefinitionKeyValuePairArray{},
					PortMappings: ecsx.TaskDefinitionPortMappingArray{
						&ecsx.TaskDefinitionPortMappingArgs{
							ContainerPort: <http://pulumi.Int|pulumi.Int>(18081),
						},
						&ecsx.TaskDefinitionPortMappingArgs{
							ContainerPort: <http://pulumi.Int|pulumi.Int>(18082),
						},
						&ecsx.TaskDefinitionPortMappingArgs{
							ContainerPort: <http://pulumi.Int|pulumi.Int>(19092),
						},
						&ecsx.TaskDefinitionPortMappingArgs{
							ContainerPort: <http://pulumi.Int|pulumi.Int>(9644),
						},
					},
				},
				"application-app-prometheus": {
					Name:      pulumi.String("application-app-prometheus"),
					Image:     pulumi.String("prom/prometheus:v2.55.0@sha256:378f4e03703557d1c6419e6caccf922f96e6d88a530f7431d66a4c4f4b1000fe"),
					Cpu:       <http://pulumi.Int|pulumi.Int>(1024 * 2),
					Memory:    <http://pulumi.Int|pulumi.Int>(2048),
					Essential: pulumi.Bool(true),
					// HealthCheck: &ecsx.TaskDefinitionHealthCheckArgs{
					// 	Command: pulumi.StringArray{
					// 		pulumi.String("CMD-SHELL"),
					// 		pulumi.String("curl -f <http://127.0.0.1:9090>"),
					// 	},
					// 	Interval:    <http://pulumi.Int|pulumi.Int>(15),
					// 	Retries:     <http://pulumi.Int|pulumi.Int>(3),
					// 	StartPeriod: <http://pulumi.Int|pulumi.Int>(10),
					// 	Timeout:     <http://pulumi.Int|pulumi.Int>(5),
					// },
					Environment: ecsx.TaskDefinitionKeyValuePairArray{},
					PortMappings: ecsx.TaskDefinitionPortMappingArray{
						&ecsx.TaskDefinitionPortMappingArgs{
							ContainerPort: <http://pulumi.Int|pulumi.Int>(9090),
						},
					},
				},
			},
		},
	})
	if err != nil {
		return err
	}

	ctx.Export("url-backend", pulumi.Sprintf("<http://%s>", lbBackend.LoadBalancer.DnsName()))
	ctx.Export("url-frontend", pulumi.Sprintf("<http://%s>", lbFrontend.LoadBalancer.DnsName()))

	return nil
}
Hello 👋, First time using pulumi and somewhat AWS newbie here. I'm trying to deploy a ECS service with some containers so we can have a simple application running on the cloud. The current state is not final just to learn and get familiar with Pulumi. After setting this up I got a few questions: • Right now as a workaround I have seperate loadbalancers for the frontend and backend. Is there a way to have a single loadbalancer for both? Really can't find how to implement this with the ecsx package... It should be supported from my understanding. • Is there a way to have one port exposed to the internet and a different internal port in the container like docker? For example 443 (external) => 4443 (container port) • Do I handle the
repoURIs
in the right way with Apply? I export them as type
pulumi.StringMap
. It works just questioning if this is the right way to do it. • When exporting the URL, in the console it shows [secret] instead of the name (url-frontend : "http://[secret]-app-lb-frontend-12345.eu.elb.amazonaws.com"). Do I need to replace when using it in code? Check the below code snippet. I assume its just the console and I'm overthinking it, just want to be sure.
Copy code
Value: lbBackend.LoadBalancer.DnsName().ApplyT(func(val string) string {
	// Replacing the secret might not be needed.
	modifiedDnsName := strings.Replace(val, "[secret]", "application", 1)
	return fmt.Sprintf("<http://%s:%d>", modifiedDnsName, 3000)
}).(pulumi.StringOutput),
Thanks for the time, and +1 rep for a pretty solid program. Just pretty steep learning curve, but also suck at AWS so that does not help 🙂.