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.