Last active
October 18, 2024 18:03
Revisions
-
atheiman revised this gist
Mar 10, 2022 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -60,7 +60,7 @@ Resources: Properties: RoleName: !Ref RoleName Path: '/' Description: Used by organization automation to delete Default VPCs AssumeRolePolicyDocument: Version: '2012-10-17' Statement: @@ -94,7 +94,7 @@ Resources: Properties: RoleName: !Ref CrossAcctRoleName Path: '/' Description: Used by organization automation to delete Default VPCs AssumeRolePolicyDocument: Version: '2012-10-17' Statement: -
atheiman revised this gist
Mar 10, 2022 . 1 changed file with 6 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,8 @@ Description: >- Scheduled Lambda function to delete Default VPC from all accounts in the organization. Deploys an IAM role via CloudFormation stackset to the organization root and schedules a Lambda function to assume the IAM roles in each account and attempt to delete the Default VPCs in every Region. If any operation fails, or if a Default VPC is not empty, or if a Default VPC has a peering connection, the function invocation fails. Parameters: OrgRootId: @@ -122,7 +126,7 @@ Resources: DeleteAllOrganizationDefaultVpcsFunction: Type: AWS::Lambda::Function Properties: Description: Deletes all Default VPCs across the AWS organization Role: !Sub '${DeleteAllOrganizationDefaultVpcsFunctionRole.Arn}' Handler: index.handler Timeout: 300 -
atheiman created this gist
Mar 10, 2022 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,315 @@ Description: Scheduled Lambda function to delete Default VPC from all accounts in the organization Parameters: OrgRootId: Type: String AllowedPattern: '^r-[a-zA-Z0-9]+$' Description: >- Organization root ID. Example: "r-abcd" CrossAcctRoleName: Type: String Default: DeleteDefaultVpc Description: Name of IAM role to be created in all accounts in the organization with permission to delete VPCs. ScheduleExpression: Type: String Default: 'cron(0 18 * * ? *)' Description: >- Frequency to invoke the Lambda function to attempt to delete all Default VPCs in the organization. Default cron expression invokes the function once daily at 6:00pm UTC. See https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html Resources: StackSet: Type: AWS::CloudFormation::StackSet Properties: StackSetName: !Ref AWS::StackName AutoDeployment: Enabled: True RetainStacksOnAccountRemoval: False Capabilities: [CAPABILITY_NAMED_IAM] OperationPreferences: RegionConcurrencyType: PARALLEL FailureTolerancePercentage: 25 MaxConcurrentPercentage: 100 PermissionModel: SERVICE_MANAGED StackInstancesGroup: - DeploymentTargets: OrganizationalUnitIds: [!Ref OrgRootId] Regions: [!Ref AWS::Region] Tags: - Key: CfnStackId Value: !Ref AWS::StackId Parameters: - ParameterKey: AssumeRolePrincipal ParameterValue: !Sub '${DeleteAllOrganizationDefaultVpcsFunctionRole.Arn}' - ParameterKey: RoleName ParameterValue: !Ref CrossAcctRoleName TemplateBody: | Parameters: AssumeRolePrincipal: Type: String RoleName: Type: String Resources: CrossAcctRole: Type: AWS::IAM::Role Properties: RoleName: !Ref RoleName Path: '/' Description: Used by organization automation to delete Default VPC AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: AWS: !Sub '${AssumeRolePrincipal}' Action: sts:AssumeRole Tags: - Key: CfnStackId Value: !Ref AWS::StackId ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonEC2ReadOnlyAccess' Policies: - PolicyName: Inline PolicyDocument: Version: '2012-10-17' Statement: # Be sure to keep these permissions in sync between stackset role and org mgmt acct role - Effect: Allow Action: - ec2:DescribeRegions - ec2:DetachInternetGateway - ec2:DeleteInternetGateway - ec2:DeleteSubnet - ec2:DeleteSecurityGroup - ec2:DeleteVpc Resource: '*' CrossAcctRole: Type: AWS::IAM::Role Properties: RoleName: !Ref CrossAcctRoleName Path: '/' Description: Used by organization automation to delete Default VPC AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: AWS: !Sub '${DeleteAllOrganizationDefaultVpcsFunctionRole.Arn}' Action: sts:AssumeRole Tags: - Key: CfnStackId Value: !Ref AWS::StackId ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonEC2ReadOnlyAccess' Policies: - PolicyName: Inline PolicyDocument: Version: '2012-10-17' Statement: # Be sure to keep these permissions in sync between stackset role and org mgmt acct role - Effect: Allow Action: - ec2:DescribeRegions - ec2:DetachInternetGateway - ec2:DeleteInternetGateway - ec2:DeleteSubnet - ec2:DeleteSecurityGroup - ec2:DeleteVpc Resource: '*' DeleteAllOrganizationDefaultVpcsFunction: Type: AWS::Lambda::Function Properties: Description: 'Deletes all Default VPCs across the AWS organization' Role: !Sub '${DeleteAllOrganizationDefaultVpcsFunctionRole.Arn}' Handler: index.handler Timeout: 300 Runtime: python3.8 Tags: - Key: CfnStackId Value: !Ref AWS::StackId Environment: Variables: ASSUME_ROLE_ARN_FMT_STR: !Sub 'arn:${AWS::Partition}:iam::{acct_id}:role/${CrossAcctRoleName}' Code: ZipFile: | import boto3 import botocore import json import os import traceback import cfnresponse orgs = boto3.client("organizations", region_name=os.environ["AWS_REGION"]) sts = boto3.client("sts", region_name=os.environ["AWS_REGION"]) local_ec2 = boto3.client("ec2", region_name=os.environ["AWS_REGION"]) def handler(event, context): print(json.dumps(event, default=str)) # Capture errors and do not raise until finished configuring all monitoring errors = [] regions = local_ec2.describe_regions()["Regions"] accts = [] for pg in orgs.get_paginator('list_accounts').paginate(): accts += pg['Accounts'] for acct in accts: print(f"Info - Deleting Default VPCs in account '{acct['Id']}'") role_arn = os.environ["ASSUME_ROLE_ARN_FMT_STR"].format(acct_id=acct['Id']) creds = sts.assume_role(RoleArn=role_arn, RoleSessionName=os.environ["AWS_LAMBDA_FUNCTION_NAME"][:63])[ "Credentials" ] boto3_client_args = { "aws_access_key_id": creds["AccessKeyId"], "aws_secret_access_key": creds["SecretAccessKey"], "aws_session_token": creds["SessionToken"], } for region in regions: regional_ec2_client = boto3.client("ec2", region_name=region["RegionName"], **boto3_client_args) print(f"Info - Deleting Default VPC in account '{acct['Id']}', Region '{region['RegionName']}'") try: delete_default_vpc(regional_ec2_client) except Exception as e: print( f"Error - Failed to delete Default VPC in account '{acct['Id']}', Region '{region['RegionName']}' - {repr(e)} - " f"{traceback.format_exc()}" ) errors.append(e) if errors: raise Exception("Errors encountered:", errors) def delete_default_vpc(ec2): try: default_vpc = ec2.describe_vpcs(Filters=[{"Name": "is-default", "Values": ["true"]}])["Vpcs"][0] except IndexError: print(f"Info - Default VPC not found") return skip_delete = False net_ifs = ec2.describe_network_interfaces( Filters=[{"Name": "vpc-id", "Values": [default_vpc["VpcId"]]}] )["NetworkInterfaces"] if net_ifs: skip_delete = True print( f"Warning - Found network interfaces in Default VPC '{default_vpc['VpcId']}'. " "Delete will likely fail, see network interfaces below:" ) print(json.dumps(net_ifs, default=str)) pcxs = ec2.describe_vpc_peering_connections( Filters=[ {"Name": "requester-vpc-info.vpc-id", "Values": [default_vpc["VpcId"]]}, {"Name": "status-code", "Values": ["pending-acceptance", "failed", "expired", "provisioning", "active", "deleting"]}, ] )["VpcPeeringConnections"] pcxs += ec2.describe_vpc_peering_connections( Filters=[ {"Name": "accepter-vpc-info.vpc-id", "Values": [default_vpc["VpcId"]]}, {"Name": "status-code", "Values": ["pending-acceptance", "failed", "expired", "provisioning", "active", "deleting"]}, ] )["VpcPeeringConnections"] if pcxs: skip_delete = True print( f"Warning - Found peering connections in Default VPC '{default_vpc['VpcId']}'. " "Delete will likely fail, see peering connections below:" ) print(json.dumps(pcxs, default=str)) if skip_delete: raise Exception( f"Found dependencies in Default VPC '{default_vpc['VpcId']}', see above. " "Not attempting to delete." ) igws = ec2.describe_internet_gateways( Filters=[{"Name": "attachment.vpc-id", "Values": [default_vpc["VpcId"]]}] )["InternetGateways"] for igw in igws: print(f"Info - Detaching internet gateway '{igw['InternetGatewayId']}' from Default VPC") ec2.detach_internet_gateway(InternetGatewayId=igw["InternetGatewayId"], VpcId=default_vpc["VpcId"]) print(f"Info - Deleting internet gateway '{igw['InternetGatewayId']}'") ec2.delete_internet_gateway(InternetGatewayId=igw["InternetGatewayId"]) subnets = ec2.describe_subnets(Filters=[{"Name": "vpc-id", "Values": [default_vpc["VpcId"]]}])["Subnets"] for subnet in subnets: print(f"Info - Deleting Default VPC subnet '{subnet['SubnetId']}'") ec2.delete_subnet(SubnetId=subnet["SubnetId"]) security_groups = ec2.describe_security_groups( Filters=[{"Name": "vpc-id", "Values": [default_vpc["VpcId"]]}] )["SecurityGroups"] for security_group in security_groups: if security_group["GroupName"] == "default": continue print( f"Info - Deleting Default VPC security group '{security_group['GroupId']}' " f"('{security_group['GroupName']}')" ) ec2.delete_security_group(GroupId=security_group["GroupId"]) print(f"Info - Deleting Default VPC '{default_vpc['VpcId']}'") ec2.delete_vpc(VpcId=default_vpc["VpcId"]) print(f"Info - Deleted Default VPC '{default_vpc['VpcId']}'") DeleteAllOrganizationDefaultVpcsFunctionLogGroup: Type: AWS::Logs::LogGroup DeletionPolicy: Retain UpdateReplacePolicy: Retain Properties: LogGroupName: !Sub /aws/lambda/${DeleteAllOrganizationDefaultVpcsFunction} RetentionInDays: 90 DeleteAllOrganizationDefaultVpcsFunctionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole Tags: - Key: CfnStackId Value: !Ref AWS::StackId ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' Policies: - PolicyName: Inline PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - organizations:ListAccounts - ec2:DescribeRegions - sts:AssumeRole Resource: '*' DeleteAllOrganizationDefaultVpcsFunctionEventsRule: Type: AWS::Events::Rule Properties: State: ENABLED ScheduleExpression: !Ref ScheduleExpression Targets: - Id: DeleteAllOrganizationDefaultVpcsFunction Arn: !Sub '${DeleteAllOrganizationDefaultVpcsFunction.Arn}' DeleteAllOrganizationDefaultVpcsFunctionPermissionEventsRule: Type: AWS::Lambda::Permission Properties: FunctionName: !Ref DeleteAllOrganizationDefaultVpcsFunction Action: lambda:InvokeFunction Principal: events.amazonaws.com SourceArn: !Sub '${DeleteAllOrganizationDefaultVpcsFunctionEventsRule.Arn}'