def test_optional(self): input_str = '{"NoTagsHere": []}' field = TagsField(optional=True) input_data = json.loads(input_str) link_collection = field.parse(data=input_data, context={}) self.assertCountEqual(link_collection, LinkCollection())
def test_optional(self): input_str = '{"NoTagsHere": []}' field = TagsField(optional=True) expected_output_data = [] input_data = json.loads(input_str) links = field.parse(data=input_data, context={}) output_data = [link.to_dict() for link in links] self.assertCountEqual(output_data, expected_output_data)
def test_invalid_input(self): input_str = ( '{"Mags": [{"Key": "tag1", "Value": "value1"}, {"Key": "tag2", "Value": "value2"}]}' ) field = TagsField(optional=False) input_data = json.loads(input_str) with self.assertRaises(TagsFieldMissingTagsKeyException): field.parse(data=input_data, context={})
def test_valid_input(self): input_str = ( '{"Tags": [{"Key": "tag1", "Value": "value1"}, {"Key": "tag2", "Value": "value2"}]}' ) field = TagsField() expected_output_data = [ {"pred": "tag1", "obj": "value1", "type": "tag"}, {"pred": "tag2", "obj": "value2", "type": "tag"}, ] input_data = json.loads(input_str) links = field.parse(data=input_data, context={}) output_data = [link.to_dict() for link in links] self.assertCountEqual(output_data, expected_output_data)
class VpcEndpointResourceSpec(EC2ResourceSpec): """Resource for VPC Endpoints""" type_name = "vpc-endpoint" schema = Schema( ScalarField("VpcEndpointType"), ScalarField("ServiceName"), ScalarField("State"), ResourceLinkField("VpcId", VPCResourceSpec), TagsField(), ) @classmethod def list_from_aws( cls: Type["VpcEndpointResourceSpec"], client: BaseClient, account_id: str, region: str ) -> ListFromAWSResult: """Return a dict of dicts of the format: {'vpc_endpoint_1_arn': {vpc_endpoint_1_dict}, 'vpc_endpoint_1_arn': {vpc_endpoint_2_dict}, ...} Where the dicts represent results from describe_vpc_endpoints.""" endpoints = {} paginator = client.get_paginator("describe_vpc_endpoints") for resp in paginator.paginate(): for endpoint in resp.get("VpcEndpoints", []): resource_arn = cls.generate_arn(account_id, region, endpoint["VpcEndpointId"]) endpoints[resource_arn] = endpoint return ListFromAWSResult(resources=endpoints)
class SubnetResourceSpec(EC2ResourceSpec): """Resource for Subnets""" type_name = "subnet" schema = Schema( ScalarField("CidrBlock"), ScalarField("FirstIp"), ScalarField("LastIp"), ScalarField("State"), TagsField(), ResourceLinkField("VpcId", VPCResourceSpec), ) @classmethod def list_from_aws( cls: Type["SubnetResourceSpec"], client: BaseClient, account_id: str, region: str ) -> ListFromAWSResult: subnets = {} resp = client.describe_subnets() for subnet in resp.get("Subnets", []): resource_arn = cls.generate_arn( account_id=account_id, region=region, resource_id=subnet["SubnetId"] ) cidr = subnet["CidrBlock"] ipv4_network = ipaddress.IPv4Network(cidr, strict=False) first_ip, last_ip = int(ipv4_network[0]), int(ipv4_network[-1]) subnet["FirstIp"] = first_ip subnet["LastIp"] = last_ip subnets[resource_arn] = subnet return ListFromAWSResult(resources=subnets)
class VPCResourceSpec(EC2ResourceSpec): """Resource for VPCs""" type_name = "vpc" schema = Schema(ScalarField("IsDefault"), ScalarField("CidrBlock"), ScalarField("State"), TagsField()) @classmethod def list_from_aws(cls: Type["VPCResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'vpc_1_arn': {vpc_1_dict}, 'vpc_2_arn': {vpc_2_dict}, ...} Where the dicts represent results from describe_vpcs.""" vpcs = {} paginator = client.get_paginator("describe_vpcs") for resp in paginator.paginate(): for vpc in resp.get("Vpcs", []): resource_arn = cls.generate_arn(account_id=account_id, region=region, resource_id=vpc["VpcId"]) vpcs[resource_arn] = vpc return ListFromAWSResult(resources=vpcs)
class EBSSnapshotResourceSpec(EC2ResourceSpec): """Resource for EBSSnapshots""" type_name = "snapshot" schema = Schema( ScalarField("VolumeSize"), ScalarField("Encrypted"), TransientResourceLinkField("KmsKeyId", KMSKeyResourceSpec, optional=True, value_is_id=True), TransientResourceLinkField("VolumeId", EBSVolumeResourceSpec), TagsField(), ) @classmethod def list_from_aws(cls: Type["EBSSnapshotResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'snapshot_1_arn': {snapshot_1_dict}, 'snapshot_2_arn': {snapshot_2_dict}, ...} Where the dicts represent results from describe_snapshots.""" snapshots = {} paginator = client.get_paginator("describe_snapshots") for resp in paginator.paginate(OwnerIds=["self"]): for snapshot in resp.get("Snapshots", []): resource_arn = cls.generate_arn( account_id=account_id, region=region, resource_id=snapshot["SnapshotId"]) snapshots[resource_arn] = snapshot return ListFromAWSResult(resources=snapshots)
class EC2ImageResourceSpec(EC2ResourceSpec): """Resource for EC2Images (AMIs)""" type_name = "image" schema = Schema( ScalarField("Name", optional=True), ScalarField("Description", optional=True), ScalarField("Public"), TagsField(), ) @classmethod def list_from_aws(cls: Type["EC2ImageResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'image_1_arn': {image_1_dict}, 'image_2_arn': {image_2_dict}, ...} Where the dicts represent results from describe_images.""" images = {} resp = client.describe_images(Owners=["self"]) for image in resp["Images"]: image_id = image["ImageId"] resource_arn = cls.generate_arn(account_id=account_id, region=region, resource_id=image_id) images[resource_arn] = image return ListFromAWSResult(resources=images)
class EC2InstanceResourceSpec(EC2ResourceSpec): """Resource for EC2Instances""" type_name = "instance" schema = Schema( ScalarField("Name", optional=True), TransientResourceLinkField("ImageId", EC2ImageResourceSpec), ScalarField("KeyName", optional=True), AnonymousDictField("Placement", ScalarField("AvailabilityZone"), ScalarField("Tenancy")), ScalarField("InstanceType"), ScalarField("LaunchTime"), AnonymousDictField("State", ScalarField("Name", "state")), ScalarField("Platform", optional=True), ScalarField("PrivateIpAddress", optional=True), ScalarField("PrivateDnsName", optional=True), ScalarField("PublicIpAddress", optional=True), ScalarField("PublicDnsName", optional=True), ResourceLinkField("VpcId", VPCResourceSpec, optional=True), ResourceLinkField("SubnetId", SubnetResourceSpec, optional=True), AnonymousListField( "SecurityGroups", AnonymousEmbeddedDictField(ResourceLinkField("GroupId", SecurityGroupResourceSpec)), ), AnonymousDictField( "IamInstanceProfile", TransientResourceLinkField( "Arn", InstanceProfileResourceSpec, alti_key="instance_profile", value_is_id=True ), optional=True, ), TagsField(), ) @classmethod def list_from_aws( cls: Type["EC2InstanceResourceSpec"], client: BaseClient, account_id: str, region: str ) -> ListFromAWSResult: """Return a dict of dicts of the format: {'instance_1_arn': {instance_1_dict}, 'instance_2_arn': {instance_2_dict}, ...} Where the dicts represent results from describe_instances.""" paginator = client.get_paginator("describe_instances") instances = {} for resp in paginator.paginate(): for reservation in resp.get("Reservations", []): for instance in reservation.get("Instances", []): resource_arn = cls.generate_arn( account_id=account_id, region=region, resource_id=instance["InstanceId"] ) instances[resource_arn] = instance for tag in instance.get("Tags", []): if tag["Key"].lower() == "name": instance["Name"] = tag["Value"] break return ListFromAWSResult(resources=instances)
class TransitGatewayResourceSpec(EC2ResourceSpec): """Resource for TransitGateways""" # type_name = "transit-gateway" type_name = "transit_gateway" schema = Schema( ScalarField("OwnerId"), ScalarField("State"), ListField( "VPCAttachments", EmbeddedDictField( ScalarField("CreationTime"), ScalarField("State"), ResourceLinkField("ResourceId", VPCResourceSpec), ), optional=True, alti_key="vpc_attachment", ), TagsField(), ) @classmethod def list_from_aws( cls: Type["TransitGatewayResourceSpec"], client: BaseClient, account_id: str, region: str ) -> ListFromAWSResult: """Return a dict of dicts of the format: {'tgw_1_arn': {tgw_1_dict}, 'tgw_2_arn': {tgw_2_dict}, ...} Where the dicts represent results from describe_transit_gateways.""" tgws = {} paginator = client.get_paginator("describe_transit_gateways") tgw_filters = [{"Name": "owner-id", "Values": [account_id]}] for resp in paginator.paginate(Filters=tgw_filters): for tgw in resp["TransitGateways"]: resource_arn = tgw["TransitGatewayArn"] vpc_attachments: List[Dict[str, Any]] = [] vpc_attachments_paginator = client.get_paginator( "describe_transit_gateway_attachments" ) vpc_filters = [ {"Name": "transit-gateway-id", "Values": [tgw["TransitGatewayId"]]}, {"Name": "resource-type", "Values": ["vpc"]}, ] for vpc_attachments_resp in vpc_attachments_paginator.paginate(Filters=vpc_filters): vpc_attachments += vpc_attachments_resp["TransitGatewayAttachments"] tgw["VPCAttachments"] = vpc_attachments tgws[resource_arn] = tgw return ListFromAWSResult(resources=tgws)
def test_valid_input(self): input_str = ( '{"Tags": [{"Key": "tag1", "Value": "value1"}, {"Key": "tag2", "Value": "value2"}]}' ) field = TagsField() expected_output_data = [ { "pred": "tag1", "obj": "value1", "type": "tag" }, { "pred": "tag2", "obj": "value2", "type": "tag" }, ] input_data = json.loads(input_str) link_collection = field.parse(data=input_data, context={}) expected_link_collection = LinkCollection(tag_links=(TagLink( pred="tag1", obj="value1"), TagLink(pred="tag2", obj="value2")), ) self.assertEqual(link_collection, expected_link_collection)
class EBSVolumeResourceSpec(EC2ResourceSpec): """Resource for EBSVolumes""" type_name = "volume" schema = Schema( ScalarField("AvailabilityZone"), ScalarField("CreateTime"), ScalarField("Size"), ScalarField("State"), ScalarField("VolumeType"), ScalarField("Encrypted"), ListField( "Attachments", EmbeddedDictField( ScalarField("AttachTime"), ScalarField("State"), ScalarField("DeleteOnTermination"), ResourceLinkField("InstanceId", EC2InstanceResourceSpec), ), optional=True, alti_key="attachment", ), TransientResourceLinkField("KmsKeyId", KMSKeyResourceSpec, optional=True, value_is_id=True), TagsField(), ) @classmethod def list_from_aws(cls: Type["EBSVolumeResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'volume_1_arn': {volume_1_dict}, 'volume_2_arn': {volume_2_dict}, ...} Where the dicts represent results from describe_volumes.""" volumes = {} paginator = client.get_paginator("describe_volumes") for resp in paginator.paginate(): for volume in resp.get("Volumes", []): resource_arn = cls.generate_arn(account_id=account_id, region=region, resource_id=volume["VolumeId"]) volumes[resource_arn] = volume return ListFromAWSResult(resources=volumes)
class InternetGatewayResourceSpec(EC2ResourceSpec): """Resource for InternetGateways""" # type_name = "internet-gateway" type_name = "internet_gateway" schema = Schema( ScalarField("OwnerId"), ListField( "Attachments", EmbeddedDictField( ScalarField("State"), ResourceLinkField("VpcId", VPCResourceSpec), ), optional=True, alti_key="attachment", ), TagsField(), ) @classmethod def list_from_aws(cls: Type["InternetGatewayResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'igw_1_arn': {igw_1_dict}, 'igw_2_arn': {igw_2_dict}, ...} Where the dicts represent results from describe_internet_gateways.""" igws = {} paginator = client.get_paginator("describe_internet_gateways") for resp in paginator.paginate(): for igw in resp["InternetGateways"]: resource_arn = cls.generate_arn( resource_id=igw["InternetGatewayId"], account_id=account_id, region=region) igws[resource_arn] = igw return ListFromAWSResult(resources=igws)
class VpcEndpointServiceResourceSpec(EC2ResourceSpec): """Resource for VPC Endpoint Services""" # type_name = "vpc-endpoint-service" type_name = "vpc_endpoint_service" schema = Schema( AnonymousListField("ServiceType", ScalarField("ServiceType")), ScalarField("ServiceName"), ScalarField("ServiceState"), ScalarField("AcceptanceRequired"), ListField("AvailabilityZones", EmbeddedScalarField()), TagsField(), ) @classmethod def list_from_aws( cls: Type["VpcEndpointServiceResourceSpec"], client: BaseClient, account_id: str, region: str, ) -> ListFromAWSResult: """Return a dict of dicts of the format: {'vpc_endpoint_svc_1_arn': {vpc_endpoint_svc_1_dict}, 'vpc_endpoint_svc_2_arn': {vpc_endpoint_svc_2_dict}, ...} Where the dicts represent results from describe_vpc_endpoint_service_configurations.""" services = {} paginator = client.get_paginator( "describe_vpc_endpoint_service_configurations") for resp in paginator.paginate(): for service in resp.get("ServiceConfigurations", []): resource_arn = cls.generate_arn( account_id=account_id, region=region, resource_id=service["ServiceId"]) services[resource_arn] = service return ListFromAWSResult(resources=services)
class RDSInstanceResourceSpec(RDSResourceSpec): """Resource for RDS""" type_name = "db" schema = Schema( TagsField(), ScalarField("DBInstanceIdentifier"), ScalarField("DBInstanceClass"), ScalarField("Engine"), ScalarField("DBInstanceStatus"), ScalarField("DBName", optional=True), AnonymousDictField( "Endpoint", ScalarField("Address", alti_key="endpoint_address", optional=True), ScalarField("Port", alti_key="endpoint_port"), ScalarField("HostedZoneId", alti_key="endpoint_hosted_zone", optional=True), optional=True, ), AnonymousDictField( "ListenerEndpoint", ScalarField("Address", alti_key="listener_address"), ScalarField("Port", alti_key="listener_port"), ScalarField("HostedZoneId", alti_key="listener_hosted_zone", optional=True), optional=True, ), ScalarField("InstanceCreateTime", optional=True), ScalarField("BackupRetentionPeriod"), AnonymousListField( "VpcSecurityGroups", AnonymousEmbeddedDictField( TransientResourceLinkField("VpcSecurityGroupId", SecurityGroupResourceSpec, optional=True)), ), ScalarField("AvailabilityZone", optional=True), AnonymousDictField("DBSubnetGroup", TransientResourceLinkField("VpcId", VPCResourceSpec), optional=True), ScalarField("MultiAZ"), ScalarField("PubliclyAccessible"), ListField("StatusInfos", EmbeddedDictField(ScalarField("Status")), optional=True), ScalarField("StorageType"), ScalarField("StorageEncrypted"), TransientResourceLinkField("KmsKeyId", KMSKeyResourceSpec, optional=True, value_is_id=True), ScalarField("DbiResourceId"), ScalarField("Timezone", optional=True), ScalarField("IAMDatabaseAuthenticationEnabled"), ScalarField("PerformanceInsightsEnabled", optional=True), ScalarField("PerformanceInsightsRetentionPeriod", optional=True), ScalarField("DeletionProtection"), ListField( "Backup", EmbeddedDictField( AnonymousDictField( "RestoreWindow", ScalarField("EarliestTime", alti_key="earliest_restore_time", optional=True), optional=True, ), AnonymousDictField( "RestoreWindow", ScalarField("LatestTime", alti_key="latest_restore_time", optional=True), optional=True, ), ScalarField("AllocatedStorage"), ScalarField("Status"), ScalarField("AvailabilityZone", optional=True), ScalarField("Engine"), ScalarField("EngineVersion"), ScalarField("Encrypted"), ScalarField("StorageType"), TransientResourceLinkField("KmsKeyId", KMSKeyResourceSpec, optional=True, value_is_id=True), ), optional=True, ), ) @classmethod def list_from_aws(cls: Type["RDSInstanceResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: logger = Logger() dbinstances = {} paginator = client.get_paginator("describe_db_instances") for resp in paginator.paginate(): for db in resp.get("DBInstances", []): resource_arn = db["DBInstanceArn"] db["Tags"] = client.list_tags_for_resource( ResourceName=resource_arn).get("TagList", []) db["Backup"] = [] dbinstances[resource_arn] = db backup_paginator = client.get_paginator( "describe_db_instance_automated_backups") for resp in backup_paginator.paginate(): for backup in resp.get("DBInstanceAutomatedBackups", []): if backup["DBInstanceArn"] in dbinstances: dbinstances[backup["DBInstanceArn"]]["Backup"].append( backup) else: logger.info( event=AWSLogEvents.ScanAWSResourcesNonFatalError, msg= (f'Unable to find matching DB Instance {backup["DBInstanceArn"]} ' "(Possible Deletion)"), ) return ListFromAWSResult(resources=dbinstances)
class S3BucketResourceSpec(S3ResourceSpec): """Resource for S3 Buckets""" type_name = "bucket" scan_granularity = ScanGranularity.ACCOUNT schema = Schema( ScalarField("Name"), ScalarField("CreationDate"), AnonymousDictField( "ServerSideEncryption", ListField( "Rules", EmbeddedDictField( AnonymousDictField( "ApplyServerSideEncryptionByDefault", ScalarField("SSEAlgorithm", "algorithm"), ), AnonymousDictField( "ApplyServerSideEncryptionByDefault", ResourceLinkField("KMSMasterKeyID", KMSKeyResourceSpec, optional=True), ), ), alti_key="server_side_default_encryption_rule", ), ), TagsField(), ) @classmethod def list_from_aws( cls: Type["S3BucketResourceSpec"], client: BaseClient, account_id: str, region: str ) -> ListFromAWSResult: """Return a dict of dicts of the format: {'bucket_1_arn': {bucket_1_dict}, 'bucket_2_arn': {bucket_2_dict}, ...} Where the dicts represent results from list_buckets.""" logger = Logger() buckets = {} buckets_resp = client.list_buckets() for bucket in buckets_resp.get("Buckets", []): bucket_name = bucket["Name"] try: try: bucket_region = get_s3_bucket_region(client, bucket_name) except S3BucketAccessDeniedException as s3ade: logger.warn( event=AWSLogEvents.ScanAWSResourcesNonFatalError, msg=f"Unable to determine region for {bucket_name}: {s3ade}", ) continue try: bucket["Tags"] = get_s3_bucket_tags(client, bucket_name) except S3BucketAccessDeniedException as s3ade: bucket["Tags"] = [] logger.warn( event=AWSLogEvents.ScanAWSResourcesNonFatalError, msg=f"Unable to determine tags for {bucket_name}: {s3ade}", ) try: bucket["ServerSideEncryption"] = get_s3_bucket_encryption(client, bucket_name) except S3BucketAccessDeniedException as s3ade: bucket["ServerSideEncryption"] = {"Rules": []} logger.warn( event=AWSLogEvents.ScanAWSResourcesNonFatalError, msg=f"Unable to determine encryption status for {bucket_name}: {s3ade}", ) resource_arn = cls.generate_arn( account_id=account_id, region=bucket_region, resource_id=bucket_name ) buckets[resource_arn] = bucket except S3BucketDoesNotExistException as s3bdnee: logger.warn( event=AWSLogEvents.ScanAWSResourcesNonFatalError, msg=f"{bucket_name}: No longer exists: {s3bdnee}", ) return ListFromAWSResult(resources=buckets)
class VPCPeeringConnectionResourceSpec(EC2ResourceSpec): """Resource for VPC Peering Connections.""" # type_name = "vpc-peering-connection" type_name = "vpc_peering_connection" schema = Schema( TransientResourceLinkField("AccepterVpc", VPCResourceSpec, value_is_id=True, alti_key="accepter_vpc"), TransientResourceLinkField("RequesterVpc", VPCResourceSpec, value_is_id=True, alti_key="requester_vpc"), ScalarField("Status"), TagsField(), ) @classmethod def list_from_aws( cls: Type["VPCPeeringConnectionResourceSpec"], client: BaseClient, account_id: str, region: str, ) -> ListFromAWSResult: """Return a dict of dicts of the format: {'vpc_peering_connection_1_arn': {vpc_peering_connection_1_dict}, 'vpc_peering_connection_2_arn': {vpc_peering_connection_2_dict}, ...} Where the dicts represent results from describe_vpc_peering_connections.""" vpc_peering_connections = {} paginator = client.get_paginator("describe_vpc_peering_connections") for resp in paginator.paginate(): for vpc_pc in resp["VpcPeeringConnections"]: resource_arn = cls.generate_arn( resource_id=vpc_pc["VpcPeeringConnectionId"], account_id=account_id, region=region, ) accepter_info = vpc_pc["AccepterVpcInfo"] accepter_account_id = accepter_info["OwnerId"] accepter_vpc_id = accepter_info["VpcId"] accepter_region = accepter_info["Region"] accepter_vpc_arn = VPCResourceSpec.generate_arn( resource_id=accepter_vpc_id, account_id=accepter_account_id, region=accepter_region, ) requester_info = vpc_pc["RequesterVpcInfo"] requester_account_id = requester_info["OwnerId"] requester_vpc_id = requester_info["VpcId"] requester_region = requester_info["Region"] requester_vpc_arn = VPCResourceSpec.generate_arn( resource_id=requester_vpc_id, account_id=requester_account_id, region=requester_region, ) vpc_peering_connection = { "AccepterVpc": accepter_vpc_arn, "RequesterVpc": requester_vpc_arn, "Status": vpc_pc["Status"]["Code"], } vpc_peering_connections[resource_arn] = vpc_peering_connection return ListFromAWSResult(resources=vpc_peering_connections)
class SecurityGroupResourceSpec(EC2ResourceSpec): """Resource for SecurityGroups""" type_name = "security-group" schema = Schema( ScalarField("GroupName", "name"), ListField( "IpPermissions", EmbeddedDictField( ScalarField("IpProtocol"), ScalarField("FromPort", default_value=0), ScalarField("ToPort", default_value=65535), ListField( "IpRanges", EmbeddedDictField( ScalarField("CidrIp"), ScalarField("FirstIp"), ScalarField("LastIp") ), alti_key="ip_range", optional=True, ), ListField( "Ipv6Ranges", EmbeddedDictField( ScalarField("CidrIpv6"), ScalarField("FirstIp"), ScalarField("LastIp") ), alti_key="ipv6_range", optional=True, ), ListField( "PrefixListIds", EmbeddedDictField(ScalarField("PrefixListId")), optional=True ), ListField( "UserIdGroupPairs", EmbeddedDictField( ResourceLinkField("GroupId", "SecurityGroupResourceSpec"), ScalarField("UserId", alti_key="account_id", optional=True), ScalarField("PeeringStatus", optional=True), ScalarField("VpcId", optional=True), ScalarField("VpcPeeringConnectionId", optional=True), ), alti_key="user_id_group_pairs", ), ), alti_key="ingress_rule", ), ListField( "IpPermissionsEgress", EmbeddedDictField( ScalarField("IpProtocol"), ScalarField("FromPort", default_value=0), ScalarField("ToPort", default_value=65535), ListField( "IpRanges", EmbeddedDictField( ScalarField("CidrIp"), ScalarField("FirstIp"), ScalarField("LastIp") ), alti_key="ip_range", optional=True, ), ListField( "Ipv6Ranges", EmbeddedDictField( ScalarField("CidrIpv6"), ScalarField("FirstIp"), ScalarField("LastIp") ), alti_key="ipv6_range", optional=True, ), ListField( "PrefixListIds", EmbeddedDictField(ScalarField("PrefixListId")), optional=True ), ListField( "UserIdGroupPairs", EmbeddedDictField( ResourceLinkField("GroupId", "SecurityGroupResourceSpec"), ScalarField("UserId", alti_key="account_id", optional=True), ScalarField("PeeringStatus", optional=True), ScalarField("VpcId", optional=True), ScalarField("VpcPeeringConnectionId", optional=True), ), alti_key="user_id_group_pairs", ), ), alti_key="egress_rule", ), TagsField(), ) @classmethod def list_from_aws( cls: Type["SecurityGroupResourceSpec"], client: BaseClient, account_id: str, region: str ) -> ListFromAWSResult: """Return a dict of dicts of the format: {'security_group_1_arn': {security_group_1_dict}, 'security_group_2_arn': {security_group_2_dict}, ...} Where the dicts represent results from describe_subnets.""" security_groups = {} paginator = client.get_paginator("describe_security_groups") for resp in paginator.paginate(): for security_group in resp.get("SecurityGroups", []): resource_arn = cls.generate_arn( account_id=account_id, region=region, resource_id=security_group["GroupId"] ) for ingress_rule in security_group.get("IpPermissions", []): for ip_range in ingress_rule.get("IpRanges", []): cidr = ip_range["CidrIp"] ipv4_network = ipaddress.IPv4Network(cidr, strict=False) first_ip, last_ip = int(ipv4_network[0]), int(ipv4_network[-1]) ip_range["FirstIp"] = first_ip ip_range["LastIp"] = last_ip for ip_range in ingress_rule.get("Ipv6Ranges", []): cidr = ip_range["CidrIpv6"] ipv6_network = ipaddress.IPv6Network(cidr, strict=False) first_ip, last_ip = int(ipv6_network[0]), int(ipv6_network[-1]) ip_range["FirstIp"] = first_ip ip_range["LastIp"] = last_ip for egress_rule in security_group.get("IpPermissionsEgress", []): for ip_range in egress_rule.get("IpRanges", []): cidr = ip_range["CidrIp"] ipv4_network = ipaddress.IPv4Network(cidr, strict=False) first_ip, last_ip = int(ipv4_network[0]), int(ipv4_network[-1]) ip_range["FirstIp"] = first_ip ip_range["LastIp"] = last_ip for ip_range in egress_rule.get("Ipv6Ranges", []): cidr = ip_range["CidrIpv6"] ipv6_network = ipaddress.IPv6Network(cidr, strict=False) first_ip, last_ip = int(ipv6_network[0]), int(ipv6_network[-1]) ip_range["FirstIp"] = first_ip ip_range["LastIp"] = last_ip security_groups[resource_arn] = security_group return ListFromAWSResult(resources=security_groups)