In the 21st century, we can do significantly better than this crap AWS CLI. We just need to think from the users’ perspective for a change and not just dump on users whatever we were using internally or seen in a nightmare and then implemented.

I’m working on a solution which is described towards the end of the post. It’s not ready yet but the (working btw) examples will convince the reader that “significantly better” is very possible… I hope.
Background
I’m building AWS library and a command line tool. Since I’m doing it for a shell-like language (NGS), the AWS library that I’m building uses AWS CLI. Frustration and anger are prominent feelings when working with AWS CLI. I will just list here some of the reasons behind that feeling.

Overall impression
Separate teams worked on different services and were not talking to each other. Then the something like this happened: “OK, we have these APIs here, let’s expose them. User experience? No, we don’t have time for that / we don’t care”.
Tags
Representing a map (key-value pairs) as an array ( "Tags": [ { "Key": "some tag name", "Value": "some tag value" }, ... ]
), as AWS CLI does is insane… but only when you think about the user (developer in this case) experience.

Reservations
When listing EC2 instances using aws ec2 describe-instances
, the data is organized as list of Reservations
and each Reservation
has list of instances. I’m using AWS for quite a few years and I never needed to do anything with Reservations
but I did spent some time unwrapping again and again the interesting data (list of instances) from the unwanted layer of madness. It really feels like “OK, Internally we have Reservations and that’s how our API works, let’s just expose it as it is”.

Results data structure
AWS CLI usually (of course not always!) returns a map/hash at the top level of the result. The most interesting data would be called for example LoadBalancerDescriptions
, or Vpcs
, or Subnets
, or … ensuring difficulty making generic tooling around AWS CLI. Thanks! Do you even use your own AWS CLI, Amazon?
Inconsistencies

Security Groups
These are of course special. Think of aws ec2 create-security-group ...
--group-name
and--description
are mandatory command line arguments. For now, I haven’t seen any other resource creation that requires both name and description.- The command returns something like
{ "GroupId": "sg-903004f8" }
which is unlike many other commands which return a hash with the properties of the newly created resource, not just the ID.
Elastic Load Balancer
Oh, I like this one. It’s unlike any other.
- The unique key by which you find a load balance is name, unlike other resources which use ids.
- Tags on load balancers work differently. When you list load balancers, you don’t get the tags with the list like you have when listing instances, subnets, vpcs, etc. You need to issue additional command:
aws elb describe-tags --load-balancer-names ...
aws elb describe-load-balancers
– items in the returned list have fieldVPCId
while other places usually name itVpcId
.
Target groups
aws elbv2 deregister-targets --target-group-arn ...
Yes, this particular command and it’s “target group” friends use ARN, not a name (as ELB) and not an ID (like most EC2 resources).
DHCP options (sets)
(Haven’t used but considered so looked at documentation and here is what I found)
Example: aws ec2 create-dhcp-options --dhcp-configuration "Key=domain-name-servers,Values=10.2.5.1,10.2.5.2"
Yes, the create syntax is unlike other commands and looks like filter syntax. Instead of say --name-servers ns1 ns2 ...
switch you have "Key=domain-name-servers,Values="
WTF?
Route 53
aws route53 list-hosted-zones
does not return the tags. Here is an output of the command:
{ "HostedZones": [ { "Id": "/hostedzone/Z2V8OM9UJRMOVJ", "Name": "test1.com.", "CallerReference": "test1.com", "Config": { "PrivateZone": false }, "ResourceRecordSetCount": 2 }, { "Id": "/hostedzone/Z3BM21F7GYXS7Y", "Name": "test2.com.", "CallerReference": "test2.com", "Config": { "PrivateZone": false }, "ResourceRecordSetCount": 2 } ] }
Wanna get the tags? F*ck you! Here is the command: aws route53 list-tags-for-resources --resource-type hostedzone --resource-ids Z2V8OM9UJRMOVJ Z3BM21F7GYXS7Y
. Get it? You are supposed to get the Id field from the list generated by list-hosted-zones
, split it by slash and then use the last part as resource ids. Tagging zones also uses the rightmost part of id: aws route53 change-tags-for-resource --resource-type hostedzone --resource-id Z2V8OM9UJRMOVJ --add-tags Key=t1,Value=v11
… but that apparently was not enough differentiation 🙂 Check this out: aws elb add-tags --load-balancer-names NAME --tags TAGS
vs aws route53 change-tags-for-resource --resource-type hostedzone --resource-id ID --add-tags TAGS
and aws elb remove-tags --load-balancer-names NAME --tags TAGS
vs aws route53 change-tags-for-resource --resource-type hostedzone --resource-id ID --remove-tag-keys TAGS
. Trick question: on how many dimensions this is different? So wrong on so many levels 🙂
Here is another one: when you fetch records from a zone you use the full id, /hostedzone/Z2V8OM9UJRMOVJ
, not Z2V8OM9UJRMOVJ
: aws route53 list-resource-record-sets --hosted-zone-id /hostedzone/Z2V8OM9UJRMOVJ
(many more to come)
Expected comments and answers

Just use Terraform
I might some day. For the cases I have in mind, for now I prefer a tool that:
- Is conceptually simpler (improved scripting, not something completely different)
- Doesn’t require a state file (I don’t want to explain to a client with two servers where the state file is and what it does)
- Can be easily used for ad-hoc listing and manipulation of the resources, even when these resources were not created/managed by the tool.
- Can stop and start instances (impossible with Terraform last time I checked a few months ago)
Just use CloudFormation
I don’t find it convenient. Also, see Terraform points above.
By the way, the phrases of the form “Just use X” are not appreciated.
You just shit on what other people do to push your tools
Yes, I reserve the right to shit on things. Here I mean specifically AWS CLI. Freedom of speech, you know… especially when:
- The product (AWS API) is technically subpar
- The creator of the product does not lack resources
- The product is used widely, wasting huge amounts of time of the users
If I’m investing my time to write my own tool purely out of frustration, I will definitely tell why I think the product is crap.
Is that just a rant or you have alternatives?
I’m working on it. The command line tool is called na
(Ngs Aws). It’s a thin wrapper around declarative primitives library for AWS.

The command line tool that I’m working on is not anywhere ready but here is a gist of what you can do with it:
# list vpcs na vpc # Find the default VPC na vpc IsDefault:true # List security groups of all vpcs which have tag "Name" "v1". na vpc Name=v1 sg # Idempotent operations, in this case deletion. # No error when this group does not exist. # Delete "test-group-name" in the given vpc(s). na vpc Name=v1 sg GroupName:test-group-name DEL # List all volumes which are attached to stopped instances na i State:stopped vol # Delete all volumes that are not attached to instances na vol State:available DEL # Stop all instances which are tagged as # "role" "ocsp-proxy" and "env" "dev". na i role=ocsp-proxy env=dev SET State:stopped
- Na never dumps huge amounts of data to your terminal. As a human, you will probably not be able to process it so when number of items (rows in a table actually) is above certain configurable threshold, you will see something like this:
$ na vol === Digest of 78 rows === ...
It will show how many unique values there are in each column, min and max value for each column. Thinking about displaying top 5 (or so) top and bottom values for each column.
- Na has concept of “related” resources so when you write something like
na i State:stopped vol
, it knows that the volumes you want to show are related to the instances that you mentioned. In this particular case, it means volumes attached to the instances. - Note the consistency of what you see in output and arguments to CLI. If something is called “State” in the data structure returned by AWS CLI, it will be called “State” in the query, not “state” (“–state”).
I will be updating this post as things come along.
Even within teams they’re not consistent. If you’re unfortunate enough to be saddled with Workspaces, then you’ll love the syntax differences between some of the commands.
aws workspaces start-workspaces –start-workspace-requests WorkspaceId=WorkspaceId1
aws workspaces reboot-workspaces –reboot-workspace-requests WorkspaceId1 WorkspaceId2
There are other nooks and crannies where you can see how individual preference even within a group shines through.
LikeLike
I guess “because f*ck you” 🙂
LikeLike
You should look at the kubernetes cli. It too has to interact with a api endpoint (rest). The interesting thing about the kubectl command line is it gives you a nice human friendly list, but you can tell it to list it in json, yaml, etc, if you are a computer!
LikeLike
… till you try to get JSON output of “kubectl describe pods” … 😦
LikeLike