Wednesday, October 10, 2018

Updating inbound rules in security group using a lambda function

Prerequisites:

  1. Visual Studio 2017 with .NET core
  2. AWS Toolkit for Visual Studio 2017
  3. AWSSDK.Core tool kit
  4. AWSSDK.EC2 tool kit

Preparing the developer environment

  1. Visual Studio 2017 with .NET core :
    1. Log on to the microsoft downloads using the ensurance microsoft account.
    2. Download the profetinal version and installe VS2017 with C# development settings
  2. Installing AWS Toolkit for VS 2017 (Using Nuget):
    1. Open visual studio and go to Tools → Extentions and Updates
    2. In the Extentions and Updates dialog box, type "aws" in the search box. 
    1. Install the toolkit
  1. Creating a new solution for Lambda function:
    1. Create a blank solution in visual studio.
    2. Right click on te solution name and go to Add New Project menu
    3. Select AWS Lambda from the project list and select AWS Lambda Project with Tests (.NET Core) from the list.
    4. Two projects will be created with the given name and one project will be used as the test project.
    5. A new class will be created as Function.cs. Please done rename it.
  2. Add AWSSDKCore toolkit (Using nuget):
    1. Right click on the solution and click on Manage NuGet Packages
    2. Type AWS sdk core in the search box and select the AWSSDK Core tool kit
  3. Similarly, install the AWSSDK.EC2 using nuget packages.

Implementing the lambda function

  1. FunctionHandler is the core function in Function class. Any relevent proceedure should be executed within the handler as below.
  2. Refere AWS SDK online edition to findout code samples and requests used to sccess AWS objects
    1. https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/how-to-ec2.html
  3. Please see the code base below to undertand the implementation

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Amazon.Lambda.Core;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

namespace UNOAutomationAWS
{
    public class Function
    {

        EC2Info e = new EC2Info();
        public string FunctionHandler(EC2Info e, ILambdaContext context)
        {
            return e.SetSecurityGroup();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Text;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace AutomationAWS
{
    public sealed class AmazonClient
    {
        private static AmazonClient _instance;
        private static readonly object simplelock = new object();
        private AmazonEC2Client _client = null;

        public static AmazonClient Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (simplelock)
                    {
                        if (_instance == null)
                        {
                            _instance = new AmazonClient();
                        }
                    }
                }
                return _instance;
            }
        }

        private AmazonClient()
        {
            _client = new AmazonEC2Client("<>", "<>", <>);
        }
       
        public AmazonEC2Client EC2Client
        {
            get
            {
                return _client;
            }
        }
    }

using System;
using System.Collections.Generic;
using System.Text;
using Amazon.EC2;
using Amazon.EC2.Model;
using System.Linq;

namespace AutomationAWS
{
    public class EC2Info
    {
       private const string SECURITYGROUPID = "<>";
        private const string INSTANCEID = "<>";
        private const string INSTANCENAME = "<>";

        private AmazonEC2Client _client;        

        public string SetSecurityGroup()
        {
            IpPermission ipPer = new IpPermission();
            _client = AmazonClient.Instance.EC2Client;

            //Get the EC2 Instance 
            var instance = GetInstance(INSTANCEID);

            if (instance != null && !instance.State.Name.Value.Equals("stopped"))
            {
                //Get the security group to modify with the new IP of EC2
                var securityGroup = GetSercurityGroup(SECURITYGROUPID);

                if (securityGroup != null)
                {
                    //Get IP permission to modify
                    var ipPermissions = securityGroup.IpPermissions.Where(i => i.Ipv4Ranges.Any(p => p.Description.Equals(INSTANCENAME)));

                    if (ipPermissions != null && ipPermissions.Count() > 0)
                    {
                        ipPer = ipPermissions.First();
                        var ip = ipPer.Ipv4Ranges.Where(p => p.Description.Equals(INSTANCENAME)).First().CidrIp;

                        //Remove the existing inbound rule
                        RemoveSecurityGroupRule(securityGroup, ipPer, INSTANCENAME, ip);
                    }                                       

                    //If no IpPermission found, create a new one.
                    if (ipPer.FromPort == 0)
                    {
                        ipPer.FromPort = 1521;
                        ipPer.ToPort = 1521;
                        ipPer.IpProtocol = "tcp";
                    }

                    //Set the new inbound rule in security group
                    SetSecurityGroupRule(securityGroup, ipPer, INSTANCENAME, instance.PublicIpAddress.Trim());
                }

                return instance.PublicIpAddress.ToString(); ;
            }

            return "";
        }      


        ///
        /// Get EC2 instance by instance id
        ///
        ///
        ///
        ///
        private Instance GetInstance(string securityGroupId)
        {
            var request = _client.DescribeInstancesAsync(new DescribeInstancesRequest()
            {
                InstanceIds = new List() { INSTANCEID }
            });

            if (request != null)
            {
                var result = request.Result;
                var reservation = result.Reservations.First();
                return reservation.Instances.First();
            }

            return null;
        }


        ///
        /// Get security group by security group ID
        ///
        ///
        ///
        ///
        private SecurityGroup GetSercurityGroup(string securityGroupId)
        {
            var request = _client.DescribeSecurityGroupsAsync(new DescribeSecurityGroupsRequest()
            {
                GroupIds = new List() { securityGroupId }
            });

            if (request != null)
            {
                var result = request.Result;
                return result.SecurityGroups.First();               
            }

            return null;
        }


        ///
        /// Removing the existing security rule
        ///
        ///
        ///
        ///
        private string RemoveSecurityGroupRule(SecurityGroup securityGroup, IpPermission ipPer, string name, string ip)
        {
            var ipr = new List { new IpRange() { CidrIp = ip, Description = name } };
            var request = _client.RevokeSecurityGroupIngressAsync(new RevokeSecurityGroupIngressRequest()
            {
                GroupId = securityGroup.GroupId,
                IpPermissions = new List
                {
                    new IpPermission()
                    {
                        FromPort = ipPer.FromPort,
                        ToPort = ipPer.ToPort,
                        IpProtocol = ipPer.IpProtocol,
                        Ipv4Ranges = ipr,
                        Ipv6Ranges = ipPer.Ipv6Ranges,
                        PrefixListIds = ipPer.PrefixListIds
                    }
                }
            });

            return request.Result.ToString();
        }


        ///
        /// Setting up the new security rule
        ///
        ///
        ///
        ///
        private string SetSecurityGroupRule(SecurityGroup securityGroup, IpPermission ipPer, string name, string ip)
        {
            var ipr = new List { new IpRange() { CidrIp = ip, Description = name } };
            var request = _client.AuthorizeSecurityGroupIngressAsync(new AuthorizeSecurityGroupIngressRequest()
            {
                GroupId = securityGroup.GroupId,
                IpPermissions = new List
                {
                    new IpPermission()
                    {
                        FromPort = ipPer.FromPort,
                        ToPort = ipPer.ToPort,
                        IpProtocol = ipPer.IpProtocol,
                        Ipv4Ranges = ipr,
                        Ipv6Ranges = ipPer.Ipv6Ranges,
                        PrefixListIds = ipPer.PrefixListIds

                    }
                }
            });

            return request.Result.ToString();
        }

    }
}