Bash quotes being tricky - bash

I have this command
aws --profile whatever ec2 describe-instances
The short story is that I no choice but to have this '--profile ' section in this command, but I need to be able to use this feature/bug using quotes:
aws --profile "" ec2 describe-instances
BUT the quotes must also be an environment variable, like so
aws --profile $AWS_PROFILE ec2 describe-instances
But I need that variable to resolve the exact same way as the line above it.
I have tried
AWS_PROFILE='""'
and
AWS_PROFILE=
and
AWS_PROFILE=""
And it never resolves the same way. Is there anything I can do?

Any quotes you put inside the variable are literal. However, in your desired command aws --profile "" ec2 describe-instances, the quotes are syntactic: That is, they're shell syntax describing how the shell is going to create a literal array of C strings to pass to the execv syscall (and how one of those strings needs to be empty); the quotation marks aren't actually passed to the aws command themselves.
So:
AWS_PROFILE=
aws --profile "$AWS_PROFILE" ec2 describe-instances
...will behave identically to
aws --profile "" ec2 describe-instances

Related

SSH ProxyCommand using aws SSM session manager and bash script with select command

In my company when we SSH to our AWS EC2 instances we are required to use the aws CLI session-manager plugin for auth. Using this SSH config snippet works:
Host my-aws-host
ProxyCommand bash -c "aws ssm start-session --target 'i-0abc123def456hij' \
--document-name AWS-StartSSHSession --parameters 'portNumber=22' \
--region us-west-1 --profile MAIN"
However, when the EC2 instance is relaunched, which happens semi-regularly, the 'target' instance ID changes. When this happens, all users need to update their SSH config with the new ID. We don't have any sort of DNS that resolves these instances to a static hostname unfortunately, and so would need to somehow publish the new instance ID to all interested users.
So instead I wrote a bash script (ssh_proxy_command.sh) that first queries our AWS account to grab the current instance ID based on a known tag value, and use that for the target - here's a cut-down version:
#!/bin/bash
INSTANCE_ID=$(aws ec2 describe-instances --region us-west-1 \
--filters Name=tag:Name,Values=my-server-nametag* \
--query "Reservations[*].Instances[*].{Instance:InstanceId}" --output text)
aws ssm start-session --target $INSTANCE_ID --document-name AWS-StartSSHSession --parameters 'portNumber=22' --region us-west-1 --profile MAIN
Now the SSH config looks like
Host my-aws-host
ProxyCommand bash -c "/path/to/my/ssh_proxy_command.sh %h"
This has been working fine. However, we have just started running multiple instances built from the same base image (AMI), and which use the same tags, etc. so the given describe-instances query now returns multiple instance IDs. So I tried wrapping the output returned by the query in a bash select loop, thinking I could offer the user a list of instance IDs and let them choose the one they want. This works when running the script directly, but not when it's used as the ProxyCommand. In the latter case when it reaches the select statement it prints out the options as expected, but doesn't wait for the user input - it just continues straight to the end of the script with an empty $INSTANCE_ID variable, which makes the aws ssm command fail.
I'm guessing this is a side-effect of the way SSH runs its ProxyCommands — from the ssh_config man page:
[the proxy command] is executed using the user's shell ‘exec’ directive [...]
I'm hoping I can find a way around this problem while still using SSH config and ProxyCommand, rather than resorting to a complete stand-alone wrapper around the ssh executable and requiring everyone use that. Any suggestions gratefully accepted...
host my-aws-host
ProxyCommand aws ssm start-session --target $(aws ec2 describe-instances --filter "Name=tag:Name,Values=%h" --query "Reservations[].Instances[?State.Name == 'running'].InstanceId[]" --output text) --document-name AWS-StartSSHSession --parameters portNumber=%p
The above will dynamically filter for your target-id based on host name (%h) provided so you can login using ssh my-aws-host. I personally have a prefix for all my machines in AWS so I ssh config is as follows:
host your-custom-prefix-*
ProxyCommand aws ssm start-session --target $(aws ec2 describe-instances --filter "Name=tag:Name,Values=%h" --query "Reservations[].Instances[?State.Name == 'running'].InstanceId[]" --output text) --document-name AWS-StartSSHSession --parameters portNumber=%p
This works only when the name of your machines in AWS match the host name provided.

Assign output from AWS CLI command to 2nd AWS CLI command

I am writing automation task for creating AWS AMI image, the goal is get output from aws import-image (.ova to ami convert) and add the name in 2nd command:
importaskid=$(aws ec2 import-image --disk-containers Format=ova,UserBucket="{S3Bucket=acp17,S3Key=XXXXX.ova}" | jq -r '.ImportTaskId')
aws ec2 create-tags --resources echo $importaskid --tags 'Key=Name, Value=acp_ami_test'
I am able to $importaskid and see needed output but when use aws ec2 create-tags the AMI image created without name and the output from 2nd command is empty.
Appreciate your assistance.
This should work for you:
# set bash variable "importaskid":
importaskid=$(aws ec2 import-image --disk-containers Format=ova,UserBucket="{S3Bucket=acp17,S3Key=XXXXX.ova}" | jq -r '.ImportTaskId')
# Verify that importaskid is set correctly
echo $importaskid
# Now use it:
aws ec2 create-tags --resources "$importaskid" --tags 'Key=Name, Value=acp_ami_test'
The "$()" syntax for assigning the output of a command to a variable is discussed here: https://www.cyberciti.biz/faq/unix-linux-bsd-appleosx-bash-assign-variable-command-output/
The double quotes in "$importaskid" would be necessary if
the value of "$importaskid" happens to have spaces in it.
'Hope that helps!
thanks for reply, so when i run the command without echo $ImportTaskId
see below
aws ec2 create-tags --resource import-ami-XXXXXXXXXXXXX --tags Key=Name,Value='name_ami_test'
i got empty response and the name is not assigned in aws console so i will speak to AWS support/check syntax /check after the assign name to AMI ID and not to import-ami-XXXXXXXXXXXXXXXX

SSH into EC2 by using a zsh script to fetch EC2 IP Address

I'd like to create an alias in my zprofile that will fetch the EC2 IP address based on name tags, and then SSH into that machine. I'm using a mac.
This zsh script successfully retrieves the IP address of the EC2 machine I want to ssh into:
aws ec2 describe-instances \
--filters "Name=tag:Name, Values=my-name" \
--query "Reservations[*].Instances[*].[PrivateIpAddress]" \
--output text
The output might look like: some.ip.address
This zsh script lets me SSH into my EC2 machine:
alias ec2="ssh person#some.ip.address.us-east-1.place"
How do I substitute the IP address of the second script with the output of my first script?
Thanks!
Probably the easiest way to do this would be to set up a second alias, so you could have something like:
alias get_ec2_ip='aws ec2 describe-instances --filters "Name=tag:Name, Values=my-name" --query "Reservations[*].Instances[*].[PrivateIpAddress]" --output text'
And then you could use zsh's command substitution, like so:
alias ec2='ssh person#`get_ec2_ip`'
Note that it's super important to use single quotes there, not double quotes! Otherwise, you'll run get_ec2_ip when setting the alias, which is not what you want—you want it to save the literal string ssh person#`get_ec2_ip` as the alias, so that get_ec2_ip is run every time you run ec2.

shell script to start ec2 instances and ssh and introducing delay in second command

I have written a small shell script to automate the starting and loggin in to my aws instances via terminal.
#!/bin/bash
aws ec2 start-instances --instance-ids i-070107834ab273992
public_ip=aws ec2 describe-instances --instance-ids i-070107834ab273992 \
--query 'Reservations[*].Instances[*].PublicDnsName' --output text
AWS_KEY="/home/debian/cs605 data management/assignment6/mumbai instance keys"
ssh -v -i "$AWS_KEY"/mumbai-instance-1.pem\
ec2-user#$public_ip
~
~
The problem is public_ip variable I want it to be used in line ssh
1) how do I get value of a variable to use in a command.
2) The instance takes some time to boot when it is switched on from power off to power on so how do I keep checking that instances has been powered on after aws start instance command in the script or retrieve the public ip once it has started fully and then ssh into it.
I am not good at python know just basics so is there a pythonic way of doing it.If there is an example script some where that would be better for me to have a look at it.
You do not set the variable public_ip in your script. It would not surprise me if the script complained about "ec2: command not found".
To set the variable:
public_ip=$(aws ec2 describe-instances --instance-ids i-070107834ab273992 --query 'Reservations[*].Instances[*].PublicDnsName' --output text)
(disclaimer: I have not used aws so I assume that the command is correct).
The information on whether an instance is running should be available with
aws ec2 describe-instance-status
You may want to apply some filters and/or grep for a specific result. You could try polling with a while loop:
while ! aws ec2 describe-instance-statusv --instance-ids i-070107834ab273992 | grep 'something that characterizes running' ; do
sleep 5
done

Windows git bash tries to resolve string as file when calling AWS CLI

I have a string parameter for AWS SSM CLI command that looks like a path to a file due to starting with a /. /path/to/my/param.
When I run the command on git bash it tries to find the file instead, no matter how I try to escape it:
aws ssm get-parameter --name "/path/to/my/param"
aws ssm get-parameter --name '/path/to/my/param'
aws ssm get-parameter --name '\/path\/to\/my\/param'
An error occurred (ValidationException) when calling the GetParameter operation: Invalid label format /Program Files/Git/path/to/my/param. A label name can't be prefixed with numbers, "ssm", or "aws" (case-insensitive). You can specify letters, numbers, and the following symbols: period (.), dash (-), or underscore (_).
Even tried back-ticks, then I get a bash error
aws ssm get-parameter --name `/path/to/my/param`
Error: bash: /path/to/my/param: No such file or directory
If I do echo /asd/asd it actually outputs /asd/asd, so it also might be how the aws cli is treating the input.
Any ideas how to escape it?
It is possible to turn off a path conversion in MSYS2 for selected paths (see Msys2 Porting, Filesystem namespaces section).
You can also disable it temporarily in the following way:
MSYS2_ARG_CONV_EXCL="/aws" aws ssm get-parameter --name '/aws/path/to/my/param'
Edit:
After stumbling upon this issue once more, but this time for: aws logs describe-log-groups --log-group-name-prefix ..., I decided to try 'n' find a more sustainable solution.
I found an SO post that provided an explanation for the underlying problem as well as what I believe to be a better fix, i.e.:
MSYS_NO_PATHCONV=1 aws ...
This goes for any aws cli command suffering from this forward slash problem when being called from Git Bash on Windows.
Kudos of course go to: https://stackoverflow.com/a/56034540
Seems like my first answer happens to work for aws ssm get-parameter because that part of the cli is a bit more forgiving in the sense that it returns the desired value, even when providing the leading space. However, upon further scrutiny of the returned value I noticed in retrospect that the name of the returned parameter also contains the leading space, making the solution of disabling path conversion - by far - the better option.
Original answer:
In case anyone bumps into this issue in the future:
I randomly managed to solve it while grasping at straws.
The trick for me was to put a space before the first forward slash after surrounding the parameter name with double quotes, i.e.:
aws ssm get-parameter --name " /path/to/my/param"
(The GitHub issue that led me to try it out: https://github.com/bmatzelle/gow/issues/196)

Resources