When working with AWS, I would not advice to run all operations through the AWS Console. And it’s not always possible or easy to do it only with pipelines and Infrastructure as Code. Sometimes, we just need to perform the same operations on many instances or we already have an automation based on a script. In those situations, AWS CLI (the command line) can be very handy. I had the chance recently to attend a workshop fully dedicated to AWS CLI, in this post I’ll give some tricks I use.

Aliases

There are some commands you may run very often. When working with multiple AWS accounts and/or temporary credentials, and you should, one of my favorite command is:

aws sts get-caller-identity

The output looks like below:

{
    "UserId": "AROA*****************:<User Id>",
    "Account": "<AWS Account Id>",
    "Arn": "arn:aws:sts::<AWS Account Id>:assumed-role/<IAM Role Name>/<User Id>"
}

It’s an easy way to check if the credentials are still valid and more important on which account the next commands will run. The command is a bit long, let’s create an alias shorter and easier to remember. The configuration is very easy, the aliases are declared in a file without your user profile.

mkdir -p ~/.aws/cli
echo '[toplevel]' > ~/.aws/cli/alias
echo 'whoami = sts get-caller-identity' >> ~/.aws/cli/alias

Then just run the following to get the same output as above:

aws whoami

There are a lot of examples of aliases in the project awscli-aliases at Github.

Filter with –query

I know there are a lots of people already using “jq” for processing JSON outputs. But AWS CLI provides a client-side filtering feature using the –query option. It’s uses JMESPath syntax which is similar but different from jq. This one is really my favorite and I use it quite a lot, the big advantage is that it works anywhere AWS CLI without the external dependency on jq and on Windows as well. Let’s start with an example, where we just want to reduce the amount of information displayed about instances.

aws ec2 describe-instances --region eu-central-1 --query 'Reservations[*].Instances[].[Tags[?Key==`Name`].Value|[0],InstanceId,State.Name,PrivateIpAddress,Platform]' --output table
------------------------------------------------------------------------------------------------
|                                       DescribeInstances                                      |
+-----------------------------------+----------------------+----------+---------------+--------+
|  bastion                          |  i-046bfzzzzzzzzzzzz |  running |  169.254.0.10 |  None  |
|  dbiinsite-management             |  i-0b544yyyyyyyyyyyy |  running |  169.254.0.11 |  None  |
|  devops-docker-1                  |  i-0fe6ammmmmmmmmmmm |  stopped |  169.254.0.12 |  None  |
|  devops-docker-2                  |  i-03bannnnnnnnnnnnn |  stopped |  169.254.0.13 |  None  |
+-----------------------------------+----------------------+----------+---------------+--------+

In the previous example, we browse 2 lists (Reservations and Instances), then display the values for some identifiers (i.e. InstanceId). But you may have noticed a more advanced syntax to display the name. Indeed, the name is part of another list (Tags) where we want only one value. So, we introduce a filter “?Key==`Name`” to get only the entry we are interested in. Finally to avoid having lists of lists, we flatten to get only the value in our table with “|[0]”.

Now, you can imagine more complex combinations, combining server-side filtering with the option “–filters” and a client-side filtering to get only the resource id or an additional filter that may not be available on server-side. For example, displaying only the running Linux instances (.i.e. Platform is empty for Linux instances but contains windows for Windows instances).

aws ec2 describe-instances --filters '[{"Name":"instance-state-name","Values":["running"]}]' --query 'Reservations[*].Instances[?Platform!=`windows`].[InstanceId]' --output text
i-046bfzzzzzzzzzzzz
i-0b544yyyyyyyyyyyy

As the filter can very quickly become big and difficult to read, the aliases can be very useful to store the complex filters and run simple commands.