class AwsInternetGateway(RouteTarget): def __init__(self, environment, physical_id, **kwargs): super().__init__("AWS::EC2::InternetGateway", environment, physical_id, **kwargs) @RouteTarget.capture_method def capture(self): ec2 = boto3.client("ec2") if self._source_data is None: source_data = ec2.describe_internet_gateways(InternetGatewayIds=[self._physical_id])["InternetGateways"][0] else: source_data = self._source_data self._source_data = None self._tags = TagSet({"CreatedBy": "CloudPrep"}) self._tags.from_api_result(source_data["Tags"]) vpc = self._route.route_table.vpc iga = AwsVpcGatewayAttachment( self._environment, vpc.physical_id + self.physical_id, vpc=vpc ) iga.set_internet_gateway(self) VpcAttachmentRegistry.register_attachment(vpc, self, iga) self._environment.add_to_todo(iga) self.is_valid = True
class AwsManagedPrefixList(AwsElement): def __init__(self, environment, physical_id, **kwargs): super().__init__(environment, "AWS::EC2::PrefixList", physical_id, **kwargs) self._physical_id = physical_id self._tags = TagSet({"CreatedBy": "CloudPrep"}) @AwsElement.capture_method def capture(self): ec2 = boto3.client("ec2") source_data = ec2.describe_managed_prefix_lists( PrefixListIds=[self._physical_id])["PrefixLists"][0] if source_data["OwnerId"] == "AWS": raise Exception("Prefix list " + self._physical_id + " appears to be AWS-managed.") self.copy_if_exists("AddressFamily", source_data) self.copy_if_exists("MaxEntries", source_data) self.copy_if_exists("PrefixListName", source_data) self.copy_if_exists( "Entries", ec2.get_managed_prefix_list_entries( PrefixListId=self._physical_id)) self._tags.from_api_result(source_data) self.is_valid = True
def __init__(self, environment, physical_id, **kwargs): super().__init__(environment, "AWS::EC2::Subnet", physical_id, **kwargs) self.set_defaults({ "AssignIpv6AddressOnCreation": False, "MapPublicIpOnLaunch": False }) self._tags = TagSet({"CreatedBy": "CloudPrep"}) self._route_table = None
class AwsSubnet(AwsElement): def __init__(self, environment, physical_id, **kwargs): super().__init__(environment, "AWS::EC2::Subnet", physical_id, **kwargs) self.set_defaults({ "AssignIpv6AddressOnCreation": False, "MapPublicIpOnLaunch": False }) self._tags = TagSet({"CreatedBy": "CloudPrep"}) self._route_table = None @AwsElement.capture_method def capture(self): ec2 = boto3.client("ec2") if self._source_data is None: source_data = ec2.describe_subnets( SubnetIds=[self._physical_id])["Subnets"][0] else: source_data = self._source_data self._source_data = None self._element["AssignIpv6AddressOnCreation"] = source_data[ "AssignIpv6AddressOnCreation"] self._element["AvailabilityZone"] = self.abstract_az( source_data["AvailabilityZone"]) self._element["CidrBlock"] = source_data["CidrBlock"] self._element["MapPublicIpOnLaunch"] = source_data[ "MapPublicIpOnLaunch"] if len(source_data["Ipv6CidrBlockAssociationSet"]) > 0: self._element["Ipv6CidrBlock"] = source_data[ "Ipv6CidrBlockAssociationSet"][0] self.copy_if_exists("OutpostArn", source_data) self._element["VpcId"] = { "Ref": (self._environment.logical_from_physical(source_data["VpcId"])) } if "Tags" in source_data: self._tags.from_api_result(source_data["Tags"]) self.is_valid = True def set_route_table(self, route_table): self._route_table = route_table def has_route_table(self): return self._route_table is not None @staticmethod def abstract_az(az_name): letter = az_name[-1] position = ord(letter) - ord('a') return {"Fn::Select": [position, {"Fn::GetAZs": ""}]}
def __init__(self, environment, physical_id, **kwargs): super().__init__(environment, "AWS::Lambda::Function", physical_id, **kwargs) self.set_defaults({ "Description": "", "MemorySize": 128, "ReservedConcurrentExecutions": 1000, "Timeout": 3 }) self._tags = TagSet({"CreatedBy": "CloudPrep"})
def __init__(self, environment, physical_id, **kwargs): super().__init__("AWS::EC2::TransitGateway", environment, physical_id, **kwargs) self.set_defaults({ "AmazonSideAsn": 64512, "AutoAcceptSharedAttachments": "disable", "DefaultRouteTableAssociation": "enable", "DefaultRouteTablePropagation": "enable", "DnsSupport": "enable", "VpnEcmpSupport": "enable", "MulticastSupport": "disable" }) self._tags = TagSet({"CreatedBy": "CloudPrep"})
class AwsTransitGateway(RouteTarget): def __init__(self, environment, physical_id, **kwargs): super().__init__("AWS::EC2::TransitGateway", environment, physical_id, **kwargs) self.set_defaults({ "AmazonSideAsn": 64512, "AutoAcceptSharedAttachments": "disable", "DefaultRouteTableAssociation": "enable", "DefaultRouteTablePropagation": "enable", "DnsSupport": "enable", "VpnEcmpSupport": "enable", "MulticastSupport": "disable" }) self._tags = TagSet({"CreatedBy": "CloudPrep"}) @RouteTarget.capture_method def capture(self): ec2 = boto3.client("ec2") source_data = ec2.describe_transit_gateways(TransitGatewayIds=[self._physical_id])["TransitGateways"][0] self.copy_if_exists("Description", source_data) self.copy_if_exists("AmazonSideAsn", source_data["Options"]) self.copy_if_exists("AutoAcceptSharedAttachments", source_data["Options"]) self.copy_if_exists("DefaultRouteTableAssociation", source_data["Options"]) self.copy_if_exists("DefaultRouteTablePropagation", source_data["Options"]) # TODO: If Multicast is true, capture the domain and the attachment (then spider!) self.copy_if_exists("MulticastSupport", source_data["Options"]) self.copy_if_exists("VpnEcmpSupport", source_data["Options"]) self.copy_if_exists("DnsSupport", source_data["Options"]) self._tags.from_api_result(source_data) # TODO: Capture TGW Routes and Route Tables. attachments = ec2.describe_transit_gateway_vpc_attachments()["TransitGatewayVpcAttachments"] for attachment in [x for x in attachments if x["TransitGatewayId"] == self.physical_id]: tgva = AwsTransitGatewayVpcAttachment( self._environment, attachment["TransitGatewayAttachmentId"], source_data=attachment ) self._environment.add_to_todo(tgva) VpcAttachmentRegistry.register_attachment(self._route.route_table.vpc, self, tgva) self.is_valid = True
class AwsSecurityGroup(AwsElement): def __init__(self, environment, physical_id, **kwargs): super().__init__(environment, "AWS::EC2::SecurityGroup", physical_id, **kwargs) self._physical_id = physical_id self._tags = TagSet({"CreatedBy": "CloudPrep"}) @AwsElement.capture_method def capture(self): ec2 = boto3.client("ec2") if self._source_data is None: source_data = ec2.describe_security_groups( GroupIds=[self._physical_id])["SecurityGroups"][0] else: source_data = self._source_data self._source_data = None self._element["GroupDescription"] = source_data["Description"] self._element["VpcId"] = self._environment.find_by_physical_id( source_data["VpcId"]).make_reference() if source_data["GroupName"] == "default": self._element["GroupName"] = "was-default" else: self._element["GroupName"] = source_data["GroupName"] if "Tags" in source_data: self._tags.from_api_result(source_data) ingress_rules = IngressRuleset(self._environment, self) ingress_rules.process(source_data["OwnerId"], source_data["IpPermissions"]) self._element["SecurityGroupIngress"] = ingress_rules.data egress_rules = EgressRuleset(self._environment, self) egress_rules.process(source_data["OwnerId"], source_data["IpPermissionsEgress"]) self._element["SecurityGroupEgress"] = egress_rules.data self.is_valid = True
class AwsLambdaFunction(AwsElement): def __init__(self, environment, physical_id, **kwargs): super().__init__(environment, "AWS::Lambda::Function", physical_id, **kwargs) self.set_defaults({ "Description": "", "MemorySize": 128, "ReservedConcurrentExecutions": 1000, "Timeout": 3 }) self._tags = TagSet({"CreatedBy": "CloudPrep"}) @AwsElement.capture_method def capture(self): # { # "Type" : "AWS::Lambda::Function", # "Properties" : { # *"Code" : Code, # "CodeSigningConfigArn" : String, # "DeadLetterConfig" : DeadLetterConfig, # "FileSystemConfigs" : [ FileSystemConfig, ... ], # "ImageConfig" : ImageConfig, # "KmsKeyArn" : String, # "Layers" : [ String, ... ], # "PackageType" : String, # "TracingConfig" : TracingConfig, # "VpcConfig" : VpcConfig # } # } lmb = boto3.client("lambda") source_data = lmb.get_function(FunctionName=self.physical_id) configuration = source_data["Configuration"] self._source_data = None self.copy_if_exists("Description", configuration) self.copy_if_exists("Environment", configuration) self.copy_if_exists("Handler", configuration) self.copy_if_exists("MemorySize", configuration) self.copy_if_exists("Runtime", configuration) self.copy_if_exists("Timeout", configuration) role = AwsRole(self._environment, AwsARN(configuration["Role"])) self._element["Role"] = role.make_getatt("Arn") self._environment.add_to_todo(role) self._tags.from_api_result(source_data) if "Concurrency" in source_data: self.copy_if_exists("ReservedConcurrentExecutions", source_data["Concurrency"]) # Code. This is complex. if source_data["Code"]["RepositoryType"] == "S3": self._code_s3(source_data["Code"]) self.is_valid = True def create_from_arn(environment, arn: AwsARN, **kwargs): return AwsLambdaFunction(environment, arn.resource_id, **kwargs) def _code_s3(self, code_data): code_request = requests.get(code_data["Location"]) if code_request.status_code != 200: raise Exception("Couldn't download code bundle from " + code_data["Location"]) afr = ArtefactRepository.get_repository() code_artefact = Artefact("lambda-" + self.logical_id + "-code.zip", code_request.content) afr.store_artefact(code_artefact) self._environment.add_parameter( Name="ArtefactBucket", Description="The bucket in which our artefacts are stored.") self._environment.add_parameter(Name=self.logical_id + "CodeKey", Description="The key for the " + self.logical_id + " lambda code package.", Default=code_artefact.name) self._element["Code"] = { "S3Bucket": { "Ref": "ArtefactBucket" }, "S3Key": { "Ref": self.logical_id + "CodeKey" } }
def __init__(self, environment, physical_id, **kwargs): super().__init__(environment, "AWS::EC2::SimpleElement", physical_id, kwargs) self.set_defaults({}) self._tags = TagSet({"CreatedBy": "CloudPrep"})
def __init__(self, environment, physical_id, **kwargs): super().__init__(environment, "AWS::EC2::RouteTable", physical_id, **kwargs) self._tags = TagSet({"CreatedBy": "CloudPrep"}) self._vpc = kwargs["vpc"] self._has_associations = False self._routes = []
class AwsRouteTable(AwsElement): def __init__(self, environment, physical_id, **kwargs): super().__init__(environment, "AWS::EC2::RouteTable", physical_id, **kwargs) self._tags = TagSet({"CreatedBy": "CloudPrep"}) self._vpc = kwargs["vpc"] self._has_associations = False self._routes = [] @AwsElement.capture_method def capture(self): ec2 = boto3.client("ec2") self._source_data = None if self._source_data is None: source_data = ec2.describe_route_tables(RouteTableIds=[self._physical_id])["RouteTables"][0] else: source_data = self._source_data self._source_data = None self._element["VpcId"] = self.vpc.make_reference() self._tags.from_api_result(source_data) # All the subnets! for association in source_data["Associations"]: # Is this the Main route table? if association["Main"]: self._vpc.set_main_route_table(self) self._tags.add_tag("cloudprep:wasMain", "True") else: self.associate_with_subnet(association["SubnetId"]) for i, route in zip(range(len(source_data["Routes"])), source_data["Routes"]): rt = AwsRoute(self._environment, self._physical_id + "-route" + str(i), source_data=route, route_table=self) self._routes.append(rt) self._environment.add_to_todo(rt) self.is_valid = True @property def vpc(self): return self._vpc def associate_with_subnet(self, subnet_id): assoc = AwsSubnetRouteTableAssociation( self._environment, self._physical_id + "-" + subnet_id, self, subnet_id ) self._environment.add_to_todo(assoc) self._has_associations = True @AwsElement.finalise_method def finalise(self): more_work = False # If we have no associations, we might not need to be here =) if not self._has_associations and str(self._tags.get_tag("cloudprep:forceCapture")).upper() != "TRUE": self.is_valid = False for route in self._routes: route.is_valid = False return # Find those that depend on a TGW and add the explicit dependency # vpc_id = self.vpc.logical_id # for route in self._routes: # if "TransitGatewayId" in route.properties: # tga = VpcAttachmentRegistry.get_attachment( # vpc_logical_id=vpc_id, # subject_logical_id=route.properties["TransitGatewayId"]["Ref"] # ) # # if tga is None: # # We get here because we have multiple VPCs pointing at a single TGW. One of those probably # # caused the TGW to be made, but it hasn't yet been linked everywhere. # # tgw = self._environment.find_by_logical_id(route.properties["TransitGatewayId"]["Ref"]) # tga = AwsTransitGatewayVpcAttachment(self._environment, tgw.physical_id + "attach" + vpc_id, route=route) # print("VPC Logical ID =", vpc_id,file=sys.stderr) # print("TGW Logical ID =", tgw.logical_id,file=sys.stderr) # print("TGA Logical ID =", tga.logical_id,file=sys.stderr) # self._environment.add_to_todo(tga) # # VpcAttachmentRegistry.register_attachment(self.vpc, tgw, tga) # # more_work = True # else: # route.add_dependency(tga.logical_id) return more_work
def __init__(self, environment, physical_id, **kwargs): super().__init__(environment, "AWS::EC2::TransitGatewayAttachment", physical_id, **kwargs) self.set_defaults({}) self._tags = TagSet({"CreatedBy": "CloudPrep"})
def __init__(self, environment, physical_id, **kwargs): super().__init__(environment, "AWS::EC2::PrefixList", physical_id, **kwargs) self._physical_id = physical_id self._tags = TagSet({"CreatedBy": "CloudPrep"})