How to detect Azure/Amazon VM - amazon-web-services

Is there any way to detect, in general, if your code is executing on an Azure or Amazon virtual machine. I am not referring to some sort of web or worker role in particular, I mean given any executable, is there anything that resolves that machine to a cloud VM - for example under Azure there is no domain so i cannot simply rely on a domain name.

AWS
If your guest has networking up then you can probe the instance metadata by accessing http://169.254.169.254
For example:
$ curl http://169.254.169.254/1.0/meta-data/instance-id
i-87dc2f76
However, hitting the network is fairly heavy-weight.
On AWS you can also check by looking at dmidecode:
$ /usr/sbin/dmidecode -s bios-version | tr "[:upper:]" "[:lower:]" | grep -q "amazon"
dmidecode is light-weight since it only hits the memory of the guest OS. However, as pointed out in previous answers it is dependent on Amazon continuing to include the word "amazon" in their version strings.
Azure
On Azure, you can detect the hypervisor details but this doesn't allow you to discriminate between Azure and HyperV. Depending on your scenario this might not be necessary.
To detect Azure/HyperV using dmidecode check the following strings:
$ /usr/sbin/dmidecode -s system-manufacturer | tr "[:upper:]" "[:lower:]" | grep -q "microsoft corporation"
$ /usr/sbin/dmidecode -s system-product-name | tr "[:upper:]" "[:lower:]" | grep -q "virtual machine"

You could look at the machine's ip address, and determine if it is in a particular cloud's ip address block.
For Azure, the published list of ip address ranges for each subregion is an xml file at:
http://download.microsoft.com/download/E/F/C/EFC5E4D8-E4EA-4EFE-9356-D8AEEBC85F50/Azure_IP_Ranges.xml
Amazon will post a blog entry when they add new ranges. They are currently:
US East (Northern Virginia):
72.44.32.0/19 (72.44.32.0 - 72.44.63.255)
67.202.0.0/18 (67.202.0.0 - 67.202.63.255)
75.101.128.0/17 (75.101.128.0 - 75.101.255.255)
174.129.0.0/16 (174.129.0.0 - 174.129.255.255)
204.236.192.0/18 (204.236.192.0 - 204.236.255.255)
184.73.0.0/16 (184.73.0.0 – 184.73.255.255)
184.72.128.0/17 (184.72.128.0 - 184.72.255.255)
184.72.64.0/18 (184.72.64.0 - 184.72.127.255)
50.16.0.0/15 (50.16.0.0 - 50.17.255.255)
50.19.0.0/16 (50.19.0.0 - 50.19.255.255)
107.20.0.0/14 (107.20.0.0 - 107.23.255.255)
23.20.0.0/14 (23.20.0.0 – 23.23.255.255)
54.242.0.0/15 (54.242.0.0 – 54.243.255.255)
54.234.0.0/15 (54.234.0.0 – 54.235.255.255) NEW
54.236.0.0/15 (54.236.0.0 – 54.237.255.255) NEW
US West (Oregon):
50.112.0.0/16 (50.112.0.0 - 50.112.255.255)
54.245.0.0/16 (54.245.0.0 – 54.245.255.255)
US West (Northern California):
204.236.128.0/18 (204.236.128.0 - 204.236.191.255)
184.72.0.0/18 (184.72.0.0 – 184.72.63.255)
50.18.0.0/16 (50.18.0.0 - 50.18.255.255)
184.169.128.0/17 (184.169.128.0 - 184.169.255.255)
54.241.0.0/16 (54.241.0.0 – 54.241.255.255)
EU (Ireland):
79.125.0.0/17 (79.125.0.0 - 79.125.127.255)
46.51.128.0/18 (46.51.128.0 - 46.51.191.255)
46.51.192.0/20 (46.51.192.0 - 46.51.207.255)
46.137.0.0/17 (46.137.0.0 - 46.137.127.255)
46.137.128.0/18 (46.137.128.0 - 46.137.191.255)
176.34.128.0/17 (176.34.128.0 - 176.34.255.255)
176.34.64.0/18 (176.34.64.0 – 176.34.127.255)
54.247.0.0/16 (54.247.0.0 – 54.247.255.255)
54.246.0.0/16 (54.246.0.0 – 54.246.255.255) NEW
Asia Pacific (Singapore)
175.41.128.0/18 (175.41.128.0 - 175.41.191.255)
122.248.192.0/18 (122.248.192.0 - 122.248.255.255)
46.137.192.0/18 (46.137.192.0 - 46.137.255.255)
46.51.216.0/21 (46.51.216.0 - 46.51.223.255)
54.251.0.0/16 (54.251.0.0 – 54.251.255.255)
Asia Pacific (Tokyo)
175.41.192.0/18 (175.41.192.0 - 175.41.255.255)
46.51.224.0/19 (46.51.224.0 - 46.51.255.255)
176.32.64.0/19 (176.32.64.0 - 176.32.95.255)
103.4.8.0/21 (103.4.8.0 - 103.4.15.255)
176.34.0.0/18 (176.34.0.0 - 176.34.63.255)
54.248.0.0/15 (54.248.0.0 - 54.249.255.255)
South America (Sao Paulo)
177.71.128.0/17 (177.71.128.0 - 177.71.255.255)
54.232.0.0/16 (54.232.0.0 – 54.232.255.255) NEW

This is an internal way to check if you machine instance is in amazon or not:
dmidecode | grep Version
version: 4.2.amazon <--- This is what you would want to key on
This would only work as long as Amazon maintains their signature in their VM's virtual BIOS settings. I have not worked with Azure, but I am sure you could extrapolate information using dmidecode as well. I have done it with VMware, and VirtualBox.

#Dan's answer no longer works for Azure, use the following URL to get a better list
http://msdn.microsoft.com/en-us/library/windowsazure/dn175718.aspx
If this url ever disappears here is a copy from today (August 12th 2013)
Europe West
65.52.128.0/19
213.199.128.0/20
168.63.0.0/19
168.63.96.0/19
137.116.192.0/19
137.117.128.0/17
168.61.56.0/21
Europe North
65.52.64.0/20
65.52.224.0/19
168.63.92.0/22
168.63.32.0/19
94.245.88.0/21
94.245.104.0/21
168.63.64.0/20
168.63.80.0/20
168.61.96.0/19
137.116.224.0/20
US East
168.62.32.0/19
157.56.176.0/21
168.62.160.0/19
168.61.32.0/20
168.61.48.0/21
137.117.64.0/18
137.135.64.0/18
138.91.96.0/19
137.116.112.0/20
US West
168.62.192.0/20
168.62.208.0/21
168.61.0.0/20
168.61.64.0/20
137.117.0.0/19
137.135.0.0/18
137.116.184.0/21
138.91.64.0/19
65.52.112.0/20
168.63.89.0/24
157.56.160.0/21
168.62.0.0/19
US North Central
65.52.0.0/19
65.52.0.0/20
65.52.16.0/20
65.52.192.0/19
65.52.48.0/20
157.55.24.0/21
157.55.64.0/20
157.55.160.0/20
157.55.136.0/21
157.55.208.0/20
157.56.8.0/21
157.55.252.0/22
168.62.96.0/19
157.55.248.0/22
168.62.224.0/19
US South Central
157.55.176.10/22
157.55.183.223/27
157.55.184.10/22
157.55.191.223/27
157.55.192.10/24
157.55.193.223/27
157.55.194.10/24
157.55.195.223/27
157.55.196.10/23
157.55.200.10/23
157.55.80.10/23
157.55.83.223/27
157.55.84.10/23
157.55.87.223/27
65.52.32.10/22
65.52.39.224/28
70.37.160.10/22
70.37.167.224/28
70.37.118.0/24
70.37.119.138/28
70.37.119.170/28
70.37.48.10/22
70.37.55.224/28
70.37.56.10/22
70.37.63.224/28
70.37.116.0/24
SE Asia
111.221.96.0/20
168.63.160.0/19
111.221.80.0/20
168.63.224.0/19
137.116.128.0/19
East Asia
65.52.160.0/19
111.221.78.0/23
168.63.128.0/19
168.63.192.0/19
137.116.160.0/20

You can use a metadata endpoint for Azure VMs as well:
Invoke-RestMethod -Headers #{"Metadata"="true"} -Method GET -Proxy $Null -Uri "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | ConvertTo-Json -Depth 64
Ref:
https://learn.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=windows

Related

powercli site recovery manager: determine which protection group an unprotected VM will be in. SRM uses array based replication

I use automation to deply VM's to various vcenter clusters.
I then confgiure SRM network mapping to create a network map between the cluster that the VM is in and the cluster which is used for DR purposes, in the protection group for those two clusters.
SRM is set up for array based replication, so as long as the VM is placed on replicated storage in the right cluster it will appear in SRM under the protection group, if a network mapping is in place then the VM will be auto protected by SRM or via my SRM config script.
I currently have the primary cluster, DR cluster and protection group hard coded, but would like to determine the protection group a VM is in and the name of the two clusters which the protection group is set up for, that way any changes to cluster configuration is automatically picked up and doesn't require manual changes to the SRM config script.
I've looked in the SRM API docs but it's not something I have worked out yet!
I have solved the issue:
$credential = Get-Credential
$server_name = "test-server"
Connect-VIServer -Server $primaryDC -Credential $credential
$srmConnection = Connect-SrmServer -Credential $credential -RemoteCredential $credential
Connect-VIServer -Server $secondaryDC -Credential $credential
$srmApi = $srmConnection.ExtensionData
$protectionGroups = $srmApi.Protection.ListProtectionGroups()
foreach ($protectionGroup in $protectionGroups){
$associatedVms = $protectionGroup.ListProtectedDatastores() | Get-VIObjectByVIView | Get-VM | Where-Object {($_.name -eq $server_name) -and($_.ExtensionData.Config.ManagedBy.ExtensionKey -ne 'com.vmware.vcDr' )}
foreach ($vm in $associatedVms) {
if ($vm.Name -eq $server_name) {
$protection_group_name = $protectionGroup.GetInfo().Name
$primary_cluster = get-vm -name $server_name | get-cluster
$primary_cluster_res_group = $primary_cluster.ExtensionData.ResourcePool
$srm_resource_groups = $srmApi.inventoryMapping.getResourcePoolMappings()
foreach ($resource_group in $srm_resource_groups){
if ($resource_group.PrimaryObject -eq $primary_cluster_res_group){
$secondary_res_group = $resource_group.SecondaryObject
}
}
}
}
}
$secondary_cluster = Get-Cluster | Where-Object {$_.ExtensionData.ResourcePool -eq $secondary_res_group}
Write-Host "VM: $vm - Protection Group: $protection_group_name - Primary cluster: $primary_cluster - Secondary cluster: $secondary_cluster - Primary ResGrp: $primary_cluster_res_group - Secondary ResGrp: $secondary_res_group"

How do I list all groups a GCP service account belongs to?

I have a GSA: my-gsa#myproject.iam.gserviceaccount.com
GCP has supported groups for a while now so I added that GSA to a bunch of groups.
How can I easily see what groups that GSA belongs to?
If this was a google user account I could go to the G Suite console and view the user's group membership. This is a GSA though and it does not appear in the G Suite console like that.
Ideally I could see this in some web console page or with gcloud. This gcloud command will show me the members of a group: https://cloud.google.com/sdk/gcloud/reference/beta/identity/groups/memberships/list. How do I do the inverse of that, again for a GSA not a google user account?
EDIT
Not a solution but a script to search all groups. Still think there has to be an API call to get this as a single step. The groups.memberships.searchTransitiveGroups() method I think is only for seeing nested group memberships.
GSA_TO_SEARCH=my-gsa#myproject.iam.gserviceaccount.com
PROJECT_ID=projectname # This can be any project in the org
ORG_ID="$(gcloud projects get-ancestors $PROJECT_ID | grep organization | cut -f1 -d' ')"
# I don't think this label includes GCP security groups just G Suite email groups
GROUPS="$(gcloud beta identity groups search --organization=$ORG_ID --labels='cloudidentity.googleapis.com/groups.discussion_forum' --format='json')"
GROUP_EMAILS="$(echo $GROUPS | jq '.groups[] | .groupKey.id')"
echo $GROUP_EMAILS | \
xargs -I {} sh -c "echo {} && \
gcloud beta identity groups memberships list --group-email="{}" --format=json | \
jq '.[] | select(.memberKey.id==\"$GSA_TO_SEARCH\").memberKey.id'"

Changing Timezone in EC2 Linux Instance

I've been trying to change the ec2 instance timezone to IST but following the aws docs isn't helping at all.
ls /usr/share/zoneinfo/Asia
Aden Atyrau Brunei Damascus Hebron Jerusalem Kolkata Makassar Phnom_Penh Saigon Tashkent Ujung_Pandang Yangon
Almaty Baghdad Calcutta Dhaka Ho_Chi_Minh Kabul Krasnoyarsk Manila Pontianak Sakhalin Tbilisi Ulaanbaatar Yekaterinburg
Amman Bahrain Chita Dili Hong_Kong Kamchatka Kuala_Lumpur Muscat Pyongyang Samarkand Tehran Ulan_Bator Yerevan
Anadyr Baku Choibalsan Dubai Hovd Karachi Kuching Nicosia Qatar Seoul Tel_Aviv Urumqi
Aqtau Bangkok Chongqing Dushanbe Irkutsk Kashgar Kuwait Novokuznetsk Qostanay Shanghai Thimbu Ust-Nera
Aqtobe Barnaul Chungking Famagusta Istanbul Kathmandu Macao Novosibirsk Qyzylorda Singapore Thimphu Vientiane
Ashgabat Beirut Colombo Gaza Jakarta Katmandu Macau Omsk Rangoon Srednekolymsk Tokyo Vladivostok
Ashkhabad Bishkek Dacca Harbin Jayapura Khandyga Magadan Oral Riyadh Taipei Tomsk Yakutsk
sudo vi /etc/sysconfig/clock
ZONE="Asia/Calcutta"
UTC=true
I edited the file to the required timezone and linked it to local time
sudo ln -sf /usr/share/zoneinfo/Asia/Calcutta /etc/localtime
Rebooted the machine and check date only to see the below
Mon Sep 16 16:06:13 UTC 2019
Did this a few times with also changing the Zone to Kolkata, nothing changes. Any suggestions would be really helpful.
The simplest way to change EC2 timezone is to run the following command when logged in
$ sudo dpkg-reconfigure tzdata
this will open screen to select geographical areas, use enter to select and further select city and enter.
This will change the timezone for your currently logged in EC2 instance.
for instance setting for Europe Amsterdam timezone. (when run as root)
ln -sf /usr/share/zoneinfo/Europe/Amsterdam /etc/localtime
echo -e "ZONE="Europe/Amsterdam"\nUTC=true”>/etc/sysconfig/clock
reboot
this worked for me , but it requires a reboot.

ec2-import-instance makes an instance with no Public IP

This is related to my previous question. Basically, to summarize: I
1) Set up a vagrant ubuntu 14.04 box locally
2) Packaged the vagrant instance into a package.box following these instructions
3) Converted the package.box into a .vmdk file using this function
4) Ran the following CLI command:
ec2-import-instance tmpdir/box-disk1.vmdk -f VMDK -t t2.micro -a x86_64 -b <S3 Bucket> -o $AWS_ACCESS_KEY -w $AWS_SECRET_KEY -p Linux
Since I suspected the problem was with something called cloud-init I read about (but have never used/don't really know what it does), I tried the above twice: once with the original /etc/cloud/cloud.cfg file and again with the /etc/cloud/cloud.cfg file I found here.
Basically, what I'm eventually seeing in the AWS Console is a running instance that does not have a Public IP address. I attached an Elastic IP to the instance, but I can't ssh into that IP address for some reason - it says port 22: Connection refused
I'm at a loss because these instances are launching in the Default VPC which has a security group attached to it that allows all ports and all protocols from any IP.
By the way: I'm pretty new to all of AWS and don't really know my way fully around the console, so any direct guidance would be much appreciated.
Original /etc/cloud/cloud.cfg file:
# The top level settings are used as module
# and system configuration.
# A set of users which may be applied and/or used by various modules
# when a 'default' entry is found it will reference the 'default_user'
# from the distro configuration specified below
users:
- default
# If this is set, 'root' will not be able to ssh in and they
# will get a message to login instead as the above $user (ubuntu)
disable_root: true
# This will cause the set+update hostname module to not operate (if true)
preserve_hostname: false
# Example datasource config
# datasource:
# Ec2:
# metadata_urls: [ 'blah.com' ]
# timeout: 5 # (defaults to 50 seconds)
# max_wait: 10 # (defaults to 120 seconds)
# The modules that run in the 'init' stage
cloud_init_modules:
- migrator
- seed_random
- bootcmd
- write-files
- growpart
- resizefs
- set_hostname
- update_hostname
- update_etc_hosts
- ca-certs
- rsyslog
- users-groups
- ssh
# The modules that run in the 'config' stage
cloud_config_modules:
# Emit the cloud config ready event
# this can be used by upstart jobs for 'start on cloud-config'.
- emit_upstart
- disk_setup
- mounts
- ssh-import-id
- locale
- set-passwords
- grub-dpkg
- apt-pipelining
- apt-configure
- package-update-upgrade-install
- landscape
- timezone
- puppet
- chef
- salt-minion
- mcollective
- disable-ec2-metadata
- runcmd
- byobu
# The modules that run in the 'final' stage
cloud_final_modules:
- rightscale_userdata
- scripts-vendor
- scripts-per-once
- scripts-per-boot
- scripts-per-instance
- scripts-user
- ssh-authkey-fingerprints
- keys-to-console
- phone-home
- final-message
- power-state-change
# System and/or distro specific settings
# (not accessible to handlers/transforms)
system_info:
# This will affect which distro class gets used
distro: ubuntu
# Default user name + that default users groups (if added/used)
default_user:
name: ubuntu
lock_passwd: True
gecos: Ubuntu
groups: [adm, audio, cdrom, dialout, dip, floppy, netdev, plugdev, sudo, video]
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
shell: /bin/bash
# Other config here will be given to the distro class and/or path classes
paths:
cloud_dir: /var/lib/cloud/
templates_dir: /etc/cloud/templates/
upstart_dir: /etc/init/
package_mirrors:
- arches: [i386, amd64]
failsafe:
primary: http://archive.ubuntu.com/ubuntu
security: http://security.ubuntu.com/ubuntu
search:
primary:
- http://%(ec2_region)s.ec2.archive.ubuntu.com/ubuntu/
- http://%(availability_zone)s.clouds.archive.ubuntu.com/ubuntu/
- http://%(region)s.clouds.archive.ubuntu.com/ubuntu/
security: []
- arches: [armhf, armel, default]
failsafe:
primary: http://ports.ubuntu.com/ubuntu-ports
security: http://ports.ubuntu.com/ubuntu-ports
ssh_svcname: ssh
Second try /etc/cloud/cloud.cfg file:
users:
- default
disable_root: 1
ssh_pwauth: 0
locale_configfile: /etc/sysconfig/i18n
mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2']
resize_rootfs_tmp: /dev
ssh_deletekeys: 0
ssh_genkeytypes: ~
syslog_fix_perms: ~
cloud_init_modules:
- bootcmd
- write-files
- resizefs
- set_hostname
- update_hostname
- update_etc_hosts
- rsyslog
- users-groups
- ssh
cloud_config_modules:
- mounts
- locale
- set-passwords
- timezone
- runcmd
cloud_final_modules:
- scripts-per-once
- scripts-per-boot
- scripts-per-instance
- scripts-user
- ssh-authkey-fingerprints
- keys-to-console
- final-message
system_info:
distro: rhel
default_user:
name: ec2-user
paths:
cloud_dir: /var/lib/cloud
templates_dir: /etc/cloud/templates
ssh_svcname: sshd
EOF
This is happening because when you transferred the instance to AWS from your local there was no any PEM key associated with that instance due to which you were not able to SSH.
After you took an Image of your instance and launched the instance again with a associated key you were able to SSH into the instance.

How to find Unused Amazon EC2 Security groups

I'm try to find a way to determine orphan security groups so I can clean up and get rid of them. Does anyone know of a way to discover unused security groups.
Either through the console or with the command line tools will work (Running command line tools on linux and OSX machines).
Note: this only considers security use in EC2, not other services like RDS. You'll need to do more work to include security groups used outside EC2. The good thing is you can't easily (might not even be possible) to delete active security groups if you miss one associated w/another service.
Using the newer AWS CLI tool, I found an easy way to get what I need:
First, get a list of all security groups
aws ec2 describe-security-groups --query 'SecurityGroups[*].GroupId' --output text | tr '\t' '\n'
Then get all security groups tied to an instance, then piped to sort then uniq:
aws ec2 describe-instances --query 'Reservations[*].Instances[*].SecurityGroups[*].GroupId' --output text | tr '\t' '\n' | sort | uniq
Then put it together and compare the 2 lists and see what's not being used from the master list:
comm -23 <(aws ec2 describe-security-groups --query 'SecurityGroups[*].GroupId' --output text | tr '\t' '\n'| sort) <(aws ec2 describe-instances --query 'Reservations[*].Instances[*].SecurityGroups[*].GroupId' --output text | tr '\t' '\n' | sort | uniq)
If you select all of your security groups in the EC2 console, then press actions -> Delete Security Groups, a popup will appear telling you that you cannot delete security groups that are attached to instances, other security groups, or network interfaces, and it will list the security groups that you can delete; ie the unused security groups.
NOTE: according to #andrewlorien’s comment this does not work for all types of AWS services.
This is the sample code written in boto (Python SDK for AWS) to list the Security Group against number of instances it is associated with.
You may use this logic to obtain the same in command line as well
Boto Code
import boto
ec2 = boto.connect_ec2()
sgs = ec2.get_all_security_groups()
for sg in sgs:
print sg.name, len(sg.instances())
Output
Security-Group-1 0
Security-Group-2 1
Security-Group-3 0
Security-Group-4 3
After about a year of unaudited use, I found it necessary to audit my AWS EC2 security groups and clean up legacy, unused groups.
This was a daunting task to perform via the web GUI, so I looked to the AWS CLI to make the task easier. I found a start on how to do this at StackOverflow, but it was far from complete. So I decided to write my own script. I used the AWS CLI, MySQL and some “Bash-foo” to perform the following:
Get a list of all EC2 security groups.
I store the group-id, group-name and description in a tabled called “groups” in a MySQL database called aws_security_groups on the localhost. The total number of groups found is reported to the user.
Get a list of all security groups associated with each of the following services and exclude them from the table:
EC2 Istances
EC2 Elastic Load Balancers
AWS RDS Instances
AWS OpsWorks (shouldn’t be removed per Amazon)
Default security groups (Can’t be deleted)
ElastiCache
For each service I report a count of the number of groups left in the table after the exclusion is complete.
Finally I display the group-id, group-name and description for the groups that are left. These are the “unused” groups that need to be audited and/or deleted. I’ve found that SG’s between instances and Elastic Load Balancers (ELBs) often refer to each other. It’s best practice to do some manual investigation to ensure they are truly not in use prior to removing the cross references and deleting the security groups. But my script at least pares this down to something mor manageable.
NOTES:
1. You will want to create a file to store your MySQL host, username and password and point the $DBCONFIG variable to it. It should be structured like this:
[mysql]
host=your-mysql-server-host.com
user=your-mysql-user
password=your-mysql-user-password
You can change the name of the database if you wish – make sure to change the $DB variable in the script
Let me know if you find this useful or have any comments,fixes or enhancements.
Here is the script.
#!/bin/bash
# Initialize Variables
DBCONFIG="--defaults-file=mysql-defaults.cnf"
DB="aws_security_groups"
SGLOOP=0
EC2LOOP=0
ELBLOOP=0
RDSLOOP=0
DEFAULTLOOP=0
OPSLOOP=0
CACHELOOP=0
DEL_GROUP=""
# Function to report back # of rows
function Rows {
ROWS=`echo "select count(*) from groups" | mysql $DBCONFIG --skip-column-names $DB`
# echo -e "Excluding $1 Security Groups.\nGroups Left to audit: "$ROWS
echo -e $ROWS" groups left after Excluding $1 Security Groups."
}
# Empty the table
echo -e "delete from groups where groupid is not null" | mysql $DBCONFIG $DB
# Get all Security Groups
aws ec2 describe-security-groups --query "SecurityGroups[*].[GroupId,GroupName,Description]" --output text > /tmp/security_group_audit.txt
while IFS=$'\t' read -r -a myArray
do
if [ $SGLOOP -eq 0 ];
then
VALUES="(\""${myArray[0]}"\",\""${myArray[1]}"\",\""${myArray[2]}"\")"
else
VALUES=$VALUES",(\""${myArray[0]}"\",\""${myArray[1]}"\",\""${myArray[2]}"\")"
fi
let SGLOOP="$SGLOOP + 1"
done < /tmp/security_group_audit.txt
echo -e "insert into groups (groupid, groupname, description) values $VALUES" | mysql $DBCONFIG $DB
echo -e $SGLOOP" security groups total."
# Exclude Security Groups assigned to Instances
for groupId in `aws ec2 describe-instances --output json | jq -r ".Reservations[].Instances[].SecurityGroups[].GroupId" | sort | uniq`
do
if [ $EC2LOOP -eq 0 ];
then
DEL_GROUP="'$groupId'"
else
DEL_GROUP=$DEL_GROUP",'$groupId'"
fi
let EC2LOOP="$EC2LOOP + 1"
done
echo -e "delete from groups where groupid in ($DEL_GROUP)" | mysql $DBCONFIG $DB
Rows "EC2 Instance"
DEL_GROUP=""
# Exclude groups assigned to Elastic Load Balancers
for elbGroupId in `aws elb describe-load-balancers --output json | jq -c -r ".LoadBalancerDescriptions[].SecurityGroups" | tr -d "\"[]\"" | sort | uniq`
do
if [ $ELBLOOP -eq 0 ];
then
DEL_GROUP="'$elbGroupId'"
else
DEL_GROUP=$DEL_GROUP",'$elbGroupId'"
fi
let ELBLOOP="$ELBLOOP + 1"
done
echo -e "delete from groups where groupid in ($DEL_GROUP)" | mysql $DBCONFIG $DB
Rows "Elastic Load Balancer"
DEL_GROUP=""
# Exclude groups assigned to RDS
for RdsGroupId in `aws rds describe-db-instances --output json | jq -c -r ".DBInstances[].VpcSecurityGroups[].VpcSecurityGroupId" | sort | uniq`
do
if [ $RDSLOOP -eq 0 ];
then
DEL_GROUP="'$RdsGroupId'"
else
DEL_GROUP=$DEL_GROUP",'$RdsGroupId'"
fi
let RDSLOOP="$RDSLOOP + 1"
done
echo -e "delete from groups where groupid in ($DEL_GROUP)" | mysql $DBCONFIG $DB
Rows "RDS Instances"
DEL_GROUP=""
# Exclude groups assigned to OpsWorks
for OpsGroupId in `echo -e "select groupid from groups where groupname like \"AWS-OpsWorks%\"" | mysql $DBCONFIG $DB`
do
if [ $OPSLOOP -eq 0 ];
then
DEL_GROUP="'$OpsGroupId'"
else
DEL_GROUP=$DEL_GROUP",'$OpsGroupId'"
fi
let OPSLOOP="$OPSLOOP + 1"
done
echo -e "delete from groups where groupid in ($DEL_GROUP)" | mysql $DBCONFIG $DB
Rows "OpsWorks"
DEL_GROUP=""
# Exclude default groups (can't be deleted)
for DefaultGroupId in `echo -e "select groupid from groups where groupname like \"default%\"" | mysql $DBCONFIG $DB`
do
if [ $DEFAULTLOOP -eq 0 ];
then
DEL_GROUP="'$DefaultGroupId'"
else
DEL_GROUP=$DEL_GROUP",'$DefaultGroupId'"
fi
let DEFAULTLOOP="$DEFAULTLOOP + 1"
done
echo -e "delete from groups where groupid in ($DEL_GROUP)" | mysql $DBCONFIG $DB
Rows "Default"
DEL_GROUP=""
# Exclude Elasticache groups
for CacheGroupId in `aws elasticache describe-cache-clusters --output json | jq -r ".CacheClusters[].SecurityGroups[].SecurityGroupId" | sort | uniq`
do
if [ $CACHELOOP -eq 0 ];
then
DEL_GROUP="'$CacheGroupId'"
else
DEL_GROUP=$DEL_GROUP",'$CacheGroupId'"
fi
let CACHELOOP="$CACHELOOP + 1"
done
echo -e "delete from groups where groupid in ($DEL_GROUP)" | mysql $DBCONFIG $DB
Rows "ElastiCache"
# Display Security Groups left to audit / delete
echo "select * from groups order by groupid" | mysql $DBCONFIG $DB | sed 's/groupid\t/groupid\t\t/'
And here is the sql to create the database.
-- MySQL dump 10.13 Distrib 5.5.41, for debian-linux-gnu (x86_64)
--
-- Host: localhost Database: aws_security_groups
-- ------------------------------------------------------
-- Server version 5.5.40-log
/*!40101 SET #OLD_CHARACTER_SET_CLIENT=##CHARACTER_SET_CLIENT */;
/*!40101 SET #OLD_CHARACTER_SET_RESULTS=##CHARACTER_SET_RESULTS */;
/*!40101 SET #OLD_COLLATION_CONNECTION=##COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET #OLD_TIME_ZONE=##TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET #OLD_UNIQUE_CHECKS=##UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET #OLD_FOREIGN_KEY_CHECKS=##FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET #OLD_SQL_MODE=##SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET #OLD_SQL_NOTES=##SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `groups`
--
DROP TABLE IF EXISTS `groups`;
/*!40101 SET #saved_cs_client = ##character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `groups` (
`groupid` varchar(12) DEFAULT NULL,
`groupname` varchar(200) DEFAULT NULL,
`description` varchar(200) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = #saved_cs_client */;
--
-- Dumping data for table `groups`
--
LOCK TABLES `groups` WRITE;
/*!40000 ALTER TABLE `groups` DISABLE KEYS */;
/*!40000 ALTER TABLE `groups` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=#OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=#OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=#OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=#OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=#OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=#OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=#OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=#OLD_SQL_NOTES */;
-- Dump completed on 2015-01-27 16:07:44
Among other functions, both ScoutSuite and Prowler report unused EC2 Security Groups. Both are open source.
A boto example printing the Group IDs and Names only of the security groups that have no current instances.
It also shows how to specify which region you are concerned with.
import boto
import boto.ec2
EC2_REGION='ap-southeast-2'
ec2region = boto.ec2.get_region(EC2_REGION)
ec2 = boto.connect_ec2(region=ec2region)
sgs = ec2.get_all_security_groups()
for sg in sgs:
if len(sg.instances()) == 0:
print ("{0}\t{1}".format(sg.id, sg.name))
To confirm which security groups are still being used you should reverse or remove the if len(sg.instances()) == 0 test and print the len(sg.instances()) value out.
E.g.
print ("{0}\t{1}\t{2} instances".format(sg.id, sg.name, len(sg.instances())))
Using the node.js AWS SDK I can confirm that AWS doesn't allow you to delete security groups that are in use. I wrote a script that simply tries to delete all groups and gracefully handles the errors. This works for classic and the modern VPC. The error message can be seen below.
Err { [DependencyViolation: resource sg-12345678 has a dependent object]
message: 'resource sg-12345678 has a dependent object',
code: 'DependencyViolation',
time: Mon Dec 07 2015 12:12:43 GMT-0500 (EST),
statusCode: 400,
retryable: false,
retryDelay: 30 }
To the SGs attached to the network interfaces:
By name:
aws ec2 describe-network-interfaces --output text --query NetworkInterfaces[*].Groups[*].GroupName | tr -d '\r' | tr "\t" "\n" | sort | uniq
By id:
aws ec2 describe-network-interfaces --output text --query NetworkInterfaces[*].Groups[*].GroupId | tr -d '\r' | tr "\t" "\n" | sort | uniq
I was searching for the same info.
How to find all security groups that are not attached to any resource? And I found this:
Using AWS config rule "EC2_SECURITY_GROUP_ATTACHED_TO_ENI," I got a list of checks that non-default security groups are attached to Amazon Elastic Compute Cloud (EC2) instances or elastic network interfaces (ENIs). The rule returns NON_COMPLIANT if the security group is not associated with an EC2 instance or an ENI.
This is a very old question and I'm sure there are more ways to skin this AWS cat, but here's my solution in bash (you'll need jq for this to work):
REGION="eu-west-1"
SGLIST=$(aws ec2 describe-security-groups --query 'SecurityGroups[*].GroupId' | jq -r .[])
echo $SGLIST | xargs -n1 | while read SG; do [ "$(aws ec2 describe-network-interfaces --filters Name=group-id,Values=$SG --region $REGION | jq .NetworkInterfaces)" != '[]' ] || echo $SG; done
Remember to replace REGION with whatever region you're using.
The 1st step is to get a list of security groups.
Then we're checking for each security group if there's a network interface associated with it - this is not limited to EC2 instances, it checks anything that has a network interface (LBs, RDS, etc).
For reference see here.
Unfortunately the chosen answer is not as accurate as I need (I've tried to investigate the why, but I've preferred to implement it).
If I check ALL NetworkInterfaces, looking for attachments to any SecurityGroup, It gets me partial results. If I check only on EC2Instances, it gets me back partial results as well.
So that's my approach to the problem:
I get ALL EC2 SecurityGroups -> all_secgrp
I get ALL EC2 Instances -> all_instances
For each Instance, I get all SecurityGroups attached to it
I remove from all_secgrp each of these SecurityGroup (because attached)
For each SecurityGroup, I check an association with any NetworkInterfaces (using the filter function and filtering using that security-group-id)
IF no association is found, I remove the security-group from all_secgrp
Attached you can see a snippet of code. Don't complain for efficiency, but try to optimize it if you want.
all_secgrp = list(ec2_connector.security_groups.all())
all_instances = ec2_connector.instances.all()
for single_instance in all_instances:
instance_secgrp = ec2_connector.Instance(single_instance.id).security_groups
for single_sec_grp in instance_secgrp:
if ec2.SecurityGroup(id=single_sec_grp['GroupId']) in all_secgrp:
all_secgrp.remove(ec2.SecurityGroup(id=single_sec_grp['GroupId']))
all_secgrp_detached_tmp = all_secgrp[:]
for single_secgrp in all_secgrp_detached_tmp:
try:
print(single_secgrp.id)
if len(list(ec2_connector.network_interfaces.filter(Filters=[{'Name': 'group-id', 'Values': [single_secgrp.id]}]))) > 0:
all_secgrp.remove(single_secgrp)
except Exception:
all_secgrp.remove(single_secgrp)
return all_secgrp_detached
There's a tool in the AWS marketplace that makes this a lot easier. It shows you which groups are attached/detached for easy deletion, but it also compares your VPC Flow Logs against the security group rules and shows you which SG rules are in use or unused. AWS posted an ELK-stack solution to do this, but it was ridiculously complex.
Here's the tool, and a disclaimer that I worked on it. But I hope you all find it pertinent:
https://www.piasoftware.net/single-post/2018/04/24/VIDEO-Watch-as-we-clean-up-EC2-security-groups-in-just-a-few-minutes
This is a difficult problem, if you have security groups that reference other security groups in the rules. If so, you'll have to resolve DependencyErrors, which is not trivial.
If you are only using IP addresses, then this solution will work, after you create a boto3 client:
# pull all security groups from all vpcs in the given profile and region and save as a set
all_sgs = {sg['GroupId'] for sg in client.describe_security_groups()['SecurityGroups']}
# create a new set for all of the security groups that are currently in use
in_use = set()
# cycle through the ENIs and add all found security groups to the in_use set
for eni in client.describe_network_interfaces()['NetworkInterfaces']:
for group in eni['Groups']:
in_use.add(group['GroupId'])
unused_security_groups = all_sgs - in_use
for security_group in unused_security_groups:
try:
response = client.delete_security_group(GroupId=security_group)
except ClientError as e:
if e.response['Error']['Code'] == 'DependencyViolation':
print('EC2/Security Group Dependencies Exist')
else:
print('Unexpected error: {}'.format(e))