Let's say you have the following files on your Terraform server.
├── required_providers.tf
├── elastic_container_services (directory)
│ ├── task_defintions (directory)
│ ├── ├── ec2 (directory)
│ ├── ├── ├── provider.tf
│ ├── ├── ├── task_definition.tf
required_providers.tf will almost always have this.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
Let's say provider.tf has the following. In this example, the "default" profile in /home/username/.aws/config and /home/username/.aws/credentials is being used. This assumes you have setup Terraform as described in Amazon Web Services (AWS) - Getting Started with Terraform.
provider "aws" {
alias = "default"
profile = "default"
region = "default"
}
This assumes you have already:
- Created an Elastic Container Service (ECS) Cluster using Terraform
- Create an EC2 instance using Terraform
An ECS deployment can be:
- EC2 - apps run in EC2 instance
- FARGATE - serverless (no EC2 instances)
When creating an EC2 instance:
- You will need to use one of the ECS Optimized image, almost always the most recent ECS Optimized image.
- The IAM AmazonEC2ContainerServiceforEC2Role will need to be applied to the EC2 Instance.
Thus, you will probably have the following Terraform data and resource blocks to create an EC2 Instance using the ECS Optimized Image and AmazonEC2ContainerServiceforEC2Role.
data "aws_ami" "most-recent-ecs-optimized-image" {
most_recent = true
filter {
name = "name"
values = ["amzn-ami*ecs-optimized"]
}
}
resource "aws_instance" "ecs-ec2-optimized-instance" {
ami = data.aws_ami.most-recent-ecs-optimized-image.id
instance_type = "t3.micro"
subnet_id = data.aws_subnets.subnets.ids[0]
iam_instance_profile = "AmazonEC2ContainerServiceforEC2Role"
tags = {
Name = "ecs-ec2-optimized-instance"
}
}
task_definitions.tf could have something like this where requires_compatibilities is EC2.
resource "aws_ecs_task_definition" "flask-ec2-task-definition" {
family = "flask"
network_mode = "awsvpc"
requires_compatibilities = ["EC2"]
cpu = 1024
memory = 2048
container_definitions = jsonencode([
{
name = "flask-container"
cpu = 10
memory = 512
image: "tiangolo/uwsgi-nginx-flask:python3.11",
portMappings: [
{
containerPort: 3000,
hostPort: 3000
}
]
}
])
}
Or like this, using JSON instead of jsonencode.
resource "aws_ecs_task_definition" "flask-ec2-task-definition" {
family = "flask"
network_mode = "awsvpc"
requires_compatibilities = ["EC2"]
cpu = 1024
memory = 2048
container_definitions = <<DEFINITION
[
{
"name": "flask-container",
"cpu": 10,
"memory": 512,
"image": "tiangolo/uwsgi-nginx-flask:python3.11",
"portMappings": [
{
"containerPort": 3000,
"hostPort": 3000
}
]
}
]
DEFINITION
}
You may need to reissue the terraform init command.
~]# terraform init
Initializing the backend...
Initializing modules...
Initializing provider plugins...
Terraform has been successfully initialized!
The terraform plan command can be used to see what Terraform will try to do.
~]$ terraform plan
Terraform will perform the following actions:
# aws_ecs_task_definition.flask-ec2-task-definition will be created
+ resource "aws_ecs_task_definition" "flask-ec2-task-definition" {
+ arn = (known after apply)
+ arn_without_revision = (known after apply)
+ container_definitions = jsonencode(
[
+ {
+ cpu = 10
+ image = "tiangolo/uwsgi-nginx-flask:python3.11"
+ memory = 512
+ name = "flask"
+ portMappings = [
+ {
+ containerPort = 80
+ hostPort = 80
},
]
},
]
)
+ cpu = "1024"
+ family = "flask"
+ id = (known after apply)
+ memory = "2048"
+ network_mode = "awsvpc"
+ requires_compatibilities = [
+ "EC2",
]
+ revision = (known after apply)
+ skip_destroy = false
+ tags_all = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
The terraform apply command can be used to create, update or delete the resource.
terraform apply -auto-approve
Did you find this article helpful?
If so, consider buying me a coffee over at