1. Answers
  2. Deploy AWS ECS with a Containerized App and PostgreSQL

How do I deploy a containerized application on AWS ECS with a PostgreSQL backend using Pulumi?

To deploy a containerized application on AWS ECS with a PostgreSQL backend, we need to create several resources. The workflow includes setting up a VPC, subnets, an ECS cluster, a task definition for our containerized application, an ECS service to run the tasks, and an RDS PostgreSQL instance.

Implementation Steps:

  1. VPC and Networking: Start by creating a VPC and subnets required for ECS and RDS.
  2. Security Groups: Define security groups to allow communication between ECS tasks and the RDS instance.
  3. IAM Roles and Policies: Create the necessary IAM roles for ECS task execution.
  4. ECS Cluster and Task Definition: Set up the ECS cluster and associate a Task Definition that defines the container resources.
  5. RDS PostgreSQL: Deploy an RDS PostgreSQL instance with the necessary security and networking configurations.
  6. ECS Service: Create an ECS service to run the task using the task definition.

Code Example:

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

// VPC and Subnets
const main = new aws.ec2.Vpc("main", {cidrBlock: "10.0.0.0/16"});
const subnet1 = new aws.ec2.Subnet("subnet1", {
    vpcId: main.id,
    cidrBlock: "10.0.1.0/24",
    availabilityZone: "us-west-2a",
});
const subnet2 = new aws.ec2.Subnet("subnet2", {
    vpcId: main.id,
    cidrBlock: "10.0.2.0/24",
    availabilityZone: "us-west-2b",
});
const ecsSg = new aws.ec2.SecurityGroup("ecs_sg", {
    vpcId: main.id,
    ingress: [{
        fromPort: 80,
        toPort: 80,
        protocol: "tcp",
        cidrBlocks: ["0.0.0.0/0"],
    }],
    egress: [{
        fromPort: 0,
        toPort: 0,
        protocol: "-1",
        cidrBlocks: ["0.0.0.0/0"],
    }],
});
const rdsSg = new aws.ec2.SecurityGroup("rds_sg", {
    vpcId: main.id,
    ingress: [{
        fromPort: 5432,
        toPort: 5432,
        protocol: "tcp",
        cidrBlocks: ["10.0.0.0/16"],
    }],
    egress: [{
        fromPort: 0,
        toPort: 0,
        protocol: "-1",
        cidrBlocks: ["0.0.0.0/0"],
    }],
});
// IAM roles and policies for ECS
const ecsTaskExecutionRole = new aws.iam.Role("ecs_task_execution_role", {
    name: "ecsTaskExecutionRole",
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Action: "sts:AssumeRole",
            Effect: "Allow",
            Principal: {
                Service: "ecs-tasks.amazonaws.com",
            },
        }],
    }),
});
const ecsTaskExecutionRolePolicy = new aws.iam.RolePolicyAttachment("ecs_task_execution_role_policy", {
    role: ecsTaskExecutionRole.name,
    policyArn: "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy",
});
// ECS Cluster
const mainCluster = new aws.ecs.Cluster("main", {name: "main-ecs-cluster"});
const app = new aws.ecs.TaskDefinition("app", {
    family: "app-task",
    requiresCompatibilities: ["FARGATE"],
    networkMode: "awsvpc",
    cpu: "256",
    memory: "512",
    executionRoleArn: ecsTaskExecutionRole.arn,
    containerDefinitions: JSON.stringify([{
        name: "app",
        image: "nginx:latest",
        portMappings: [{
            containerPort: 80,
            hostPort: 80,
        }],
    }]),
});
const mainSubnetGroup = new aws.rds.SubnetGroup("main", {
    name: "main-subnet-group",
    subnetIds: [
        subnet1.id,
        subnet2.id,
    ],
});
// RDS PostgreSQL Instance
const postgresql = new aws.rds.Instance("postgresql", {
    allocatedStorage: 20,
    engine: "postgres",
    engineVersion: "12.4",
    instanceClass: aws.rds.InstanceType.T3_Micro,
    name: "mydatabase",
    username: "username",
    password: "password",
    parameterGroupName: "default.postgres12",
    skipFinalSnapshot: true,
    dbSubnetGroupName: mainSubnetGroup.name,
    vpcSecurityGroupIds: [rdsSg.id],
});
// ECS Service
const appService = new aws.ecs.Service("app", {
    name: "app-service",
    cluster: mainCluster.id,
    taskDefinition: app.arn,
    desiredCount: 1,
    launchType: "FARGATE",
    networkConfiguration: {
        subnets: [
            subnet1.id,
            subnet2.id,
        ],
        securityGroups: [ecsSg.id],
        assignPublicIp: true,
    },
});
export const vpcId = main.id;
export const ecsClusterId = mainCluster.id;
export const ecsServiceName = appService.name;
export const rdsEndpoint = postgresql.endpoint;

Summary

This configuration sets up an AWS ECS Fargate cluster running a simple containerized application and provisions an RDS PostgreSQL instance for database services. The necessary VPC, subnets, security groups, IAM roles, and policies for ECS tasks and RDS security are included. The ECS service deploys the app container and connects it to the database backend, ensuring network communication through security groups and subnets. Finally, stack outputs provide references to key resource IDs and endpoints.

Deploy this code

Want to deploy this code? Sign up for a free Pulumi account to deploy in a few clicks.

Sign up

New to Pulumi?

Want to deploy this code? Sign up with Pulumi to deploy in a few clicks.

Sign up