Skip to content

Fargate teardown

Tear down in reverse: remove the services (which drains the routes and deprovisions the ALB), then the gateway, then the cluster. The shared VPC and IAM roles come last — and only if you are finished with both paths.

1Servicesroutes drain · ALB deprovisions
2Gatewayprotection off · disassociate · delete
3Clustertask defs · log group
4Shared VPC + IAMonly when both paths are done
Terminal window
export PATH_NAME="${NAME}-fargate"
export CLUSTER="$PATH_NAME"
export GATEWAY="$PATH_NAME"
echo "path ready: CLUSTER=$CLUSTER GATEWAY=$GATEWAY"
path ready: CLUSTER=ecs-gw-lab-fargate GATEWAY=ecs-gw-lab-fargate
Terminal window
aws ecs update-service --cluster "$CLUSTER" --service "${PATH_NAME}-a" \
--desired-count 0 \
--query 'service.{name:serviceName,desired:desiredCount,status:status}'
{
"name": "ecs-gw-lab-fargate-a",
"desired": 0,
"status": "ACTIVE"
}
Terminal window
aws ecs update-service --cluster "$CLUSTER" --service "${PATH_NAME}-b" \
--desired-count 0 \
--query 'service.{name:serviceName,desired:desiredCount,status:status}'
{
"name": "ecs-gw-lab-fargate-b",
"desired": 0,
"status": "ACTIVE"
}
Terminal window
aws ecs delete-service --cluster "$CLUSTER" --service "${PATH_NAME}-a" --force \
--query 'service.{name:serviceName,status:status}'
{
"name": "ecs-gw-lab-fargate-a",
"status": "DRAINING"
}
Terminal window
aws ecs delete-service --cluster "$CLUSTER" --service "${PATH_NAME}-b" --force \
--query 'service.{name:serviceName,status:status}'
{
"name": "ecs-gw-lab-fargate-b",
"status": "DRAINING"
}

Routes (and the shared ALB) go away after the services disconnect. This often takes several minutes — statuses move through DELETE_IN_PROGRESS before the list is empty.

Terminal window
for i in $(seq 1 60); do
COUNT=$(aws ecs list-gateway-routes --gateway "$GATEWAY" --cluster "$CLUSTER" \
--query 'length(gatewayRoutes)' --output text 2>/dev/null || echo 0)
echo "routes: $COUNT"
[ "$COUNT" = "0" ] && break
sleep 15
done
routes: 2
routes: 1
routes: 0

update-gateway requires the infrastructure role ARN and a configuration object. For a protection-only change, pass an empty hostSharedAlb (create-time fields like scheme / subnets are immutable and rejected on update).

Terminal window
aws ecs update-gateway --gateway "$GATEWAY" \
--infrastructure-role-arn "$GATEWAY_INFRA_ROLE_ARN" \
--no-deletion-protection \
--configuration '{"hostSharedAlb":{}}' \
--query 'gateway.{name:name,deletionProtection:deletionProtection}'
{
"name": "ecs-gw-lab-fargate",
"deletionProtection": false
}

Wait until the gateway leaves UPDATE_IN_PROGRESS before deleting:

Terminal window
for i in $(seq 1 36); do
STATUS=$(aws ecs describe-gateway --gateway "$GATEWAY" \
--query 'gateway.status' --output text)
echo "$i: $STATUS"
[ "$STATUS" = "UPDATE_COMPLETE" ] && break
sleep 10
done
1: UPDATE_IN_PROGRESS
2: UPDATE_COMPLETE

Raw association payloads include ARNs — hush them and poll status counts.

Terminal window
aws ecs disassociate-gateway-from-cluster \
--gateway "$GATEWAY" \
--cluster "$CLUSTER" >/dev/null
(no stdout — success is silent)
Terminal window
for i in $(seq 1 36); do
ASSOC=$(aws ecs list-gateway-cluster-associations --gateway "$GATEWAY" \
--query 'length(gatewayClusterAssociations)' --output text 2>/dev/null || echo 0)
echo "associations: $ASSOC"
[ "$ASSOC" = "0" ] && break
sleep 10
done
associations: 1
associations: 0
Terminal window
aws ecs delete-gateway --gateway "$GATEWAY" \
--query '{name:name,status:status}'
{
"name": "ecs-gw-lab-fargate",
"status": "DELETED"
}
Terminal window
aws ecs delete-cluster --cluster "$CLUSTER" \
--query 'cluster.{name:clusterName,status:status}'
{
"name": "ecs-gw-lab-fargate",
"status": "INACTIVE"
}

Family prefix matches both ${PATH_NAME}-a and ${PATH_NAME}-b (and any earlier family with the same prefix).

Terminal window
aws ecs list-task-definitions --family-prefix "$PATH_NAME" \
--query 'length(taskDefinitionArns)' --output text
4
Terminal window
aws ecs list-task-definitions --family-prefix "$PATH_NAME" --query 'taskDefinitionArns[]' \
--output text | tr '\t' '\n' | while read -r ARN; do
[ -n "$ARN" ] && aws ecs deregister-task-definition --task-definition "$ARN" >/dev/null
done
echo "deregistered"
deregistered
Terminal window
aws logs delete-log-group --log-group-name "/ecs/${PATH_NAME}" || true
echo "log group gone (or already missing)"
log group gone (or already missing)

Fargate-path resources are removed. Stop here if you still want the shared VPC for the EC2 path.

Skip this section if you still plan to run (or keep) the EC2 path.

Terminal window
aws iam detach-role-policy \
--role-name "${NAME}-gateway-infra" \
--policy-arn arn:aws:iam::aws:policy/AmazonECSInfrastructureRoleForGatewayHostSharedALB || true
aws iam delete-role --role-name "${NAME}-gateway-infra" || true
echo "gateway infra role removed"
gateway infra role removed
Terminal window
aws iam detach-role-policy \
--role-name "${NAME}-exec" \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy || true
aws iam delete-role --role-name "${NAME}-exec" || true
echo "exec role removed"
exec role removed

ALB / task ENIs must clear before the VPC can delete.

Terminal window
for i in $(seq 1 36); do
COUNT=$(aws ec2 describe-network-interfaces \
--filters "Name=vpc-id,Values=${VPC_ID}" \
--query 'length(NetworkInterfaces)' --output text)
echo "ENIs: $COUNT"
[ "$COUNT" = "0" ] && break
sleep 10
done
ENIs: 3
ENIs: 1
ENIs: 0

IDs stay in shell variables — do not echo full resource ARNs into tickets.

Terminal window
aws ec2 delete-nat-gateway --nat-gateway-id "$NAT_GW_ID" \
--query 'NatGatewayId' --output text
nat-<id>
Terminal window
aws ec2 wait nat-gateway-deleted --nat-gateway-ids "$NAT_GW_ID"
echo "NAT deleted"
NAT deleted
Terminal window
aws ec2 release-address --allocation-id "$EIP_ALLOC_ID"
echo "EIP released"
EIP released
Terminal window
for RTB in "$PRIVATE_RTB_ID" "$PUBLIC_RTB_ID"; do
for ASSOC_ID in $(aws ec2 describe-route-tables --route-table-ids "$RTB" \
--query 'RouteTables[0].Associations[?SubnetId!=`null`].RouteTableAssociationId' \
--output text); do
aws ec2 disassociate-route-table --association-id "$ASSOC_ID"
done
done
echo "subnet route associations cleared"
subnet route associations cleared
Terminal window
aws ec2 delete-route --route-table-id "$PRIVATE_RTB_ID" --destination-cidr-block 0.0.0.0/0 || true
aws ec2 delete-route-table --route-table-id "$PRIVATE_RTB_ID"
aws ec2 delete-route --route-table-id "$PUBLIC_RTB_ID" --destination-cidr-block 0.0.0.0/0 || true
aws ec2 delete-route-table --route-table-id "$PUBLIC_RTB_ID"
echo "route tables deleted"
route tables deleted
Terminal window
aws ec2 detach-internet-gateway --internet-gateway-id "$IGW_ID" --vpc-id "$VPC_ID"
aws ec2 delete-internet-gateway --internet-gateway-id "$IGW_ID"
echo "IGW deleted"
IGW deleted
Terminal window
aws ec2 delete-subnet --subnet-id "$PRIVATE_SUBNET_A"
aws ec2 delete-subnet --subnet-id "$PRIVATE_SUBNET_B"
aws ec2 delete-subnet --subnet-id "$PUBLIC_SUBNET_A"
aws ec2 delete-subnet --subnet-id "$PUBLIC_SUBNET_B"
echo "subnets deleted"
subnets deleted
Terminal window
aws ec2 delete-vpc --vpc-id "$VPC_ID"
echo "VPC deleted"
VPC deleted