├── .gitignore ├── LICENSE.md ├── README.md ├── remove_vpc.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 2 | 3 | # General 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | 12 | # Thumbnails 13 | ._* 14 | 15 | # Files that might appear in the root of a volume 16 | .DocumentRevisions-V100 17 | .fseventsd 18 | .Spotlight-V100 19 | .TemporaryItems 20 | .Trashes 21 | .VolumeIcon.icns 22 | .com.apple.timemachine.donotpresent 23 | 24 | # Directories potentially created on remote AFP share 25 | .AppleDB 26 | .AppleDesktop 27 | Network Trash Folder 28 | Temporary Items 29 | .apdisk 30 | 31 | # https://github.com/github/gitignore/blob/master/Python.gitignore 32 | 33 | # Byte-compiled / optimized / DLL files 34 | __pycache__/ 35 | *.py[cod] 36 | *$py.class 37 | 38 | # C extensions 39 | *.so 40 | 41 | # Distribution / packaging 42 | .Python 43 | build/ 44 | develop-eggs/ 45 | dist/ 46 | downloads/ 47 | eggs/ 48 | .eggs/ 49 | lib/ 50 | lib64/ 51 | parts/ 52 | sdist/ 53 | var/ 54 | wheels/ 55 | *.egg-info/ 56 | .installed.cfg 57 | *.egg 58 | MANIFEST 59 | 60 | # PyInstaller 61 | # Usually these files are written by a python script from a template 62 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 63 | *.manifest 64 | *.spec 65 | 66 | # Installer logs 67 | pip-log.txt 68 | pip-delete-this-directory.txt 69 | 70 | # Unit test / coverage reports 71 | htmlcov/ 72 | .tox/ 73 | .nox/ 74 | .coverage 75 | .coverage.* 76 | .cache 77 | nosetests.xml 78 | coverage.xml 79 | *.cover 80 | .hypothesis/ 81 | .pytest_cache/ 82 | 83 | # Translations 84 | *.mo 85 | *.pot 86 | 87 | # Django stuff: 88 | *.log 89 | local_settings.py 90 | db.sqlite3 91 | 92 | # Flask stuff: 93 | instance/ 94 | .webassets-cache 95 | 96 | # Scrapy stuff: 97 | .scrapy 98 | 99 | # Sphinx documentation 100 | docs/_build/ 101 | 102 | # PyBuilder 103 | target/ 104 | 105 | # Jupyter Notebook 106 | .ipynb_checkpoints 107 | 108 | # IPython 109 | profile_default/ 110 | ipython_config.py 111 | 112 | # pyenv 113 | .python-version 114 | 115 | # celery beat schedule file 116 | celerybeat-schedule 117 | 118 | # SageMath parsed files 119 | *.sage.py 120 | 121 | # Environments 122 | .env 123 | .venv 124 | env/ 125 | venv/ 126 | ENV/ 127 | env.bak/ 128 | venv.bak/ 129 | 130 | # Spyder project settings 131 | .spyderproject 132 | .spyproject 133 | 134 | # Rope project settings 135 | .ropeproject 136 | 137 | # mkdocs documentation 138 | /site 139 | 140 | # mypy 141 | .mypy_cache/ 142 | .dmypy.json 143 | dmypy.json 144 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Todd Murchison 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Remove AWS Default VPCs 2 | 3 | This Python script attempts to delete the AWS default VPC in each region. 4 | 5 | **Requirements:** 6 | 7 | * Tested with: 8 | * Python version: 3.7.0 9 | * Boto3 version: 1.7.50 10 | * Botocore version: 1.10.50 11 | * Valid AWS API keys/profile 12 | 13 | **Setup:** 14 | 15 | Update with your AWS profile / credentials. 16 | 17 | ``` 18 | main(profile = '') 19 | ``` 20 | 21 | **Usage:** 22 | 23 | ``` 24 | python remove_vpc.py 25 | ``` 26 | 27 | **Output:** 28 | 29 | ``` 30 | VPC vpc-0b43a362 has been deleted from the ap-south-1 region. 31 | VPC vpc-b22dd5db has been deleted from the eu-west-3 region. 32 | VPC vpc-74b7551d has been deleted from the eu-west-2 region. 33 | VPC vpc-3f71855a has been deleted from the eu-west-1 region. 34 | VPC vpc-d58e6cbc has been deleted from the ap-northeast-2 region. 35 | VPC (default) was not found in the ap-northeast-1 region. 36 | VPC vpc-4053e625 has been deleted from the sa-east-1 region. 37 | VPC vpc-4c06ea25 has been deleted from the ca-central-1 region. 38 | VPC vpc-7b80631e has been deleted from the ap-southeast-1 region. 39 | VPC vpc-41db3924 has been deleted from the ap-southeast-2 region. 40 | VPC vpc-47ea0b2e has been deleted from the eu-central-1 region. 41 | VPC vpc-1c558e79 has existing resources in the us-east-1 region. 42 | VPC (default) was not found in the us-east-2 region. 43 | VPC (default) was not found in the us-west-1 region. 44 | VPC vpc-1839c57d has existing resources in the us-west-2 region. 45 | ``` 46 | 47 | **References:** 48 | 49 | * https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html 50 | 51 | -------------------------------------------------------------------------------- /remove_vpc.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Remove those pesky AWS default VPCs. 4 | 5 | Python Version: 3.7.0 6 | Boto3 Version: 1.7.50 7 | 8 | """ 9 | 10 | import boto3 11 | from botocore.exceptions import ClientError 12 | 13 | 14 | def delete_igw(ec2, vpc_id): 15 | """ 16 | Detach and delete the internet gateway 17 | """ 18 | 19 | args = { 20 | 'Filters' : [ 21 | { 22 | 'Name' : 'attachment.vpc-id', 23 | 'Values' : [ vpc_id ] 24 | } 25 | ] 26 | } 27 | 28 | try: 29 | igw = ec2.describe_internet_gateways(**args)['InternetGateways'] 30 | except ClientError as e: 31 | print(e.response['Error']['Message']) 32 | 33 | if igw: 34 | igw_id = igw[0]['InternetGatewayId'] 35 | 36 | try: 37 | result = ec2.detach_internet_gateway(InternetGatewayId=igw_id, VpcId=vpc_id) 38 | except ClientError as e: 39 | print(e.response['Error']['Message']) 40 | 41 | try: 42 | result = ec2.delete_internet_gateway(InternetGatewayId=igw_id) 43 | except ClientError as e: 44 | print(e.response['Error']['Message']) 45 | 46 | return 47 | 48 | 49 | def delete_subs(ec2, args): 50 | """ 51 | Delete the subnets 52 | """ 53 | 54 | try: 55 | subs = ec2.describe_subnets(**args)['Subnets'] 56 | except ClientError as e: 57 | print(e.response['Error']['Message']) 58 | 59 | if subs: 60 | for sub in subs: 61 | sub_id = sub['SubnetId'] 62 | 63 | try: 64 | result = ec2.delete_subnet(SubnetId=sub_id) 65 | except ClientError as e: 66 | print(e.response['Error']['Message']) 67 | 68 | return 69 | 70 | 71 | def delete_rtbs(ec2, args): 72 | """ 73 | Delete the route tables 74 | """ 75 | 76 | try: 77 | rtbs = ec2.describe_route_tables(**args)['RouteTables'] 78 | except ClientError as e: 79 | print(e.response['Error']['Message']) 80 | 81 | if rtbs: 82 | for rtb in rtbs: 83 | main = 'false' 84 | for assoc in rtb['Associations']: 85 | main = assoc['Main'] 86 | if main == True: 87 | continue 88 | rtb_id = rtb['RouteTableId'] 89 | 90 | try: 91 | result = ec2.delete_route_table(RouteTableId=rtb_id) 92 | except ClientError as e: 93 | print(e.response['Error']['Message']) 94 | 95 | return 96 | 97 | 98 | def delete_acls(ec2, args): 99 | """ 100 | Delete the network access lists (NACLs) 101 | """ 102 | 103 | try: 104 | acls = ec2.describe_network_acls(**args)['NetworkAcls'] 105 | except ClientError as e: 106 | print(e.response['Error']['Message']) 107 | 108 | if acls: 109 | for acl in acls: 110 | default = acl['IsDefault'] 111 | if default == True: 112 | continue 113 | acl_id = acl['NetworkAclId'] 114 | 115 | try: 116 | result = ec2.delete_network_acl(NetworkAclId=acl_id) 117 | except ClientError as e: 118 | print(e.response['Error']['Message']) 119 | 120 | return 121 | 122 | 123 | def delete_sgps(ec2, args): 124 | """ 125 | Delete any security groups 126 | """ 127 | 128 | try: 129 | sgps = ec2.describe_security_groups(**args)['SecurityGroups'] 130 | except ClientError as e: 131 | print(e.response['Error']['Message']) 132 | 133 | if sgps: 134 | for sgp in sgps: 135 | default = sgp['GroupName'] 136 | if default == 'default': 137 | continue 138 | sg_id = sgp['GroupId'] 139 | 140 | try: 141 | result = ec2.delete_security_group(GroupId=sg_id) 142 | except ClientError as e: 143 | print(e.response['Error']['Message']) 144 | 145 | return 146 | 147 | 148 | def delete_vpc(ec2, vpc_id, region): 149 | """ 150 | Delete the VPC 151 | """ 152 | 153 | try: 154 | result = ec2.delete_vpc(VpcId=vpc_id) 155 | except ClientError as e: 156 | print(e.response['Error']['Message']) 157 | 158 | else: 159 | print('VPC {} has been deleted from the {} region.'.format(vpc_id, region)) 160 | 161 | return 162 | 163 | 164 | def get_regions(ec2): 165 | """ 166 | Return all AWS regions 167 | """ 168 | 169 | regions = [] 170 | 171 | try: 172 | aws_regions = ec2.describe_regions()['Regions'] 173 | except ClientError as e: 174 | print(e.response['Error']['Message']) 175 | 176 | else: 177 | for region in aws_regions: 178 | regions.append(region['RegionName']) 179 | 180 | return regions 181 | 182 | 183 | def main(profile): 184 | """ 185 | Do the work.. 186 | 187 | Order of operation: 188 | 189 | 1.) Delete the internet gateway 190 | 2.) Delete subnets 191 | 3.) Delete route tables 192 | 4.) Delete network access lists 193 | 5.) Delete security groups 194 | 6.) Delete the VPC 195 | """ 196 | 197 | # AWS Credentials 198 | # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html 199 | 200 | session = boto3.Session(profile_name=profile) 201 | ec2 = session.client('ec2', region_name='us-east-1') 202 | 203 | regions = get_regions(ec2) 204 | 205 | for region in regions: 206 | 207 | ec2 = session.client('ec2', region_name=region) 208 | 209 | try: 210 | attribs = ec2.describe_account_attributes(AttributeNames=[ 'default-vpc' ])['AccountAttributes'] 211 | except ClientError as e: 212 | print(e.response['Error']['Message']) 213 | return 214 | 215 | else: 216 | vpc_id = attribs[0]['AttributeValues'][0]['AttributeValue'] 217 | 218 | if vpc_id == 'none': 219 | print('VPC (default) was not found in the {} region.'.format(region)) 220 | continue 221 | 222 | # Are there any existing resources? Since most resources attach an ENI, let's check.. 223 | 224 | args = { 225 | 'Filters' : [ 226 | { 227 | 'Name' : 'vpc-id', 228 | 'Values' : [ vpc_id ] 229 | } 230 | ] 231 | } 232 | 233 | try: 234 | eni = ec2.describe_network_interfaces(**args)['NetworkInterfaces'] 235 | except ClientError as e: 236 | print(e.response['Error']['Message']) 237 | return 238 | 239 | if eni: 240 | print('VPC {} has existing resources in the {} region.'.format(vpc_id, region)) 241 | continue 242 | 243 | result = delete_igw(ec2, vpc_id) 244 | result = delete_subs(ec2, args) 245 | result = delete_rtbs(ec2, args) 246 | result = delete_acls(ec2, args) 247 | result = delete_sgps(ec2, args) 248 | result = delete_vpc(ec2, vpc_id, region) 249 | 250 | return 251 | 252 | 253 | if __name__ == "__main__": 254 | 255 | main(profile = '') 256 | 257 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | boto3==1.7.50 2 | botocore==1.10.50 3 | --------------------------------------------------------------------------------