The Not-so-short Introduction to EC2 Instances in AWS SDK for .NET

Taras Kushnir
Software engineer at ELEKS

Today is the era of cloud computing: never-ending computing resources available on demand. Amazon is one of the biggest players on the market of cloud computing. It provides different cloud services: Elastic Cloud, Elastic Block Store, Simple Email Service, Cloud Drive and others. Amazon Elastic Compute Cloud (EC2) allows to launch virtual servers in the Amazon Web Services (AWS) cloud. It provides various types of virtual computing environments, storage and virtual isolated networks. In this post we’ll learn how to work with EC2 using AWS SDK for .NET. You can download SDK from the official website. We’ll assume you’ve already created some sample project in your favorite C#/.NET development environment and referenced AWSSDK.dll in that project. We’ll mostly use Amazon.EC2, Amazon.EC2.Model and Amazon.Runtime namespaces. So let’s take a look on some common operations with AWS cloud: launching, tagging and stopping EC2 instances, describing environment and others.

Instantiation

Every operation with AWS is executed using AmazonEC2 interface. We can create Amazon EC2 client just using a simple constructor:

AmazonEC2 amazonClient = new AmazonEC2Client(accessKey, secretKey);

where accessKey and secretKey are credentials, which we can get in our Amazon account after registration.

Another simple constructor uses accessKey and secretKey from application configuration file and we have to pass just an element of RegionEndpoint enumeration:

public AmazonEC2Client(RegionEndpoint region);

In case we need something more sophisticated, there are tons of constructor overloads. One of the most useful is a constructor with AWSCredentials and AmazonEC2Config parameters:

public AmazonEC2Client(AWSCredentials credentials, AmazonEC2Config config)

For example, we can pass BasicAWSCredentials instance with accessKey and secretKey for the first parameter and setup proxy settings with AmazonEC2Config class and ProxyPort/ProxyHost properties for the second parameter.

Validating credentials

We can create any simple request to AWS in order to validate credentials. Let’s choose a request that won’t lead to big data transfer between our application and AWS services, because credentials verification can be used quite frequently. For example we will use DescribeAvailabilityZones request but we can also use DescribeRegions request or any other. If request call throws an exception (AmazonEC2Exception), we can check it’s type from string property ErrorCode. If it’s equal to "AuthFailure", than our credentials are invalid. Source code can look like this:

try
{
    var ec2Client = new AmazonEC2Client(accessKey, secretKey);
    var response = ec2Client.DescribeAvailabilityZones();
    return true;
}
catch (AmazonEC2Exception e)
{
    return false;
}

Describing environment

If our application lets user to configure any API credentials or we’re just showing some useful information about it’s service, we will have to describe our Amazon environment: Key Pair Names, Security Groups, Placement Groups, Availability Zones, VPC subnets etc.

General requesting template looks like this:

var ec2Client = new AmazonEC2Client(accessKey, secretKey);
var describeGroupsRequest = new DescribePlacementGroupsRequest();

try
{
    var response = ec2Client.DescribePlacementGroups(describeGroupsRequest);
    var placementGroupsResult = response.DescribePlacementGroupsResult;
    var placementGroupsInfo = placementGroupsResult.PlacementGroupInfo;
    placementGroupNames = placementGroups.Select(group => group.GroupName);
}
catch (Exception e)
{
    placementGroupNames = Enumerable.Empty<string>();
}

If we’re going to describe many environment items and copy-paste code above into each method, it will do some odd job: each time creating and destroying instance of AmazonEC2Client class. That’s why we can create it’s instance only one time and then execute all needed requests, accumulate results in some storage and then return it.

Describing instances

DescribeInstances request is used to track all instances that we own. We can request useful information about all or some certain instances. In order to choose these certain instances we can fill filter parameter of DescribeInstancesRequest to match specified instance IDs, key pair names, availability zone, instance type, current instance state and many others.

var ec2Client = new AmazonEC2Client(accessKey, secretKey);
var describeRequest = new DescribeInstancesRequest();

describeRequest.Filter.Add(new Filter(){Name="instance-state-name",
Value="running"});

var runningInstancesResponse = ec2Client.DescribeInstances(describeRequest);
var runningInstances =
runningInstancesResponse.DescribeInstancesResult.Reservation.SelectMany(reservation =>
reservation.RunningInstance).Select(instance => instance.InstanceId);

Variable runningInstances will contain IDs of all instances that are running as a result of such DescribeInstancesRequest with instance-state-name filter. We can notice interesting code convention of Amazon SDK when list of objects (like list of RunningInstance instances) is named in singular (named RunningInstance). It’s because of real nature of these classes: all of them are just object model of XML response of AWS.

Running instances

Running instances is definitely one of the main purposes of using AWS EC2. It’s a bit different for On-Demand and Spot Instances.

To launch an On-Demand instance, we have to create a RunInstances request and fill it properties wisely. First, we need to set Image Id to launch and preferable number of instances from this image. This preferable number consists of minimum and maximum number of instances to launch. If Amazon capacity allows to launch maximum number of instances it does so. If no it tries it’s best to satisfy us. Our request fails if Amazon is not able to launch minimum value of instances we requested. Secondly, we can specify Key Pair name, instance type, security group and many other in our RunInstancesRequest.

var ec2Client = new AmazonEC2Client(accessKey, secretKey);
 
var runRequest = new RunInstancesRequest();
runRequest.ImageId = imageID;
runRequest.MinCount = minimumValue;
runRequest.MaxCount = maximumValue;
runRequest.InstanceType = "t1.micro";
// some other configurations

var runInstancesResponse = ec2Client.RunInstances(runRequest);
var runInstancesResult = runInstancesResponse.RunInstancesResult;
var runningIDs = runInstancesResult.Reservation.RunningInstance.Select(i => i.InstanceId);

Response contains actually started running instances. Such request can throw AmazonEC2Exception with ErrorCode == "InstanceLimitExceeded" if we are not allowed to run as many instances as were requested (limitations of current plan in Amazon account).

Requesting on-demand instances can fail even not for our fault, but because Amazon is not able to provide us with such number of EC2 instances in our region at the moment. A "InsufficientInstanceCapacity" error code is thrown with AmazonEC2Exception in this case.

Tagging instances

EC2 tag is just a key value pair which we can assign to each running instance (and spot requests). Tags are useful if we want to supply our instances with some additional information (specific for our application). Keys and values are simple strings, but no one prohibit to base64-encode anything we like in that tag.

We can tag only already running instances with CreateTagsRequest: we need to specify list of instance id’s and tags we want to assign to it and any some additional information. See sample code below:

var ec2Client = new AmazonEC2Client(accessKey, secretKey);
 
var createTagRequest = new CreateTagsRequest();
createTagRequest.ResourceId.Add(someInstanceId);
createTagRequest.Tag.Add(new Tag { Key = "NodeRole", Value = "LogCollector" });

ec2Client.CreateTags(createTagRequest);

Terminating instances

Just like running AWS SDK provides us with API to terminate running instances. And the procedure is also a bit different for On-Demand and Spot instances.

To terminate an On-Demand instance, we have to create TerminateInstances request and pass ID of an instance we want to terminate. Simple as that:

var ec2Client = new AmazonEC2Client(accessKey, secretKey);
 
var terminateRequest = new TerminateInstancesRequest();
terminateRequest.InstanceId.Add(instancesId);

var terminateResponse = ec2Client.TerminateInstances(terminateRequest);
var terminatingInstances = terminateResponse.TerminateInstancesResult.TerminatingInstance.Select(ti => ti.InstanceId);

Conclusion

AWS SDK allows us to manage EC2 instances easily and it’s consistent in terms of code conventions. SDK allows us to manage everything from code like if we were working from Amazon web dashboard and it’s worth looking into because cloud computing becomes a new trend today. The current post covers some basic operations with SDK. To continue learning AWS SDK, take a look at the official documentation.

tags

Comments

  • developer learn999

    i tried to use your code, but always stuck in the line
    var placementGroupsResult = response.DescribePlacementGroupsResult;
    which says not such DescribePlacementGroupsResult
    why?