def test_disappearing_access_key_race_condition(self): account_id = "123456789012" user_name = "foo" region_name = "us-east-1" session = boto3.Session() client = session.client("iam") client.create_user(UserName=user_name) client.create_access_key(UserName=user_name) scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with patch( "altimeter.aws.resource.iam.user.IAMUserResourceSpec.get_access_key_last_used" ) as mock_get_group_users: mock_get_group_users.side_effect = ClientError( operation_name="GetAccessKeyLastUsed", error_response={ "Error": { "Code": "AccessDenied", "Message": "", } }, ) resources = IAMUserResourceSpec.scan(scan_accessor=scan_accessor) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, "arn:aws:iam::123456789012:user/foo")
def test_disappearing_policy_race_condition(self): account_id = "123456789012" policy_name = "foo" region_name = "us-east-1" session = boto3.Session() client = session.client("iam") policy_json = { "Version": "2012-10-17", "Statement": [{"Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "*"}], } policy_resp = client.create_policy( PolicyName=policy_name, PolicyDocument=json.dumps(policy_json), ) policy_arn = policy_resp["Policy"]["Arn"] scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with patch( "altimeter.aws.resource.iam.policy.IAMPolicyResourceSpec.get_policy_version_document_text" ) as mock_get_policy_version_document_text: mock_get_policy_version_document_text.side_effect = ClientError( operation_name="GetPolicyVersion", error_response={ "Error": { "Code": "NoSuchEntity", "Message": f"Policy {policy_arn} version v1 does not exist or is not attachable.", } }, ) resources = IAMPolicyResourceSpec.scan(scan_accessor=scan_accessor) self.assertEqual(resources, [])
def test_disappearing_user_race_condition_get_user_mfa_devices(self): account_id = "123456789012" user_name = "foo" region_name = "us-east-1" session = boto3.Session() client = session.client("iam") client.create_user(UserName=user_name) scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with patch( "altimeter.aws.resource.iam.user.IAMUserResourceSpec.get_user_mfa_devices" ) as mock_get_group_users: mock_get_group_users.side_effect = ClientError( operation_name="ListMFADevices", error_response={ "Error": { "Code": "NoSuchEntity", "Message": f"The user with name {user_name} cannot be found.", } }, ) resources = IAMUserResourceSpec.scan(scan_accessor=scan_accessor) self.assertEqual(resources, [])
def test_disappearing_rule_race_condition(self): account_id = "123456789012" region_name = "us-east-1" rule_name = "test_rule" session = boto3.Session() client = session.client("events", region_name=region_name) client.put_rule( Name=rule_name, Description="Capture all events and forward them to 012345678901", EventPattern=f"""{{"account":["012345678901"]}}""", State="ENABLED", ) scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with patch( "altimeter.aws.resource.events.cloudwatchevents_rule.list_targets_by_rule" ) as mock_list_targets_by_rule: mock_list_targets_by_rule.side_effect = ClientError( operation_name="ListTargetsByRule", error_response={ "Error": { "Code": "ResourceNotFoundException", "Message": f"Rule {rule_name} does not exist on EventBus default.", } }, ) resources = EventsRuleResourceSpec.scan( scan_accessor=scan_accessor) self.assertEqual(resources, [])
def test_scan(self): account_id = "123456789012" region_name = "us-east-1" session = boto3.Session() scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) resources = AccountResourceSpec.scan(scan_accessor=scan_accessor) expected_resources = [ { "type": "aws:account", "links": [{"pred": "account_id", "obj": "123456789012", "type": "simple"}], } ] expected_api_call_stats = { "count": 1, "123456789012": { "count": 1, "us-east-1": { "count": 1, "sts": {"count": 1, "GetCallerIdentity": {"count": 1}}, }, }, } self.assertListEqual([resource.to_dict() for resource in resources], expected_resources) self.assertDictEqual(scan_accessor.api_call_stats.to_dict(), expected_api_call_stats)
def test_disappearing_role_race_condition(self): account_id = "123456789012" role_name = "foo" region_name = "us-east-1" session = boto3.Session() client = session.client("iam") client.create_role(RoleName=role_name, AssumeRolePolicyDocument="{}") scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with patch("altimeter.aws.resource.iam.role.get_attached_role_policies" ) as mock_get_group_users: mock_get_group_users.side_effect = ClientError( operation_name="ListAttachedRolePolicies", error_response={ "Error": { "Code": "NoSuchEntity", "Message": f"The role with name {role_name} cannot be found.", } }, ) resources = IAMRoleResourceSpec.scan(scan_accessor=scan_accessor) self.assertEqual(resources, [])
def test_disappearing_table_get_continuous_backup_table_data_race_condition(self): account_id = "123456789012" region_name = "us-east-1" table_name = "foo" session = boto3.Session() client = session.client("dynamodb", region_name=region_name) client.create_table( AttributeDefinitions=[{"AttributeName": "string", "AttributeType": "S",},], KeySchema=[{"AttributeName": "string", "KeyType": "HASH",},], TableName=table_name, ) scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with patch( "altimeter.aws.resource.dynamodb.dynamodb_table.get_continuous_backup_table_data" ) as mock_get_continuous_backup_table_data: mock_get_continuous_backup_table_data.side_effect = ClientError( operation_name="DescribeContinuousBackups", error_response={ "Error": { "Code": "TableNotFoundException", "Message": f"Table not found: {table_name}", } }, ) resources = DynamoDbTableResourceSpec.scan(scan_accessor=scan_accessor) self.assertEqual(resources, [])
def test_disappearing_target_group_race_condition(self): account_id = "123456789012" region_name = "us-east-1" tg_name = "foo" session = boto3.Session() client = session.client("elbv2", region_name=region_name) resp = client.create_target_group(Name=tg_name, Port=443) tg_arn = resp["TargetGroups"][0]["TargetGroupArn"] scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with patch( "altimeter.aws.resource.elbv2.target_group.get_target_group_health" ) as mock_get_target_group_health: mock_get_target_group_health.side_effect = ClientError( operation_name="DescribeTargetHealth", error_response={ "Error": { "Code": "TargetGroupNotFound", "Message": f"Target groups '{tg_arn}' not found", } }, ) resources = TargetGroupResourceSpec.scan( scan_accessor=scan_accessor) self.assertEqual(resources, [])
def test_disappearing_saml_provider_race_condition(self): account_id = "123456789012" saml_provider_name = "foo" region_name = "us-east-1" session = boto3.Session() client = session.client("iam") saml_provider_resp = client.create_saml_provider( Name=saml_provider_name, SAMLMetadataDocument="a" * 1024) saml_provider_arn = saml_provider_resp["SAMLProviderArn"] scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with patch( "altimeter.aws.resource.iam.iam_saml_provider.IAMSAMLProviderResourceSpec.get_saml_provider_metadata_doc" ) as mock_get_saml_provider_metadata_doc: mock_get_saml_provider_metadata_doc.side_effect = ClientError( operation_name="GetSAMLProvider", error_response={ "Error": { "Code": "NoSuchEntity", "Message": f"GetSAMLProvider operation: Manifest not found for arn {saml_provider_arn}", } }, ) resources = IAMSAMLProviderResourceSpec.scan( scan_accessor=scan_accessor) self.assertEqual(resources, [])
def scan_services( graph_name: str, graph_version: str, account_id: str, region: str, service: str, access_key: str, secret_key: str, token: str, resource_spec_classes: Tuple[Type[AWSResourceSpec], ...], ) -> Dict[str, Any]: logger = Logger() with logger.bind(region=region, service=service): logger.info(event=AWSLogEvents.ScanAWSAccountServiceStart) session = boto3.Session( aws_access_key_id=access_key, aws_secret_access_key=secret_key, aws_session_token=token, region_name=region, ) aws_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region) graph_spec = GraphSpec( name=graph_name, version=graph_version, resource_spec_classes=resource_spec_classes, scan_accessor=aws_accessor, ) graph_set = graph_spec.scan() graph_set_dict = graph_set.to_dict() logger.info(event=AWSLogEvents.ScanAWSAccountServiceEnd) return graph_set_dict
def test_detect_account_id_session_mismatch(self): account_id = "234567890121" region_name = "us-east-1" session = boto3.Session() scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with self.assertRaises(ValueError): AccountResourceSpec.scan(scan_accessor=scan_accessor)
def scan_scan_unit(scan_unit: ScanUnit) -> Tuple[str, Dict[str, Any]]: logger = Logger() with logger.bind( account_id=scan_unit.account_id, region=scan_unit.region_name, service=scan_unit.service, resource_classes=sorted([ resource_spec_class.__name__ for resource_spec_class in scan_unit.resource_spec_classes ]), ): start_t = time.time() logger.info(event=AWSLogEvents.ScanAWSAccountServiceStart) session = boto3.Session( aws_access_key_id=scan_unit.access_key, aws_secret_access_key=scan_unit.secret_key, aws_session_token=scan_unit.token, region_name=scan_unit.region_name, ) scan_accessor = AWSAccessor(session=session, account_id=scan_unit.account_id, region_name=scan_unit.region_name) graph_spec = GraphSpec( name=scan_unit.graph_name, version=scan_unit.graph_version, resource_spec_classes=scan_unit.resource_spec_classes, scan_accessor=scan_accessor, ) start_time = int(time.time()) resources: List[Resource] = [] errors = [] try: resources = graph_spec.scan() except Exception as ex: error_str = str(ex) trace_back = traceback.format_exc() logger.error(event=AWSLogEvents.ScanAWSAccountError, error=error_str, trace_back=trace_back) error = f"{str(ex)}\n{trace_back}" errors.append(error) end_time = int(time.time()) graph_set = GraphSet( name=scan_unit.graph_name, version=scan_unit.graph_version, start_time=start_time, end_time=end_time, resources=resources, errors=errors, stats=scan_accessor.api_call_stats, ) end_t = time.time() elapsed_sec = end_t - start_t logger.info(event=AWSLogEvents.ScanAWSAccountServiceEnd, elapsed_sec=elapsed_sec) return (scan_unit.account_id, graph_set.to_dict())
def test_scan(self): account_id = "123456789012" region_name = "us-east-1" session = boto3.Session() client = session.client("iam") oidc_url = "https://oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E" oidc_client_ids = ["sts.amazonaws.com"] oidc_thumbprints = ["9999999999999999999999999999999999999999"] _ = client.create_open_id_connect_provider( Url=oidc_url, ClientIDList=oidc_client_ids, ThumbprintList=oidc_thumbprints, ) scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) resources = IAMOIDCProviderResourceSpec.scan( scan_accessor=scan_accessor) expected_resources = [ Resource( resource_id= "arn:aws:iam::123456789012:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E", type="aws:iam:oidc-provider", link_collection=LinkCollection( simple_links=( SimpleLink( pred="url", obj= "oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E", ), SimpleLink( pred="create_date", obj=resources[0].link_collection.simple_links[1]. obj, ), SimpleLink(pred="client_id", obj="sts.amazonaws.com"), SimpleLink( pred="thumbprint", obj="9999999999999999999999999999999999999999"), ), multi_links=None, tag_links=None, resource_links=(ResourceLink( pred="account", obj="arn:aws::::account/123456789012"), ), transient_resource_links=None, ), ) ] self.assertEqual(resources, expected_resources)
def test_scan(self): account_id = "123456789012" region_name = "us-east-1" session = boto3.Session() ec2_client = session.client("ec2", region_name=region_name) list_resp = ec2_client.describe_vpcs() present_vpcs = list_resp["Vpcs"] self.assertEqual(len(present_vpcs), 1) present_vpc_id = present_vpcs[0]["VpcId"] present_vpc_arn = f"arn:aws:ec2:us-east-1:123456789012:vpc/{present_vpc_id}" create_resp = ec2_client.create_vpc(CidrBlock="10.0.0.0/16") created_vpc_id = create_resp["Vpc"]["VpcId"] created_vpc_arn = f"arn:aws:ec2:us-east-1:123456789012:vpc/{created_vpc_id}" scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) resources = VPCResourceSpec.scan(scan_accessor=scan_accessor) expected_resources = [ Resource( resource_id=present_vpc_arn, type="aws:ec2:vpc", link_collection=LinkCollection( simple_links=( SimpleLink(pred="is_default", obj=True), SimpleLink(pred="cidr_block", obj="172.31.0.0/16"), SimpleLink(pred="state", obj="available"), ), resource_links=( ResourceLink(pred="account", obj="arn:aws::::account/123456789012"), ResourceLink(pred="region", obj="arn:aws:::123456789012:region/us-east-1"), ), ), ), Resource( resource_id=created_vpc_arn, type="aws:ec2:vpc", link_collection=LinkCollection( simple_links=( SimpleLink(pred="is_default", obj=False), SimpleLink(pred="cidr_block", obj="10.0.0.0/16"), SimpleLink(pred="state", obj="available"), ), resource_links=( ResourceLink(pred="account", obj="arn:aws::::account/123456789012"), ResourceLink(pred="region", obj="arn:aws:::123456789012:region/us-east-1"), ), ), ), ] self.assertEqual(resources, expected_resources)
def test_scan(self): account_id = "123456789012" region_name = "us-east-1" session = boto3.Session() ec2_client = session.client("ec2", region_name=region_name) resp = ec2_client.create_volume(Size=1, AvailabilityZone="us-east-1a") create_time = resp["CreateTime"] scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) scan_result = EBSVolumeResourceSpec.scan(scan_accessor=scan_accessor) scan_result_dict = scan_result.to_dict() expected_scan_result_dict = { "resources": [ { "type": "aws:ec2:volume", "links": [ {"pred": "availability_zone", "obj": "us-east-1a", "type": "simple"}, {"pred": "create_time", "obj": create_time, "type": "simple"}, {"pred": "size", "obj": 1, "type": "simple"}, {"pred": "state", "obj": "available", "type": "simple"}, {"pred": "volume_type", "obj": "standard", "type": "simple"}, {"pred": "encrypted", "obj": False, "type": "simple"}, { "pred": "account", "obj": "arn:aws::::account/123456789012", "type": "resource_link", }, { "pred": "region", "obj": "arn:aws:::123456789012:region/us-east-1", "type": "resource_link", }, ], } ], "stats": { "count": 1, "123456789012": { "count": 1, "us-east-1": {"count": 1, "ec2": {"count": 1, "DescribeVolumes": {"count": 1}}}, }, }, "errors": [], } self.assertDictEqual(scan_result_dict, expected_scan_result_dict)
def main(argv: Optional[List[str]] = None) -> int: import argparse if argv is None: argv = sys.argv[1:] parser = argparse.ArgumentParser() parser.add_argument( "resource_spec_class", type=str, help= "Name of class in altimeter.aws.scan.settings.RESOURCE_SPEC_CLASSES to scan", ) parser.add_argument("region", type=str, help="AWS region name to scan") args_ns = parser.parse_args(argv) resource_spec_class_name = args_ns.resource_spec_class region = args_ns.region resource_spec_class: Optional[Type[AWSResourceSpec]] = None for cls in RESOURCE_SPEC_CLASSES: if cls.__name__ == resource_spec_class_name: resource_spec_class = cls break if resource_spec_class is None: print(( f"Unable to find a class named {resource_spec_class_name} in " f"altimeter.aws.scan.settings.RESOURCE_SPEC_CLASSES: {RESOURCE_SPEC_CLASSES}." )) return 1 session = boto3.Session(region_name=region) sts_client = session.client("sts") account_id = sts_client.get_caller_identity()["Account"] aws_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region) resource_scan_result = resource_spec_class.scan(aws_accessor) resource_dicts = [] for resource in resource_scan_result: resource_dicts.append(resource.to_dict()) resource_scan_result_json = json.dumps(resource_dicts, indent=2, default=json_encoder) print(resource_scan_result_json) return 0
def _list_from_aws(cls: Type["AWSResourceSpec"], scan_accessor: AWSAccessor) -> ListFromAWSResult: try: resource_client = scan_accessor.client(cls.get_client_name()) if cls.skip_resource_scan( client=resource_client, account_id=scan_accessor.account_id, region=scan_accessor.region, ): return ListFromAWSResult(resources={}) return cls.list_from_aws(resource_client, scan_accessor.account_id, scan_accessor.region) except ClientError as c_e: response_error = getattr(c_e, "response", {}).get("Error", {}) error_code = response_error.get("Code", "") if error_code not in AWS_API_IGNORE_ERRORS: raise c_e return ListFromAWSResult(resources={})
def test_scan(self): account_id = "123456789012" region_name = "us-east-1" session = boto3.Session() scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) resources = AccountResourceSpec.scan(scan_accessor=scan_accessor) expected_resources = [ Resource( resource_id="arn:aws::::account/123456789012", type="aws:account", link_collection=LinkCollection(simple_links=(SimpleLink( pred="account_id", obj="123456789012"), ), ), ) ] self.assertEqual(resources, expected_resources)
def test_scan(self): account_id = "123456789012" region_name = "us-east-1" session = boto3.Session() ec2_client = session.client("ec2", region_name=region_name) resp = ec2_client.create_volume(Size=1, AvailabilityZone="us-east-1a") create_time = resp["CreateTime"] created_volume_id = resp["VolumeId"] created_volume_arn = f"arn:aws:ec2:us-east-1:123456789012:volume/{created_volume_id}" scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) resources = EBSVolumeResourceSpec.scan(scan_accessor=scan_accessor) expected_resources = [ Resource( resource_id=created_volume_arn, type="aws:ec2:volume", link_collection=LinkCollection( simple_links=( SimpleLink(pred="availability_zone", obj="us-east-1a"), SimpleLink(pred="create_time", obj=create_time), SimpleLink(pred="size", obj=True), SimpleLink(pred="state", obj="available"), SimpleLink(pred="volume_type", obj="standard"), SimpleLink(pred="encrypted", obj=False), ), resource_links=( ResourceLink(pred="account", obj="arn:aws::::account/123456789012"), ResourceLink( pred="region", obj="arn:aws:::123456789012:region/us-east-1"), ), ), ) ] self.assertEqual(resources, expected_resources)
def test_disappearing_elb_race_condition(self): account_id = "123456789012" region_name = "us-east-1" lb_name = "foo" session = boto3.Session() client = session.client("elb", region_name=region_name) client.create_load_balancer( LoadBalancerName=lb_name, Listeners=[{ "Protocol": "HTTP", "LoadBalancerPort": 80, "InstancePort": 80 }], Tags=[{ "Key": "Name", "Value": lb_name }], ) scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with patch( "altimeter.aws.resource.elbv1.load_balancer.ClassicLoadBalancerResourceSpec.get_lb_attrs" ) as mock_get_lb_attrs: mock_get_lb_attrs.side_effect = ClientError( operation_name="DescribeLoadBalancerAttributes", error_response={ "Error": { "Code": "LoadBalancerNotFound", "Message": f"There is no ACTIVE Load Balancer named '{lb_name}'", } }, ) resources = ClassicLoadBalancerResourceSpec.scan( scan_accessor=scan_accessor) self.assertEqual(resources, [])
def test_disappearing_instance_race_condition(self): account_id = "123456789012" region_name = "us-east-1" instance_name = "foo" session = boto3.Session() client = session.client("rds", region_name=region_name) client.create_db_instance( DBInstanceIdentifier=instance_name, Engine="postgres", DBName=instance_name, DBInstanceClass="db.m1.small", MasterUsername="******", MasterUserPassword="******", ) scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with patch( "altimeter.aws.resource.rds.instance.RDSInstanceResourceSpec.get_instance_tags" ) as mock_get_instance_tags: with patch( "altimeter.aws.resource.rds.instance.RDSInstanceResourceSpec.set_automated_backups" ) as mock_set_automated_backups: mock_set_automated_backups.return_value = None mock_get_instance_tags.side_effect = ClientError( operation_name="ListTagsForResource", error_response={ "Error": { "Code": "DBInstanceNotFound", "Message": f"Could not find a DB Instance matching the resource name: '{instance_name}'", } }, ) resources = RDSInstanceResourceSpec.scan( scan_accessor=scan_accessor) self.assertEqual(resources, [])
def test_scan(self): account_id = "123456789012" region_name = "us-east-1" session = boto3.Session() scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) scan_result = AccountResourceSpec.scan(scan_accessor=scan_accessor) scan_result_dict = scan_result.to_dict() expected_scan_result_dict = { "resources": [{ "type": "aws:account", "links": [{ "pred": "account_id", "obj": "123456789012", "type": "simple" }], }], "stats": { "count": 1, "123456789012": { "count": 1, "us-east-1": { "count": 1, "sts": { "count": 1, "GetCallerIdentity": { "count": 1 } }, }, }, }, "errors": [], } self.assertDictEqual(scan_result_dict, expected_scan_result_dict)
def test_disappearing_oidc_provider_race_condition(self): account_id = "123456789012" region_name = "us-east-1" oidc_url = "https://oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E" oidc_client_ids = ["sts.amazonaws.com"] oidc_thumbprints = ["9999999999999999999999999999999999999999"] session = boto3.Session() client = session.client("iam") oidc_provider_resp = client.create_open_id_connect_provider( Url=oidc_url, ClientIDList=oidc_client_ids, ThumbprintList=oidc_thumbprints, ) oidc_provider_arn = oidc_provider_resp["OpenIDConnectProviderArn"] scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with patch( "altimeter.aws.resource.iam.iam_oidc_provider.IAMOIDCProviderResourceSpec" ".get_oidc_provider_details" ) as mock_get_oidc_provider_details: mock_get_oidc_provider_details.side_effect = ClientError( operation_name="GetOIDCProvider", error_response={ "Error": { "Code": "NoSuchEntity", "Message": f"OpenIDConnect Provider not found for arn {oidc_provider_arn}", } }, ) resources = IAMOIDCProviderResourceSpec.scan( scan_accessor=scan_accessor) self.assertEqual(resources, [])
def test_disappearing_elb_race_condition(self): account_id = "123456789012" region_name = "us-east-1" lb_name = "foo" session = boto3.Session() ec2_client = session.client("ec2", region_name=region_name) moto_subnets = [ subnet["SubnetId"] for subnet in ec2_client.describe_subnets()["Subnets"] ] client = session.client("elbv2", region_name=region_name) resp = client.create_load_balancer( Name=lb_name, Subnets=moto_subnets[:2], ) lb_arn = resp["LoadBalancers"][0]["LoadBalancerArn"] scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) with patch( "altimeter.aws.resource.elbv2.load_balancer.LoadBalancerResourceSpec.get_lb_attrs" ) as mock_get_lb_attrs: mock_get_lb_attrs.side_effect = ClientError( operation_name="DescribeLoadBalancerAttributes", error_response={ "Error": { "Code": "LoadBalancerNotFound", "Message": f"Load balancer '{lb_arn}' not found", } }, ) resources = LoadBalancerResourceSpec.scan( scan_accessor=scan_accessor) self.assertEqual(resources, [])
def test_scan(self): account_id = "123456789012" region_name = "us-east-1" session = boto3.Session() ec2_client = session.client("ec2", region_name=region_name) ec2_client.create_vpc(CidrBlock="10.0.0.0/16") scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) resources = VPCResourceSpec.scan(scan_accessor=scan_accessor) expected_resources = [ { "type": "aws:ec2:vpc", "links": [ { "pred": "is_default", "obj": True, "type": "simple" }, { "pred": "cidr_block", "obj": "172.31.0.0/16", "type": "simple", }, # from moto { "pred": "state", "obj": "available", "type": "simple" }, { "pred": "account", "obj": "arn:aws::::account/123456789012", "type": "resource_link", }, { "pred": "region", "obj": "arn:aws:::123456789012:region/us-east-1", "type": "resource_link", }, ], }, { "type": "aws:ec2:vpc", "links": [ { "pred": "is_default", "obj": False, "type": "simple" }, { "pred": "cidr_block", "obj": "10.0.0.0/16", "type": "simple" }, { "pred": "state", "obj": "available", "type": "simple" }, { "pred": "account", "obj": "arn:aws::::account/123456789012", "type": "resource_link", }, { "pred": "region", "obj": "arn:aws:::123456789012:region/us-east-1", "type": "resource_link", }, ], }, ] expected_api_call_stats = { "count": 1, "123456789012": { "count": 1, "us-east-1": { "count": 1, "ec2": { "count": 1, "DescribeVpcs": { "count": 1 } } }, }, } self.assertListEqual([resource.to_dict() for resource in resources], expected_resources) self.assertDictEqual(scan_accessor.api_call_stats.to_dict(), expected_api_call_stats)
def test_scan(self): account_id = "123456789012" region_name = "us-east-1" session = boto3.Session() iam_client = session.client("iam") test_assume_role_policy_doc = { "Version": "2012-10-17", "Statement": [{ "Sid": "abc", "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole", }], } iam_role_resp = iam_client.create_role( RoleName="testrole", AssumeRolePolicyDocument=json.dumps(test_assume_role_policy_doc), ) iam_role_arn = iam_role_resp["Role"]["Arn"] lambda_client = session.client("lambda", region_name=region_name) lambda_client.create_function( FunctionName="func_name", Runtime="python3.7", Role=iam_role_arn, Handler="testhandler", Description="testdescr", Timeout=90, MemorySize=128, Code={"ZipFile": b"1234"}, Publish=False, VpcConfig={ "SubnetIds": ["subnet-123"], "SecurityGroupIds": ["sg-123"] }, DeadLetterConfig={"TargetArn": "test_dl_config"}, Environment={"Variables": { "TEST_VAR": "test_val" }}, KMSKeyArn="test_kms_arn", TracingConfig={"Mode": "Active"}, Tags={ "tagkey1": "tagval1", "tagkey2": "tagval2" }, ) scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) resources = LambdaFunctionResourceSpec.scan( scan_accessor=scan_accessor) expected_resources = [ Resource( resource_id= "arn:aws:lambda:us-east-1:123456789012:function:func_name", type="aws:lambda:function", link_collection=LinkCollection( simple_links=( SimpleLink(pred="function_name", obj="func_name"), SimpleLink(pred="runtime", obj="python3.7"), ), resource_links=( ResourceLink(pred="account", obj="arn:aws::::account/123456789012"), ResourceLink( pred="region", obj="arn:aws:::123456789012:region/us-east-1"), ), transient_resource_links=( TransientResourceLink( pred="vpc", obj= "arn:aws:ec2:us-east-1:123456789012:vpc/vpc-123abc" ), TransientResourceLink( pred="role", obj="arn:aws:iam::123456789012:role/testrole"), ), ), ) ] self.maxDiff = None self.assertEqual(resources, expected_resources)
def test_get_embedded_policy(self): account_id = "123456789012" region_name = "us-east-1" role_name = "foo" role_policy = "foo_policy" policy_document = """{ "Statement": [ { "Action": ["sts:assumeRole"], "Effect": "Allow", "Resource": ["*"] } ], "Version": "2012-10-17" }""" role_policy2 = "foo_policy2" policy_document2 = """{ "Statement": [ { "Action": ["sqs:queue"], "Effect": "Allow", "Resource": ["*"] } ], "Version": "2012-10-17" }""" assume_role_policy_document = """{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }""" session = boto3.Session() client = session.client("iam") client.create_role( RoleName=role_name, AssumeRolePolicyDocument=assume_role_policy_document) iam = boto3.resource("iam") policy = iam.RolePolicy(role_name, role_policy) policy.put(PolicyDocument=policy_document) policy = iam.RolePolicy(role_name, role_policy2) policy.put(PolicyDocument=policy_document2) scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) resources = IAMRoleResourceSpec.scan(scan_accessor=scan_accessor) embedded_resources_links = [ link for link in resources[0].link_collection.multi_links if link.pred == "embedded_policy" ] self.assertEqual(len(embedded_resources_links), 2) # First policy. embedded_resources_link = embedded_resources_links[0] self.assertTrue( compare_embedded_policy(embedded_resources_link, role_policy, policy_document)) # Second policy. embedded_resources_link = embedded_resources_links[1] self.assertTrue( compare_embedded_policy(embedded_resources_link, role_policy2, policy_document2))
def test_scan(self): account_id = "123456789012" region_name = "us-east-1" session = boto3.Session() lambda_client = session.client("lambda", region_name=region_name) lambda_client.create_function( FunctionName="func_name", Runtime="python3.7", Role="testrole", Handler="testhandler", Description="testdescr", Timeout=90, MemorySize=128, Code={"ZipFile": b"1234"}, Publish=False, VpcConfig={ "SubnetIds": ["subnet-123"], "SecurityGroupIds": ["sg-123"] }, DeadLetterConfig={"TargetArn": "test_dl_config"}, Environment={"Variables": { "TEST_VAR": "test_val" }}, KMSKeyArn="test_kms_arn", TracingConfig={"Mode": "Active"}, Tags={ "tagkey1": "tagval1", "tagkey2": "tagval2" }, Layers=["test_layer1"], ) scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) scan_result = LambdaFunctionResourceSpec.scan( scan_accessor=scan_accessor) scan_result_dict = scan_result.to_dict() self.maxDiff = None expected_scan_result_dict = { "resources": [{ "type": "aws:lambda:function", "links": [ { "pred": "function_name", "obj": "func_name", "type": "simple" }, { "pred": "runtime", "obj": "python3.7", "type": "simple", }, { "pred": "vpc", "obj": f"arn:aws:ec2:{region_name}:{account_id}:vpc/vpc-123abc", "type": "transient_resource_link", }, { "pred": "account", "obj": f"arn:aws::::account/{account_id}", "type": "resource_link", }, { "pred": "region", "obj": f"arn:aws:::{account_id}:region/{region_name}", "type": "resource_link", }, ], }], "stats": { "count": 1, account_id: { "count": 1, region_name: { "count": 1, "lambda": { "count": 1, "ListFunctions": { "count": 1 } }, }, }, }, "errors": [], } self.assertDictEqual(scan_result_dict, expected_scan_result_dict)
def test_scan(self): account_id = "123456789012" region_name = "us-east-1" session = boto3.Session() iam_client = session.client("iam") test_assume_role_policy_doc = { "Version": "2012-10-17", "Statement": [{ "Sid": "abc", "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole", }], } iam_role_resp = iam_client.create_role( RoleName="testrole", AssumeRolePolicyDocument=json.dumps(test_assume_role_policy_doc), ) iam_role_arn = iam_role_resp["Role"]["Arn"] lambda_client = session.client("lambda", region_name=region_name) lambda_client.create_function( FunctionName="func_name", Runtime="python3.7", Role=iam_role_arn, Handler="testhandler", Description="testdescr", Timeout=90, MemorySize=128, Code={"ZipFile": b"1234"}, Publish=False, VpcConfig={ "SubnetIds": ["subnet-123"], "SecurityGroupIds": ["sg-123"] }, DeadLetterConfig={"TargetArn": "test_dl_config"}, Environment={"Variables": { "TEST_VAR": "test_val" }}, KMSKeyArn="test_kms_arn", TracingConfig={"Mode": "Active"}, Tags={ "tagkey1": "tagval1", "tagkey2": "tagval2" }, Layers=["test_layer1"], ) scan_accessor = AWSAccessor(session=session, account_id=account_id, region_name=region_name) resources = LambdaFunctionResourceSpec.scan( scan_accessor=scan_accessor) self.maxDiff = None expected_resources = [{ "type": "aws:lambda:function", "links": [ { "pred": "function_name", "obj": "func_name", "type": "simple" }, { "pred": "runtime", "obj": "python3.7", "type": "simple", }, { "pred": "vpc", "obj": f"arn:aws:ec2:{region_name}:{account_id}:vpc/vpc-123abc", "type": "transient_resource_link", }, { "pred": "account", "obj": f"arn:aws::::account/{account_id}", "type": "resource_link", }, { "pred": "region", "obj": f"arn:aws:::{account_id}:region/{region_name}", "type": "resource_link", }, ], }] expected_api_call_stats = { "count": 1, account_id: { "count": 1, region_name: { "count": 1, "lambda": { "count": 1, "ListFunctions": { "count": 1 } }, }, }, } self.assertListEqual([resource.to_dict() for resource in resources], expected_resources) self.assertDictEqual(scan_accessor.api_call_stats.to_dict(), expected_api_call_stats)