Add security group to all ec2 instances

If you ever need to take over management of an AWS farm, it’s very likely you will need to attach SG to all instances. Be that for monitoring or access. Here is a bash script to add 1 SG to all instances.

You will need to first setup a profile on awscli. Then run the script with the profile name as first argument, and the SG id as the second.

Note: The script will not work on instances with multiple NICs

#!/usr/bin/env bash
# script to add 1 SG to all instances.
# this scripts takes 2 arguments, first is the aws profile name, second is the SG to add.
# e.g. ./ acme sg-1234567
# you will need awscli for this script to work, and an aws profile
# associated with an IAM user with the AmazonEC2FullAccess policy

export AWSPROFILE=$1
export ADDSG=$2

doit() {
    echo "Checking $1..."
    SG=$(aws --profile=$AWSPROFILE ec2 describe-instances --instance-ids $1 --output json | jq ".[][].Instances[].SecurityGroups[].GroupId" -r | xargs)
    echo "Existing SGs: $SG"
    if [[ $SG == *$ADDSG* ]]; then
        echo "$ADDSG already associated, do nothing"
      aws --profile=$AWSPROFILE ec2 modify-instance-attribute --dry-run --instance-id $1 --groups $SG $ADDSG
      echo "New SGs: $(aws --profile=$AWSPROFILE ec2 describe-instances --instance-ids $1 --output json | jq ".[][].Instances[].SecurityGroups[].GroupId" -r | xargs)"

export -f doit

aws --profile=$AWSPROFILE ec2 describe-instances --output json \
| jq ".[][].Instances[].InstanceId" -r | parallel -j10 doit

Working with JSON output from AWSCLI

The program looks short but the way Python handles JSON is not very elegant. JSON is supposed to be a serialized object, so it should be processed as an object. But here, it’s processed as an array.

import json
import sys

print "Usage: cat JSON | | column -t"
jsonInput = sys.stdin
awsObject = json.load(jsonInput)

instances = awsObject[‘Reservations’]

for instance in instances:
print instance[‘Instances’][0][‘Tags’][0][‘Value’], instance[‘Instances’][0][‘InstanceId’], instance[‘Instances’][0][‘PublicDnsName’]

Not only that, there is no type definition with any of the variables. I have no idea what was returned by json.load.

Also, exception handling is quite poor. I tried to do a try-except block but the program got stuck with no stdin was piped. If I try to read from stdin and calculate the length, the read line is consumed and I couldn’t go back.

Anyway, this program will take output from awscli (json format) and extract 3 fields that I want.

On a relevant note, it is also possible to use the –query option to narrow down the search fields. For example

$ aws ec2 describe-volumes --output=text --query 'Volumes[*].{ID:VolumeId,Size:Size}'
vol-d74xxx93	30
vol-784xxx3c	30
vol-854xxxc1	60