April 12, 2019

Cleaning up unused security groups in AWS using golang

As the AWS infrastructure becomes bigger, maintaining security groups becomes harder. This is very hard to do manually. In this blog we will look at how to cleanup unused security groups using script in golang. Whenever a security group is applied to an EC2 instance, ElastiCache, RDS, Elastic Search Service etc., a corresponding entry is added to Network Interfaces. Let us verify the same, go to EC2 -> Network & Security -> Security Groups. Get the security group ID from the Group ID column. Go to EC2 -> Network & Security -> Network Interfaces. In the search filter, add new filter Security Group ID: {group_id}. You will the corresponding network interfaces, if there are no entries that means that the security group is unused.

Script in golang

Now let us find out all the unused security groups using golang script. AWS provides an official AWS SDK for golang, you can view it here. If you want to look at the API references, click here. First, parse through all the security groups. Then, check if there is any associated Network interfaces. If there are no network interfaces, then the security group is deleted. You can find the code here.


package main

import (
	"log"
	"os"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
)

func main() {
	svc := ec2.New(session.New())
	var maxResults int64
	maxResults = 500
	var nextToken string
	shouldDelete := true

	for {
		descSGInput := &ec2.DescribeSecurityGroupsInput{
			MaxResults: aws.Int64(maxResults),
		}

		if nextToken != "" {
			descSGInput.NextToken = aws.String(nextToken)
		}

		descSGOut, err := svc.DescribeSecurityGroups(descSGInput)
		if err != nil {
			log.Println("Unable to get security groups. Err:", err)
			os.Exit(1)
		}

		for _, sg := range descSGOut.SecurityGroups {
			niInput := &ec2.DescribeNetworkInterfacesInput{
				Filters: []*ec2.Filter{&ec2.Filter{
					Name:   aws.String("group-id"),
					Values: []*string{sg.GroupId},
				}},
			}

			niOut, err := svc.DescribeNetworkInterfaces(niInput)
			if err != nil {
				log.Println("Unable to get network interfaces for the security group. Err:", err)
				os.Exit(1)
			}

			if len(niOut.NetworkInterfaces) == 0 && shouldDelete {
				log.Println("Deleting Security Group: ", *sg.GroupId, *sg.GroupName)
				svc.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{
					GroupId: sg.GroupId,
				})
			}
		}

		if descSGOut.NextToken != nil {
			nextToken = *descSGOut.NextToken
		} else {
			break
		}
	}
}

The following environmental variables are set before running the script.

  • AWS_ACCESS_KEY_ID – Access key ID from you IAM user credentials.
  • AWS_SECRET_ACCESS_KEY – Secret Key ID from your IAM user credentials.
  • AWS_REGION – AWS region to clean up the security group

To run script:

 go run main.go