Esempio n. 1
0
    def test_value_not_a_string(self):
        input_str = '{"FieldName": [1, 2, 3]}'
        field = ResourceLinkField("FieldName", TestResourceSpec)

        input_data = json.loads(input_str)
        with self.assertRaises(ResourceLinkFieldValueNotAStringException):
            field.parse(data=input_data, context={})
Esempio n. 2
0
    def test_key_absent_without_optional(self):
        input_str = "{}"
        field = ResourceLinkField("FieldName", TestResourceSpec)

        input_data = json.loads(input_str)
        with self.assertRaises(ResourceLinkFieldSourceKeyNotFoundException):
            field.parse(data=input_data, context={})
Esempio n. 3
0
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)
Esempio n. 4
0
    def test_key_absent_with_optional(self):
        input_str = "{}"
        field = ResourceLinkField("FieldName", TestResourceSpec, 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)
Esempio n. 5
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)
Esempio n. 6
0
class EC2NetworkInterfaceResourceSpec(EC2ResourceSpec):
    """Resource for EC2NetworkInterfaces"""

    # type_name = "network-interface"
    type_name = "network_interface"
    schema = Schema(
        AnonymousDictField(
            "Association",
            ScalarField("PublicDnsName", optional=True),
            ScalarField("PublicIp", optional=True),
            optional=True,
        ),
        ScalarField("Description"),
        ScalarField("InterfaceType"),
        ScalarField("MacAddress"),
        ScalarField("PrivateDnsName", optional=True),
        ScalarField("PrivateIpAddress", optional=True),
        ScalarField("Status"),
        ResourceLinkField("SubnetId", SubnetResourceSpec, optional=True),
        ResourceLinkField("VpcId", VPCResourceSpec, optional=True),
        AnonymousListField(
            "Groups",
            AnonymousEmbeddedDictField(
                ResourceLinkField("GroupId", SecurityGroupResourceSpec)),
        ),
    )

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

            {'network_interface_1_arn': {network_interface_1_dict},
             'network_interface_2_arn': {network_interface_2_dict},
             ...}

        Where the dicts represent results from describe_network_interfaces."""
        paginator = client.get_paginator("describe_network_interfaces")
        interfaces = {}
        for resp in paginator.paginate():
            for interface in resp.get("NetworkInterfaces", []):
                resource_arn = cls.generate_arn(
                    account_id=account_id,
                    region=region,
                    resource_id=interface["NetworkInterfaceId"],
                )
                interfaces[resource_arn] = interface
        return ListFromAWSResult(resources=interfaces)
Esempio n. 7
0
    def test_valid_input_str_classname(self):
        input_str = '{"FieldName": "Value"}'
        field = ResourceLinkField("FieldName", "TestResourceSpec")
        expected_output_data = [{
            "pred": "test_type_name",
            "obj": "test_type_name:Value",
            "type": "resource_link"
        }]

        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)
Esempio n. 8
0
    def test_key_present_with_optional(self):
        input_str = '{"FieldName": "Value"}'
        field = ResourceLinkField("FieldName", TestResourceSpec, optional=True)
        expected_output_data = [{
            "pred": "test_type_name",
            "obj": "test_type_name:Value",
            "type": "resource_link"
        }]

        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)
Esempio n. 9
0
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)
Esempio n. 10
0
class EBSSnapshotResourceSpec(EC2ResourceSpec):
    """Resource for EBSSnapshots"""

    type_name = "snapshot"
    schema = Schema(
        ScalarField("VolumeSize"),
        ScalarField("Encrypted"),
        ResourceLinkField("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, region, snapshot["SnapshotId"])
                snapshots[resource_arn] = snapshot
        return ListFromAWSResult(resources=snapshots)
Esempio n. 11
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)
Esempio n. 12
0
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 InstanceProfileResourceSpec(IAMResourceSpec):
    """Resource for Instance Profiles"""

    # type_name = "instance-profile"
    type_name = "instance_profile"
    schema = Schema(
        ScalarField("InstanceProfileName", alti_key="name"),
        AnonymousListField(
            "Roles",
            AnonymousEmbeddedDictField(
                ResourceLinkField("Arn",
                                  IAMRoleResourceSpec,
                                  value_is_id=True,
                                  alti_key="attached_role")),
        ),
    )

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

            {'instance_profile_1_arn': {instance_profile_1_dict},
             'instance_profile_2_arn': {instance_profile_2_dict},
             ...}

        Where the dicts represent results from list_instance_profiles."""
        paginator = client.get_paginator("list_instance_profiles")
        instance_profiles = {}
        for resp in paginator.paginate():
            for instance_profile in resp.get("InstanceProfiles", []):
                resource_arn = instance_profile["Arn"]
                instance_profiles[resource_arn] = instance_profile
        return ListFromAWSResult(resources=instance_profiles)
Esempio n. 14
0
class OUResourceSpec(OrganizationsResourceSpec):
    """Resource representing an AWS OU."""

    type_name = "ou"
    schema = Schema(
        ScalarField("Path"),
        ResourceLinkField("OrganizationArn", OrgResourceSpec,
                          value_is_id=True))

    @classmethod
    def get_full_type_name(cls: Type["OUResourceSpec"]) -> str:
        return f"{cls.provider_name}:{cls.type_name}"

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

            {'ou_1_arn': {ou_1_dict},
             'ou_2_arn': {ou_2_dict},
             ...}

        Where the dicts represent results from list_organizational_units_for_parent
        with some additional info 'Path') tagged on."""
        org_resp = client.describe_organization()
        org_arn = org_resp["Organization"]["Arn"]
        ous = {}
        paginator = client.get_paginator("list_roots")
        for resp in paginator.paginate():
            for root in resp["Roots"]:
                root_id, root_arn = root["Id"], root["Arn"]
                root_path = f"/{root['Name']}"
                ous[root_arn] = root
                ous[root_arn]["OrganizationArn"] = org_arn
                ous[root_arn]["Path"] = root_path
                ou_details = cls._recursively_get_ou_details_for_parent(
                    client=client, parent_id=root_id, parent_path=root_path)
                for ou_detail in ou_details:
                    arn = ou_detail["Arn"]
                    ou_detail["OrganizationArn"] = org_arn
                    ous[arn] = ou_detail
        return ListFromAWSResult(resources=ous)

    @classmethod
    def _recursively_get_ou_details_for_parent(
            cls: Type["OUResourceSpec"], client: BaseClient, parent_id: str,
            parent_path: str) -> List[Dict[str, Any]]:
        ous = []
        paginator = client.get_paginator(
            "list_organizational_units_for_parent")
        for resp in paginator.paginate(ParentId=parent_id):
            for ou in resp["OrganizationalUnits"]:
                ou_id = ou["Id"]
                path = f"{parent_path}/{ou['Name']}"
                ou["Path"] = path
                ous.append(ou)
                ous += cls._recursively_get_ou_details_for_parent(
                    client=client, parent_id=ou_id, parent_path=path)
        return ous
Esempio n. 15
0
class OrgsAccountResourceSpec(OrganizationsResourceSpec):
    """Resource representing an AWS Account as viewed in Orgs."""

    type_name = "account"
    schema = Schema(
        ScalarField("Name"),
        ScalarField("Status"),
        ScalarField("JoinedTimestamp"),
        ScalarField("Email"),
        ScalarField("Id", alti_key="account_id"),
        ResourceLinkField("OrganizationArn", OrgResourceSpec, value_is_id=True),
        ResourceLinkField("OUArn", OUResourceSpec, value_is_id=True),
    )
    allow_clobber = [UnscannedAccountResourceSpec]

    @classmethod
    def get_full_type_name(cls: Type["OrgsAccountResourceSpec"]) -> str:
        return f"{cls.provider_name}:{cls.type_name}"

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

            {'ou_1_arn': {org_1_dict},
             'ou_2_arn': {org_2_dict},
             ...}

        Where the dicts represent results from list_accounts_for_parent."""
        org_resp = client.describe_organization()
        org_arn = org_resp["Organization"]["Arn"]
        # get all ou ids and arns as a dict
        ou_ids_arns = get_ou_ids_arns(client)
        # now look up accounts for each ou
        orgs_accounts = {}
        accounts_paginator = client.get_paginator("list_accounts_for_parent")
        for parent_id, parent_arn in ou_ids_arns.items():
            for accounts_resp in accounts_paginator.paginate(ParentId=parent_id):
                for account in accounts_resp["Accounts"]:
                    account_id = account["Id"]
                    account_arn = f"arn:aws::::account/{account_id}"
                    account["OrganizationArn"] = org_arn
                    account["OUArn"] = parent_arn
                    orgs_accounts[account_arn] = account
        return ListFromAWSResult(resources=orgs_accounts)
Esempio n. 16
0
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",
        ),
        ResourceLinkField("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, region, volume["VolumeId"])
                volumes[resource_arn] = volume
        return ListFromAWSResult(resources=volumes)
Esempio n. 17
0
class RDSSnapshotResourceSpec(RDSResourceSpec):
    """Resource for RDS Snapshot"""

    type_name = "snapshot"
    schema = Schema(
        ScalarField("DBSnapshotIdentifier"),
        ScalarField("SnapshotCreateTime", optional=True),
        ScalarField("Engine"),
        ScalarField("AllocatedStorage"),
        ScalarField("Status"),
        TransientResourceLinkField("VpcId", VPCResourceSpec, optional=True),
        ScalarField("InstanceCreateTime"),
        ScalarField("SnapshotType"),
        ScalarField("PercentProgress"),
        ScalarField("Region", optional=True),
        ScalarField("Encrypted"),
        ResourceLinkField("KmsKeyID", KMSKeyResourceSpec, optional=True),
        ScalarField("Timezone", optional=True),
        ScalarField("IAMDatabaseAuthenticationEnabled"),
        TransientResourceLinkField("DBInstanceArn",
                                   RDSInstanceResourceSpec,
                                   value_is_id=True),
    )

    @classmethod
    def generate_instance_arn(cls: Type["RDSSnapshotResourceSpec"],
                              account_id: str, region: str,
                              resource_id: str) -> str:
        """Generate an ARN for this resource, e.g. arn:aws:rds:<region>:<account>:db:<name>
        """
        return ":".join((
            "arn",
            cls.provider_name,
            cls.service_name,
            region,
            account_id,
            cls.type_name,
            resource_id,
        ))

    @classmethod
    def list_from_aws(cls: Type["RDSSnapshotResourceSpec"], client: BaseClient,
                      account_id: str, region: str) -> ListFromAWSResult:
        dbinstances = {}
        paginator = client.get_paginator("describe_db_snapshots")
        for resp in paginator.paginate():
            for db in resp.get("DBSnapshots", []):
                resource_arn = db["DBSnapshotArn"]
                db["DBInstanceArn"] = cls.generate_instance_arn(
                    account_id, region, db["DBInstanceIdentifier"])
                dbinstances[resource_arn] = db
        return ListFromAWSResult(resources=dbinstances)
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)
Esempio n. 19
0
class IAMGroupResourceSpec(IAMResourceSpec):
    """Resource for IAM Groups"""

    type_name = "group"
    schema = Schema(
        ScalarField("GroupName", "name"),
        ScalarField("GroupId"),
        ScalarField("CreateDate"),
        AnonymousListField(
            "Users",
            AnonymousEmbeddedDictField(
                ResourceLinkField("Arn",
                                  IAMUserResourceSpec,
                                  value_is_id=True,
                                  alti_key="user")),
        ),
    )

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

            {'group_1_arn': {group_1_dict},
             'group_2_arn': {group_2_dict},
             ...}

        Where the dicts represent results from list_groups."""
        groups = {}
        paginator = client.get_paginator("list_groups")
        for resp in paginator.paginate():
            for group in resp.get("Groups", []):
                resource_arn = group["Arn"]
                try:
                    group["Users"] = cls.get_group_users(
                        client=client, group_name=group["GroupName"])
                    groups[resource_arn] = group
                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=groups)

    @classmethod
    def get_group_users(cls: Type["IAMGroupResourceSpec"], client: BaseClient,
                        group_name: str) -> List[Dict[str, Any]]:
        group_resp = client.get_group(GroupName=group_name)
        return group_resp["Users"]
Esempio n. 20
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)
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)
Esempio n. 22
0
class IAMGroupResourceSpec(IAMResourceSpec):
    """Resource for IAM Groups"""

    type_name = "group"
    schema = Schema(
        ScalarField("GroupName", "name"),
        ScalarField("GroupId"),
        ScalarField("CreateDate"),
        AnonymousListField(
            "Users",
            AnonymousEmbeddedDictField(
                ResourceLinkField("Arn",
                                  IAMUserResourceSpec,
                                  value_is_id=True,
                                  alti_key="user")),
        ),
    )

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

            {'group_1_arn': {group_1_dict},
             'group_2_arn': {group_2_dict},
             ...}

        Where the dicts represent results from list_groups."""
        groups = {}
        paginator = client.get_paginator("list_groups")
        for resp in paginator.paginate():
            for group in resp.get("Groups", []):
                resource_arn = group["Arn"]
                group_resp = client.get_group(GroupName=group["GroupName"])
                group["Users"] = group_resp["Users"]
                groups[resource_arn] = group
        return ListFromAWSResult(resources=groups)
Esempio n. 23
0
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)
Esempio n. 24
0
class LoadBalancerResourceSpec(ELBV2ResourceSpec):
    """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"),
        ScalarField("AccessLogsEnabled"),
        TransientResourceLinkField(
            "AccessLogsS3Bucket",
            S3BucketResourceSpec,
            alti_key="access_logs_s3_bucket",
            optional=True,
        ),
        ScalarField("AccessLogsS3Prefix", optional=True),
    )

    @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"]
                try:
                    lb_attrs = cls.get_lb_attrs(client, resource_arn)
                    lb.update(lb_attrs)
                    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["LoadBalancerResourceSpec"],
        client: BaseClient,
        lb_arn: str,
    ) -> Dict[str, str]:
        """Get lb attributes that Altimeter graphs."""
        lb_attrs = {}
        resp = client.describe_load_balancer_attributes(LoadBalancerArn=lb_arn)
        for attr in resp["Attributes"]:
            if attr["Key"] == "access_logs.s3.enabled":
                lb_attrs["AccessLogsEnabled"] = attr["Value"]
            elif attr["Key"] == "access_logs.s3.bucket":
                if attr["Value"]:
                    lb_attrs["AccessLogsS3Bucket"] = attr["Value"]
            elif attr["Key"] == "access_logs.s3.prefix":
                if attr["Value"]:
                    lb_attrs["AccessLogsS3Prefix"] = attr["Value"]
        return lb_attrs
Esempio n. 25
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)
Esempio n. 26
0
class OrgsAccountResourceSpec(OrganizationsResourceSpec):
    """Resource representing an AWS Account as viewed in Orgs."""

    type_name = "account"
    schema = Schema(
        ScalarField("Name"),
        ScalarField("Status"),
        ScalarField("JoinedTimestamp"),
        ScalarField("Email"),
        ScalarField("Id", alti_key="account_id"),
        ResourceLinkField("OrganizationArn", OrgResourceSpec,
                          value_is_id=True),
        ResourceLinkField("OUArn", OUResourceSpec, value_is_id=True),
    )
    allow_clobber = [UnscannedAccountResourceSpec]

    @classmethod
    def get_full_type_name(cls: Type["OrgsAccountResourceSpec"]) -> str:
        return f"{cls.provider_name}:{cls.type_name}"

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

            {'ou_1_arn': {org_1_dict},
             'ou_2_arn': {org_2_dict},
             ...}

        Where the dicts represent results from list_accounts_for_parent."""
        org_resp = client.describe_organization()
        org_arn = org_resp["Organization"]["Arn"]
        # get all parents. parents include roots and ous.
        parent_ids_arns = {}
        paginator = client.get_paginator("list_roots")
        for resp in paginator.paginate():
            for root in resp["Roots"]:
                root_id, root_arn = root["Id"], root["Arn"]
                parent_ids_arns[root_id] = root_arn
                root_path = f"/{root['Name']}"
                ou_details = cls._recursively_get_ou_details_for_parent(
                    client=client, parent_id=root_id, parent_path=root_path)
                for ou_detail in ou_details:
                    ou_id, ou_arn = ou_detail["Id"], ou_detail["Arn"]
                    parent_ids_arns[ou_id] = ou_arn
        # now look up accounts for each ou
        orgs_accounts = {}
        accounts_paginator = client.get_paginator("list_accounts_for_parent")
        for parent_id, parent_arn in parent_ids_arns.items():
            for accounts_resp in accounts_paginator.paginate(
                    ParentId=parent_id):
                for account in accounts_resp["Accounts"]:
                    account_id = account["Id"]
                    account_arn = f"arn:aws::::account/{account_id}"
                    account["OrganizationArn"] = org_arn
                    account["OUArn"] = parent_arn
                    orgs_accounts[account_arn] = account
        return ListFromAWSResult(resources=orgs_accounts)

    @classmethod
    def _recursively_get_ou_details_for_parent(
            cls: Type["OrgsAccountResourceSpec"], client: BaseClient,
            parent_id: str, parent_path: str) -> List[Dict[str, Any]]:
        ous = []
        paginator = client.get_paginator(
            "list_organizational_units_for_parent")
        for resp in paginator.paginate(ParentId=parent_id):
            for ou in resp["OrganizationalUnits"]:
                ou_id = ou["Id"]
                path = f"{parent_path}/{ou['Name']}"
                ou["Path"] = path
                ous.append(ou)
                ous += cls._recursively_get_ou_details_for_parent(
                    client=client, parent_id=ou_id, parent_path=path)
        return ous
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
Esempio n. 28
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)