forked from duo-labs/cloudmapper
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request duo-labs#484 from 0xdabbad00/find_more_unused_reso…
…urces Find more unused resources
- Loading branch information
Showing
4 changed files
with
399 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,14 @@ | ||
from __future__ import print_function | ||
from shared.common import parse_arguments | ||
from commands.prepare import build_data_structure | ||
import pyjq | ||
from shared.common import parse_arguments, make_list, query_aws, get_regions | ||
from shared.nodes import Account, Region | ||
import json | ||
|
||
__description__ = "Find unused resources in accounts" | ||
from shared.common import parse_arguments | ||
from shared.find_unused import find_unused_resources | ||
|
||
|
||
__description__ = "Find unused resources in accounts" | ||
|
||
def run(arguments): | ||
_, accounts, config = parse_arguments(arguments) | ||
|
||
unused_resources = [] | ||
for account in accounts: | ||
unused_resources_for_account = [] | ||
for region_json in get_regions(Account(None, account)): | ||
unused_resources_for_region = {} | ||
used_sgs = set() | ||
|
||
region = Region(Account(None, account), region_json) | ||
defined_sgs = query_aws( | ||
Account(None, account), "ec2-describe-security-groups", region | ||
) | ||
|
||
network_interfaces = query_aws( | ||
Account(None, account), "ec2-describe-network-interfaces", region | ||
) | ||
|
||
defined_sg_set = {} | ||
|
||
for sg in pyjq.all(".SecurityGroups[]", defined_sgs): | ||
defined_sg_set[sg["GroupId"]] = sg | ||
|
||
for used_sg in pyjq.all( | ||
".NetworkInterfaces[].Groups[].GroupId", network_interfaces | ||
): | ||
used_sgs.add(used_sg) | ||
|
||
unused_sg_ids = set(defined_sg_set) - used_sgs | ||
unused_sgs = [] | ||
for sg_id in unused_sg_ids: | ||
unused_sgs.append( | ||
{ | ||
"id": sg_id, | ||
"name": defined_sg_set[sg_id]["GroupName"], | ||
"description": defined_sg_set[sg_id].get("Description", ""), | ||
} | ||
) | ||
|
||
unused_resources_for_region["security_groups"] = unused_sgs | ||
|
||
unused_resources_for_account.append( | ||
{ | ||
"region": region_json["RegionName"], | ||
"unused_resources": unused_resources_for_region, | ||
} | ||
) | ||
unused_resources.append( | ||
{ | ||
"account": {"id": account["id"], "name": account["name"]}, | ||
"regions": unused_resources_for_account, | ||
} | ||
) | ||
unused_resources = find_unused_resources(accounts) | ||
|
||
print(json.dumps(unused_resources, indent=2, sort_keys=True)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import pyjq | ||
|
||
from shared.common import query_aws, get_regions | ||
from shared.nodes import Account, Region | ||
|
||
|
||
def find_unused_security_groups(region): | ||
# Get the defined security groups, then find all the Security Groups associated with the | ||
# ENIs. Then diff these to find the unused Security Groups. | ||
used_sgs = set() | ||
|
||
defined_sgs = query_aws(region.account, "ec2-describe-security-groups", region) | ||
|
||
network_interfaces = query_aws( | ||
region.account, "ec2-describe-network-interfaces", region | ||
) | ||
|
||
defined_sg_set = {} | ||
|
||
for sg in pyjq.all(".SecurityGroups[]", defined_sgs): | ||
defined_sg_set[sg["GroupId"]] = sg | ||
|
||
for used_sg in pyjq.all( | ||
".NetworkInterfaces[].Groups[].GroupId", network_interfaces | ||
): | ||
used_sgs.add(used_sg) | ||
|
||
unused_sg_ids = set(defined_sg_set) - used_sgs | ||
unused_sgs = [] | ||
for sg_id in unused_sg_ids: | ||
unused_sgs.append( | ||
{ | ||
"id": sg_id, | ||
"name": defined_sg_set[sg_id]["GroupName"], | ||
"description": defined_sg_set[sg_id].get("Description", ""), | ||
} | ||
) | ||
return unused_sgs | ||
|
||
|
||
def find_unused_volumes(region): | ||
unused_volumes = [] | ||
volumes = query_aws(region.account, "ec2-describe-volumes", region) | ||
for volume in pyjq.all('.Volumes[]|select(.State=="available")', volumes): | ||
unused_volumes.append({"id": volume["VolumeId"]}) | ||
|
||
return unused_volumes | ||
|
||
|
||
def find_unused_elastic_ips(region): | ||
unused_ips = [] | ||
ips = query_aws(region.account, "ec2-describe-addresses", region) | ||
for ip in pyjq.all(".Addresses[] | select(.AssociationId == null)", ips): | ||
unused_ips.append({"id": ip["AllocationId"], "ip": ip["PublicIp"]}) | ||
|
||
return unused_ips | ||
|
||
|
||
def find_unused_network_interfaces(region): | ||
unused_network_interfaces = [] | ||
network_interfaces = query_aws( | ||
region.account, "ec2-describe-network-interfaces", region | ||
) | ||
for network_interface in pyjq.all( | ||
'.NetworkInterfaces[]|select(.Status=="available")', network_interfaces | ||
): | ||
unused_network_interfaces.append( | ||
{"id": network_interface["NetworkInterfaceId"]} | ||
) | ||
|
||
return unused_network_interfaces | ||
|
||
|
||
def add_if_exists(dictionary, key, value): | ||
if value: | ||
dictionary[key] = value | ||
|
||
|
||
def find_unused_resources(accounts): | ||
unused_resources = [] | ||
for account in accounts: | ||
unused_resources_for_account = [] | ||
for region_json in get_regions(Account(None, account)): | ||
region = Region(Account(None, account), region_json) | ||
|
||
unused_resources_for_region = {} | ||
|
||
add_if_exists( | ||
unused_resources_for_region, | ||
"security_groups", | ||
find_unused_security_groups(region), | ||
) | ||
add_if_exists( | ||
unused_resources_for_region, "volumes", find_unused_volumes(region) | ||
) | ||
add_if_exists( | ||
unused_resources_for_region, | ||
"elastic_ips", | ||
find_unused_elastic_ips(region), | ||
) | ||
add_if_exists( | ||
unused_resources_for_region, | ||
"network_interfaces", | ||
find_unused_network_interfaces(region), | ||
) | ||
|
||
unused_resources_for_account.append( | ||
{ | ||
"region": region_json["RegionName"], | ||
"unused_resources": unused_resources_for_region, | ||
} | ||
) | ||
unused_resources.append( | ||
{ | ||
"account": {"id": account["id"], "name": account["name"]}, | ||
"regions": unused_resources_for_account, | ||
} | ||
) | ||
return unused_resources |
Oops, something went wrong.