AWSSDK.EC2 (for AWS EC2 )

ahmedadel

Ahmed Adel

Posted on September 4, 2021

AWSSDK.EC2 (for AWS EC2 )

✦ The AWS SDK for .NET supports Amazon EC2, which is a web service that provides resizable computing capacity. You use this computing capacity to build and host your software systems.

First of all, let's have a brief about Amazon EC2 in AWS...

➽What is Amazon EC2 ?

Amazon Elastic Compute Cloud (EC2) is a web service that provides resizable computing capacity—literally, servers in Amazon's data centers—that you use to build and host your software systems.
☞ Read more about Amazon EC2


➽Installing AWSSDK.EC2 :

AWSSDK.EC2 is installed mainly from Nuget
☞There is 3 ways to install AWSSDK.EC2, they are the same as installing AWSSDK.S3 from Part 1 of this series
☞let's use the easiest one, from Package Manager Console by using the Install-Package command.

PM> Install-Package AWSSDK.EC2
Enter fullscreen mode Exit fullscreen mode

🌟 Second step is to connect to our AWS account using __ Access keys (Access Key ID and Secret Access Key)__, this was explained before briefly in the first article under (Get AWS Access keys)


Let's now check what we can do with this SDK :

☞ Working with Security Groups:

✦ A security group acts as a virtual firewall that controls the network traffic for one or more EC2 instances. By default, EC2 associates your instances with a security group that allows no inbound traffic. You can create a security group that allows your EC2 instances to accept certain traffic.
✦ Read more about security groups in the EC2 user guide for Linux and the EC2 user guide for Windows.
1- Creating security groups:

// Method to create a new security group (either EC2-Classic or EC2-VPC)
// If vpcID is empty, the security group will be for EC2-Classic
private static async Task<List<SecurityGroup>> CreateSecurityGroup(
  IAmazonEC2 ec2Client, string groupName, string vpcID)
{
  // See if one or more security groups with that name
  // already exist in the given VPC. If so, return the list of them.
  var securityGroups = await FindSecurityGroups(ec2Client, groupName, vpcID);
  if (securityGroups.Count > 0)
  {
    Console.WriteLine($"\n Security groups with name {groupName} already exists.\n");
    return securityGroups;
  }
  // If the security group doesn't already exists, create it.
  var createRequest = new CreateSecurityGroupRequest{ GroupName = groupName };
  if(string.IsNullOrEmpty(vpcID))
  {
    createRequest.Description = "My .NET example security group for EC2-Classic";
  }
  else
  {
    createRequest.VpcId = vpcID;
    createRequest.Description = "My .NET example security group for EC2-VPC";
  }
  CreateSecurityGroupResponse createResponse = await ec2Client.CreateSecurityGroupAsync(createRequest);
  // Return the new security group
  DescribeSecurityGroupsResponse describeResponse =
    await ec2Client.DescribeSecurityGroupsAsync(new DescribeSecurityGroupsRequest{
      GroupIds = new List<string>() { createResponse.GroupId }
    });
  return describeResponse.SecurityGroups;
}

// Method to determine if a security group with the specified name
// already exists in the VPC
private static async Task<List<SecurityGroup>> FindSecurityGroups(
  IAmazonEC2 ec2Client, string groupName, string vpcID)
{
  var request = new DescribeSecurityGroupsRequest();
  request.Filters.Add(new Filter{
    Name = "group-name",
    Values = new List<string>() { groupName }
  });
  if(!string.IsNullOrEmpty(vpcID))
    request.Filters.Add(new Filter{
      Name = "vpc-id",
      Values = new List<string>() { vpcID }
    });
  var response = await ec2Client.DescribeSecurityGroupsAsync(request);
  return response.SecurityGroups;
}
Enter fullscreen mode Exit fullscreen mode

2- Loop through your security groups:

// Method to enumerate the security groups
private static async Task EnumerateGroups(IAmazonEC2 ec2Client, string vpcID)
{
  // A request object, in case we need it.
  var request = new DescribeSecurityGroupsRequest();
  // Put together the properties, if needed
  if(!string.IsNullOrEmpty(vpcID))
  {
    // We have a VPC ID. Find the security groups for just that VPC.
    Console.WriteLine($"\nGetting security groups for VPC {vpcID}...\n");
    request.Filters.Add(new Filter
    {
      Name = "vpc-id",
      Values = new List<string>() { vpcID }
    });
  }
  // Get the list of security groups
  DescribeSecurityGroupsResponse response =
    await ec2Client.DescribeSecurityGroupsAsync(request);
  // Display the list of security groups.
  foreach (SecurityGroup item in response.SecurityGroups)
  {
    Console.WriteLine("Security group: " + item.GroupId);
    Console.WriteLine("\tGroupId: " + item.GroupId);
    Console.WriteLine("\tGroupName: " + item.GroupName);
    Console.WriteLine("\tVpcId: " + item.VpcId);
    Console.WriteLine();
  }
}
Enter fullscreen mode Exit fullscreen mode

3- Update security group (Add inbound rule):

// Method that adds a TCP ingress rule to a security group
private static async Task AddIngressRule(
  IAmazonEC2 eC2Client, string groupID, string ipAddress, int port)
{
  // Create an object to hold the request information for the rule.
  // It uses an IpPermission object to hold the IP information for the rule.
  var ingressRequest = new AuthorizeSecurityGroupIngressRequest{
    GroupId = groupID};
  ingressRequest.IpPermissions.Add(new IpPermission{
    IpProtocol = "tcp",
    FromPort = port,
    ToPort = port,
    Ipv4Ranges = new List<IpRange>() { new IpRange { CidrIp = ipAddress } }
  });
  // Create the inbound rule for the security group
  AuthorizeSecurityGroupIngressResponse responseIngress =
    await eC2Client.AuthorizeSecurityGroupIngressAsync(ingressRequest);
  Console.WriteLine($"\nNew RDP rule was written in {groupID} for {ipAddress}.");
  Console.WriteLine($"Result: {responseIngress.HttpStatusCode}");
}
Enter fullscreen mode Exit fullscreen mode

☞ Working with Amazon EC2 key pairs:

Amazon EC2 uses public–key cryptography to encrypt and decrypt login information. Public–key cryptography uses a public key to encrypt data, and then the recipient uses the private key to decrypt the data. The public and private keys are known as a key pair.
✦ Read more about Amazon EC2 key pairs in the EC2 user guide for Linux or the EC2 user guide for Windows.

1- Create the key pair:

// Method to create a key pair and save the key material in a PEM file
private static async Task CreateKeyPair(
  IAmazonEC2 ec2Client, string keyPairName, string pemFileName)
{
  // Create the key pair
  CreateKeyPairResponse response =
    await ec2Client.CreateKeyPairAsync(new CreateKeyPairRequest{
      KeyName = keyPairName
    });
  Console.WriteLine($"\nCreated new key pair: {response.KeyPair.KeyName}");
  // Save the private key in a PEM file
  using (var s = new FileStream(pemFileName, FileMode.Create))
  using (var writer = new StreamWriter(s))
  {
    writer.WriteLine(response.KeyPair.KeyMaterial);
  }
}
Enter fullscreen mode Exit fullscreen mode

2- Display available key pairs:

// Method to show the key pairs that are available
private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
{
  DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
  Console.WriteLine("Available key pairs:");
  foreach (KeyPairInfo item in response.KeyPairs)
    Console.WriteLine($"  {item.KeyName}");
}
Enter fullscreen mode Exit fullscreen mode

3- Delete the key pair:

// Method to delete a key pair
private static async Task DeleteKeyPair(IAmazonEC2 ec2Client, string keyName)
{
  await ec2Client.DeleteKeyPairAsync(new DeleteKeyPairRequest{
    KeyName = keyName});
  Console.WriteLine($"\nKey pair {keyName} has been deleted (if it existed).");
}
Enter fullscreen mode Exit fullscreen mode

☞ Seeing your Amazon EC2 Regions and Availability Zones:

Amazon EC2 is hosted in multiple locations worldwide. These locations are composed of Regions and Availability Zones. Each Region is a separate geographic area that has multiple, isolated locations known as Availability Zones.
✦ Read more about Regions and Availability Zones in the EC2 user guide for Linux or the EC2 user guide for Windows.

using System;
using System.Threading.Tasks;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2RegionsAndZones
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      // Display the Regions and Availability Zones
      await DescribeRegions(ec2Client);
      await DescribeAvailabilityZones(ec2Client);
    }

    // Method to display Regions
    private static async Task DescribeRegions(IAmazonEC2 ec2Client)
    {
      Console.WriteLine("\nRegions that are enabled for the EC2 client:");
      DescribeRegionsResponse response = await ec2Client.DescribeRegionsAsync();
      foreach (Region region in response.Regions)
        Console.WriteLine(region.RegionName);
    }


    //
    // Method to display Availability Zones
    private static async Task DescribeAvailabilityZones(IAmazonEC2 ec2Client)
    {
      Console.WriteLine("\nAvailability Zones for the EC2 client's region:");
      DescribeAvailabilityZonesResponse response = await ec2Client.DescribeAvailabilityZonesAsync();
      foreach (AvailabilityZone az in response.AvailabilityZones)
        Console.WriteLine(az.ZoneName);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

☞ Working with Amazon EC2 instances:

1- Launch an instance:

// Method to launch the instances
// Returns a list with the launched instance IDs
private static async Task<List<string>> LaunchInstances(
  IAmazonEC2 ec2Client, RunInstancesRequest requestLaunch)
{
  var instanceIds = new List<string>();
  RunInstancesResponse responseLaunch =
    await ec2Client.RunInstancesAsync(requestLaunch);
  Console.WriteLine("\nNew instances have been created.");
  foreach (Instance item in responseLaunch.Reservation.Instances)
  {
    instanceIds.Add(item.InstanceId);
    Console.WriteLine($"  New instance: {item.InstanceId}");
  }
  return instanceIds;
}
Enter fullscreen mode Exit fullscreen mode

2- Monitor the instance:

// Method to wait until the instances are running (or at least not pending)
private static async Task CheckState(IAmazonEC2 ec2Client, List<string> instanceIds)
{
  Console.WriteLine(
    "\nWaiting for the instances to start." +
    "\nPress any key to stop waiting. (Response might be slightly delayed.)");
  int numberRunning;
  DescribeInstancesResponse responseDescribe;
  var requestDescribe = new DescribeInstancesRequest{
    InstanceIds = instanceIds};
  // Check every couple of seconds
  int wait = 2000;
  while(true)
  {
    // Get and check the status for each of the instances to see if it's past pending.
    // Once all instances are past pending, break out.
    // (For this example, we are assuming that there is only one reservation.)
    Console.Write(".");
    numberRunning = 0;
    responseDescribe = await ec2Client.DescribeInstancesAsync(requestDescribe);
    foreach(Instance i in responseDescribe.Reservations[0].Instances)
    {
      // Check the lower byte of State.Code property
      // Code == 0 is the pending state
      if((i.State.Code & 255) > 0) numberRunning++;
    }
    if(numberRunning == responseDescribe.Reservations[0].Instances.Count)
      break;
    // Wait a bit and try again (unless the user wants to stop waiting)
    Thread.Sleep(wait);
    if(Console.KeyAvailable)
      break;
  }
  Console.WriteLine("\nNo more instances are pending.");
  foreach(Instance i in responseDescribe.Reservations[0].Instances)
  {
    Console.WriteLine($"For {i.InstanceId}:");
    Console.WriteLine($"  VPC ID: {i.VpcId}");
    Console.WriteLine($"  Instance state: {i.State.Name}");
    Console.WriteLine($"  Public IP address: {i.PublicIpAddress}");
    Console.WriteLine($"  Public DNS name: {i.PublicDnsName}");
    Console.WriteLine($"  Key pair name: {i.KeyName}");
  }
}
Enter fullscreen mode Exit fullscreen mode

3- Terminating an EC2 instance:

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2TerminateInstance
{
  class Program
  {
    static async Task Main(string[] args)
    {
      if((args.Length == 1) && (args[0].StartsWith("i-")))
      {
        // Terminate the instance
        var ec2Client = new AmazonEC2Client();
        await TerminateInstance(ec2Client, args[0]);
      }
      else
      {
        Console.WriteLine("\nCommand-line argument missing or incorrect.");
        Console.WriteLine("\nUsage: EC2TerminateInstance instance-ID");
        Console.WriteLine("  instance-ID - The EC2 instance you want to terminate.");
        return;
      }
    }

    // Method to terminate an EC2 instance
    private static async Task TerminateInstance(IAmazonEC2 ec2Client, string instanceID)
    {
      var request = new TerminateInstancesRequest{
        InstanceIds = new List<string>() { instanceID }};
      TerminateInstancesResponse response =
        await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
          InstanceIds = new List<string>() { instanceID }
        });
      foreach (InstanceStateChange item in response.TerminatingInstances)
      {
        Console.WriteLine("Terminated instance: " + item.InstanceId);
        Console.WriteLine("Instance state: " + item.CurrentState.Name);
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

☞ Amazon EC2 Spot Instance tutorial:

Spot Instances enable you to request unused Amazon EC2 capacity for less than the On-Demand price. This can significantly lower your EC2 costs for applications that can be interrupted.
✦ Read more about Spot Instances in the EC2 user guide for Linux or the EC2 user guide for Windows.

1- Creating a Spot Instance request:

// Method to create a Spot Instance request
private static async Task<SpotInstanceRequest> CreateSpotInstanceRequest(
  IAmazonEC2 ec2Client, string amiId, string securityGroupName,
  InstanceType instanceType, string spotPrice, int instanceCount)
{
  var launchSpecification = new LaunchSpecification{
    ImageId = amiId,
    InstanceType = instanceType
  };
  launchSpecification.SecurityGroups.Add(securityGroupName);
  var request = new RequestSpotInstancesRequest{
    SpotPrice = spotPrice,
    InstanceCount = instanceCount,
    LaunchSpecification = launchSpecification
  };
  RequestSpotInstancesResponse result = await ec2Client.RequestSpotInstancesAsync(request);
  return result.SpotInstanceRequests[0];
}
Enter fullscreen mode Exit fullscreen mode

2- Get status of your Spot Instance request:

// Method to get information about a Spot Instance request, including the status,
// instance ID, etc.
// It gets the information for a specific request (as opposed to all requests).
private static async Task<SpotInstanceRequest> GetSpotInstanceRequestInfo(
  IAmazonEC2 ec2Client, string requestId)
{
  var describeRequest = new DescribeSpotInstanceRequestsRequest();
  describeRequest.SpotInstanceRequestIds.Add(requestId);
  DescribeSpotInstanceRequestsResponse describeResponse =
    await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);
  return describeResponse.SpotInstanceRequests[0];
}
Enter fullscreen mode Exit fullscreen mode

3- Clean up your Spot Instance requests:

// Method to cancel a Spot Instance request
private static async Task CancelSpotInstanceRequest(
  IAmazonEC2 ec2Client, string requestId)
{
  var cancelRequest = new CancelSpotInstanceRequestsRequest();
  cancelRequest.SpotInstanceRequestIds.Add(requestId);
  await ec2Client.CancelSpotInstanceRequestsAsync(cancelRequest);
}
Enter fullscreen mode Exit fullscreen mode

4- Clean up your Spot Instances:
✦ To avoid unnecessary costs, it's important that you terminate any instances that were started from Spot Instance requests; simply canceling Spot Instance requests will not terminate your instances, which means that you'll continue to be charged for them.

// Method to terminate a Spot Instance
private static async Task TerminateSpotInstance(
  IAmazonEC2 ec2Client, string requestId)
{
  var describeRequest = new DescribeSpotInstanceRequestsRequest();
  describeRequest.SpotInstanceRequestIds.Add(requestId);
  // Retrieve the Spot Instance request to check for running instances.
  DescribeSpotInstanceRequestsResponse describeResponse =
    await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);
  // If there are any running instances, terminate them
  if(   (describeResponse.SpotInstanceRequests[0].Status.Code
          == "request-canceled-and-instance-running")
     || (describeResponse.SpotInstanceRequests[0].State == SpotInstanceState.Active))
  {
    TerminateInstancesResponse response =
      await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
        InstanceIds = new List<string>(){
          describeResponse.SpotInstanceRequests[0].InstanceId } });
    foreach (InstanceStateChange item in response.TerminatingInstances)
    {
      Console.WriteLine($"\n  Terminated instance: {item.InstanceId}");
      Console.WriteLine($"  Instance state: {item.CurrentState.Name}\n");
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

References: AWS official Documentation

💖 💪 🙅 🚩
ahmedadel
Ahmed Adel

Posted on September 4, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

AWSSDK.EC2 (for AWS EC2 )
cloud AWSSDK.EC2 (for AWS EC2 )

September 4, 2021