示例#1
0
class EC2RouteTableResourceSpec(EC2ResourceSpec):
    """Resource for Route Tables"""

    # type_name = "route-table"
    type_name = "route_table"

    schema = Schema(
        ScalarField("RouteTableId"),
        ResourceLinkField("VpcId", VPCResourceSpec),
        ScalarField("OwnerId", optional=True),
        AnonymousListField("PropagatingVgws",
                           ScalarField("GatewayId"),
                           optional=True),
        ListField(
            "Routes",
            EmbeddedDictField(
                ScalarField("DestinationCidrBlock", optional=True),
                ScalarField("DestinationIpv6CidrBlock", optional=True),
                ScalarField("DestinationPrefixListId", optional=True),
                ScalarField("EgressOnlyInternetGatewayId", optional=True),
                ScalarField("GatewayId", optional=True),
                ScalarField("InstanceId", optional=True),
                ScalarField("InstanceOwnerId", optional=True),
                ScalarField("NatGatewayId", optional=True),
                ScalarField("TransitGatewayId", optional=True),
                ScalarField("NetworkInterfaceId", optional=True),
                ScalarField("Origin", optional=True),
                ScalarField("State", optional=True),
                ScalarField("VpcPeeringConnectionId", optional=True),
            ),
            optional=True,
            alti_key="route",
        ),
        ListField(
            "Associations",
            EmbeddedDictField(
                ScalarField("Main", optional=True),
                ScalarField("RouteTableAssociationId", optional=True),
                ScalarField("RouteTableId", optional=True),
                ScalarField("SubnetId", optional=True),
            ),
            optional=True,
            alti_key="association",
        ),
    )

    @classmethod
    def list_from_aws(cls: Type["EC2RouteTableResourceSpec"],
                      client: BaseClient, account_id: str,
                      region: str) -> ListFromAWSResult:
        paginator = client.get_paginator("describe_route_tables")
        route_tables = {}
        for resp in paginator.paginate():
            for attachment in resp.get("RouteTables", []):
                resource_arn = cls.generate_arn(
                    account_id=account_id,
                    region=region,
                    resource_id=attachment["RouteTableId"])
                route_tables[resource_arn] = attachment
        return ListFromAWSResult(resources=route_tables)
示例#2
0
    def test_valid_strings_input(self):
        input_str = '{"Animals": ["cow", "pig", "human"]}'
        field = ListField("Animals", EmbeddedScalarField())
        expected_output_data = [
            {
                "pred": "animals",
                "obj": "cow",
                "type": "simple"
            },
            {
                "pred": "animals",
                "obj": "pig",
                "type": "simple"
            },
            {
                "pred": "animals",
                "obj": "human",
                "type": "simple"
            },
        ]

        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)
示例#3
0
class TargetGroupResourceSpec(ElasticLoadBalancingResourceSpec):
    """Resource for target group"""

    type_name = "targetgroup"
    schema = Schema(
        ScalarField("TargetGroupName"),
        ScalarField("Protocol", optional=True),
        ScalarField("Port", optional=True),
        TransientResourceLinkField("VpcId", VPCResourceSpec, optional=True),
        ScalarField("HealthCheckProtocol", optional=True),
        ScalarField("HealthCheckPort", optional=True),
        ScalarField("HealthCheckEnabled"),
        ListField(
            "LoadBalancerArns",
            EmbeddedResourceLinkField(LoadBalancerResourceSpec,
                                      value_is_id=True),
        ),
        ScalarField("TargetType"),
        ListField(
            "TargetHealthDescriptions",
            EmbeddedDictField(
                AnonymousDictField(
                    "Target",
                    ScalarField("Id", alti_key="target_id"),
                    ScalarField("Port", alti_key="target_port", optional=True),
                    ScalarField("AvailabilityZone",
                                alti_key="target_az",
                                optional=True),
                ),
                ScalarField("HealthCheckPort", optional=True),
                AnonymousDictField(
                    "TargetHealth",
                    ScalarField("State"),
                    ScalarField("Reason", optional=True),
                    ScalarField("Description", optional=True),
                    optional=True,
                ),
            ),
        ),
    )

    @classmethod
    def list_from_aws(cls: Type["TargetGroupResourceSpec"], client: BaseClient,
                      account_id: str, region: str) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'target_group_1_arn': {target_group_1_dict},
             'target_group_2_arn': {target_group_2_dict},
             ...}

        Where the dicts represent results from describe_target_groups."""
        paginator = client.get_paginator("describe_target_groups")
        resources = {}
        for resp in paginator.paginate():
            for resource in resp.get("TargetGroups", []):
                resource_arn = resource["TargetGroupArn"]
                resource["TargetHealthDescriptions"] = get_target_group_health(
                    client, resource_arn)
                resources[resource_arn] = resource
        return ListFromAWSResult(resources=resources)
    def test_valid_dicts_input_with_alti_key(self):
        input_str = '{"People": [{"Name": "Bob", "Age": 49}, {"Name": "Sue", "Age": 42}]}'
        field = ListField(
            "People", EmbeddedDictField(ScalarField("Name"), ScalarField("Age")), alti_key="person"
        )

        input_data = json.loads(input_str)
        link_collection = field.parse(data=input_data, context={})

        expected_link_collection = LinkCollection(
            multi_links=(
                MultiLink(
                    pred="person",
                    obj=LinkCollection(
                        simple_links=(
                            SimpleLink(pred="name", obj="Bob"),
                            SimpleLink(pred="age", obj=49),
                        ),
                    ),
                ),
                MultiLink(
                    pred="person",
                    obj=LinkCollection(
                        simple_links=(
                            SimpleLink(pred="name", obj="Sue"),
                            SimpleLink(pred="age", obj=42),
                        ),
                    ),
                ),
            ),
        )
        self.assertEqual(link_collection, expected_link_collection)
 def test_invalid_input_missing_source_key(self):
     input_str = '{"People": [{"Name": "Bob", "Age": 49}, {"Name": "Sue", "Age": 42}]}'
     field = ListField(
         "Stuff", EmbeddedDictField(ScalarField("Name"), ScalarField("Age")), alti_key="person"
     )
     input_data = json.loads(input_str)
     with self.assertRaises(ListFieldSourceKeyNotFoundException):
         field.parse(data=input_data, context={"parent_alti_key": "test_parent"})
    def test_optional(self):
        input_str = "{}"
        field = ListField("People", EmbeddedScalarField(), alti_key="person", optional=True)

        input_data = json.loads(input_str)
        link_collection = field.parse(data=input_data, context={})

        self.assertEqual(link_collection, LinkCollection())
 def test_invalid_input_not_list(self):
     input_str = '{"People": "foo"}'
     field = ListField(
         "People", EmbeddedDictField(ScalarField("Name"), ScalarField("Age")), alti_key="person"
     )
     input_data = json.loads(input_str)
     with self.assertRaises(ListFieldValueNotAListException):
         field.parse(data=input_data, context={"parent_alti_key": "test_parent"})
示例#8
0
class LoadBalancerResourceSpec(ElasticLoadBalancingResourceSpec):
    """Resource for load balancer"""

    type_name = "loadbalancer"
    schema = Schema(
        ScalarField("DNSName"),
        ScalarField("CreatedTime"),
        ScalarField("LoadBalancerName"),
        ScalarField("Scheme"),
        ResourceLinkField("VpcId", VPCResourceSpec, optional=True),
        AnonymousDictField("State",
                           ScalarField("Code",
                                       alti_key="load_balancer_state")),
        ScalarField("Type"),
        ListField(
            "AvailabilityZones",
            EmbeddedDictField(
                ScalarField("ZoneName"),
                ResourceLinkField("SubnetId",
                                  SubnetResourceSpec,
                                  optional=True),
                ListField(
                    "LoadBalancerAddresses",
                    EmbeddedDictField(
                        ScalarField("IpAddress", optional=True),
                        ScalarField("AllocationId", optional=True),
                    ),
                    optional=True,
                ),
            ),
        ),
        ListField("SecurityGroups",
                  EmbeddedResourceLinkField(SecurityGroupResourceSpec),
                  optional=True),
        ScalarField("IpAddressType"),
    )

    @classmethod
    def list_from_aws(cls: Type["LoadBalancerResourceSpec"],
                      client: BaseClient, account_id: str,
                      region: str) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'lb_1_arn': {lb_1_dict},
             'lb_2_arn': {lb_2_dict},
             ...}

        Where the dicts represent results from describe_load_balancers."""
        paginator = client.get_paginator("describe_load_balancers")
        load_balancers = {}
        for resp in paginator.paginate():
            for lb in resp.get("LoadBalancers", []):
                resource_arn = lb["LoadBalancerArn"]
                load_balancers[resource_arn] = lb
        return ListFromAWSResult(resources=load_balancers)
    def test_allow_scalar(self):
        input_str = '{"People": "bob"}'
        field = ListField("People", EmbeddedScalarField(), alti_key="person", allow_scalar=True)

        input_data = json.loads(input_str)
        link_collection = field.parse(data=input_data, context={})

        expected_link_collection = LinkCollection(
            simple_links=(SimpleLink(pred="person", obj="bob"),),
        )
        self.assertEqual(link_collection, expected_link_collection)
示例#10
0
    def test_optional(self):
        input_str = "{}"
        field = ListField("People",
                          EmbeddedScalarField(),
                          alti_key="person",
                          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)
示例#11
0
    def test_valid_dicts_input_with_alti_key(self):
        input_str = '{"People": [{"Name": "Bob", "Age": 49}, {"Name": "Sue", "Age": 42}]}'
        field = ListField("People",
                          EmbeddedDictField(ScalarField("Name"),
                                            ScalarField("Age")),
                          alti_key="person")
        expected_output_data = [
            {
                "pred":
                "person",
                "obj": [
                    {
                        "pred": "name",
                        "obj": "Bob",
                        "type": "simple"
                    },
                    {
                        "pred": "age",
                        "obj": 49,
                        "type": "simple"
                    },
                ],
                "type":
                "multi",
            },
            {
                "pred":
                "person",
                "obj": [
                    {
                        "pred": "name",
                        "obj": "Sue",
                        "type": "simple"
                    },
                    {
                        "pred": "age",
                        "obj": 42,
                        "type": "simple"
                    },
                ],
                "type":
                "multi",
            },
        ]

        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_valid_strings_input(self):
        input_str = '{"Animals": ["cow", "pig", "human"]}'
        field = ListField("Animals", EmbeddedScalarField())

        input_data = json.loads(input_str)
        link_collection = field.parse(data=input_data, context={})

        expected_link_collection = LinkCollection(
            simple_links=(
                SimpleLink(pred="animals", obj="cow"),
                SimpleLink(pred="animals", obj="pig"),
                SimpleLink(pred="animals", obj="human"),
            ),
        )
        self.assertEqual(link_collection, expected_link_collection)
示例#13
0
    def test_allow_scalar(self):
        input_str = '{"People": "bob"}'
        field = ListField("People",
                          EmbeddedScalarField(),
                          alti_key="person",
                          allow_scalar=True)
        expected_output_data = ({
            "pred": "person",
            "obj": "bob",
            "type": "simple"
        }, )

        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 TransitGatewayVpcAttachmentResourceSpec(EC2ResourceSpec):
    """Resource for Transit Gateway VPC Attachments"""

    type_name = "transit-gateway-vpc-attachment"
    schema = Schema(
        ScalarField("TransitGatewayAttachmentId"),
        ScalarField("TransitGatewayId"),
        ScalarField("VpcId"),
        ScalarField("VpcOwnerId"),
        ScalarField("State"),
        ScalarField("CreationTime"),
        ListField("SubnetIds", EmbeddedScalarField(), alti_key="subnet_id"),
        AnonymousDictField("Options", ScalarField("DnsSupport"),
                           ScalarField("Ipv6Support")),
    )

    @classmethod
    def list_from_aws(
        cls: Type["TransitGatewayVpcAttachmentResourceSpec"],
        client: BaseClient,
        account_id: str,
        region: str,
    ) -> ListFromAWSResult:
        paginator = client.get_paginator(
            "describe_transit_gateway_vpc_attachments")
        attachments = {}
        for resp in paginator.paginate():
            for attachment in resp.get("TransitGatewayVpcAttachments", []):
                resource_arn = cls.generate_arn(
                    account_id, region,
                    attachment["TransitGatewayAttachmentId"])
                attachments[resource_arn] = attachment
        return ListFromAWSResult(resources=attachments)
示例#15
0
class IAMOIDCProviderResourceSpec(IAMResourceSpec):
    """Resource for IAM OIDC Providers"""

    type_name = "oidc-provider"
    schema = Schema(
        ScalarField("Url"),
        ScalarField("CreateDate"),
        ListField("ClientIDList", EmbeddedScalarField(), alti_key="client_id"),
        ListField("ThumbprintList",
                  EmbeddedScalarField(),
                  alti_key="thumbprint"),
    )

    @classmethod
    def list_from_aws(cls: Type["IAMOIDCProviderResourceSpec"],
                      client: BaseClient, account_id: str,
                      region: str) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'oidc_provider_1_arn': {oidc_provider_1_dict},
             'oidc_provider_2_arn': {oidc_provider_2_dict},
             ...}

        Where the dicts represent results from list_oidc_providers and additional info per
        oidc_provider list_oidc_providers. An additional 'Name' key is added."""
        oidc_providers = {}
        resp = client.list_open_id_connect_providers()
        for oidc_provider in resp.get("OpenIDConnectProviderList", []):
            resource_arn = oidc_provider["Arn"]
            try:
                oidc_provider_details = cls.get_oidc_provider_details(
                    client=client, arn=resource_arn)
                oidc_provider.update(oidc_provider_details)
                oidc_providers[resource_arn] = oidc_provider
            except ClientError as c_e:
                error_code = getattr(c_e, "response",
                                     {}).get("Error", {}).get("Code", {})
                if error_code != "NoSuchEntity":
                    raise c_e
        return ListFromAWSResult(resources=oidc_providers)

    @classmethod
    def get_oidc_provider_details(cls: Type["IAMOIDCProviderResourceSpec"],
                                  client: BaseClient, arn: str) -> str:
        oidc_provider_resp = client.get_open_id_connect_provider(
            OpenIDConnectProviderArn=arn)
        return oidc_provider_resp
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)
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 EventsRuleResourceSpec(EventsResourceSpec):
    """Resource for CloudWatchEvents Rules"""

    type_name = "rule"
    schema = Schema(
        ScalarField("Name"),
        ScalarField("State"),
        ScalarField("EventPattern", optional=True),
        ScalarField("ScheduleExpression", optional=True),
        ListField(
            "Targets",
            EmbeddedDictField(ScalarField("Id", "name"), ScalarField("Arn"),
                              ScalarField("RoleArn", optional=True)),
            alti_key="target",
        ),
    )

    @classmethod
    def list_from_aws(cls: Type["EventsRuleResourceSpec"], client: BaseClient,
                      account_id: str, region: str) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'rule_1_arn': {rule_1_dict},
             'rule_2_arn': {rule_2_dict},
             ...}

        Where the dicts represent results from list_rules and additional info per rule from
        list_targets_by_rule."""
        rules = {}
        paginator = client.get_paginator("list_rules")
        for resp in paginator.paginate():
            for rule in resp.get("Rules", []):
                resource_arn = rule["Arn"]
                try:
                    rule["Targets"] = list_targets_by_rule(
                        client=client, rule_name=rule["Name"])
                    rules[resource_arn] = rule
                except ClientError as c_e:
                    error_code = getattr(c_e, "response",
                                         {}).get("Error", {}).get("Code", {})
                    if error_code != "ResourceNotFoundException":
                        raise c_e
        return ListFromAWSResult(resources=rules)
示例#19
0
class EC2ImageResourceSpec(EC2ResourceSpec):
    """Resource for EC2Images (AMIs)"""

    type_name = "image"
    schema = Schema(
        ScalarField("Name"),
        ScalarField("Description", optional=True),
        ScalarField("Public"),
        ListField(
            "LaunchPermissions",
            EmbeddedDictField(
                ResourceLinkField("UserId",
                                  AccountResourceSpec,
                                  optional=True,
                                  alti_key="account")),
            alti_key="launch_permission",
        ),
    )

    @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"]
            time.sleep(
                0.25)  # seems necessary to avoid frequent RequestLimitExceeded
            perms_resp = client.describe_image_attribute(
                Attribute="launchPermission", ImageId=image_id)
            launch_permissions = perms_resp["LaunchPermissions"]
            image["LaunchPermissions"] = launch_permissions
            resource_arn = cls.generate_arn(account_id, region, image_id)
            images[resource_arn] = image
        return ListFromAWSResult(resources=images)
示例#20
0
class EventsRuleResourceSpec(EventsResourceSpec):
    """Resource for CloudWatchEvents Rules"""

    type_name = "rule"
    schema = Schema(
        ScalarField("Name"),
        ScalarField("State"),
        ScalarField("EventPattern", optional=True),
        ScalarField("ScheduleExpression", optional=True),
        ListField(
            "Targets",
            EmbeddedDictField(ScalarField("Id", "name"),
                              ScalarField("Arn", "arn")),
            alti_key="target",
        ),
    )

    @classmethod
    def list_from_aws(cls: Type["EventsRuleResourceSpec"], client: BaseClient,
                      account_id: str, region: str) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'rule_1_arn': {rule_1_dict},
             'rule_2_arn': {rule_2_dict},
             ...}

        Where the dicts represent results from list_rules and additional info per rule from
        list_targets_by_rule."""
        rules = {}
        paginator = client.get_paginator("list_rules")
        for resp in paginator.paginate():
            for rule in resp.get("Rules", []):
                resource_arn = rule["Arn"]
                rules[resource_arn] = rule
                targets_paginator = client.get_paginator(
                    "list_targets_by_rule")
                rule["Targets"] = []
                for targets_resp in targets_paginator.paginate(
                        Rule=rule["Name"]):
                    rule["Targets"] += targets_resp.get("Targets", [])
        return ListFromAWSResult(resources=rules)
示例#21
0
class HostedZoneResourceSpec(Route53ResourceSpec):
    """Resource for S3 Buckets"""

    type_name = "hostedzone"
    schema = Schema(
        ScalarField("Name"),
        ListField(
            "ResourceRecordSets",
            EmbeddedDictField(
                ScalarField("Name"), ScalarField("Type"), ScalarField("TTL", optional=True)
            ),
            optional=True,
            alti_key="resource_record_set",
        ),
    )

    @classmethod
    def list_from_aws(
        cls: Type["HostedZoneResourceSpec"], client: BaseClient, account_id: str, region: str
    ) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'hosted_zone_1_arn': {hosted_zone_1_dict},
             'hosted_zone_2_arn': {hosted_zone_2_dict},
             ...}

        Where the dicts represent results from list_hosted_zones."""
        hosted_zones = {}
        paginator = client.get_paginator("list_hosted_zones")
        for resp in paginator.paginate():
            for hosted_zone in resp.get("HostedZones", []):
                hosted_zone_id = hosted_zone["Id"].split("/")[-1]
                resource_arn = cls.generate_arn(resource_id=hosted_zone_id, account_id=account_id)
                record_sets_paginator = client.get_paginator("list_resource_record_sets")
                zone_resource_record_sets = []
                for record_sets_resp in record_sets_paginator.paginate(HostedZoneId=hosted_zone_id):
                    zone_resource_record_sets += record_sets_resp.get("ResourceRecordSets", [])
                hosted_zone["ResourceRecordSets"] = zone_resource_record_sets
                hosted_zones[resource_arn] = hosted_zone
        return ListFromAWSResult(resources=hosted_zones)
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)
示例#23
0
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)
示例#24
0
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)
示例#25
0
文件: role.py 项目: jparten/altimeter
class IAMRoleResourceSpec(IAMResourceSpec):
    """Resource for IAM Roles"""

    type_name = "role"
    schema = Schema(
        ScalarField("RoleName", "name"),
        ScalarField("MaxSessionDuration"),
        AnonymousListField(
            "PolicyAttachments",
            AnonymousEmbeddedDictField(
                ResourceLinkField(
                    "PolicyArn",
                    IAMPolicyResourceSpec,
                    optional=True,
                    value_is_id=True,
                    alti_key="attached_policy",
                )),
        ),
        DictField(
            "AssumeRolePolicyDocument",
            ScalarField("Version"),
            ListField(
                "Statement",
                EmbeddedDictField(
                    ScalarField("Effect"),
                    ScalarField("Action"),
                    DictField(
                        "Principal",
                        ListField("AWS",
                                  EmbeddedScalarField(),
                                  optional=True,
                                  allow_scalar=True),
                        ListField("Federated",
                                  EmbeddedScalarField(),
                                  optional=True,
                                  allow_scalar=True),
                    ),
                ),
            ),
        ),
        ScalarField("AssumeRolePolicyDocumentText"),
    )

    @classmethod
    def list_from_aws(cls: Type["IAMRoleResourceSpec"], client: BaseClient,
                      account_id: str, region: str) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'role_1_arn': {role_1_dict},
             'role_2_arn': {role_2_dict},
             ...}

        Where the dicts represent results from list_roles and additional info per role from
        list_targets_by_role."""
        roles = {}
        paginator = client.get_paginator("list_roles")
        for resp in paginator.paginate():
            for role in resp.get("Roles", []):
                role_name = role["RoleName"]
                assume_role_policy_document = copy.deepcopy(
                    role["AssumeRolePolicyDocument"])
                assume_role_policy_document_text = policy_doc_dict_to_sorted_str(
                    assume_role_policy_document)
                role[
                    "AssumeRolePolicyDocumentText"] = assume_role_policy_document_text
                for statement in assume_role_policy_document.get(
                        "Statement", []):
                    for obj in statement.get("Condition", {}).values():
                        for obj_key in obj.keys():
                            if obj_key.lower() == "sts:externalid":
                                obj[obj_key] = "REMOVED"
                policies_result = get_attached_role_policies(client, role_name)
                policies = policies_result
                role["PolicyAttachments"] = policies
                resource_arn = role["Arn"]
                roles[resource_arn] = role
        return ListFromAWSResult(resources=roles)
示例#26
0
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)
示例#27
0
class IAMUserResourceSpec(IAMResourceSpec):
    """Resource for IAM Users"""

    type_name = "user"
    schema = Schema(
        ScalarField("UserName", "name"),
        ScalarField("UserId"),
        ScalarField("CreateDate"),
        ScalarField("PasswordLastUsed", optional=True),
        ListField(
            "AccessKeys",
            EmbeddedDictField(
                ScalarField("AccessKeyId"),
                ScalarField("Status"),
                ScalarField("CreateDate"),
                AnonymousDictField("AccessKeyLastUsed",
                                   ScalarField("LastUsedDate", optional=True)),
            ),
            optional=True,
            alti_key="access_key",
        ),
        DictField(
            "LoginProfile",
            ScalarField("CreateDate"),
            ScalarField("PasswordResetRequired"),
            optional=True,
        ),
        ListField(
            "MfaDevices",
            EmbeddedDictField(ScalarField("SerialNumber"),
                              ScalarField("EnableDate")),
            optional=True,
        ),
    )

    @classmethod
    def list_from_aws(cls: Type["IAMUserResourceSpec"], client: BaseClient,
                      account_id: str, region: str) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'user_1_arn': {user_1_dict},
             'user_2_arn': {user_2_dict},
             ...}

        Where the dicts represent results from list_users and additional info per user from
        list_targets_by_user."""
        users = {}
        paginator = client.get_paginator("list_users")
        for resp in paginator.paginate():
            for user in resp.get("Users", []):
                resource_arn = user["Arn"]
                user_name = user["UserName"]
                access_keys_paginator = client.get_paginator(
                    "list_access_keys")
                access_keys: List[Dict[str, Any]] = []
                for access_keys_resp in access_keys_paginator.paginate(
                        UserName=user_name):
                    for resp_access_key in access_keys_resp[
                            "AccessKeyMetadata"]:
                        access_key = copy.deepcopy(resp_access_key)
                        access_key_id = access_key["AccessKeyId"]
                        last_used_resp = client.get_access_key_last_used(
                            AccessKeyId=access_key_id)
                        access_key["AccessKeyLastUsed"] = last_used_resp[
                            "AccessKeyLastUsed"]
                        access_keys.append(access_key)
                user["AccessKeys"] = access_keys
                mfa_devices_paginator = client.get_paginator(
                    "list_mfa_devices")
                mfa_devices: List[Dict[str, Any]] = []
                for mfa_devices_resp in mfa_devices_paginator.paginate(
                        UserName=user_name):
                    mfa_devices += mfa_devices_resp["MFADevices"]
                    user["MfaDevices"] = mfa_devices
                try:
                    login_profile_resp = client.get_login_profile(
                        UserName=user_name)
                    user["LoginProfile"] = login_profile_resp["LoginProfile"]
                except ClientError as c_e:
                    if "NoSuchEntity" not in str(c_e):
                        raise c_e
                users[resource_arn] = user
        return ListFromAWSResult(resources=users)
class ClassicLoadBalancerResourceSpec(ELBV1ResourceSpec):
    """Resource for classic load balancer"""

    type_name = "loadbalancer"
    schema = Schema(
        ScalarField("DNSName"),
        ScalarField("CreatedTime"),
        ScalarField("LoadBalancerName"),
        ScalarField("Scheme"),
        ResourceLinkField("VPCId", VPCResourceSpec, optional=True),
        ListField("Subnets",
                  EmbeddedResourceLinkField(SubnetResourceSpec),
                  optional=True),
        ListField("SecurityGroups",
                  EmbeddedResourceLinkField(SecurityGroupResourceSpec),
                  optional=True),
        ScalarField("Type"),
        ScalarField("AccessLogsEnabled"),
        TransientResourceLinkField(
            "AccessLogsS3Bucket",
            S3BucketResourceSpec,
            alti_key="access_logs_s3_bucket",
            optional=True,
        ),
        ScalarField("AccessLogsS3Prefix", optional=True),
    )

    @classmethod
    def list_from_aws(
        cls: Type["ClassicLoadBalancerResourceSpec"],
        client: BaseClient,
        account_id: str,
        region: str,
    ) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'lb_1_arn': {lb_1_dict},
             'lb_2_arn': {lb_2_dict},
             ...}

        Where the dicts represent results from describe_load_balancers."""
        paginator = client.get_paginator("describe_load_balancers")
        load_balancers = {}
        for resp in paginator.paginate():
            for lb in resp["LoadBalancerDescriptions"]:
                lb_name = lb["LoadBalancerName"]
                resource_arn = cls.generate_arn(account_id=account_id,
                                                region=region,
                                                resource_id=lb_name)
                try:
                    lb_attrs = cls.get_lb_attrs(client, lb_name)
                    lb.update(lb_attrs)
                    lb["Type"] = "classic"
                    load_balancers[resource_arn] = lb
                except ClientError as c_e:
                    if (getattr(c_e, "response", {}).get("Error", {}).get(
                            "Code", {}) != "LoadBalancerNotFound"):
                        raise c_e
        return ListFromAWSResult(resources=load_balancers)

    @classmethod
    def get_lb_attrs(
        cls: Type["ClassicLoadBalancerResourceSpec"],
        client: BaseClient,
        lb_name: str,
    ) -> Dict[str, str]:
        """Get lb attributes that Altimeter graphs."""
        lb_attrs = {}
        resp = client.describe_load_balancer_attributes(
            LoadBalancerName=lb_name)
        access_log_attrs = resp["LoadBalancerAttributes"]["AccessLog"]
        lb_attrs["AccessLogsEnabled"] = access_log_attrs["Enabled"]
        if "S3BucketName" in access_log_attrs:
            lb_attrs["AccessLogsS3Bucket"] = access_log_attrs["S3BucketName"]
        if "S3BucketPrefix" in access_log_attrs:
            lb_attrs["AccessLogsS3Prefix"] = access_log_attrs["S3BucketPrefix"]
        return lb_attrs
class DetectorResourceSpec(GuardDutyResourceSpec):
    """Resource for GuardDuty Detectors"""

    type_name = "detector"
    schema = Schema(
        ScalarField("CreatedAt"),
        ScalarField("FindingPublishingFrequency"),
        ScalarField("ServiceRole"),
        ScalarField("Status"),
        ScalarField("UpdatedAt"),
        ListField(
            "Members",
            EmbeddedDictField(
                TransientResourceLinkField("DetectorArn",
                                           "DetectorResourceSpec",
                                           value_is_id=True),
                ScalarField("Email"),
                ScalarField("RelationshipStatus"),
                ScalarField("InvitedAt"),
                ScalarField("UpdatedAt"),
            ),
            alti_key="member",
        ),
        AnonymousDictField(
            "Master",
            ScalarField("AccountId", alti_key="master_account_id"),
            ScalarField("RelationshipStatus",
                        alti_key="master_relationship_status"),
            # ScalarField("InvitedAt", alti_key="master_invited_at"),
            ScalarField("InvitedAt",
                        alti_key="master_invited_at",
                        optional=True),
            optional=True,
        ),
    )

    @classmethod
    def list_from_aws(cls: Type["DetectorResourceSpec"], client: BaseClient,
                      account_id: str, region: str) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'detector_1_arn': {detector_1_dict},
             'detector_2_arn': {detector_2_dict},
             ...}

        Where the dicts represent results from list_detectors and list_members, get_detector for
        each listed detector."""
        list_detectors_paginator = client.get_paginator("list_detectors")
        detectors: Dict[str, Dict[str, Any]] = {}
        for list_detectors_resp in list_detectors_paginator.paginate():
            detector_ids = list_detectors_resp["DetectorIds"]
            for detector_id in detector_ids:
                resource_arn = cls.generate_arn(account_id=account_id,
                                                region=region,
                                                resource_id=detector_id)
                try:
                    detectors[resource_arn] = cls.get_detector(
                        client, detector_id, region)
                except ClientError as c_e:
                    error_code = getattr(c_e, "response",
                                         {}).get("Error", {}).get("Code", {})
                    if error_code != "BadRequestException":
                        raise c_e
        return ListFromAWSResult(resources=detectors)

    @classmethod
    def get_detector(cls: Type["DetectorResourceSpec"], client: BaseClient,
                     detector_id: str, region: str) -> Dict[str, Any]:
        detector_resp = client.get_detector(DetectorId=detector_id)
        detector = {
            key: detector_resp[key]
            for key in (
                "CreatedAt",
                "FindingPublishingFrequency",
                "ServiceRole",
                "Status",
                "UpdatedAt",
            )
        }
        detector["Members"] = cls.get_detector_members(client, detector_id,
                                                       region)
        master_account_resp = client.get_master_account(DetectorId=detector_id)
        master_account_dict = master_account_resp.get("Master")
        if master_account_dict:
            detector["Master"] = {
                # key: master_account_dict[key]
                key: master_account_dict.get(key)
                for key in (
                    "AccountId",
                    "RelationshipStatus",
                    "InvitedAt",
                )
            }
        return detector

    @classmethod
    def get_detector_members(cls: Type["DetectorResourceSpec"],
                             client: BaseClient, detector_id: str,
                             region: str) -> List[Dict[str, Any]]:
        member_resps: List[Dict[str, Any]] = []
        list_members_paginator = client.get_paginator("list_members")
        for list_members_resp in list_members_paginator.paginate(
                DetectorId=detector_id):
            member_resps += list_members_resp.get("Members", [])
        members = []
        if member_resps:
            for member_resp in member_resps:
                member_account_id = member_resp["AccountId"]
                member_detector_id = member_resp["DetectorId"]
                # member_email = member_resp["Email"]
                member_email = member_resp.get("Email", "[NONE]")
                member_relationship_status = member_resp["RelationshipStatus"]
                # member_invited_at = member_resp["InvitedAt"]
                member_invited_at = member_resp.get("InvitedAt", "[NONE]")
                member_updated_at = member_resp["UpdatedAt"]
                member_detector_arn = cls.generate_arn(
                    account_id=member_account_id,
                    region=region,
                    resource_id=member_detector_id,
                )
                member = {
                    "DetectorArn": member_detector_arn,
                    "Email": member_email,
                    "RelationshipStatus": member_relationship_status,
                    "InvitedAt": member_invited_at,
                    "UpdatedAt": member_updated_at,
                }
                members.append(member)
        return members
示例#30
0
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)