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:

Importing key pairs for credentials

Creating security groups

Running a new EC2 instance

Querying the EC2 instance’s status

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/id_rsa.pub , 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:

amazonka-ec2

aws-via-haskell

base

bytestring

directory

filepath

lens

text

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:

ec2Service

EC2Service

EC2Session

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

Loads the public key portion from $HOME/.ssh/id_rsa.pub

Connects to EC2 in the “Ohio” region: you’ll need to tweak this to match your own region

Imports the public key portion into EC2 if it’s not already there present: the function evaluated here demonstrates error handling using a custom matcher _DuplicateKeyPair : many of the other amazonka subpackages provide custom error matchers which is not the case for amazonka-ec2, presumably because the number of errors that are routinely encountered in EC2 land is large: fortunately, it’s straightforward to implement custom matchers for the kinds of errors we’ll encounter in our simple example

: many of the other amazonka subpackages provide custom error matchers which is not the case for amazonka-ec2, presumably because the number of errors that are routinely encountered in EC2 land is large: fortunately, it’s straightforward to implement custom matchers for the kinds of errors we’ll encounter in our simple example Lists all public keys registered in EC2

Creates a security group if not already there: the newly created security group allows inbound traffic on port 22 from any IP address, thus enabling remote access via SSH; it also defines another custom error matcher _DuplicateGroup

Runs a single instance using the ami-caaf84af AMI and the name of the key and ID of the security group created previously

Demonstrates how to wait until the instance is running

Demonstrates how to wait until the instance’s status is OK

Gets the new instance’s public DNS name

Writes the SSH command line used to connect to the new instance

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.

Part 7: Related posts