def test_parse(self): schema = Schema(ScalarField("Key1"), ScalarField("Key2")) data = {"Key1": "Value1", "Key2": "Value2"} link_collection = schema.parse(data, {}) expected_link_collection = LinkCollection(simple_links=( SimpleLink(pred="key1", obj="Value1"), SimpleLink(pred="key2", obj="Value2"), )) self.assertEqual(link_collection, expected_link_collection)
class CloudTrailTrailResourceSpec(CloudTrailResourceSpec): """Resource representing a CloudTrail Trail""" type_name = "trail" schema = Schema( ScalarField("Name"), ScalarField("S3BucketName"), ScalarField("IncludeGlobalServiceEvents"), ScalarField("IsMultiRegionTrail"), ) @classmethod def list_from_aws( cls: Type["CloudTrailTrailResourceSpec"], client: BaseClient, account_id: str, region: str, ) -> ListFromAWSResult: """Return a dict of dicts of the format: {'trail_1_arn': {trail_1_dict}, 'trail_2_arn': {trail_2_dict}, ...} Where the dicts represent results from describe_trails.""" trails = {} resp = client.describe_trails(includeShadowTrails=False) for trail in resp.get("trailList", []): resource_arn = trail["TrailARN"] trails[resource_arn] = trail return ListFromAWSResult(resources=trails)
class SeverityLevelResourceSpec(SupportResourceSpec): """Resource representing an AWS Support severity level.""" type_name = "severity-level" schema = Schema(ScalarField("code")) @classmethod def list_from_aws(cls: Type["SeverityLevelResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'severity_level_arn': {severity_level_dict}, 'severity_level_arn': {severity_level_dict}, ...} Where the dicts represent results from describe_organization.""" resp = client.describe_severity_levels() severity_levels_resp = resp["severityLevels"] severity_levels = {} for s_l in severity_levels_resp: code = s_l["code"] code_arn = cls.generate_arn(resource_id=code, account_id=account_id) severity_levels[code_arn] = {"code": code} return ListFromAWSResult(resources=severity_levels)
class EBSSnapshotResourceSpec(EC2ResourceSpec): """Resource for EBSSnapshots""" type_name = "snapshot" schema = Schema( ScalarField("VolumeSize"), ScalarField("Encrypted"), TransientResourceLinkField("KmsKeyId", KMSKeyResourceSpec, optional=True, value_is_id=True), TransientResourceLinkField("VolumeId", EBSVolumeResourceSpec), TagsField(), ) @classmethod def list_from_aws(cls: Type["EBSSnapshotResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'snapshot_1_arn': {snapshot_1_dict}, 'snapshot_2_arn': {snapshot_2_dict}, ...} Where the dicts represent results from describe_snapshots.""" snapshots = {} paginator = client.get_paginator("describe_snapshots") for resp in paginator.paginate(OwnerIds=["self"]): for snapshot in resp.get("Snapshots", []): resource_arn = cls.generate_arn( account_id=account_id, region=region, resource_id=snapshot["SnapshotId"]) snapshots[resource_arn] = snapshot return ListFromAWSResult(resources=snapshots)
class IAMSAMLProviderResourceSpec(IAMResourceSpec): """Resource for IAM SAML Providers""" type_name = "saml-provider" schema = Schema( ScalarField("Name"), ScalarField("ValidUntil"), ScalarField("CreateDate"), ScalarField("MetadataDocumentChecksum"), ) @classmethod def list_from_aws(cls: Type["IAMSAMLProviderResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'saml_provider_1_arn': {saml_provider_1_dict}, 'saml_provider_2_arn': {saml_provider_2_dict}, ...} Where the dicts represent results from list_saml_providers and additional info per saml_provider list_saml_providers. An additional 'Name' key is added.""" saml_providers = {} resp = client.list_saml_providers() for saml_provider in resp.get("SAMLProviderList", []): resource_arn = saml_provider["Arn"] saml_provider["Name"] = "/".join(resource_arn.split("/")[1:]) saml_provider_resp = client.get_saml_provider( SAMLProviderArn=resource_arn) saml_metadata_document = saml_provider_resp["SAMLMetadataDocument"] hash_object = hashlib.sha256(saml_metadata_document.encode()) saml_provider["MetadataDocumentChecksum"] = hash_object.hexdigest() saml_providers[resource_arn] = saml_provider return ListFromAWSResult(resources=saml_providers)
class AccountResourceSpec(AWSResourceSpec): """Resource representing an AWS Account""" type_name = "account" service_name = "sts" scan_granularity = ScanGranularity.ACCOUNT schema = Schema(ScalarField("account_id")) allow_clobber: List[Type[ResourceSpec]] = [UnscannedAccountResourceSpec] @classmethod def get_full_type_name(cls: Type["AccountResourceSpec"]) -> str: return f"{cls.provider_name}:{cls.type_name}" @classmethod def list_from_aws( cls: Type["AccountResourceSpec"], client: BaseClient, account_id: str, region: str ) -> ListFromAWSResult: """This resource is somewhat synthetic, this method simply returns a dict of form {'account_arn': {account_dict}""" sts_account_id = client.get_caller_identity()["Account"] if sts_account_id != account_id: raise ValueError(f"BUG: sts detected account_id {sts_account_id} != {account_id}") accounts = {f"arn:aws::::account/{sts_account_id}": {"account_id": sts_account_id}} return ListFromAWSResult(resources=accounts) @classmethod def generate_arn( cls: Type["AccountResourceSpec"], account_id: str, region: str, resource_id: str ) -> str: """Generate an ARN for this resource""" return f"arn:aws::::account/{resource_id}"
class EC2ImageResourceSpec(EC2ResourceSpec): """Resource for EC2Images (AMIs)""" type_name = "image" schema = Schema( ScalarField("Name", optional=True), ScalarField("Description", optional=True), ScalarField("Public"), TagsField(), ) @classmethod def list_from_aws(cls: Type["EC2ImageResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'image_1_arn': {image_1_dict}, 'image_2_arn': {image_2_dict}, ...} Where the dicts represent results from describe_images.""" images = {} resp = client.describe_images(Owners=["self"]) for image in resp["Images"]: image_id = image["ImageId"] resource_arn = cls.generate_arn(account_id=account_id, region=region, resource_id=image_id) images[resource_arn] = image return ListFromAWSResult(resources=images)
class 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)
class RegionResourceSpec(EC2ResourceSpec): """Resource representing an AWS Region""" type_name = "region" scan_granularity = ScanGranularity.ACCOUNT schema = Schema(ScalarField("RegionName", "name"), ScalarField("OptInStatus")) @classmethod def get_full_type_name(cls: Type["RegionResourceSpec"]) -> str: return f"{cls.provider_name}:{cls.type_name}" @classmethod def list_from_aws(cls: Type["RegionResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'region_1_arn': {region_1_dict}, 'region_2_arn': {region_2_dict}, ...} Where the dicts represent results from describe_regions.""" regions = {} resp = client.describe_regions(AllRegions=True) for region_resp in resp["Regions"]: region_name = region_resp["RegionName"] region_arn = f"arn:aws:::{account_id}:region/{region_name}" regions[region_arn] = region_resp return ListFromAWSResult(resources=regions)
class IAMAWSManagedPolicyResourceSpec(IAMResourceSpec): """Resource for AWS-managed IAM Policies""" type_name = "policy" schema = Schema(ScalarField("PolicyName", "name"), ScalarField("PolicyId")) @classmethod def list_from_aws( cls: Type["IAMAWSManagedPolicyResourceSpec"], 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_policies and additional info per role from list_targets_by_role.""" policies = {} paginator = client.get_paginator("list_policies") for resp in paginator.paginate(Scope="AWS", OnlyAttached=True): for policy in resp.get("Policies", []): resource_arn = policy["Arn"] policies[resource_arn] = policy return ListFromAWSResult(resources=policies)
class VpcEndpointResourceSpec(EC2ResourceSpec): """Resource for VPC Endpoints""" type_name = "vpc-endpoint" schema = Schema( ScalarField("VpcEndpointType"), ScalarField("ServiceName"), ScalarField("State"), ResourceLinkField("VpcId", VPCResourceSpec), TagsField(), ) @classmethod def list_from_aws( cls: Type["VpcEndpointResourceSpec"], client: BaseClient, account_id: str, region: str ) -> ListFromAWSResult: """Return a dict of dicts of the format: {'vpc_endpoint_1_arn': {vpc_endpoint_1_dict}, 'vpc_endpoint_1_arn': {vpc_endpoint_2_dict}, ...} Where the dicts represent results from describe_vpc_endpoints.""" endpoints = {} paginator = client.get_paginator("describe_vpc_endpoints") for resp in paginator.paginate(): for endpoint in resp.get("VpcEndpoints", []): resource_arn = cls.generate_arn(account_id, region, endpoint["VpcEndpointId"]) endpoints[resource_arn] = endpoint return ListFromAWSResult(resources=endpoints)
class SubnetResourceSpec(EC2ResourceSpec): """Resource for Subnets""" type_name = "subnet" schema = Schema( ScalarField("CidrBlock"), ScalarField("FirstIp"), ScalarField("LastIp"), ScalarField("State"), TagsField(), ResourceLinkField("VpcId", VPCResourceSpec), ) @classmethod def list_from_aws( cls: Type["SubnetResourceSpec"], client: BaseClient, account_id: str, region: str ) -> ListFromAWSResult: subnets = {} resp = client.describe_subnets() for subnet in resp.get("Subnets", []): resource_arn = cls.generate_arn( account_id=account_id, region=region, resource_id=subnet["SubnetId"] ) cidr = subnet["CidrBlock"] ipv4_network = ipaddress.IPv4Network(cidr, strict=False) first_ip, last_ip = int(ipv4_network[0]), int(ipv4_network[-1]) subnet["FirstIp"] = first_ip subnet["LastIp"] = last_ip subnets[resource_arn] = subnet return ListFromAWSResult(resources=subnets)
class LambdaFunctionResourceSpec(LambdaResourceSpec): """Resource for Lambda Functions""" type_name = "function" schema = Schema( ScalarField("FunctionName"), ScalarField("Runtime", optional=True), AnonymousDictField( "VpcConfig", TransientResourceLinkField("VpcId", VPCResourceSpec, optional=True), optional=True, ), ) @classmethod def list_from_aws(cls: Type["LambdaFunctionResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'function_1_arn': {function_1_dict}, 'function_2_arn': {function_2_dict}, ...} Where the dicts represent results from list_functions.""" functions = {} paginator = client.get_paginator("list_functions") for resp in paginator.paginate(): for function in resp.get("Functions", []): resource_arn = function["FunctionArn"] functions[resource_arn] = function return ListFromAWSResult(resources=functions)
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)
class EKSClusterResourceSpec(EKSResourceSpec): """Resource for Clusters""" type_name = "cluster" schema = Schema(ScalarField("Name"), ) @classmethod def list_from_aws(cls: Type["EKSClusterResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'cluster_1_arn': {cluster_1_dict}, 'cluster_2_arn': {cluster_2_dict}, ...} Where the dicts represent results from list_clusters.""" clusters = {} try: paginator = client.get_paginator("list_clusters") for resp in paginator.paginate(): for cluster_name in resp.get("clusters", []): resource_arn = cls.generate_arn(account_id=account_id, region=region, resource_id=cluster_name) clusters[resource_arn] = {"Name": cluster_name} except ClientError as c_e: response_error = getattr(c_e, "response", {}).get("Error", {}) error_code = response_error.get("Code", "") if error_code != "AccessDeniedException": raise c_e error_msg = response_error.get("Message", "") if error_msg != f"Account {account_id} is not authorized to use this service": raise c_e return ListFromAWSResult(resources=clusters)
class VPCResourceSpec(EC2ResourceSpec): """Resource for VPCs""" type_name = "vpc" schema = Schema(ScalarField("IsDefault"), ScalarField("CidrBlock"), ScalarField("State"), TagsField()) @classmethod def list_from_aws(cls: Type["VPCResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'vpc_1_arn': {vpc_1_dict}, 'vpc_2_arn': {vpc_2_dict}, ...} Where the dicts represent results from describe_vpcs.""" vpcs = {} paginator = client.get_paginator("describe_vpcs") for resp in paginator.paginate(): for vpc in resp.get("Vpcs", []): resource_arn = cls.generate_arn(account_id=account_id, region=region, resource_id=vpc["VpcId"]) vpcs[resource_arn] = vpc return ListFromAWSResult(resources=vpcs)
class 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)
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)
class OrgResourceSpec(OrganizationsResourceSpec): """Resource representing an AWS Org.""" type_name = "organization" schema = Schema(ScalarField("MasterAccountId"), ScalarField("MasterAccountEmail")) @classmethod def get_full_type_name(cls: Type["OrgResourceSpec"]) -> str: return f"{cls.provider_name}:{cls.type_name}" @classmethod def list_from_aws(cls: Type["OrgResourceSpec"], client: BaseClient, account_id: str, region: str) -> ListFromAWSResult: """Return a dict of dicts of the format: {'org_1_arn': {org_1_dict}, 'org_2_arn': {org_2_dict}, ...} Where the dicts represent results from describe_organization.""" resp = client.describe_organization() org = resp["Organization"] orgs = {org["Arn"]: org} return ListFromAWSResult(resources=orgs)
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
class EC2InstanceResourceSpec(EC2ResourceSpec): """Resource for EC2Instances""" type_name = "instance" schema = Schema( ScalarField("Name", optional=True), TransientResourceLinkField("ImageId", EC2ImageResourceSpec), ScalarField("KeyName", optional=True), AnonymousDictField("Placement", ScalarField("AvailabilityZone"), ScalarField("Tenancy")), ScalarField("InstanceType"), ScalarField("LaunchTime"), AnonymousDictField("State", ScalarField("Name", "state")), ScalarField("Platform", optional=True), ScalarField("PrivateIpAddress", optional=True), ScalarField("PrivateDnsName", optional=True), ScalarField("PublicIpAddress", optional=True), ScalarField("PublicDnsName", optional=True), ResourceLinkField("VpcId", VPCResourceSpec, optional=True), ResourceLinkField("SubnetId", SubnetResourceSpec, optional=True), AnonymousListField( "SecurityGroups", AnonymousEmbeddedDictField(ResourceLinkField("GroupId", SecurityGroupResourceSpec)), ), AnonymousDictField( "IamInstanceProfile", TransientResourceLinkField( "Arn", InstanceProfileResourceSpec, alti_key="instance_profile", value_is_id=True ), optional=True, ), TagsField(), ) @classmethod def list_from_aws( cls: Type["EC2InstanceResourceSpec"], client: BaseClient, account_id: str, region: str ) -> ListFromAWSResult: """Return a dict of dicts of the format: {'instance_1_arn': {instance_1_dict}, 'instance_2_arn': {instance_2_dict}, ...} Where the dicts represent results from describe_instances.""" paginator = client.get_paginator("describe_instances") instances = {} for resp in paginator.paginate(): for reservation in resp.get("Reservations", []): for instance in reservation.get("Instances", []): resource_arn = cls.generate_arn( account_id=account_id, region=region, resource_id=instance["InstanceId"] ) instances[resource_arn] = instance for tag in instance.get("Tags", []): if tag["Key"].lower() == "name": instance["Name"] = tag["Value"] break return ListFromAWSResult(resources=instances)
class IAMPolicyResourceSpec(IAMResourceSpec): """Resource for user-managed IAM Policies""" type_name = "policy" parallel_scan = True schema = Schema( ScalarField("PolicyName", "name"), ScalarField("PolicyId"), ScalarField("DefaultVersionId"), ScalarField("DefaultVersionPolicyDocumentText"), ) @classmethod def list_from_aws(cls: Type["IAMPolicyResourceSpec"], 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_policies and additional info per role from list_targets_by_role.""" policies = {} paginator = client.get_paginator("list_policies") for resp in paginator.paginate(Scope="Local"): for policy in resp.get("Policies", []): resource_arn = policy["Arn"] default_policy_version = policy["DefaultVersionId"] try: default_policy_version_document_text = cls.get_policy_version_document_text( client=client, policy_arn=resource_arn, policy_version=default_policy_version, ) policy[ "DefaultVersionPolicyDocumentText"] = policy_doc_dict_to_sorted_str( default_policy_version_document_text) policies[resource_arn] = policy 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=policies) @classmethod def get_policy_version_document_text( cls: Type["IAMPolicyResourceSpec"], client: BaseClient, policy_arn: str, policy_version: str, ) -> Dict[str, Any]: policy_version_resp = client.get_policy_version( PolicyArn=policy_arn, VersionId=policy_version) return policy_version_resp["PolicyVersion"]["Document"]
def test_parse(self): schema = Schema(ScalarField("Key1"), ScalarField("Key2")) data = {"Key1": "Value1", "Key2": "Value2"} links = schema.parse(data, {}) expected_link_data = [ { "pred": "key1", "obj": "Value1", "type": "simple" }, { "pred": "key2", "obj": "Value2", "type": "simple" }, ] link_data = [link.to_dict() for link in links] self.assertEqual(expected_link_data, link_data)
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)
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"), TransientResourceLinkField("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 TestResourceSpec(ResourceSpec): type_name = "test_type_name" schema = Schema() @classmethod def scan(cls, scan_accessor: Any) -> List[Resource]: return [] @classmethod def get_full_type_name(cls) -> str: return cls.type_name
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)
class TestResourceSpec(ResourceSpec): type_name = "test_type_name" schema = Schema() @classmethod def scan(cls, scan_accessor: Any) -> ResourceScanResult: return ResourceScanResult(resources=[], stats=MultilevelCounter(), errors=[]) @classmethod def get_full_type_name(cls) -> str: return cls.type_name
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 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"]