aws cli hangs when in background - bash

I have a command which runs pretty well when I just run it:
time aws sqs send-message --queue-url https://my_sqs_url --message-body "$(date "+%H:%M:%S_%N")"
{
"MD5OfMessageBody": "a19f365993d45d4885f7f15bce8aac97",
"MessageId": "30971fa7-d8ac-4540-9541-aebc38598856"
}
real 0m1.321s
user 0m1.174s
sys 0m0.117s
If I want to run in in background then the sqs message is sent however the process hangs infinitely (or at least I'm not patient enough to see when it eventually ends):
aws sqs send-message --queue-url https://my_sqs_url --message-body "$(date "+%H:%M:%S_%N")" &
[1] 9561
During that I see two processes instead of one:
ps -eFH | grep "aws sqs"
root 9561 2980 0 2210 912 1 09:29 pts/0 00:00:00 aws sqs send-message --queue-url https://my_sqs_url --message-body 09:29:30_009996044
root 9563 9561 0 63048 59172 1 09:29 pts/0 00:00:01 aws sqs send-message --queue-url https://my_sqs_url --message-body 09:29:30_009996044
The questions: why it hangs and how to do it properly?

This should work :
time aws sqs send-message --queue-url https://my_sqs_url --message-body "$(date "+%H:%M:%S_%N")" & wait $!

Related

Deploy API Gateway from Command Line

I am trying for two days to deploy a POST Rest API, which would be the trigger for an already existing Lambda function on AWS, through a bash script using the aws-cli. The thing is that I am able to upload and deploy the API but it doesn't work. I tested my Lambda fuction through the test feature on AWS itself and it works. But when I call the API it returns this as header
{"x-amzn-ErrorType":"InternalServerErrorException"}
and this as body
{
"message": "Internal server error"
}
and this is the log I find on API Gateway test functionality
Execution log for request ba8d70b6-bb5e-49c5-9ff4-7927983c51d8
Thu Nov 26 16:41:55 UTC 2020 : Starting execution for request: ba8d70b6-bb5e-49c5-9ff4-7927983c51d8
Thu Nov 26 16:41:55 UTC 2020 : HTTP Method: POST, Resource Path: /provoletta
Thu Nov 26 16:41:55 UTC 2020 : Method request path: {}
Thu Nov 26 16:41:55 UTC 2020 : Method request query string: {}
Thu Nov 26 16:41:55 UTC 2020 : Method request headers: {}
Thu Nov 26 16:41:55 UTC 2020 : Method request body before transformations: {
"device": {
"uuid": "4",
"lastPosition": "4",
"lastSeen": "4",
"raspberryId": "4",
"roomNumber": "34"
}
}
Thu Nov 26 16:41:55 UTC 2020 : Endpoint request URI: https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:***:function:provoletta/invocations
Thu Nov 26 16:41:55 UTC 2020 : Endpoint request headers: {x-amzn-lambda-integration-tag=ba8d70b6-bb5e-49c5-9ff4-7927983c51d8, Authorization=************************************************************************************************************************************************************************************************************************************************************************************************************************53a5a7, X-Amz-Date=20201126T164155Z, x-amzn-apigateway-api-id=eca0u6a3ed, X-Amz-Source-Arn=arn:aws:execute-api:us-east-1:***:eca0u6a3ed/test-invoke-stage/POST/provoletta, Accept=application/json, User-Agent=AmazonAPIGateway_eca0u6a3ed, X-Amz-Security-Token=*** [TRUNCATED]
Thu Nov 26 16:41:55 UTC 2020 : Endpoint request body after transformations: {
"device": {
"uuid": "4",
"lastPosition": "4",
"lastSeen": "4",
"raspberryId": "4",
"roomNumber": "34"
}
}
Thu Nov 26 16:41:55 UTC 2020 : Sending request to https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:***:function:provoletta/invocations
Thu Nov 26 16:41:55 UTC 2020 : Execution failed due to configuration error: Invalid permissions on Lambda function
Thu Nov 26 16:41:55 UTC 2020 : Method completed with status: 500
This is how I am trying to create and build the API through bash script
#!/bin/sh
api_id=$(aws apigateway create-rest-api --name 'provoletta' --query 'id' --output text)
resource_id=$(aws apigateway get-resources --rest-api-id $api_id --query 'items' --output text)
resource_id=${resource_id::-2}
result_id=$(aws apigateway create-resource --rest-api-id $api_id --parent-id $resource_id --path-part provoletta --query 'id' --output text)
aws apigateway put-method \
--rest-api-id $api_id \
--region $AWS_REGION \
--resource-id $result_id \
--http-method POST \
--authorization-type "NONE"
aws apigateway put-method-response \
--region $AWS_REGION \
--rest-api-id $api_id \
--resource-id $result_id \
--http-method POST \
--status-code 200
aws apigateway put-integration \
--region $AWS_REGION \
--rest-api-id $api_id \
--resource-id $result_id \
--http-method POST \
--type AWS \
--integration-http-method POST \
--uri arn:aws:apigateway:$AWS_REGION:lambda:path/2015-03-31/functions/arn:aws:lambda:$AWS_REGION:$ACCOUNT_ID:function:provoletta/invocations \
--request-templates '{"application/x-www-form-urlencoded":"{\"body\": $input.json(\"$\")}"}'
aws apigateway put-integration-response \
--region $AWS_REGION \
--rest-api-id $api_id \
--resource-id $result_id \
--http-method POST \
--status-code 200 \
--selection-pattern ""
aws apigateway create-deployment --rest-api-id $api_id --stage-name provoletta
If I create an API for the same Lambda function on API Gateway console itself it works without problems. So, what's the problem with this script?
It looks like a permission problem. Specifically, it looks like you did not give permission for the API Gateway to invoke your Lambda function:
Execution failed due to configuration error: Invalid permissions on Lambda function.
To fix this you should "add permissions" to your Lambda as described in the AWS CLI documentation.
Based on your "code", this might already do the trick, assuming your Lambda function is called provoletta:
aws lambda add-permission \
--region $AWS_REGION \
--function-name provoletta \
--action lambda:InvokeFunction \
--statement-id AllowGatewayToInvokeFunction \
--principal apigateway.amazonaws.com

Shell scripting- Running operations on statistics extracted from json of aws cloudwatch statistics using bash

I am running aws cli aws cloudwatch get-metric-statistics --metric-name CPUUtilization --start-time 2010-02-20T12:00:00 --end-time 2010-02-20T15:00:00 --period 60 --namespace AWS/EC2 --extended-statistics p80 --dimensions Name=InstanceId,Value=i-0b123423423 in a
loop and extracting the metrics from json(using jq) in the below format for around 10-20 instances.
Illustrative JSON
{
"Label": "CPUUtilization",
"Datapoints": [
{
"Timestamp": "2020-02-20T12:15:00Z",
"Unit": "Percent",
"ExtendedStatistics": {
"p80": 0.16587132264856133
}
},
Instance-ABC
19.514049550078127
12.721997782508938
13.318820949213313
15.994192991030545
18.13096421299414
Instance-BCD
19.5140495
12.7219977
13.3188209
15.9941929
18.1309642
13.3188209
15.9941929
18.1309642
How can i calculate and run operations from the values in above output to get results like below in using Bash scripting
Instance above 70%
Instance-ABC
Instance-BCD
Instances below 20%
Instance-EFG
Instance-HIJ
I'm currently retrieving statistics with:
for i in $(aws ec2 describe-instances | jq -r '.["Reservations"]|.[]|.Instances|.[]| .InstanceId' | sort -n); do
echo "Instance $i"
aws cloudwatch get-metric-statistics --metric-name CPUUtilization --start-time 2019-02-20T15:00:00T --end-time 2019-02-20T18:00:00 --period 60 --namespace AWS/EC2 --extended-statistics p80 --dimensions Name=InstanceId,Value=$i \
| jq '.Datapoints[].ExtendedStatistics[]'
done

aws cli: ssm start-session not working with a variable as a parameter value

I am trying to automate some part of my work by creating a bash function that let's me easily ssm into one of our instances. To do that, I only need to know the instance id. Then I run aws ssm start-session with the proper profile. Here's the function:
function ssm_to_cluster() {
local instance_id=$(aws ec2 describe-instances --filters \
"Name=tag:Environment,Values=staging" \
"Name=tag:Name,Values=my-cluster-name" \
--query 'Reservations[*].Instances[*].[InstanceId]' \
| grep i- | awk '{print $1}' | tail -1)
aws ssm start-session --profile AccountProfile --target $instance_id
}
When I run this function, I always get an error like the following:
An error occurred (TargetNotConnected) when calling the StartSession operation: "i-0599385eb144ff93c" is not connected.
However, then I take that instance id and run it from my terminal directly, it works:
aws ssm start-session --profile MyProfile --target i-0599385eb144ff93c
Why is this?
You're sending instance ID as "i-0599385eb144ff93c" instead of i-0599385eb144ff93c.
Modified function that should work -
function ssm_to_cluster() {
local instance_id=$(aws ec2 describe-instances --filters \
"Name=tag:Environment,Values=staging" \
"Name=tag:Name,Values=my-cluster-name" \
--query 'Reservations[*].Instances[*].[InstanceId]' \
| grep i- | awk '{print $1}' | tail -1 | tr -d '"')
aws ssm start-session --profile AccountProfile --target $instance_id
}

Create API gateway in localstack

I was able to setup localstack (https://github.com/atlassian/localstack) and also create lambda function in it (using create-function ... command). However, I couldnt find a way to create an APIGateway in localstack so that the lambda function can be called using it.
Basically, I need an APIGateway(and its arn), so that using that the lambda function can be called.
Walkthrough for creating a NodeJS Lambda together with API Gateway per CLI:
First we create a simple NodeJS Lambda:
const apiTestHandler = (payload, context, callback) => {
console.log(`Function apiTestHandler called with payload ${JSON.stringify(payload)}`);
callback(null, {
statusCode: 201,
body: JSON.stringify({
somethingId: payload.pathParameters.somethingId
}),
headers: {
"X-Click-Header": "abc"
}
});
}
module.exports = {
apiTestHandler,
}
Put that into a zip File called apiTestHandler.zip and upload it to localstack:
aws lambda create-function \
--region us-east-1 \
--function-name api-test-handler \
--runtime nodejs6.10 \
--handler index.apiTestHandler \
--memory-size 128 \
--zip-file fileb://apiTestHandler.zip \
--role arn:aws:iam::123456:role/role-name --endpoint-url=http://localhost:4574
Now we can create our Rest-Api:
aws apigateway create-rest-api --region us-east-1 --name 'API Test' --endpoint-url=http://localhost:4567
This gives the following response:
{
"name": "API Test",
"id": "487109A-Z548",
"createdDate": 1518081479
}
With the ID we got here, we can ask for its parent-ID:
aws apigateway get-resources --region us-east-1 --rest-api-id 487109A-Z548 --endpoint-url=http://localhost:4567
Response:
{
"items": [
{
"path": "/",
"id": "0270A-Z23550",
"resourceMethods": {
"GET": {}
}
}
]
}
Now we have everything to create our resource together with its path:
aws apigateway create-resource \
--region us-east-1 \
--rest-api-id 487109A-Z548 \
--parent-id 0270A-Z23550 \
--path-part "{somethingId}" --endpoint-url=http://localhost:4567
Response:
{
"resourceMethods": {
"GET": {}
},
"pathPart": "{somethingId}",
"parentId": "0270A-Z23550",
"path": "/{somethingId}",
"id": "0662807180"
}
The ID we got here is needed to create our linked GET Method:
aws apigateway put-method \
--region us-east-1 \
--rest-api-id 487109A-Z548 \
--resource-id 0662807180 \
--http-method GET \
--request-parameters "method.request.path.somethingId=true" \
--authorization-type "NONE" \
--endpoint-url=http://localhost:4567
We are almost there - one of the last things to do is to create our integration with the already uploaded lambda:
aws apigateway put-integration \
--region us-east-1 \
--rest-api-id 487109A-Z548 \
--resource-id 0662807180 \
--http-method GET \
--type AWS_PROXY \
--integration-http-method POST \
--uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:api-test-handler/invocations \
--passthrough-behavior WHEN_NO_MATCH \
--endpoint-url=http://localhost:4567
Last but not least: Deploy our API to our desired stage:
aws apigateway create-deployment \
--region us-east-1 \
--rest-api-id 487109A-Z548 \
--stage-name test \
--endpoint-url=http://localhost:4567
Now we can test it:
curl http://localhost:4567/restapis/487109A-Z548/test/_user_request_/HowMuchIsTheFish
Response:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 34 100 34 0 0 9 0 0:00:03 0:00:03 --:--:-- 9
{"somethingId":"HowMuchIsTheFish"}
I hope this helps.
Hint 1: For easier use I recommend to install AWSCLI Local ( https://github.com/localstack/awscli-local ) - with this tool you can use the command "awslocal" and don't have to type "--endpoint-url= ..." for each command
Walkthrough for using Serverless Framework and Localstack:
You can also use the Serverless Framework (https://serverless.com/).
First install it via npm:
npm install serverless -g
Now you can create a sample application based on a nodejs-aws template:
serverless create --template aws-nodejs
In order to have an HTTP endpoint, you have to edit the serverless.yml and add the corresponding event :
functions:
hello:
handler: handler.hello
events:
- http:
path: ping
method: get
In order to run this against your localstack installation you have to use the serverless-localstack plugin ( https://github.com/temyers/serverless-localstack):
npm install serverless-localstack
Now you have to edit your serverless.yml again, add the plugin and adjust your endpoints. In my case localstack is running inside the Docker toolbox, so it's IP is 192.168.99.100 - you may have to change this to localhost, depending on your use:
plugins:
- serverless-localstack
custom:
localstack:
debug: true
stages:
- local
- dev
host: http://192.168.99.100
endpoints:
S3: http://192.168.99.100:4572
DynamoDB: http://192.168.99.100:4570
CloudFormation: http://192.168.99.100:4581
Elasticsearch: http://192.168.99.100:4571
ES: http://192.168.99.100:4578
SNS: http://192.168.99.100:4575
SQS: http://192.168.99.100:4576
Lambda: http://192.168.99.100:4574
Kinesis: http://192.168.99.100:4568
Now you can try to deploy it:
serverless deploy --verbose --stage local
This will create an S3 bucket, upload your lambda and create a cloudformation stack. However, the process will fail due to some inconsistencies of localstack when compared against AWS. Don't be dismayed though, the created cloudformation template works fine and you just need an additional request and you are done:
awslocal cloudformation update-stack --template-body file://.serverless/cloudformation-template-update-stack.json --stack-name aws-nodejs-local
Now your lambda is deployed and can be tested:
curl http://192.168.99.100:4567/restapis/75A-Z278430A-Z/local/_user_request_/ping
Response:
% Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed
100 364 100 364 0 0 111 0 0:00:03 0:00:03 --:--:-- 111
{"message":"Go Serverless v1.0! Your function executed successfully!","input":{"body":null,"headers":{"host":"192.168.99.100:4567","accept":"*/*","user-agent":"curl/7.49.1"},"resource":"/restapis/75A-Z278430A-Z/local/_user_request_/ping","queryStringParameters":{},"httpMethod":"GET","stageVariables":{},"path":"/ping","pathParameters":{},"isBase64Encoded":false}}
Hope this helps.
Looks like there is an open issue related to setting up API Gateway with localstack on GitHub:
https://github.com/localstack/localstack/issues/129
You could try following the steps provided in the answer there.
Copied from the GitHub issue:
"""
One option would be to use the serverless framework (https://github.com/serverless/serverless). Otherwise, you can call the LocalStack services directly (via the CLI or an SDK) to create an API Gateway resource+method+integration, and connect them to your Lambda function.
Here are a few pointers that might be helpful:
https://ig.nore.me/2016/03/setting-up-lambda-and-a-gateway-through-the-cli/ (the "Creating a role" part can be skipped)
https://github.com/atlassian/localstack/issues/101
https://github.com/temyers/serverless-localstack
"""

Autoscaling: Newly created instance always OutOfService

I have setup autoscaling using these steps...
$ elb-create-lb autoscalelb --headers --listener
"lb-port=80,instance-port=80,protocol=http" --listener
"lb-port=443,instance-port=443,protocol=tcp" --availability-zones
us-east-1d
$ elb-describe-lbs autoscalelb
$ elb-register-instances-with-lb autoscalelb --instances i-ee364697
$ elb-configure-healthcheck autoscalelb --headers --target "TCP:80"
--interval 5 --timeout 3 --unhealthy-threshold 2 --healthy-threshold 4
$ as-create-launch-config autoscalelc --image-id ami-baba68d3
--instance-type t1.micro
$ as-create-auto-scaling-group autoscleasg --availability-zones
us-east-1d --launch-configuration autoscalelc --min-size 1 --max-size
5 --desired-capacity 1 --load-balancers autoscalelb
$ as-describe-auto-scaling-groups autoscleasg
$ as-put-scaling-policy MyScaleUpPolicy --auto-scaling-group
autoscleasg --adjustment=1 --type ChangeInCapacity --cooldown 300
$ mon-put-metric-alarm MyHighCPUAlarm --comparison-operator
GreaterThanThreshold --evaluation-periods 1 --metric-name
CPUUtilization --namespace "AWS/EC2" --period 600 --statistic Average
--threshold 80 --alarm-actions arn:aws:autoscaling:us-east-1:616259365041:scalingPolicy:46c2d3b3-7f29-42b6-ab64-548f45de334f:autoScalingGroupName/autoscleasg:policyName/MyScaleUpPolicy
--dimensions "AutoScalingGroupName=autoscleasg"
$ as-put-scaling-policy MyScaleDownPolicy --auto-scaling-group
autoscleasg --adjustment=-1 --type ChangeInCapacity --cooldown 300
$ mon-put-metric-alarm MyLowCPUAlarm --comparison-operator
LessThanThreshold --evaluation-periods 1 --metric-name CPUUtilization
--namespace "AWS/EC2" --period 600 --statistic Average --threshold 50 --alarm-actions arn:aws:autoscaling:us-east-1:616259365041:scalingPolicy:30ccd42c-06fe-401a-8b8f-a4e49bbb9c7d:autoScalingGroupName/autoscleasg:policyName/MyScaleDownPolicy
--dimensions "AutoScalingGroupName=autoscleasg"
After this I'm running this command:
$ as-describe-auto-scaling-groups autoscleasg --headers
Response:
AUTO-SCALING-GROUP GROUP-NAME LAUNCH-CONFIG AVAILABILITY-ZONES
LOAD-BALANCERS MIN-SIZE MAX-SIZE DESIRED-CAPACITY
AUTO-SCALING-GROUP autoscleasg autoscalelc us-east-1d
autoscalelb 1 5 1 INSTANCE INSTANCE-ID
AVAILABILITY-ZONE STATE STATUS LAUNCH-CONFIG INSTANCE
i-acf48bd5 us-east-1d InService Healthy autoscalelc
And then:
$ elb-describe-instance-health autoscalelb --headers
It shows:
INSTANCE_ID INSTANCE_ID STATE DESCRIPTION
REASON-CODE INSTANCE_ID i-ee364697 InService N/A
N/A INSTANCE_ID i-acf48bd5 OutOfService Instance has failed at
least the UnhealthyThreshold number of health checks consecutively.
Instance
My first problem is:
It automatically creates One extra instance when there is no load on Main instance.
Secondly,
Newly created instance is always OutOfService.
if I change Min Size to 0 using following command:
$ as-update-auto-scaling-group autoscleasg --launch-configuration
autoscalelc --availability-zones us-east-1d --min-size 0 --max-size 5
And trying to put load on instance using xen:
hg clone http://xenbits.xensource.com/xen-unstable.hg
Autoscaling not creating any instance. Even if I'm running above command on upto 5 session, CPU Utilization reaches to 100% and still no instance is being created.
Please help me...
I am not sure what you want to achieve but if you want to use autoscaling capabilities to add more instances based on traffic increase or decrease , you need to use the load balancer parameters (i.e. Latency):
Change yours to:
--namespace='AWS/ELB'
--metric-name Latency
--period 60 (this is super quick)
--threshold 2.0 (this is very low)
To test if it works, I use Apache Bench, I run below command on multiple micro instances
$ ab -n 10000 -c 10 http://<your ELB>.us-east-1.elb.amazonaws.com/index.php

Resources