Passing json to aws glue create-job after replacement done using jq - bash

I have the following bash script that I execute in order to create new Glue Job via CLI:
#!/usr/bin/env bash
set -e
NAME=$1
PROFILE=$2
SCRIPT_LOCATION='s3://bucket/scripts/'$1'.py'
echo [*]--- Creating new job on AWS
aws glue create-job --profile $PROFILE --name $NAME --cli-input-json | jq '.Command.ScriptLocation = '\"$SCRIPT_LOCATION\"'' ./resources/config.json
I'm using jq as i need one of the values to be replaced on runtime before i pass the .json as --cli-input-json argument. How can i pass json with replaced value to this command? As of now, it prints out the json content (although with value already replaced).
Running the command above causes the following error:
[*]--- Creating new job on AWS
{
"Description": "Template for Glue Job",
"LogUri": "",
"Role": "arn:aws:iam::11111111111:role/role",
"ExecutionProperty": {
"MaxConcurrentRuns": 1
},
"Command": {
"Name": "glueetl",
"ScriptLocation": "s3://bucket/scripts/script.py",
"PythonVersion": "3"
},
"DefaultArguments": {
"--TempDir": "s3://temp/admin/",
"--job-bookmark-option": "job-bookmark-disable",
"--enable-metrics": "",
"--enable-glue-datacatalog": "",
"--enable-continuous-cloudwatch-log": "",
"--enable-spark-ui": "true",
"--spark-event-logs-path": "s3://assets/sparkHistoryLogs/"
},
"NonOverridableArguments": {
"KeyName": ""
},
"MaxRetries": 0,
"AllocatedCapacity": 0,
"Timeout": 2880,
"MaxCapacity": 0,
"Tags": {
"KeyName": ""
},
"NotificationProperty": {
"NotifyDelayAfter": 60
},
"GlueVersion": "3.0",
"NumberOfWorkers": 2,
"WorkerType": "G.1X"
}
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:
aws help
aws <command> help
aws <command> <subcommand> help
aws.exe: error: argument --cli-input-json: expected one argument

The command line
aws glue create-job --profile $PROFILE --name $NAME --cli-input-json | jq '.Command.ScriptLocation = '\"$SCRIPT_LOCATION\"'' ./resources/config.json
executes the command
aws glue create-job --profile $PROFILE --name $NAME --cli-input-json,
takes its standard output and uses it as input to
jq '.Command.ScriptLocation = '\"$SCRIPT_LOCATION\"'' ./resources/config.json
(which will ignore the input and read from the file given as argument). Please also note that blanks or spaces in $SCRIPT_LOCATION will break your script, because it is not quoted (your quotes are off).
To use the output of one command in the argument list of another command, you must use Command Substitution: outer_command --some-arg "$(inner_command)".
So your command should become:
aws glue create-job --profile $PROFILE --name $NAME --cli-input-json "$(jq '.Command.ScriptLocation = "'"$SCRIPT_LOCATION"'"' ./resources/config.json)"
# or simplified with only double quotes:
aws glue create-job --profile $PROFILE --name $NAME --cli-input-json "$(jq ".Command.ScriptLocation = \"$SCRIPT_LOCATION\"" ./resources/config.json)"
See https://superuser.com/questions/1306071/aws-cli-using-cli-input-json-in-a-pipeline for additional examples.
Although, I have to admit I am not 100% certain that the JSON content can be passed directly on the command line. From looking at the docs and some official examples, it looks like this parameter expects a file name, not a JSON document's content. So it could be possible that your command in fact needs to be:
# if "-" filename is specially handled:
jq ".Command.ScriptLocation = \"$SCRIPT_LOCATION\"" ./resources/config.json | aws glue create-job --profile $PROFILE --name $NAME --cli-input-json -
# "-" filename not recognized:
jq ".Command.ScriptLocation = \"$SCRIPT_LOCATION\"" ./resources/config.json > ./resources/config.replaced.json && aws glue create-job --profile $PROFILE --name $NAME --cli-input-json file://./resources/config.replaced.json
Let us know which one worked.

Related

Issue with single quotes running Azure CLI command

My script snippet is as below:
End goal to accomplish is to create a Azure DevOps variable group and inject key-values from another variable group into it(the newly created Azure DevOps Variable Group)
set -x
echo "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | az devops login --organization https://dev.azure.com/tempcompenergydev
az devops configure --defaults organization=https://dev.azure.com/tempcompenergydev project=Discovery
export target_backend=automation
export target_backend="tempcomp.Sales.Configuration.Spa ${target_backend}"
export new_env="abc"
values=(addressSearchBaseUrl addressSearchSubscriptionKey cacheUrl calendarApiUrl checkoutBffApiUrl cpCode)
az_create_options=""
for ptr in "${values[#]}"
do
result=$(
az pipelines variable-group list --group-name "${target_backend}" | jq '.[0].variables.'${ptr}'.value'
)
printf "%s\t%s\t%d\n" "$ptr" "$result" $?
# add the variable and value to the array
az_create_options="${az_create_options} ${ptr}=${result}"
done
az pipelines variable-group create \
--name "test ${new_env}" \
--variables "${az_create_options}"
However, when the above command executes, I get an unexpected output as below:
+ az pipelines variable-group create --name 'test abc' --variables ' addressSearchBaseUrl="https://qtruat-api.platform.tempcomp.com.au/shared" addressSearchSubscriptionKey="xxxxxxxxxxxxxxxxxxx" cacheUrl="https://tempcompdstqtruat.digital.tempcomp.com.au/app/config" calendarApiUrl="https://qtruat-api.platform.tempcomp.com.au/sales/calendar/v1;rev=deadpool/AvailableDates/EnergyMovers/" checkoutBffApiUrl="https://qtruat-api.platform.tempcomp.com.au/sales/checkout-experience/v1;rev=deadpool/" cpCode="1067076"'
cpCode "1067076" 0
WARNING: Command group 'pipelines variable-group' is in preview and under development. Reference and support levels: https://aka.ms/CLI_refstatus
{
"authorized": false,
"description": null,
"id": 1572,
"name": "test abc",
"providerData": null,
"type": "Vsts",
"variables": {
"addressSearchBaseUrl": {
"isSecret": null,
"value": "\"https://qtruat-api.platform.tempcomp.com.au/shared\" addressSearchSubscriptionKey=\"xxxxxxxxxxxxxxxxxxxxxxxxx\" cacheUrl=\"https://tempcompdstqtruat.digital.tempcomp.com.au/app/config\" calendarApiUrl=\"https://qtruat-api.platform.tempcomp.com.au/sales/calendar/v1;rev=deadpool/AvailableDates/EnergyMovers/\" checkoutBffApiUrl=\"https://qtruat-api.platform.tempcomp.com.au/sales/checkout-experience/v1;rev=deadpool/\" cpCode=\"1067076\""
}
}
}
##[section]Finishing: Bash Script
On a side note, If I run manually, I do get correct response. Example below:
az pipelines variable-group create --name "test abc" --variables addressSearchBaseUr="https://qtruat-api.platform.tempcomp.com.au/shared" addressSearchSubscriptionKey="xxxxxxxxxxxxxxxxxxxxxxxxx" cacheUrl="https://tempcompdstqtruat.digital.tempcomp.com.au/app/config" calendarApiUrl="https://qtruat-api.platform.tempcomp.com.au/sales/calendar/v1;rev=deadpool/AvailableDates/EnergyMovers/" checkoutBffApiUrl="https://qtruat-api.platform.tempcomp.com.au/sales/checkout-experience/v1;rev=deadpool/" cpCode="1067076"
Output:
+ az pipelines variable-group create --name 'test abc' --variables addressSearchBaseUr=https://qtruat-api.platform.tempcomp.com.au/shared addressSearchSubscriptionKey=xxxxxxxxxxxxxxxxxxx cacheUrl=https://tempcompdstqtruat.digital.tempcomp.com.au/app/config 'calendarApiUrl=https://qtruat-api.platform.tempcomp.com.au/sales/calendar/v1;rev=deadpool/AvailableDates/EnergyMovers/' 'checkoutBffApiUrl=https://qtruat-api.platform.tempcomp.com.au/sales/checkout-experience/v1;rev=deadpool/' cpCode=1067076
WARNING: Command group 'pipelines variable-group' is in preview and under development. Reference and support levels: https://aka.ms/CLI_refstatus
{
"authorized": false,
"description": null,
"id": 1573,
"name": "test abc",
"providerData": null,
"type": "Vsts",
"variables": {
"addressSearchBaseUr": {
"isSecret": null,
"value": "https://qtruat-api.platform.tempcomp.com.au/shared"
},
"addressSearchSubscriptionKey": {
"isSecret": null,
"value": "xxxxxxxxxx"
},
"cacheUrl": {
"isSecret": null,
"value": "https://tempcompdstqtruat.digital.tempcomp.com.au/app/config"
},
"calendarApiUrl": {
"isSecret": null,
"value": "https://qtruat-api.platform.tempcomp.com.au/sales/calendar/v1;rev=deadpool/AvailableDates/EnergyMovers/"
},
"checkoutBffApiUrl": {
"isSecret": null,
"value": "https://qtruat-api.platform.tempcomp.com.au/sales/checkout-experience/v1;rev=deadpool/"
},
"cpCode": {
"isSecret": null,
"value": "1067076"
}
}
}
##[section]Finishing: Bash Script
Work Around
I am not a bash pro, but I found a solution that should work for you. I think the core of the issue is that when you print out your array there, it is treating the set of --variables arguments like they're one long string. So you're getting this...
az pipelines variable-group create --name "test ${new_env}" --variables ' addressSearchBaseUrl="val" addressSearchSubscriptionKey="val" ...'
instead of this...
az pipelines variable-group create --name "test ${new_env}" --variables addressSearchBaseUrl="val" addressSearchSubscriptionKey="val" ...
Bad: I think you could get around this using eval instead of printing out arguments but using eval in almost any situation is advised against.
Good: Instead, I thought this problem could be tackled a different way. Instead of doing a batch where all variables are created at once, this script will copy the desired variables to a new group one at a time:
set -x
echo "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | az devops login --organization https://dev.azure.com/tempcompenergydev
az devops configure --defaults organization=https://dev.azure.com/tempcompenergydev project=Discovery
export target_backend=automation
export target_backend="tempcomp.Sales.Configuration.Spa ${target_backend}"
export new_env="abc"
values=(addressSearchBaseUrl addressSearchSubscriptionKey cacheUrl calendarApiUrl checkoutBffApiUrl cpCode)
az_create_options=()
for ptr in "${values[#]}"
do
result=$( az pipelines variable-group list --group-name "${target_backend}" | jq '.[0].variables.'${ptr}'.value' )
echo "$result"
# Result from this az command always wraps the response in double quotes. They should be removed.
stripQuote=$( echo $result | sed 's/^.//;s/.$//' )
printf "%s\t%s\t%d\n" "$ptr" "$stripQuote" $?
# add the variable and value to the array
# When adding this way you ensure each key|value pair gets added as a separate element
az_create_options=("${az_create_options[#]}" "${ptr}|${stripQuote}")
# ^ Note on this: The | is used because there needs to be a way to separate
# the key from the value. What you're really trying to do here is an
# Associative Array, but not all versions of bash support that out of the
# box. This is a bit of a hack to get the same result. CAUTION if your
# variable value contains a | character in it, it will cause problems
# during the copy.
done
# Create the new group, save the group id (would be best to add a check to see if the group by this name exists or not first)
groupId=$( az pipelines variable-group create --name "test ${new_env}" --variables bootstrap="start" | jq '.id' )
for var in "${az_create_options[#]}"
do
# Split the key|value pair at the | character and use the set to create the new variable in the new group
# Some check could also be added here to see if the variable already exists in the new group or not if you want to use this create or update an existing group
arrVar=(${var//|/ })
echo "Parsing variable ${arrVar[0]} with val of ${arrVar[1]}"
az pipelines variable-group variable create \
--id "${groupId}" \
--name "${arrVar[0]}" \
--value "${arrVar[1]}"
done
# This is needed cause the az command is dumb and won't let you create a group in the first place without a --variables argument. So I had it create a dummy var and then delete it
az pipelines variable-group variable delete --id "${groupId}" --name "bootstrap" --yes
Notes to call out
I would suggest using an Associative Array instead of the array I listed here if possible. However, Associative Arrays are only supported in bash v4 or higher. Use bash --version to see if you'd be able to use them. See this guide as an example of some ways you could work with those if you're able.
If using my method, be wary that any | character in a variable value that's being copied will cause the script to fail. You may need to pick a different delimiter to split on.
The output of the az pipelines variable-group list command will give the values of the variables wrapped in ". If you try to turn around and throw the exact result you get from the list at a variable create command, you will get a bunch of variables in your group with values like
{
"variable": {
"isSecret": null,
"value": "\”value\”"
}
}
instead of
{
"variable": {
"isSecret": null,
"value": "value"
}
}
The az command is dumb and won't let you create a new variable group in the first place without a --variables argument. So I had it create a dummy var bootstrap="start" and then delete it.
As I mentioned, there may be a better way to print out the array that I'm missing. Comments on my post are encouraged if you know a better way.
This might help....
There is an open issue on how Azure hosted agents authenticate back to ADO using the az-pipelines command.
Feel this is related but if it's not feel free to respond and I'll remove the answer.

pass arguments of make commands

I have a sequence of make commands to upload zip file to s3 bucket and then update the lambda function reading that s3 file as source code. Once I update the lambda function, I wish to publish it and after publishing it, I want to attach an event to that lambda function using lambda bridge.
I can do most of these commands automatically using make. For example:
clean:
#rm unwanted_build_files.zip
build-lambda-pkg:
mkdir pkg
cd pkg && docker run #something something
cd pkg && zip -9qr build.zip
cp pkg/build.zip .
rm pkg
upload-s3:
aws s3api put-object --bucket my_bucket \
--key build.zip --body build.zip
update-lambda:
aws lambda update-function-code --function-name my_lambda \
--s3-bucket my_bucket \
--s3-key build.zip
publish-lambda:
aws lambda publish-version --function-name my_lambda
## I can get "Arn" value from publish-lambda command. publish-lambda ##returns a json (or I would say it prints a json type structure on cmd) which has one key as "FunctionArn"
attach-event:
aws events put-targets --rule rstats-post-explaination-at-10pm-ist \
--targets "Id"="1","Arn"="arn:aws:lambda:::function/my_lambda/version_number"
## the following combines the above command into single command
build-n-update: clean build-lambda-pkg upload-s3 update-lambda
I am stuck at the last step i.e. to combine and include publish-lambda and attach-event in the build-n-update command. The problem is I am unable to pass argument from previous command to next command. I will try to explain it better:
publish-lambda prints a json style output on terminal:
{
"FunctionName": "my_lambda",
"FunctionArn": "arn:aws:lambda:us-east-2:12345:function:my_lambda:5",
"Runtime": "python3.6",
"Role": "arn:aws:iam::12345:role/my_role",
"Handler": "lambda_function.lambda_handler",
"CodeSize": 62403592,
"Description": "",
"Timeout": 180,
"MemorySize": 512,
"LastModified": "2021-02-28T17:34:04.374+0000",
"CodeSha256": "ErfsYHVMFCQBg4iXx5ev9Z0U=",
"Version": "5",
"Environment": {
"Variables": {
"PATH": "/var/task/bin",
"PYTHONPATH": "/var/task/src:/var/task/lib"
}
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "49b5-acdd-c1032aa16bfb",
"State": "Active",
"LastUpdateStatus": "Successful"
}
I wish to extract function arn from the above output stored in key "FunctionArn" and use it in the next command i.e. attach-event as attach-event has a --targets argument which takes the "Arn" of last published function.
Is it possible to do in single command?
I have tried to experiment a bit as follows:
build-n-update: clean build-lambda-pkg upload-s3 update-lambda
make publish-lambda | xargs jq .FunctionArn -r {}
But this throws an error:
jq: Unknown option --function-name
Please help
Well, running:
make publish-lambda | xargs jq .FunctionArn -r {}
will print the command to be run, then the output of the command (run it yourself from you shell prompt and see). Of course, jq cannot parse the command line make prints.
Anyway, what would be the goal of this? You'd just print the function name to stdout and it wouldn't do you any good.
You basically have two choices: one is to combine the two commands into a single make recipe, so you can capture the information you need in a shell variable:
build-n-update: clean build-lambda-pkg upload-s3 update-lambda
func=$$(aws lambda publish-version --function-name my_lambda \
| jq .FunctionArn -r); \
aws events put-targets --rule rstats-post-explaination-at-10pm-ist \
--targets "Id"="1","Arn"="$$func"
The other alternative is to redirect the output of publish-version to a file, then parse that file in the attach-event target recipe:
publish-lambda:
aws lambda publish-version --function-name my_lambda > publish.json
attach-event:
aws events put-targets --rule rstats-post-explaination-at-10pm-ist \
--targets "Id"="1","Arn"="$$(jq .FunctionArn -r publish.json)"

Trying to verify jq command output equal string or string has more than one occurrence (AWS ELB instances state query)

I'm trying to check that all instances attached to an AWS ELB are in a state of "InService",
For that, I created an AWS CLI command to check the status of the instances.
problem is that the JSON output returns the status of both instances.
So it is not that trivial to examine the output as I wish.
When I run the command:
aws elb describe-instance-health --load-balancer-name ELB-NAME | jq -r '.[] | .[] | .State'
The output is:
InService
InService
The complete JSON is:
{
"InstanceStates": [
{
"InstanceId": "i-0cc1e6d50ccbXXXXX",
"State": "InService",
"ReasonCode": "N/A",
"Description": "N/A"
},
{
"InstanceId": "i-0fc21ddf457eXXXXX",
"State": "InService",
"ReasonCode": "N/A",
"Description": "N/A"
}
]
}
What I've done so far is creating that one liner shell command:
export STR=$'InService\nInService'
if aws elb describe-instance-health --load-balancer-name ELB-NAME | jq -r '.[] | .[] | .State' | grep -q "$STR"; then echo 'yes'; fi
But I get "yes" as long as there is "InService" at the first command output
Is there a way I can get TRUE/YES only if I get twice "InService" as an output?
or any other way to determine that this is indeed what I got in return?
Without seeing an informative sample of the JSON it's not clear what the best solution would be, but the following meets the functional requirements as I understand them, without requiring any further post-processing:
jq -r '
def count(stream): reduce stream as $s (0; .+1);
if count(.[][] | select(.State == "InService")) > 1 then "yes" else empty end
'

How do you add spaces for aws cloudformation deploy --parameter-overrides and/or --tags?

I am trying to get spaces into the tags parameter for the aws cli and it works if I hardcode it but not if I use bash variables. What is going on and how do I fix it?
This works with out spaces:
aws cloudformation deploy \
--template-file /path_to_template/template.json \
--stack-name my-new-stack \
--tags Key1=Value1 Key2=Value2
This works with out spaces but with variables:
tags="Key1=Value1 Key2=Value2"
aws cloudformation deploy \
--template-file /path_to_template/template.json \
--stack-name my-new-stack \
--tags $tags
This works with spaces:
aws cloudformation deploy \
--template-file /path_to_template/template.json \
--stack-name my-new-stack \
--tags 'Key1=Value1' 'Key Two=Value2'
This does not work, spaces and variables:
tags="'Key1=Value1' 'Key Two=Value2'"
aws cloudformation deploy \
--template-file /path_to_template/template.json \
--stack-name my-new-stack \
--tags $tags
Attempting to fix bash expansion, also does not work, spaces and variables:
tags="'Key1=Value1' 'Key Two=Value2'"
aws cloudformation deploy \
--template-file /path_to_template/template.json \
--stack-name my-new-stack \
--tags "$tags"
Attempting to fix bash expansion, also does not work, spaces and variables:
tags="'Key1=Value1' 'Key Two=Value2'"
aws cloudformation deploy \
--template-file /path_to_template/template.json \
--stack-name my-new-stack \
--tags "$(printf '%q' $tags)"
Error:
Invalid parameter: Tags Reason: The given tag(s) contain invalid
characters (Service: AmazonSNS; Status Code: 400; Error Code:
InvalidParameter; Request ID
Would you please try:
tags=('Key1=Value1' 'Key Two=Value2')
aws cloudformation deploy \
--template-file /path_to_template/template.json \
--stack-name my-new-stack \
--tags "${tags[#]}"
Stealing some ideas from https://github.com/aws/aws-cli/issues/3274 I was able to get this working by doing the following
deploy=(aws cloudformation deploy
...
--tags $(cat tags.json | jq '.[] | (.Key + "=" + .Value)'))
eval $(echo ${deploy[#]})
With a tags.json file structure of
[
{
"Key": "Name With Spaces",
"Value": "Value With Spaces"
},
{
"Key": "Foo",
"Value": "Bar"
}
]
Try this :
tags="'Key1=Value1' 'Key Two=Value2'"
aws cloudformation deploy \
--template-file /path_to_template/template.json \
--stack-name my-new-stack \
--tags "$tags"
#  ^ ^
#  double quotes
Learn how to quote properly in shell, it's very important :
"Double quote" every literal that contains spaces/metacharacters and every expansion: "$var", "$(command "$var")", "${array[#]}", "a & b". Use 'single quotes' for code or literal $'s: 'Costs $5 US', ssh host 'echo "$HOSTNAME"'. See
http://mywiki.wooledge.org/Quotes
http://mywiki.wooledge.org/Arguments
http://wiki.bash-hackers.org/syntax/words
As of 2022-02 this was still an issue described
here
here also
and a little here
#esolomon is correct you have to array expansion. His answer which works just fine:
deploy=(aws cloudformation deploy
...
--tags $(cat tags.json | jq '.[] | (.Key + "=" + .Value)'))
eval $(echo ${deploy[#]})
The actual problem is a result of the shell environment (bin/bash here) that is used in combination with how python cli executable's handling of values. Since the aws cloudformation deploy does not standardize the input but expects the shell program to standardize array input this was causing my problem.
So my errors with the --debug flag turned on produced the first response which is the error and the second response is the expected input into aws cloudformation deploy
Error input:
2022-02-10 17:32:28,137 - MainThread - awscli.clidriver - DEBUG - Arguments entered to CLI: ['cloudformation', 'deploy', '--region', 'us-east-1', ..., '--parameter-overrides', 'PARAM1=dev PARAM2=blah', '--tags', "TAG1='Test Project' TAG2='blah'...", '--debug']
Expected input:
2022-02-10 17:39:40,390 - MainThread - awscli.clidriver - DEBUG - Arguments entered to CLI: ['cloudformation', 'deploy', '--region', 'us-east-1', ..., '--parameter-overrides', 'PARAM1=dev', 'PARAM2=blah', '--tags', "TAG2='Test Project'", 'TAG2=blah',..., '--debug']
I was unexpectedly sending in a string instead of array of strings this error resulted in several errors depending on how I sent it:
example TAG: TAG1=Test Project
['Project'] value passed to --tags must be of format Key=Value
the means IFS needs to be set to something other than the default ' \t\n', solution below
An error occurred (ValidationError) when calling the CreateChangeSet operation: 1 validation error detected: Value 'Test Project Tag2=Value2 ...' at 'tags.1.member.value' failed to satisfy constraint: Member must have length less than or equal to 256
the error starts after the first = this error means that I am sending in one long string instead of array items, as seen here when doing [*] instead of [#] aws cloudformation deploy ... --tags "${TAGS[*]}" diff between [*] and [#]
To fix this the most important thing was that IFS needed to be set to anything other than ' \t\n' and secondly I still need to do array expansion with [#] and could not input a string. The --parameter-overrides for me did not have this problem even though similar variable loading BECAUSE it did not have a string.
This was my solution, my params and tags input is all over the place, spaces + sometimes arrays + bad indenting thus the sed:
export IFS=$'\n'
# Build up the parameters and Tags
PARAMS=($(jq '.[] | .ParameterKey + "=" + if .ParameterValue|type=="array" then .ParameterValue | join(",") else .ParameterValue end ' parameters-${environment}.json \
| sed -e 's/"//g' \
| sed -e $'s/\r//g' | tr '\n' ' '))
TAGS=("$(jq -r '.[] | [.Key, .Value] | "\(.[0])=\(.[1])"' tags-common.json)")
TAGS=($TAGS "$(jq -r '.[] | [.Key, .Value] | "\(.[0])=\(.[1])"' tags-${environment}.json)")
aws cloudformation deploy \
--region "${REGION}" \
--no-fail-on-empty-changeset \
--template-file "stack-name-cfn-transform.yaml" \
--stack-name "stack-name-${environment}" \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides "${params[#]}" \
--tags "${TAGS[#]}" \
--profile "${PROFILE}"
parameters file
[
{
"ParameterKey": "Environment",
"ParameterValue": "dev"
}
]
tags file - both common and environment specific tag files have same format
[
{
"Key": "TAG1",
"Value": "Test Project"
},
{
"Key": "Name With Spaces",
"Value": "Value With Spaces"
},
{
"Key": "Foo",
"Value": "Bar"
}
]
I resolved this scenario using options below:
"scripts": { "invoke": "sam ... --parameter-overrides \"$(jq -j 'to_entries[] | \"\\(.key)='\\\\\\\"'\\(.value)'\\\\\\\"''\\ '\"' params.json)\"" }
Or
sam ... --parameter-overrides "$(jq -j 'to_entries[] | "\(.key)='\\\"'\(.value)'\\\"''\ '"' params.json)"

Method of finding instances attached to ELB

I am using the Amazon AWS ELB command line tools. Is there a way of finding out the instances attached to a particular Elastic Load Balancer (ELB)?
2013/12/18: To update this and since the links are dead!
I installed the new AWS cli tools:
$ pip install awscli
Then ran:
$ aws configure
AWS Access Key ID [None]: my-key
AWS Secret Access Key [None]: my-secret
Default region name [None]: us-east-1
Default output format [None]:
This data is saved into ~/.aws/config.
Then I can find instances connected to a loadbalancer like so:
$ aws elb describe-load-balancers --load-balancer-name "my-name"
{
"LoadBalancerDescriptions": [
{
"Subnets": [],
"CanonicalHostedZoneNameID": "ID",
"CanonicalHostedZoneName": "my-name-foo.us-east-1.elb.amazonaws.com",
"ListenerDescriptions": [
{
"Listener": {
"InstancePort": 80,
"LoadBalancerPort": 80,
"Protocol": "HTTP",
"InstanceProtocol": "HTTP"
},
"PolicyNames": []
},
{
"Listener": {
"InstancePort": 80,
"SSLCertificateId": "arn:aws:iam::x:server-certificate/x-ssl-prod",
"LoadBalancerPort": 443,
"Protocol": "HTTPS",
"InstanceProtocol": "HTTP"
},
"PolicyNames": [
"AWSConsole-SSLNegotiationPolicy-api-production"
]
}
],
"HealthCheck": {
"HealthyThreshold": 10,
"Interval": 30,
"Target": "HTTP:80/healthy.php",
"Timeout": 5,
"UnhealthyThreshold": 2
},
"BackendServerDescriptions": [],
"Instances": [
{
"InstanceId": "i-FIRST-INSTANCEID"
},
{
"InstanceId": "i-SECOND-INSTANCEID"
}
],
"DNSName": "my-name-foo.us-east-1.elb.amazonaws.com",
"SecurityGroups": [],
"Policies": {
"LBCookieStickinessPolicies": [],
"AppCookieStickinessPolicies": [],
"OtherPolicies": [
"AWSConsole-SSLNegotiationPolicy-my-name"
]
},
"LoadBalancerName": "my-name",
"CreatedTime": "2013-08-05T16:55:22.630Z",
"AvailabilityZones": [
"us-east-1d"
],
"Scheme": "internet-facing",
"SourceSecurityGroup": {
"OwnerAlias": "amazon-elb",
"GroupName": "amazon-elb-sg"
}
}
]
}
The data is in LoadBalancerDescriptions.Instances.
My loadbalancer is called my-name — this is the name you selected when you created it.
Old answer below!
I'm not familiar with the cli tool, but I used the API.
I'd check these two requests:
DescribeLoadBalancers
DescribeInstanceHealth
The cli tool probably has something to resemble these?
HTH!
Assuming you have aws-cli and jq installed, you can use the following command to get associated ec2 instance ids:
aws elb describe-load-balancers --load-balancer-name my-elb \
| jq -r '.LoadBalancerDescriptions[].Instances[].InstanceId'
This will return the ec2 ids associated with that ELB.
Side note: I recommend you setup aws cli profiles so you don't have to fiddle with environment variables and region params (as much).
Because I love answers that can be used with a minimum of search/replace and copy paste
Prerequisites : aws-cli configured
pip install awscli
aws configure
Configure : your ELB name
$ELB_NAME = "Your-elb-name"
Copy-n-Paste in terminal
for ID in $(aws elb describe-load-balancers --load-balancer-name $ELB_NAME \
--query LoadBalancerDescriptions[].Instances[].InstanceId \
--output=text);
do
aws ec2 describe-instances --instance-ids $ID \
--query Reservations[].Instances[].PublicIpAddress \
--output text
done
Will output a list of Public IPs. You could also just execute the query inside the parenthesis of the for ID in $(...) to just get the instance IDs
Want something different ?
Feel free to have a look at the structure of
aws elb describe-load-balancers --load-balancer-name $ELB_NAME
aws ec2 describe-instances --instance-ids $INSTANCE_ID
and change the query accordingly!
If anyone arrives here from a search as to why the elb-describe-lbs command returns nothing when they have ELBs up and running, what I realised was I needed to add EC2_REGION=eu-west-1 to my environment variables (or use elb-describe-lbs --region command)
If you want to see all your ELB's and the instances attached use JMESPath like this:
aws elb describe-load-balancers --query "LoadBalancerDescriptions[*].{ID:LoadBalancerName,InstanceId:Instances[*].InstanceId}[*]. {ELB:ID,InstanceId:InstanceId[*]}" --output=json
Result
[
{
"ELB": "my_name",
"InstanceId": [
"i-0cc72"
]
},
{
"ELB": "my_name2",
"InstanceId": [
"i-02ff5f",
"i-09e467"
]
}
]
If you know the name of the ELB and want to see what is attached use JMESPath like this:
aws elb describe-load-balancers --load-balancer-name "my_name" --query "LoadBalancerDescriptions[].{ID:LoadBalancerName,InstanceId:Instances[].InstanceId}[].{ELB:ID,InstanceId:InstanceId[]}" --output=json
Result:
[
{
"ELB": "my_name",
"InstanceId": [
"i-02ff5f72",
"i-09e46743"
]
}
]
replace INSTANCEID with actual instance id
aws elb describe-load-balancers --query "LoadBalancerDescriptions[*].{ID:LoadBalancerName,InstanceId:Instances[?InstanceId=='INSTANCEID'].InstanceId}[*].{ID:ID,InstanceId:InstanceId[0]}" --output=text | grep INSTANCEID | awk '{print $1}'
In node.js you can do this by using aws-sdk.
var AWS = require('aws-sdk')
var options = {
accessKeyId: 'accessKeyId',
secretAccessKey: 'secretAccessKey',
region: 'region'
}
var elb = new AWS.ELB(options)
elb.describeLoadBalancers({LoadBalancerNames: ['elbName']}, function(err, data) {
if (err) {
console.log('err: ', err)
}
else {
console.log('data: ', data.LoadBalancerDescriptions)
}
})
data.LoadBalancerDescriptions is an array and each element in the array is an object with the property Instances that has the instance id.
You can loop trough all you load balancer instance ids as follows:
while read -r lb ; do echo -e "\n\n start lb: $lb " ; \
echo run cmd on lb: $lb ; echo " stop lb: $lb" ; \
done < <(aws elb describe-load-balancers --query \
'LoadBalancerDescriptions[].Instances[].InstanceId' \
--profile dev|perl -nle 's/\s+/\n/g;print')
You can loop trough your load balancers names as follows :
# how-to loop trough all your load balancer names
while read -r lb ; do \
echo -e "\n\n start lb: $lb " ; \
echo run cmd on lb: $lb ; \
echo " stop lb: $lb" ; \
done < <(aws elb describe-load-balancers --query \
'LoadBalancerDescriptions[].LoadBalancerName' \
--profile rnd|perl -nle 's/\s+/\n/g;print')
Provided that you have configured your aws cli :
src: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html
cat << "EOF" > ~/.aws/config
[profile dev]
output = text
region = us-east-1
[profile dev]
output = text
region = us-east-1
[default]
output = text
region = Global
EOF
And configured your security credentials:
# in aws admin console :
# Services => iam => users => <<your_username>> => Security Credentials => Access Keys
# configure the aws cli
cat << "EOF" > ~/.aws/credentials
[dev]
aws_access_key_id = <<your_aws_access_key_id_in_the_dev_environment>>
aws_secret_access_key = <<your_aws_secret_access_key_in_dev_env>>
[dev]
aws_access_key_id = <<your_aws_access_key_id_in_the_dev_environment>>
aws_secret_access_key = <<your_aws_secret_access_key_in_dev_env>>
[default]
aws_access_key_id = <<your_aws_access_key_id_in_the_dev_environment>>
aws_secret_access_key = <<your_aws_secret_access_key_in_dev_env>>
EOF
aws elb describe-load-balancers --load-balancer-name "LB_NAME" | grep "InstanceId" | awk '{print $2}' | sed 's/\"//g'
First do elb-describe-lbs to get a list of your load balancers and their names.
Then do elb-describe-instance-health <LB_NAME> to get a list of instances behind that load balancer. LB_NAME is the value of the 2nd column in the output of elb-describe-lbs.
You can use AWS command line tools with some bash piping:
elb-describe-instance-health loadbalancer_name --region eu-west-1 | awk '{ print $2 }' | xargs ec2-describe-instances --region eu-west-1 | grep ^INSTANCE | awk '{ print $4 }'
This will give you the public DNS name for every instance attached to the ELB, you can change the awk columns respectively to get other details.

Resources