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.
Path variables
Section titled “Path variables”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-fargateScale services to zero
Section titled “Scale services to zero”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"}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"}Delete services
Section titled “Delete services”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"}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"}Wait for routes to clear
Section titled “Wait for routes to clear”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.
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 15doneroutes: 2routes: 1routes: 0Disable deletion protection
Section titled “Disable deletion protection”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).
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:
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 10done1: UPDATE_IN_PROGRESS2: UPDATE_COMPLETEDisassociate gateway from cluster
Section titled “Disassociate gateway from cluster”Raw association payloads include ARNs — hush them and poll status counts.
aws ecs disassociate-gateway-from-cluster \ --gateway "$GATEWAY" \ --cluster "$CLUSTER" >/dev/null(no stdout — success is silent)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 10doneassociations: 1associations: 0Delete gateway
Section titled “Delete gateway”aws ecs delete-gateway --gateway "$GATEWAY" \ --query '{name:name,status:status}'{ "name": "ecs-gw-lab-fargate", "status": "DELETED"}Delete cluster
Section titled “Delete cluster”aws ecs delete-cluster --cluster "$CLUSTER" \ --query 'cluster.{name:clusterName,status:status}'{ "name": "ecs-gw-lab-fargate", "status": "INACTIVE"}Deregister task definitions
Section titled “Deregister task definitions”Family prefix matches both ${PATH_NAME}-a and ${PATH_NAME}-b (and any earlier family with the same prefix).
aws ecs list-task-definitions --family-prefix "$PATH_NAME" \ --query 'length(taskDefinitionArns)' --output text4aws 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/nulldoneecho "deregistered"deregisteredDelete log group
Section titled “Delete log group”aws logs delete-log-group --log-group-name "/ecs/${PATH_NAME}" || trueecho "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.
Shared VPC and roles
Section titled “Shared VPC and roles”Skip this section if you still plan to run (or keep) the EC2 path.
Shared IAM roles
Section titled “Shared IAM roles”aws iam detach-role-policy \ --role-name "${NAME}-gateway-infra" \ --policy-arn arn:aws:iam::aws:policy/AmazonECSInfrastructureRoleForGatewayHostSharedALB || trueaws iam delete-role --role-name "${NAME}-gateway-infra" || trueecho "gateway infra role removed"gateway infra role removedaws iam detach-role-policy \ --role-name "${NAME}-exec" \ --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy || trueaws iam delete-role --role-name "${NAME}-exec" || trueecho "exec role removed"exec role removedWait for ENIs
Section titled “Wait for ENIs”ALB / task ENIs must clear before the VPC can delete.
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 10doneENIs: 3ENIs: 1ENIs: 0NAT, routes, subnets, VPC
Section titled “NAT, routes, subnets, VPC”IDs stay in shell variables — do not echo full resource ARNs into tickets.
aws ec2 delete-nat-gateway --nat-gateway-id "$NAT_GW_ID" \ --query 'NatGatewayId' --output textnat-<id>aws ec2 wait nat-gateway-deleted --nat-gateway-ids "$NAT_GW_ID"echo "NAT deleted"NAT deletedaws ec2 release-address --allocation-id "$EIP_ALLOC_ID"echo "EIP released"EIP releasedfor 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" donedoneecho "subnet route associations cleared"subnet route associations clearedaws ec2 delete-route --route-table-id "$PRIVATE_RTB_ID" --destination-cidr-block 0.0.0.0/0 || trueaws 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 || trueaws ec2 delete-route-table --route-table-id "$PUBLIC_RTB_ID"echo "route tables deleted"route tables deletedaws 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 deletedaws 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 deletedaws ec2 delete-vpc --vpc-id "$VPC_ID"echo "VPC deleted"VPC deleted