# Terraform AWS ECS Fargate Cluster Deployment & Docker Container Publishing

> How-to use Terraform or OpenTofu to create an AWS ECS (Elastic Container Service) running in Fargate and deploy a Docker container.

- Source: https://www.itwonderlab.com/containers-aws-ecs-terraform-fargate/
- Published: 2023-11-20
- Updated: 2026-10-05
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## Programmatically creating an AWS ECS Cluster to deploy a Docker container running on Fargate with Terraform

This how-to shows **using Terraform or OpenTofu** to deploy a Docker container in AWS ECS (Elastic Container Service) running in Fargate:

-   [AWS ECS](https://www.itwonderlab.com/aws-ecs/) (Amazon Elastic Container Service) is a fully managed container orchestration service.
-   [AWS Fargate](https://www.itwonderlab.com/aws-fargate/) is a **serverless computing engine** for containers.

![Terraform AWS ECS Fargate Bricks](https://www.itwonderlab.com/media/tutorials/Terraform-AWS-ECS-Fargate/Terraform-AWS-ECS-Fargate-Bricks.svg)

This tutorial series focuses on **using Terraform/OpenTofu with AWS, emphasizing minimal resource usage and complexity** while crafting basic cloud infrastructures through Infrastructure as Code (IaC).

Each component is broken down with explanations and diagrams. Readers will grasp the essentials for handling intricate AWS architectures, laying the foundation for [advanced practices](https://www.itwonderlab.com/best-practices/) like [modularization](https://www.itwonderlab.com/aws-terraform-tutorial-terraform-modules/), [autoscaling](https://www.itwonderlab.com/aws-terraform-tutorial-aws-auto-scaling/) and [CI/CD](https://www.itwonderlab.com/aws-terraform-tutorial-terraform-cicd/).

### Prerequisites

You need:

-   A working Terraform or OpenTofu installation. See Installing [OpenTofu](https://www.itwonderlab.com/how-to-install-opentofu/).
-   Download the source code from the [GitHub repository for the Terraform Tutorial AWS ECS](https://github.com/itwonderlab/terraform-aws-ecs) (optional)

### Terraform Plan to Create the Network Needed for ECS and Fargate

> [!NOTE]
> In this tutorial, for simplifying learning, the Terraform code will be in a single file and no modules will be used, no remote state storage, single subnets, and limited resources. See other IT Wonder Lab tutorials to learn how to apply best practices for Terraform and AWS using modules and a remote backend.

Create the Terraform infrastructure definition file **aws-ecs-color-app.tf** or download its contents from the [GitHub repository for the Terraform Tutorial AWS ECS](https://github.com/itwonderlab/terraform-aws-ecs).

#### Terraform and AWS provider configuration

Defines the Terraform version and the AWS required provider. Additionally sets the AWS region and the profile (IAM User & Security Credentials) to use. This tutorial uses the Public Docker Repository which doesn’t require any configuration for image pulling.

![Terraform AWS ECS Fargate Basic](https://www.itwonderlab.com/media/tutorials/Terraform-AWS-ECS-Fargate/Terraform-AWS-ECS-Fargate-Basic.svg)

```yaml title="aws-ecs-color-app.tf"
# Copyright (C) 2018 - 2023 IT Wonder Lab (https://www.itwonderlab.com)
#
# This software may be modified and distributed under the terms
# of the MIT license.  See the LICENSE file for details.
# -------------------------------- WARNING --------------------------------
# IT Wonder Lab's best practices for infrastructure include modularizing 
# Terraform/OpenTofu configuration. 
# In this example, we define everything in a single file. 
# See other tutorials for best practices at itwonderlab.com
# -------------------------------- WARNING --------------------------------

#Define Terrraform Providers and Backend
terraform {
  required_version = "> 1.5"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

#-----------------------------------------
# Default provider: AWS
#-----------------------------------------
provider "aws" {
  shared_credentials_files = ["~/.aws/credentials"]
  profile                  = "ditwl_infradmin"
  region                   = "us-east-1" //See BUG https://github.com/hashicorp/terraform-provider-aws/issues/30488
}
```

#### VPC and Subnet

Creates a **[VPC](https://www.itwonderlab.com/aws-vpc/)** (Virtual Private Cloud) named `ditlw-vpc` and a **single public [subnet](https://www.itwonderlab.com/aws-subnets/)** named `ditwl-sn-za-pro-pub-00`.

Names are used to identify the AWS resources and also to be a reference for Terraform dependencies. For example, the subnet references the VPC ID using `aws_vpc.ditlw-vpc.id`.

The subnet will be located in one of the AWS region availability zones, for tutorial simplicity we will not define which availability zone to use.

![Terraform AWS ECS Fargate VPC Subnet](https://www.itwonderlab.com/media/tutorials/Terraform-AWS-ECS-Fargate/Terraform-AWS-ECS-Fargate-VPC-Subnet.svg)

The **subnet** is public meaning that:

-   Assigns **public IP addresses** to hosts and containers using the subnet,
-   It has a **routing table** that includes an Internet Gateway (see Internet Gateway and Routing Table below).

```hcl
# VPC
resource "aws_vpc" "ditlw-vpc" {
  cidr_block = "172.21.0.0/19" #172.21.0.0 - 172.21.31.254
  tags = {
    Name = "ditlw-vpc"
  }
}

# Subnet
resource "aws_subnet" "ditwl-sn-za-pro-pub-00" {
  vpc_id                  = aws_vpc.ditlw-vpc.id
  cidr_block              = "172.21.0.0/23" #172.21.0.0 - 172.21.1.255
  map_public_ip_on_launch = true            #Assign a public IP address
  tags = {
    Name = "ditwl-sn-za-pro-pub-00"
  }
}
```

#### Internet Gateway and Routing Table

Creates an **Internet Gateway** named `ditwl-ig` that allows the ECS Container Internet access needed to download the Container image from the [Public Docker Hub repository](https://hub.docker.com/r/itwonderlab/color) and Internet users to access the Public IP assigned to the container by AWS ECS Fargate.

![Terraform AWS ECS Fargate IG Route Table](https://www.itwonderlab.com/media/tutorials/Terraform-AWS-ECS-Fargate/Terraform-AWS-ECS-Fargate-IG-Route-Table.svg)

A **routing table** named `ditwl-rt-pub-main` and a rule to access the Internet is added. The rule sends all traffic (except in the CIDR range “172.21.0.0/19”) to the **Internet Gateway**.

The new routing table is added as the main or default routing table for the VPC. In a production VPC environment, the default route will not include Internet access as a security best practice.

```yaml
# Internet Gateway
resource "aws_internet_gateway" "ditwl-ig" {
  vpc_id = aws_vpc.ditlw-vpc.id
  tags = {
    Name = "ditwl-ig"
  }
}

# Routing table for public subnet (access to Internet)
resource "aws_route_table" "ditwl-rt-pub-main" {
  vpc_id = aws_vpc.ditlw-vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.ditwl-ig.id
  }

  tags = {
    Name = "ditwl-rt-pub-main"
  }
}

# Set new main_route_table as main
resource "aws_main_route_table_association" "ditwl-rta-default" {
  vpc_id         = aws_vpc.ditlw-vpc.id
  route_table_id = aws_route_table.ditwl-rt-pub-main.id
}
```

#### Security Group and Rules

By default, AWS denies inbound access from the Internet.

An [AWS Security Group](https://www.itwonderlab.com/aws-security-groups/) holds ingress and egress network traffic rules that apply to the assigned containers or EC2 instances allowing traffic to pass. Ingress rules apply to traffic going into the assigned element, egress rules apply to traffic leaving the assigned element.

A **Security Group** named `ditwl-sg-ecs-color-app` is created specifically for the new deployment and the following rules are added to the group:

-   Rule named `ditwl-sr-internet-to-ecs-color-app-8080` Allows ingress TCP traffic from the Internet (“0.0.0.0/0”) to port 8080
-   Rule named `ditwl-sr-all-outbund` allows egress traffic using any protocol to the Internet

![Terraform AWS ECS Fargate Security Group](https://www.itwonderlab.com/media/tutorials/Terraform-AWS-ECS-Fargate/Terraform-AWS-ECS-Fargate-Security-Group.svg)

The new security group will be **assigned** later on to our Container allowing it to access the Internet and users to access the container at port 8080 using TCP from the Internet.

```yaml
# Create a Security Group
resource "aws_security_group" "ditwl-sg-ecs-color-app" {
  name        = "ditwl-sg-ecs-color-app"
  vpc_id      = aws_vpc.ditlw-vpc.id
}

# Allow access from the Intert to port 8008
resource "aws_security_group_rule" "ditwl-sr-internet-to-ecs-color-app-8080" {
  security_group_id        = aws_security_group.ditwl-sg-ecs-color-app.id
  type                     = "ingress"
  from_port                = 8080
  to_port                  = 8080
  protocol                 = "tcp"
  cidr_blocks              = ["0.0.0.0/0"] # Internet
}

# Allow all outbound traffic to Internet
resource "aws_security_group_rule" "ditwl-sr-all-outbund" {
  security_group_id = aws_security_group.ditwl-sg-ecs-color-app.id
  type              = "egress"
  from_port         = "0"
  to_port           = "0"
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
}
```

#### ECS Cluster, Service, and Task (Container)

Create an **ECS Cluster**.

Create an **ECS Task Definition** that uses the AWSVPC network model (the VPC created before) and uses Fargate to run the containers (Instead of EC2 or remote runners), define the CPU and Memory needed, the container, and the ports to publish.

This example deploys an application named color from the [itwonderlab/color Docker repository](https://hub.docker.com/r/itwonderlab/color).

![Terraform AWS ECS Fargate Task](https://www.itwonderlab.com/media/tutorials/Terraform-AWS-ECS-Fargate/Terraform-AWS-ECS-Fargate-Task.svg)

The **AWS Container Task Definition** accepts many options (e.g. disk volumes), check all the possibilities in the [AWS Official ECS API Container Definition](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html).

Create the **ECS Service** that launches the ECS Task in the ECS Cluster. The service includes the number of replicas to run (1) and the subnet, public IP association, and the security group. A public IP is needed for the container to download the docker image and to be able to access the container from the Internet.

```hcl
# Create an ECS Cluster
resource "aws_ecs_cluster" "ditwl-ecs-01" {
  name = "ditwl-ecs-01"
}

# ECS Task definition (Define infrastructure and container images)
resource "aws_ecs_task_definition" "ditwl-ecs-td-color-app" {
  family                   = "service"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = 256 # Number of cpu units 1024 units = 1 vCPU
  memory                   = 512 # Amount (in MiB)
  container_definitions = jsonencode([
    {
      name      = "ditwl-ecs-td-color-app"
      image     = "itwonderlab/color"
      memory    = 50
      essential = true 
      portMappings = [
        {
          containerPort = 8080
        }
      ]
    }
  ])
}

# ECS Service 
resource "aws_ecs_service" "ditwl-ecs-serv-color-app" {
  name            = "ditwl-ecs-serv-color-app"
  cluster         = aws_ecs_cluster.ditwl-ecs-01.id
  task_definition = aws_ecs_task_definition.ditwl-ecs-td-color-app.arn
  launch_type     = "FARGATE"
  desired_count   = 1
  network_configuration {
    subnets = [aws_subnet.ditwl-sn-za-pro-pub-00.id]
    assign_public_ip = "true"
    security_groups = [aws_security_group.ditwl-sg-ecs-color-app.id]
  }
}
```

### Init, Plan, and Apply the Terraform Plan

Run the init, plan and apply sub-commands using terraform or opentofu.

```yaml
$ tofu init
...
$ tofu plan
...
$ tofu apply

OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  + create

OpenTofu will perform the following actions:

  # aws_ecs_cluster.ditwl-ecs-01 will be created
  + resource "aws_ecs_cluster" "ditwl-ecs-01" {
      + arn      = (known after apply)
      + id       = (known after apply)
      + name     = "ditwl-ecs-01"
      + tags_all = (known after apply)
    }

  # aws_ecs_service.ditwl-ecs-serv-color-app will be created
  + resource "aws_ecs_service" "ditwl-ecs-serv-color-app" {
      + cluster                            = (known after apply)
      + deployment_maximum_percent         = 200
      + deployment_minimum_healthy_percent = 100
      + desired_count                      = 1
      + enable_ecs_managed_tags            = false
      + enable_execute_command             = false
      + iam_role                           = (known after apply)
      + id                                 = (known after apply)
      + launch_type                        = "FARGATE"
      + name                               = "ditwl-ecs-serv-color-app"
      + platform_version                   = (known after apply)
      + scheduling_strategy                = "REPLICA"
      + tags_all                           = (known after apply)
      + task_definition                    = (known after apply)
      + triggers                           = (known after apply)
      + wait_for_steady_state              = false

      + network_configuration {
          + assign_public_ip = true
          + security_groups  = (known after apply)
          + subnets          = (known after apply)
        }
    }

  # aws_ecs_task_definition.ditwl-ecs-td-color-app will be created
  + resource "aws_ecs_task_definition" "ditwl-ecs-td-color-app" {
      + arn                      = (known after apply)
      + arn_without_revision     = (known after apply)
      + container_definitions    = jsonencode(
            [
              + {
                  + essential    = true
                  + image        = "itwonderlab/color"
                  + memory       = 50
                  + name         = "ditwl-ecs-td-color-app"
                  + portMappings = [
                      + {
                          + containerPort = 8080
                        },
                    ]
                },
            ]
        )
      + cpu                      = "256"
      + family                   = "service"
      + id                       = (known after apply)
      + memory                   = "512"
      + network_mode             = "awsvpc"
      + requires_compatibilities = [
          + "FARGATE",
        ]
      + revision                 = (known after apply)
      + skip_destroy             = false
      + tags_all                 = (known after apply)
    }

  # aws_internet_gateway.ditwl-ig will be created
  + resource "aws_internet_gateway" "ditwl-ig" {
      + arn      = (known after apply)
      + id       = (known after apply)
      + owner_id = (known after apply)
      + tags     = {
          + "Name" = "ditwl-ig"
        }
      + tags_all = {
          + "Name" = "ditwl-ig"
        }
      + vpc_id   = (known after apply)
    }

  # aws_main_route_table_association.ditwl-rta-default will be created
  + resource "aws_main_route_table_association" "ditwl-rta-default" {
      + id                      = (known after apply)
      + original_route_table_id = (known after apply)
      + route_table_id          = (known after apply)
      + vpc_id                  = (known after apply)
    }

  # aws_route_table.ditwl-rt-pub-main will be created
  + resource "aws_route_table" "ditwl-rt-pub-main" {
      + arn              = (known after apply)
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = [
          + {
              + carrier_gateway_id         = ""
              + cidr_block                 = "0.0.0.0/0"
              + core_network_arn           = ""
              + destination_prefix_list_id = ""
              + egress_only_gateway_id     = ""
              + gateway_id                 = (known after apply)
              + ipv6_cidr_block            = ""
              + local_gateway_id           = ""
              + nat_gateway_id             = ""
              + network_interface_id       = ""
              + transit_gateway_id         = ""
              + vpc_endpoint_id            = ""
              + vpc_peering_connection_id  = ""
            },
        ]
      + tags             = {
          + "Name" = "ditwl-rt-pub-main"
        }
      + tags_all         = {
          + "Name" = "ditwl-rt-pub-main"
        }
      + vpc_id           = (known after apply)
    }

  # aws_security_group.ditwl-sg-ecs-color-app will be created
  + resource "aws_security_group" "ditwl-sg-ecs-color-app" {
      + arn                    = (known after apply)
      + description            = "Managed by Terraform"
      + egress                 = (known after apply)
      + id                     = (known after apply)
      + ingress                = (known after apply)
      + name                   = "ditwl-sg-ecs-color-app"
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + tags_all               = (known after apply)
      + vpc_id                 = (known after apply)
    }

  # aws_security_group_rule.all_outbund will be created
  + resource "aws_security_group_rule" "ditwl-sr-all-outbund" {
      + cidr_blocks              = [
          + "0.0.0.0/0",
        ]
      + from_port                = 0
      + id                       = (known after apply)
      + protocol                 = "-1"
      + security_group_id        = (known after apply)
      + security_group_rule_id   = (known after apply)
      + self                     = false
      + source_security_group_id = (known after apply)
      + to_port                  = 0
      + type                     = "egress"
    }

  # aws_security_group_rule.ditwl-sr-internet-to-ecs-color-app-8080 will be created
  + resource "aws_security_group_rule" "ditwl-sr-internet-to-ecs-color-app-8080" {
      + cidr_blocks              = [
          + "0.0.0.0/0",
        ]
      + from_port                = 8080
      + id                       = (known after apply)
      + protocol                 = "tcp"
      + security_group_id        = (known after apply)
      + security_group_rule_id   = (known after apply)
      + self                     = false
      + source_security_group_id = (known after apply)
      + to_port                  = 8080
      + type                     = "ingress"
    }

  # aws_subnet.ditwl-sn-za-pro-pub-00 will be created
  + resource "aws_subnet" "ditwl-sn-za-pro-pub-00" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = (known after apply)
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "172.21.0.0/23"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = true
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "ditwl-sn-za-pro-pub-00"
        }
      + tags_all                                       = {
          + "Name" = "ditwl-sn-za-pro-pub-00"
        }
      + vpc_id                                         = (known after apply)
    }

  # aws_vpc.ditlw-vpc will be created
  + resource "aws_vpc" "ditlw-vpc" {
      + arn                                  = (known after apply)
      + cidr_block                           = "172.21.0.0/19"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_dns_hostnames                 = (known after apply)
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags                                 = {
          + "Name" = "ditlw-vpc"
        }
      + tags_all                             = {
          + "Name" = "ditlw-vpc"
        }
    }

Plan: 11 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  OpenTofu will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_ecs_cluster.ditwl-ecs-01: Creating...
aws_vpc.ditlw-vpc: Creating...
aws_ecs_task_definition.ditwl-ecs-td-color-app: Creating...
aws_ecs_task_definition.ditwl-ecs-td-color-app: Creation complete after 2s [id=service]
aws_vpc.ditlw-vpc: Creation complete after 4s [id=vpc-035734781521fcdc3]
aws_internet_gateway.ditwl-ig: Creating...
aws_subnet.ditwl-sn-za-pro-pub-00: Creating...
aws_security_group.ditwl-sg-ecs-color-app: Creating...
aws_internet_gateway.ditwl-ig: Creation complete after 2s [id=igw-08fc214fa6601560d]
aws_route_table.ditwl-rt-pub-main: Creating...
aws_security_group.ditwl-sg-ecs-color-app: Creation complete after 4s [id=sg-0c9a289298481dbb1]
aws_security_group_rule.ditwl-sr-all-outbund: Creating...
aws_security_group_rule.ditwl-sr-internet-to-ecs-color-app-8080: Creating...
aws_security_group_rule.all_outbund: Creation complete after 1s [id=sgrule-1126779566]
aws_route_table.ditwl-rt-pub-main: Creation complete after 3s [id=rtb-026ad44da5f78a8b0]
aws_main_route_table_association.ditwl-rta-default: Creating...
aws_security_group_rule.ditwl-sr-internet-to-ecs-color-app-8080: Creation complete after 2s [id=sgrule-3192629380]
aws_ecs_cluster.ditwl-ecs-01: Still creating... [10s elapsed]
aws_main_route_table_association.ditwl-rta-default: Creation complete after 2s [id=rtbassoc-0f590b4c366a14a7b]
aws_ecs_cluster.ditwl-ecs-01: Creation complete after 13s [id=arn:aws:ecs:us-east-1:*************:cluster/ditwl-ecs-01]
aws_subnet.ditwl-sn-za-pro-pub-00: Still creating... [10s elapsed]
aws_subnet.ditwl-sn-za-pro-pub-00: Creation complete after 13s [id=subnet-08ec262201ac3c03e]
aws_ecs_service.ditwl-ecs-serv-color-app: Creating...
aws_ecs_service.ditwl-ecs-serv-color-app: Creation complete after 1s [id=arn:aws:ecs:us-east-1:*************:service/ditwl-ecs-01/ditwl-ecs-serv-color-app]

Apply complete! Resources: 11 added, 0 changed, 0 destroyed
```

If you get an error: `aws_ecs_service InvalidParameterException: Creation of service was not idempotent.` See [Bug at GitHub](https://github.com/hashicorp/terraform-provider-aws/issues/2283). Fix by changing the name to the ECS Service: `name = "ditwl-ecs-serv-color-app-001"` or manually delete in AWS Console.

### Access the Container

In a production environment a Load Balancer will be used to publish and balance the container/s, in this demo we will access the container using its assigned public IP.

Access the AWS Console and search for ECS, then navigate to:

1.  The ECS Cluster: `ditwl-ecs-01`,
2.  The ECS Service `ditwl-ecs-serv-color-app`,
3.  The ECS Service tasks,
4.  The Task Container Details, Network Binding
5.  Click on the External link

![Task configuration Elastic Container Service us east](https://www.itwonderlab.com/media/tutorials/Terraform-AWS-ECS-Fargate/Task-configuration-Elastic-Container-Service-us-east-1.png)

The Container Web Page is shown:

![AWS ECS with Terraform and Fargate Color App](https://www.itwonderlab.com/media/tutorials/Terraform-AWS-ECS-Fargate/AWS-ECS-with-Terraform-and-Fargate-Color-App.png)

### Remove the Infrastructure using Terraform

Run the destroy sub-commands using terraform or opentofu.

```yaml
$ tofu destroy
aws_ecs_cluster.ditwl-ecs-01: Refreshing state... [id=arn:aws:ecs:us-east-1:***********:cluster/ditwl-ecs-01]
aws_vpc.ditlw-vpc: Refreshing state... [id=vpc-035734781521fcdc3]
aws_ecs_task_definition.ditwl-ecs-td-color-app: Refreshing state... [id=service]
aws_security_group.ditwl-sg-ecs-color-app: Refreshing state... [id=sg-0c9a289298481dbb1]
aws_internet_gateway.ditwl-ig: Refreshing state... [id=igw-08fc214fa6601560d]
aws_subnet.ditwl-sn-za-pro-pub-00: Refreshing state... [id=subnet-08ec262201ac3c03e]
aws_route_table.ditwl-rt-pub-main: Refreshing state... [id=rtb-026ad44da5f78a8b0]
aws_security_group_rule.ditwl-sr-all-outbund: Refreshing state... [id=sgrule-1126779566]
aws_security_group_rule.ditwl-sr-internet-to-ecs-color-app-8080: Refreshing state... [id=sgrule-3192629380]
aws_ecs_service.ditwl-ecs-serv-color-app: Refreshing state... [id=arn:aws:ecs:us-east-1:***********:service/ditwl-ecs-01/ditwl-ecs-serv-color-app]
aws_main_route_table_association.ditwl-rta-default: Refreshing state... [id=rtbassoc-0f590b4c366a14a7b]

OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  - destroy

OpenTofu will perform the following actions:

  # aws_ecs_cluster.ditwl-ecs-01 will be destroyed
  - resource "aws_ecs_cluster" "ditwl-ecs-01" {
 ....
aws_ecs_task_definition.ditwl-ecs-td-color-app: Destroying... [id=service]
aws_security_group.ditwl-sg-ecs-color-app: Destroying... [id=sg-0c9a289298481dbb1]
aws_subnet.ditwl-sn-za-pro-pub-00: Destroying... [id=subnet-08ec262201ac3c03e]
aws_ecs_task_definition.ditwl-ecs-td-color-app: Destruction complete after 1s
aws_ecs_cluster.ditwl-ecs-01: Destruction complete after 1s
aws_subnet.ditwl-sn-za-pro-pub-00: Destruction complete after 2s
aws_security_group.ditwl-sg-ecs-color-app: Destruction complete after 2s
aws_vpc.ditlw-vpc: Destroying... [id=vpc-035734781521fcdc3]
aws_vpc.ditlw-vpc: Destruction complete after 2s

Destroy complete! Resources: 11 destroyed.
```

### Troubleshooting

#### CannotPullContainerError

Fargate requires access to the Internet to download the Docker Image. You might get an error similar to this one if there is no connectivity:

`CannotPullContainerError: pull image manifest has been retried 5 time(s): failed to resolve ref docker.io/itwonderlab/color:latest: failed to do request: Head "https://registry-1.docker.io/v2/itwonderlab/color/manifests/latest": dial tcp 3.216.34.172:443: i/o timeout`

Check that the Security group allows Internet access, the routing table associated with the subnet has an entry pointing to an Internet Gateway, and the Fargate task network configuration assigns a public IP to the task.

### Next Steps

Explore [Terraform modules](https://www.itwonderlab.com/aws-terraform-tutorial-terraform-modules/), [AWS Auto Scaling](https://www.itwonderlab.com/aws-terraform-tutorial-aws-auto-scaling/), [AWS Load Balancers](https://www.itwonderlab.com/aws-terraform-tutorial-aws-load-balancers/) and [CI/CD](https://www.itwonderlab.com/aws-terraform-tutorial-terraform-cicd/). Use Kubernetes locally for the development of containers by [Installing K3s as a local Kubernetes cluster](https://www.itwonderlab.com/install-kubernetes-k3s/).
