Over the last couple of days, I have been working to setup my iPad Pro 10.5 so that I can use it as a primary system when I am on call but not near my desk. However due to the limitations of the iOS platform (at least compared to the dual monitor setup I normaly use), I have found that switching back and forth between a terminal window and a browser for the AWS Console was not as smooth as I would like it. I decided it was time to invest in mastering the CLI.

As I have started to invest in learning the patterns for getting looking up systems and gathering information, one of the first things that I picked up was that there are a number of commands that I will be running on a normal basis that were fairly long commands. For example, if I wanted to get a list of all the instances running in an account, I would need to type the following command:

aws ec2 describe-instances --filter "Name=instance-state-name,Values=running" --query "Reservations[*].Instances[*].[Tags[?Key=='Name'].Value|[0],PrivateIpAddress, KeyName]" --output text

As an old sysadmin, I don’t want to type that much. so I decided to make an alias in my .bash_aliases file (which is sourced from my .bash_profile):

alias ec2er='aws ec2 describe-instances --filter "Name=instance-state-name,Values=running" --query "Reservations[*].Instances[*].[Tags[?Key=='\''Name'\''].Value|[0],PrivateIpAddress, KeyName]" --output text'

Which works out pretty well to give me the whole list of hosts I have running in an account. But what if I want to look for a specific host? I can do that from the command line with something like this:

aws ec2 describe-instances --filter "Name=tag:Name,Values=salt-master" --query "Reservations[*].Instances[*].[Tags[?Key=='Name'].Value|[0],PrivateIpAddress, KeyName]" --output text

But again, that’s a lot of typing. So how can you create a shortened command that you can pass arguments to? Bash Functions.

Bash functions can be sourced by the .bash_profile script similar to the way aliases can be. I use a seperate .bash_functions file that I source directly to help keep my environment clean. In this file I can create all sorts of mini commands and they can even be kind of complicated. In the case of the example above, I can do something like:

ec2info() {
aws ec2 describe-instances --filter "Name=tag:Name,Values=$1" --query "Reservations[*].Instances[*].[Tags[?Key=='Name'].Value|[0],PrivateIpAddress, KeyName]" --output text
}

This would allow me to run the command ec2info salt-master and return the Name Tag, Private IP Address, and Key used for the instance. If you really want to setup it up to the next level, you can add the ssh command to that function in something like this:

ec2ssh() {
read IP KEY <<< $(aws ec2 describe-instances --filters "Name=tag:Name,Values=$1" --query "Reservations[*].Instances[*].[PrivateIpAddress, KeyName]" --output text)

ssh -i ~/.ssh/$KEY $2@$IP
}

Then you can just type ec2ssh salt-master ec2-user and it will look up the IP Address and key then connect to the instance. Of course, this only works if your name tags are unique. If they are not, you could switch the lookup to ImageID for something more unique or do some kind of for loop in the function to connect to them all sequentially.