View a markdown version of this page

Create an Amazon ECS Linux task for the Fargate launch type - Amazon Elastic Compute Cloud
Services or capabilities described in Amazon Web Services documentation might vary by Region. To see the differences applicable to the China Regions, see Getting Started with Amazon Web Services in China (PDF).

Create an Amazon ECS Linux task for the Fargate launch type

The following code example shows how to:

  • Create the cluster

  • Create a task definition

  • Create the service

  • Clean up

Bash
Amazon CLI with Bash script
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the Sample developer tutorials repository.

#!/bin/bash # Amazon ECS Fargate Tutorial Script - Version 5 # This script creates an ECS cluster, task definition, and service using Fargate launch type # Fixed version with proper resource dependency handling during cleanup set -e # Exit on any error # Initialize logging LOG_FILE="ecs-fargate-tutorial-v5.log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting Amazon ECS Fargate tutorial at $(date)" echo "Log file: $LOG_FILE" # Generate random identifier for unique resource names RANDOM_ID=$(openssl rand -hex 6) CLUSTER_NAME="fargate-cluster-${RANDOM_ID}" SERVICE_NAME="fargate-service-${RANDOM_ID}" TASK_FAMILY="sample-fargate-${RANDOM_ID}" SECURITY_GROUP_NAME="ecs-fargate-sg-${RANDOM_ID}" # Array to track created resources for cleanup CREATED_RESOURCES=() # Function to log and execute commands execute_command() { local cmd="$1" local description="$2" echo "" echo "==========================================" echo "EXECUTING: $description" echo "COMMAND: $cmd" echo "==========================================" local output local exit_code set +e # Temporarily disable exit on error output=$(eval "$cmd" 2>&1) exit_code=$? set -e # Re-enable exit on error if [[ $exit_code -eq 0 ]]; then echo "SUCCESS: $description" echo "OUTPUT: $output" return 0 else echo "FAILED: $description" echo "EXIT CODE: $exit_code" echo "OUTPUT: $output" return 1 fi } # Function to check for actual AWS API errors in command output check_for_aws_errors() { local output="$1" local description="$2" # Look for specific AWS error patterns, not just the word "error" if echo "$output" | grep -qi "An error occurred\|InvalidParameter\|AccessDenied\|ResourceNotFound\|ValidationException"; then echo "AWS API ERROR detected in output for: $description" echo "Output: $output" return 1 fi return 0 } # Function to wait for network interfaces to be cleaned up wait_for_network_interfaces_cleanup() { local security_group_id="$1" local max_attempts=30 local attempt=1 echo "Waiting for network interfaces to be cleaned up..." while [[ $attempt -le $max_attempts ]]; do echo "Attempt $attempt/$max_attempts: Checking for dependent network interfaces..." # Check if there are any network interfaces still using this security group local eni_count eni_count=$(aws ec2 describe-network-interfaces \ --filters "Name=group-id,Values=$security_group_id" \ --query "length(NetworkInterfaces)" \ --output text 2>/dev/null || echo "0") if [[ "$eni_count" == "0" ]]; then echo "No network interfaces found using security group $security_group_id" return 0 else echo "Found $eni_count network interface(s) still using security group $security_group_id" echo "Waiting 10 seconds before next check..." sleep 10 ((attempt++)) fi done echo "WARNING: Network interfaces may still be attached after $max_attempts attempts" echo "This is normal and the security group deletion will be retried" return 1 } # Function to retry security group deletion with exponential backoff retry_security_group_deletion() { local security_group_id="$1" local max_attempts=10 local attempt=1 local wait_time=5 while [[ $attempt -le $max_attempts ]]; do echo "Attempt $attempt/$max_attempts: Trying to delete security group $security_group_id" if execute_command "aws ec2 delete-security-group --group-id $security_group_id" "Delete security group (attempt $attempt)"; then echo "Successfully deleted security group $security_group_id" return 0 else if [[ $attempt -eq $max_attempts ]]; then echo "FAILED: Could not delete security group $security_group_id after $max_attempts attempts" echo "This may be due to network interfaces that are still being cleaned up by AWS" echo "You can manually delete it later using: aws ec2 delete-security-group --group-id $security_group_id" return 1 else echo "Waiting $wait_time seconds before retry..." sleep $wait_time wait_time=$((wait_time * 2)) # Exponential backoff ((attempt++)) fi fi done } # Function to cleanup resources with proper dependency handling cleanup_resources() { echo "" echo "===========================================" echo "CLEANUP PROCESS" echo "===========================================" echo "The following resources were created:" for resource in "${CREATED_RESOURCES[@]}"; do echo " - $resource" done echo "" echo "Do you want to clean up all created resources? (y/n): " read -r CLEANUP_CHOICE if [[ "$CLEANUP_CHOICE" =~ ^[Yy]$ ]]; then echo "Starting cleanup process..." # Step 1: Scale service to 0 tasks first, then delete service if [[ " ${CREATED_RESOURCES[*]} " =~ " ECS Service: $SERVICE_NAME " ]]; then echo "" echo "Step 1: Scaling service to 0 tasks..." if execute_command "aws ecs update-service --cluster $CLUSTER_NAME --service $SERVICE_NAME --desired-count 0" "Scale service to 0 tasks"; then echo "Waiting for service to stabilize after scaling to 0..." execute_command "aws ecs wait services-stable --cluster $CLUSTER_NAME --services $SERVICE_NAME" "Wait for service to stabilize" echo "Deleting service..." execute_command "aws ecs delete-service --cluster $CLUSTER_NAME --service $SERVICE_NAME" "Delete ECS service" else echo "WARNING: Failed to scale service. Attempting to delete anyway..." execute_command "aws ecs delete-service --cluster $CLUSTER_NAME --service $SERVICE_NAME --force" "Force delete ECS service" fi fi # Step 2: Wait a bit for tasks to fully terminate echo "" echo "Step 2: Waiting for tasks to fully terminate..." sleep 15 # Step 3: Delete cluster if [[ " ${CREATED_RESOURCES[*]} " =~ " ECS Cluster: $CLUSTER_NAME " ]]; then echo "" echo "Step 3: Deleting cluster..." execute_command "aws ecs delete-cluster --cluster $CLUSTER_NAME" "Delete ECS cluster" fi # Step 4: Wait for network interfaces to be cleaned up, then delete security group if [[ -n "$SECURITY_GROUP_ID" ]]; then echo "" echo "Step 4: Cleaning up security group..." # First, wait for network interfaces to be cleaned up wait_for_network_interfaces_cleanup "$SECURITY_GROUP_ID" # Then retry security group deletion with backoff retry_security_group_deletion "$SECURITY_GROUP_ID" fi # Step 5: Clean up task definition (deregister all revisions) if [[ " ${CREATED_RESOURCES[*]} " =~ " Task Definition: $TASK_FAMILY " ]]; then echo "" echo "Step 5: Deregistering task definition revisions..." # Get all revisions of the task definition local revisions revisions=$(aws ecs list-task-definitions --family-prefix "$TASK_FAMILY" --query "taskDefinitionArns" --output text 2>/dev/null || echo "") if [[ -n "$revisions" && "$revisions" != "None" ]]; then for revision_arn in $revisions; do echo "Deregistering task definition: $revision_arn" execute_command "aws ecs deregister-task-definition --task-definition $revision_arn" "Deregister task definition $revision_arn" || true done else echo "No task definition revisions found to deregister" fi fi echo "" echo "===========================================" echo "CLEANUP COMPLETED" echo "===========================================" echo "All resources have been cleaned up successfully!" else echo "Cleanup skipped. Resources remain active." echo "" echo "To clean up manually later, use the following commands in order:" echo "1. Scale service to 0: aws ecs update-service --cluster $CLUSTER_NAME --service $SERVICE_NAME --desired-count 0" echo "2. Wait for stability: aws ecs wait services-stable --cluster $CLUSTER_NAME --services $SERVICE_NAME" echo "3. Delete service: aws ecs delete-service --cluster $CLUSTER_NAME --service $SERVICE_NAME" echo "4. Delete cluster: aws ecs delete-cluster --cluster $CLUSTER_NAME" echo "5. Wait 2-3 minutes, then delete security group: aws ec2 delete-security-group --group-id $SECURITY_GROUP_ID" if [[ " ${CREATED_RESOURCES[*]} " =~ " Task Definition: $TASK_FAMILY " ]]; then echo "6. Deregister task definitions: aws ecs list-task-definitions --family-prefix $TASK_FAMILY" echo " Then for each ARN: aws ecs deregister-task-definition --task-definition <ARN>" fi fi } # Trap to handle script interruption trap cleanup_resources EXIT echo "Using random identifier: $RANDOM_ID" echo "Cluster name: $CLUSTER_NAME" echo "Service name: $SERVICE_NAME" echo "Task family: $TASK_FAMILY" # Step 1: Ensure ECS task execution role exists echo "" echo "===========================================" echo "STEP 1: VERIFY ECS TASK EXECUTION ROLE" echo "===========================================" ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) EXECUTION_ROLE_ARN="arn:aws:iam::${ACCOUNT_ID}:role/ecsTaskExecutionRole" # Check if role exists if aws iam get-role --role-name ecsTaskExecutionRole >/dev/null 2>&1; then echo "ECS task execution role already exists" else echo "Creating ECS task execution role..." # Create trust policy cat > trust-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF execute_command "aws iam create-role --role-name ecsTaskExecutionRole --assume-role-policy-document file://trust-policy.json" "Create ECS task execution role" execute_command "aws iam attach-role-policy --role-name ecsTaskExecutionRole --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" "Attach ECS task execution policy" # Clean up temporary file rm -f trust-policy.json CREATED_RESOURCES+=("IAM Role: ecsTaskExecutionRole") fi # Step 2: Create ECS cluster echo "" echo "===========================================" echo "STEP 2: CREATE ECS CLUSTER" echo "===========================================" CLUSTER_OUTPUT=$(execute_command "aws ecs create-cluster --cluster-name $CLUSTER_NAME" "Create ECS cluster") check_for_aws_errors "$CLUSTER_OUTPUT" "Create ECS cluster" CREATED_RESOURCES+=("ECS Cluster: $CLUSTER_NAME") # Step 3: Create task definition echo "" echo "===========================================" echo "STEP 3: CREATE TASK DEFINITION" echo "===========================================" # Create task definition JSON cat > task-definition.json << EOF { "family": "$TASK_FAMILY", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], "cpu": "256", "memory": "512", "executionRoleArn": "$EXECUTION_ROLE_ARN", "containerDefinitions": [ { "name": "fargate-app", "image": "public.ecr.aws/docker/library/httpd:latest", "portMappings": [ { "containerPort": 80, "hostPort": 80, "protocol": "tcp" } ], "essential": true, "entryPoint": ["sh", "-c"], "command": [ "/bin/sh -c \"echo '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p> </div></body></html>' > /usr/local/apache2/htdocs/index.html && httpd-foreground\"" ] } ] } EOF TASK_DEF_OUTPUT=$(execute_command "aws ecs register-task-definition --cli-input-json file://task-definition.json" "Register task definition") check_for_aws_errors "$TASK_DEF_OUTPUT" "Register task definition" # Clean up temporary file rm -f task-definition.json CREATED_RESOURCES+=("Task Definition: $TASK_FAMILY") # Step 4: Set up networking echo "" echo "===========================================" echo "STEP 4: SET UP NETWORKING" echo "===========================================" # Get default VPC ID VPC_ID=$(aws ec2 describe-vpcs --filters "Name=is-default,Values=true" --query "Vpcs[0].VpcId" --output text) if [[ "$VPC_ID" == "None" || -z "$VPC_ID" ]]; then echo "ERROR: No default VPC found. Please create a default VPC or specify a custom VPC." exit 1 fi echo "Using default VPC: $VPC_ID" # Create security group with restricted access # Note: This allows HTTP access from anywhere for demo purposes # In production, restrict source to specific IP ranges or security groups SECURITY_GROUP_OUTPUT=$(execute_command "aws ec2 create-security-group --group-name $SECURITY_GROUP_NAME --description 'Security group for ECS Fargate tutorial - HTTP access' --vpc-id $VPC_ID" "Create security group") check_for_aws_errors "$SECURITY_GROUP_OUTPUT" "Create security group" SECURITY_GROUP_ID=$(echo "$SECURITY_GROUP_OUTPUT" | grep -o '"GroupId": "[^"]*"' | cut -d'"' -f4) if [[ -z "$SECURITY_GROUP_ID" ]]; then SECURITY_GROUP_ID=$(aws ec2 describe-security-groups --group-names "$SECURITY_GROUP_NAME" --query "SecurityGroups[0].GroupId" --output text) fi echo "Created security group: $SECURITY_GROUP_ID" CREATED_RESOURCES+=("Security Group: $SECURITY_GROUP_ID") # Add HTTP inbound rule # WARNING: This allows HTTP access from anywhere (0.0.0.0/0) # In production environments, restrict this to specific IP ranges execute_command "aws ec2 authorize-security-group-ingress --group-id $SECURITY_GROUP_ID --protocol tcp --port 80 --cidr 0.0.0.0/0" "Add HTTP inbound rule to security group" # Get subnet IDs from default VPC echo "Getting subnet IDs from default VPC..." SUBNET_IDS_RAW=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" --query "Subnets[*].SubnetId" --output text) if [[ -z "$SUBNET_IDS_RAW" ]]; then echo "ERROR: No subnets found in default VPC" exit 1 fi # Convert to proper comma-separated format, handling both spaces and tabs SUBNET_IDS_COMMA=$(echo "$SUBNET_IDS_RAW" | tr -s '[:space:]' ',' | sed 's/,$//') echo "Raw subnet IDs: $SUBNET_IDS_RAW" echo "Formatted subnet IDs: $SUBNET_IDS_COMMA" # Validate subnet IDs format if [[ ! "$SUBNET_IDS_COMMA" =~ ^subnet-[a-z0-9]+(,subnet-[a-z0-9]+)*$ ]]; then echo "ERROR: Invalid subnet ID format: $SUBNET_IDS_COMMA" exit 1 fi # Step 5: Create ECS service echo "" echo "===========================================" echo "STEP 5: CREATE ECS SERVICE" echo "===========================================" # Create the service with proper JSON formatting for network configuration SERVICE_CMD="aws ecs create-service --cluster $CLUSTER_NAME --service-name $SERVICE_NAME --task-definition $TASK_FAMILY --desired-count 1 --launch-type FARGATE --network-configuration '{\"awsvpcConfiguration\":{\"subnets\":[\"$(echo $SUBNET_IDS_COMMA | sed 's/,/","/g')\"],\"securityGroups\":[\"$SECURITY_GROUP_ID\"],\"assignPublicIp\":\"ENABLED\"}}'" echo "Service creation command: $SERVICE_CMD" SERVICE_OUTPUT=$(execute_command "$SERVICE_CMD" "Create ECS service") check_for_aws_errors "$SERVICE_OUTPUT" "Create ECS service" CREATED_RESOURCES+=("ECS Service: $SERVICE_NAME") # Step 6: Wait for service to stabilize and get public IP echo "" echo "===========================================" echo "STEP 6: WAIT FOR SERVICE AND GET PUBLIC IP" echo "===========================================" echo "Waiting for service to stabilize (this may take a few minutes)..." execute_command "aws ecs wait services-stable --cluster $CLUSTER_NAME --services $SERVICE_NAME" "Wait for service to stabilize" # Get task ARN TASK_ARN=$(aws ecs list-tasks --cluster $CLUSTER_NAME --service-name $SERVICE_NAME --query "taskArns[0]" --output text) if [[ "$TASK_ARN" == "None" || -z "$TASK_ARN" ]]; then echo "ERROR: No running tasks found for service" exit 1 fi echo "Task ARN: $TASK_ARN" # Get network interface ID ENI_ID=$(aws ecs describe-tasks --cluster $CLUSTER_NAME --tasks $TASK_ARN --query "tasks[0].attachments[0].details[?name=='networkInterfaceId'].value" --output text) if [[ "$ENI_ID" == "None" || -z "$ENI_ID" ]]; then echo "ERROR: Could not retrieve network interface ID" exit 1 fi echo "Network Interface ID: $ENI_ID" # Get public IP PUBLIC_IP=$(aws ec2 describe-network-interfaces --network-interface-ids $ENI_ID --query "NetworkInterfaces[0].Association.PublicIp" --output text) if [[ "$PUBLIC_IP" == "None" || -z "$PUBLIC_IP" ]]; then echo "WARNING: No public IP assigned to the task" echo "The task may be in a private subnet or public IP assignment failed" else echo "" echo "===========================================" echo "SUCCESS! APPLICATION IS RUNNING" echo "===========================================" echo "Your application is available at: http://$PUBLIC_IP" echo "You can test it by opening this URL in your browser" echo "" fi # Display service information echo "" echo "===========================================" echo "SERVICE INFORMATION" echo "===========================================" execute_command "aws ecs describe-services --cluster $CLUSTER_NAME --services $SERVICE_NAME" "Get service details" echo "" echo "===========================================" echo "TUTORIAL COMPLETED SUCCESSFULLY" echo "===========================================" echo "Resources created:" for resource in "${CREATED_RESOURCES[@]}"; do echo " - $resource" done if [[ -n "$PUBLIC_IP" && "$PUBLIC_IP" != "None" ]]; then echo "" echo "Application URL: http://$PUBLIC_IP" fi echo "" echo "Script completed at $(date)"

For a complete list of Amazon SDK developer guides and code examples, see Create Amazon EC2 resources using an Amazon SDK. This topic also includes information about getting started and details about previous SDK versions.