Stopping task on AWS ECS via CLI (program output as argument input bash) - bash

I'm trying to kill a task in ECS via the CLI.
I can fetch the task name by executing:
aws ecs list-tasks --cluster "my-cluster" --service-name "my-service" | jq .taskArns[0]
which outputs:
"arn:aws:ecs:REGION:ACCOUNT-ID:task/TASK-GUID"
the full ARN of the task as a string (I have a global defaulting output to JSON).
I can kill the task by executing:
aws ecs stop-task --cluster "my-cluster" --task "task-arn"
However when I try and combine it:
aws ecs stop-task --cluster "my-cluster" --task $(aws ecs list-tasks --cluster "my-cluster" --service-name "my-service" | jq .taskArns[0])
I get:
An error occurred (InvalidParameterException) when calling the StopTask operation: taskId longer than 36.
I know this is probably bash program output/argument input interpolation but I've looked that up and cannot get to the bottom of it.

AWS cli essentially has jq built in so a better (simpler) way to query your task arn would be with:
aws ecs list-tasks --cluster "my-cluster" --service "my-service" --output text --query taskArns[0]

Maybe that helps someone:
Killing task with unique task definition name:
OLD_TASK_ID=$(aws ecs list-tasks --cluster ${ecsClusterName} --desired-status RUNNING --family ${nameTaskDefinition} | egrep "task/" | sed -E "s/.*task\/(.*)\"/\1/")
aws ecs stop-task --cluster ${ecsClusterName} --task ${OLD_TASK_ID}
Killing multiple tasks (same task definition name but different task ids):
OLD_TASK_IDS=$(aws ecs list-tasks --cluster ${ecsClusterName} --desired-status RUNNING --family ${nameTaskDefinition} | egrep "task/" | sed -E "s/.*task\/(.*)\"/\1/" | sed -z 's/\n/ /g')
IFS=', ' read -r -a array <<< "$OLD_TASK_IDS"
for element in "${array[#]}"
do
aws ecs stop-task --cluster ${ecsClusterName} --task ${element}
done

One-liner command to stop tasks in cluster/service
for taskarn in $(aws ecs list-tasks --cluster ${YOUR_CLUSTER} --service ${YOUR_SERVICE} --desired-status RUNNING --output text --query 'taskArns'); do aws ecs stop-task --cluster ${YOUR_CLUSTER} --task $taskarn; done;

One-liner version of nathanpecks great answer:
aws ecs stop-task --cluster "my-cluster" --task $(aws ecs list-tasks --cluster "my-cluster" --service "my-service" --output text --query taskArns[0])

Related

How to open shell with colours using AWS ECS execute-command?

I'm using execute-command to open a shell in a AWS Fargate container:
aws ecs execute-command --cluster MtStack-MyCluster7G3C63FE-D8338439438C \
--task d5d35723871267123672312a \
--interactive \
--command "/bin/bash"
The does not show any colours. Is there a way to enable colours?

ECS sh script - update service

I have a service on ECS that has one task with 2 containers. The 2 containers have the same image but different entry points.
I'm trying to set up a pipeline that after tests, takes the image pushed on the registry and updates the service on ECS.
I created a script for that but it only updates one of the containers. So only one container is up-to-date with the changes. How do I make it so that the changes are taken from both containers?
Definition of the task:
register_definition() {
TASK_DEFINITION=$(aws ecs describe-task-definition --task-definition ${ECS_TASK_DEFINITION} | jq '.taskDefinition')
current_revision=$(echo ${TASK_DEFINITION} | jq '.taskDefinitionArn')
echo "Current revision: ${current_revision}"
extra_keys=".compatibilities, .status, .taskDefinition, .revision, .requiresAttributes, .taskDefinitionArn"
echo "Deleting extra keys... ${extra_keys}"
TASK_DEFINITION=$(echo ${TASK_DEFINITION} | jq "del(${extra_keys})")
prev_image=$(echo ${TASK_DEFINITION} | jq ".containerDefinitions[0].image")
echo "Replacing ${prev_image} with ${IMAGE}"
UPDATED_TASK_DEFINITION=$(echo ${TASK_DEFINITION} | jq ".containerDefinitions[0].image = \"${IMAGE}\"")
new_revision=$(aws ecs register-task-definition --cli-input-json "${UPDATED_TASK_DEFINITION}")
new_revision_arn=$(echo ${new_revision} | jq '.taskDefinition.taskDefinitionArn')
export NEW_VERSION=$(echo ${new_revision} | jq '.taskDefinition.revision')
echo "New revision ${NEW_VERSION} created"
}
the deploy to ecs:
deploy(){
echo "Update service task revision"
aws ecs update-service --cluster "${ECS_CLUSTER_NAME}" --service "${ECS_SERVICE_NAME}" --task-definition "${ECS_TASK_DEFINITION}:${NEW_VERSION}"
Any help is really appreciated.
I found the answer after some trial and error. So if someone happens to have the same issue here's the solution.
prev_image=$(echo ${TASK_DEFINITION} | jq ".containerDefinitions[0].image | jq ".containerDefinitions[1].image")
Needed only to get the image in the other container.

xargs not splitting on whitespace from aws cli output

This commands returns all the AWS regions separated by whitespace:
aws ec2 describe-regions --query 'Regions[*].RegionName' --output text
eu-north-1 ap-south-1 eu-west-3 eu-west-2 eu-west-1 ap-northeast-2 ap-northeast-1 sa-east-1 ca-central-1 ap-southeast-1 ap-southeast-2 eu-central-1 us-east-1 us-east-2 us-west-1 us-west-2
I'm trying to pipe this to xargs but it's seeing it as a single string:
aws ec2 describe-regions --query 'Regions[*].RegionName' --output text | gxargs -I {} aws cloudformation list-stacks --region {}
Invalid endpoint: https://cloudformation.eu-north-1 ap-south-1 eu-west-3 eu-west-2 eu-west-1 ap-northeast-2 ap-northeast-1 sa-east-1 ca-central-1 ap-southeast-1 ap-southeast-2 eu-central-1 us-east-1 us-east-2 us-west-1 us-west-2.amazonaws.com
gxargs: aws: exited with status 255; aborting
gxargs is just gnu xargs (I'm on Mac).
Also, tried this to use jmespath to create a string from an array with a specific delimiter (which I could use with xargs):
aws ec2 describe-regions --query 'Regions[*].join(",",#.RegionName)'
In function join(), invalid type for value: None, expected one of: ['string'], received: "null"
EDIT: just following up, this is what I wound up with. It insists on throwing an error when it doesn't find a stack- probably the same for other aws cli commands
aws ec2 describe-regions --query 'Regions[*].RegionName' --output text | gxargs -n 1 sh -c 'aws cloudformation describe-stacks --stack-name findme --region $0 || true'
Here's man xargs for -I:
-I replace-str
Replace occurrences of replace-str in the initial-arguments with names read from standard input. Also, unquoted blanks
do not terminate input items; instead the separator is the newline character. Implies -x and -L 1.
You can use xargs -n 1 aws cloudformation list-stacks --region instead

Shell script syntax, escape character

I have a shell script as given below. This script actually add AWS instance in autoscalling scale in protection group. When I run individual commands that went fine. But when I created a shell file and tried to execute same there are error. See below script
set -x
INSTANCE_ID=$(wget -q -O - http://169.254.169.254/latest/meta-data/instance-id)
ASG_NAME=$(aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" --region us-east-2 | jq '.Tags[] | select(.["Key"] | contains("a:autoscaling:groupName")) | .Value')
ASG_NAME=$(echo $ASG_NAME | tr -d '"')
aws autoscaling set-instance-protection --instance-ids $INSTANCE_ID --auto-scaling-group-name $ASG_NAME --protected-from-scale-in --region us-east-2
error is as given below. I think issue is with second line. It is not able to get ASG_NAME, I tried some of escape character but nothing is working.
+++ wget -q -O - http://169.254.169.254/latest/meta-data/instance-id
++ INSTANCE_ID=i-----
+++ aws ec2 describe-tags --filters Name=resource-id,Values=i------ --region us-east-2
+++ jq '.Tags[] | select(.["Key"] | contains("a:autoscaling:groupName")) | .Value'
++ ASG_NAME=
+++ echo
+++ tr -d '"'
++ ASG_NAME=
++ aws autoscaling set-instance-protection --instance-ids i---- --auto-scaling-group-name --protected-from-scale-in --region us-east-2
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:
aws help
aws <command> help
aws <command> <subcommand> help
aws: error: argument --auto-scaling-group-name: expected one argument
> Blockquote
Solved issue by recommendation of #chepner. Modified second line by
ASG_NAME=$(aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" --region us-east-2 --query 'Tags[1].Value')

Create Jenkinsfile from "execute shell" commands

I have a series of Execute shell boxes on a Jenkins build. After 3 days of Google and watching videos I need help. I am more a sysadmin than a coder so I'm having difficulty in creating a Jenkinsfile with the correct options and syntax. Can anyone advise? I need to create a pipeline. Anything in <name> is like that for security reasons, I have the real values in the files.
Execute shell
mkdir -p deploy
Execute shell
cp -R code/api deploy/
cp docker/Dockerfile.dev deploy/
(cd deploy/api/<Name>.<Name>.Web/ && aws s3 cp --recursive --region=eu-west-1 s3://config.<name>/audience-view/atg/dev/API/ .)
Execute shell
cd deploy && docker build -t <name> -f Dockerfile.dev .
Execute shell
aws ecr get-login --region eu-west-1 > docker_login.sh && chmod +x
docker_login.sh && ./docker_login.sh
docker tag <name>:latest 543573289192.dkr.ecr.eu-west-
1.amazonaws.com/<name>:latest
docker push <name>.dkr.ecr.eu-west-1.amazonaws.com/<name>:latest
Execute shell
docker rmi audience-view-dev-api
docker rmi 543573289192.dkr.ecr.eu-west-1.amazonaws.com/<name>:latest
Execute shell
RUNNING_TASKS=$(aws ecs list-tasks --region=eu-west-1 --cluster <name> --family <name> --query 'taskArns')
if [ "$RUNNING_TASKS" != "[]" ]; then
TASK_ARN=$(aws ecs list-tasks --region=eu-west-1 --cluster a<name> --family <name> --query 'taskArns[0]' | sed 's/\"//g')
aws ecs stop-task --region=eu-west-1 --cluster=<name> --task=$TASK_ARN --reason="Deployment from Jenkins"
while [ $RUNNING_TASKS != "[]" ]; do
sleep 5
RUNNING_TASKS=$(aws ecs list-tasks --region=eu-west-1 --cluster <name> --family <name> --query 'taskArns')
done
fi
Execute shell
TASK_ARN=$(aws ecs start-task --region=eu-west-1 --cluster <name> --task-definition <name> --container-instances 5f0c5b75-64a2-45cf-8ced-d6a6d13d2666 --query 'tasks[0].taskArn' | sed 's/arn:aws:ecs:eu-west-1:543573289192:task\///' | sed 's/\"//g')
TASK_STATUS=$(aws ecs describe-tasks --region=eu-west-1 --cluster <name> --tasks $TASK_ARN --query 'tasks[0].lastStatus')
while [ $TASK_STATUS == "PENDING" ]; do
echo $TASK_STATUS
TASK_STATUS=$(aws ecs describe-tasks --region=eu-west-1 --cluster <name> --tasks $TASK_ARN --query 'tasks[0].lastStatus' | sed 's/\"//g')
if [ $TASK_STATUS == "STOPPED" ]; then
echo $(aws ecs describe-tasks --region=eu-west-1 --cluster <name> --tasks $TASK_ARN --query 'tasks[0].containers[0].exitCode')
exit 1
fi
done
Jenkins is best used as the glue to connect all the build pieces together, not the build script itself. As Alfe mentioned, it would be best to have this all in a shell script and then run the shell script with Jenkins.
BUT, if you really want to do this in a Pipeline job, it would look something like this (declarative pipeline):
pipeline {
agent any
stages {
stage('setup') {
steps {
sh "mkdir -p deploy"
}
stage('nextStage') {
steps {
sh """
cp -R code/api deploy/
cp docker/Dockerfile.dev deploy/
(cd deploy/api/<Name>.<Name>.Web/ && aws s3 cp --recursive --region=eu-west-1 s3://config.<name>/audience-view/atg/dev/API/ .)
"""
}
}
stage('anotherStage') {
steps {
echo "repeat for all your shell steps"
}
}
}
}

Resources