After completing the workshop, delete all resources to avoid ongoing charges.
[!CAUTION] The full LiveCap stack is managed by Terraform. Deleting resources manually with the CLI can cause Terraform state drift – leading to errors on the next
applyor duplicate resource creation. Always preferterraform destroyafter carefully reviewing the plan.
cd infrastructure/terraform # or the directory that holds your Terraform state
# Preview everything that will be deleted
terraform plan -destroy -out=destroy.tfplan
# Read the output carefully, paying attention to:
# - WAF association must be detached before ALB/CloudFront
# - Listeners and Target Groups must be removed before the ALB
# - S3 objects (including versioned objects) must be emptied before bucket deletion
# - NAT Gateway takes a few minutes to delete; EIP must be released after
Only proceed when you have confirmed the resource list is correct.
Terraform does not remove S3 objects or versioned objects automatically. Empty them manually first:
# Empty the frontend bucket (no versioning)
aws s3 rm s3://livecap-frontend-dev-720459752315 --recursive `
--region ap-southeast-1 --profile <aws-profile>
# Empty the transcript bucket (including delete markers and versions)
aws s3api list-object-versions `
--bucket livecap-transcripts-dev-720459752315 `
--region ap-southeast-1 `
--query "Versions[].{Key:Key,VersionId:VersionId}" `
--output json | ConvertFrom-Json | ForEach-Object {
aws s3api delete-object `
--bucket livecap-transcripts-dev-720459752315 `
--key $_.Key `
--version-id $_.VersionId `
--region ap-southeast-1 --profile <aws-profile>
}
# Apply the plan reviewed in Step 1
terraform apply destroy.tfplan
Terraform handles the correct dependency order: WAF disassociation → Listeners/Target Groups → ALB → ECS Service → ECS Cluster → Lambda → ECR → NAT Gateway → Subnets → VPC → IAM Roles → CloudWatch Log Groups → S3 Buckets → CloudFront Distribution → Budget Alert.
After terraform destroy completes, spot-check manually:
# ECS cluster
aws ecs list-clusters --region ap-southeast-1 --profile <aws-profile>
# ECR repositories
aws ecr describe-repositories --region ap-southeast-1 --profile <aws-profile> `
--query "repositories[?contains(repositoryName, 'livecap')].repositoryName"
# S3 buckets
aws s3 ls --profile <aws-profile> | Select-String "livecap"
# ALB
aws elbv2 describe-load-balancers --region ap-southeast-1 --profile <aws-profile> `
--query "LoadBalancers[?contains(LoadBalancerName, 'livecap')].LoadBalancerName"
# Lambda
aws lambda list-functions --region ap-southeast-1 --profile <aws-profile> `
--query "Functions[?contains(FunctionName, 'livecap')].FunctionName"
# CloudWatch log groups
aws logs describe-log-groups --region ap-southeast-1 --profile <aws-profile> `
--query "logGroups[?contains(logGroupName, 'livecap')].logGroupName"
# Unassociated Elastic IPs left over from NAT Gateway
aws ec2 describe-addresses --region ap-southeast-1 --profile <aws-profile> `
--query "Addresses[?AssociationId==null].PublicIp"
If any unassociated Elastic IPs remain, release them via EC2 Console → Elastic IPs → Actions → Release Elastic IP address.
aws ecr delete-repository `
--repository-name livecap-backend `
--force `
--region ap-southeast-1 --profile <aws-profile>
aws budgets delete-budget `
--account-id 720459752315 `
--budget-name livecap-monthly-budget `
--profile <aws-profile>
After completing this workshop, you should be able to:
linux/amd64 from a Python FastAPI backend