AWS via Haskell Part 6 (EC2)


Welcome back! It’s been a week or two since my last post on this subject, but here it is! Today, I’m going to talk about how to interact with AWS EC2 using the amazonka and amazonka-ec2 packages. EC2, like Lambda, is a more involved service than some of the others. Furthermore, because the APIs I’ll talk about in this post are used to start and stop real, live, actual virtual machines, some of the APIs take longer to run and consume more AWS resources. Fortunately, the AWS free tier provides sufficient juice to learn how to use the APIs.

There are many moving parts to EC2. Consequently, the EC2 API is big. If you don’t believe me, then feel free to check out the number of types in amazonka-ec2. I have managed to pare down the APIs required to produce a minimally useful demo that demonstrates the following:

The end result is a program that will start an EC2 instance and provide enough information for users to connect to the instance via SSH. The resulting example program will run a single instance of one of the standard Amazon Linux AMIs. It will assume that you have a private-public key pair in the standard locations, i.e. $HOME/.ssh/id_rsa and $HOME/.ssh/, on your system. To grab the key pairs from a different location, please edit the program as necessary. Windows users will need to generate an ssh-keygen-style key pair using PuTTY or similar and edit the program as appropriate. The program will import the public portion of your key pair in order to allow remote access to your newly created EC2 instance.

Part 1: Shared code

Since my previous instalments, the shared code has undergone some more refactoring. I’ve simplified some of the names and also introduced an AWS-specific prelude in the form of AWSViaHaskell.Prelude:

This imports the most commonly used amazonka functions and types in order to slim down the import lists in each Haskell sample. This is the best approach I have devised so far to deal with “Haskell import hell”. Similarly, I have extracted all the amazonka-ec2 imports for this program in the form of EC2Imports.hs:

Part 2: Prerequisites

To run this example code, you’ll need access to EC2. The most obvious way is to use the AWS free tier. This code will assume that you set up an appropriate account. Unfortunately, I have not yet found a local-only test environment for this kind of thing. localstack does not, yet, provide emulation of EC2.

Part 3: The dependencies

We have a pretty standard set of dependencies:

You’ll notice that we do not require a direct dependency on amazonka. This is handled by the re-exports provided by AWSViaHaskell.Prelude.

Part 4: The program

EC2 service wrappers

We generate type-safe wrappers for the EC2 service using wrapAWSService:

This generates the following items:

newtype wrappers for function arguments

We declare a number of newtype wrappers around the Text type. These are intended to prevent the developer from passing one type of Text when a function expects another Text.

Aside: amazonka, like many frameworks, is somewhat “stringly-typed” and this is my attempt to impose some order on some of its functions which take multiple Text arguments. In fact, there is concrete example of a bug resulting from stringly-typeness and code generation where the order of multiple Text arguments have been changed between version 1.4.5 and 1.5.0. This results in unfortunately runtime bugs in the code. newtype wrappers for my sample functions here should minimize the chance of this happening at least at the level of these new functions.

Summary of main function

Here is the code:

Part 5: Notes

So, this should be enough to provision all the resources required to run an instance and to provide an ssh command line to connect to it.

Don’t forget to terminate the EC2 instance after you’re done!

Part 6: The full working demo project

I’ve gathered this all together into this buildable project. As always, I like to build using Stack.

Tags: Haskell, AWS, EC2

All content © 2017 Richard Cook. All rights reserved.