Repository: lyft/cartography

Issue:
Issue while re syncing the AWS resources to Neo4J - get_role_tags crash
**Bug Template**

*Title*: *Issue while re syncing the AWS resources to Neo4J*

*Description*:
> An unhandled exception is raised when cartography attempts to sync stage 'aws'

*To Reproduce*:

DockerFile content:

FROM python:3.9.16-slim

WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

ENV NEO4J_DB=neo4j
ENV NEO4J_URI=bolt://host.docker.internal:7687
ENV AWS_DEFAULT_REGION=ap-south-1
ADD app.py .app.py
CMD echo $NEO4J_URI; echo $NEO4J_DB;pip freeze | grep "cartography"; cartography --aws-best-effort-mode --neo4j-uri $NEO4J_URI --neo4j-database $NEO4J_DB

docker command:
> docker run -e AWS_ACCESS_KEY_ID='' -e AWS_SECRET_ACCESS_KEY='' -e NEO4J_DB='neo4j' -e NEO4J_URI='bolt://host.docker.internal:7687' --rm --name temp1 carto-demo

*Logs*:

ERROR:cartography.intel.aws:AWS sync failed for accounts ['**ACCOUNTID**']
ERROR:cartography.sync:Unhandled exception during sync stage 'aws'
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/cartography/sync.py", line 85, in run
    stage_func(neo4j_session, config)
  File "/usr/local/lib/python3.9/site-packages/cartography/util.py", line 133, in timed
    return method(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/__init__.py", line 243, in start_aws_ingestion
    sync_successful = _sync_multiple_accounts(
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/__init__.py", line 188, in _sync_multiple_accounts
    raise Exception('\n'.join(exception_tracebacks))
Exception: 2023-01-17 05:17:56.668129 - Exception for account ID: **ACCOUNTID**
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/__init__.py", line 167, in _sync_multiple_accounts
    _sync_one_account(
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/__init__.py", line 74, in _sync_one_account
    RESOURCE_FUNCTIONS['resourcegroupstaggingapi'](**sync_args)
  File "/usr/local/lib/python3.9/site-packages/cartography/util.py", line 133, in timed
    return method(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/resourcegroupstaggingapi.py", line 234, in sync
    tag_data = get_tags(boto3_session, resource_type, region)
  File "/usr/local/lib/python3.9/site-packages/cartography/util.py", line 133, in timed
    return method(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/backoff/_sync.py", line 105, in retry
    ret = target(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/cartography/util.py", line 214, in inner_function
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/resourcegroupstaggingapi.py", line 129, in get_tags
    return get_role_tags(boto3_session)
  File "/usr/local/lib/python3.9/site-packages/cartography/util.py", line 133, in timed
    return method(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/iam.py", line 172, in get_role_tags
    name = role["RoleName"]
TypeError: string indices must be integers

Traceback (most recent call last):
  File "/usr/local/bin/cartography", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.9/site-packages/cartography/cli.py", line 581, in main
    sys.exit(CLI(default_sync, prog='cartography').main(argv))
  File "/usr/local/lib/python3.9/site-packages/cartography/cli.py", line 561, in main
    return cartography.sync.run_with_config(self.sync, config)
  File "/usr/local/lib/python3.9/site-packages/cartography/sync.py", line 163, in run_with_config
    return sync.run(neo4j_driver, config)
  File "/usr/local/lib/python3.9/site-packages/cartography/sync.py", line 85, in run
    stage_func(neo4j_session, config)
  File "/usr/local/lib/python3.9/site-packages/cartography/util.py", line 133, in timed
    return method(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/__init__.py", line 243, in start_aws_ingestion
    sync_successful = _sync_multiple_accounts(
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/__init__.py", line 188, in _sync_multiple_accounts
    raise Exception('\n'.join(exception_tracebacks))
Exception: 2023-01-17 05:17:56.668129 - Exception for account ID: **ACCOUNTID**
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/__init__.py", line 167, in _sync_multiple_accounts
    _sync_one_account(
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/__init__.py", line 74, in _sync_one_account
    RESOURCE_FUNCTIONS['resourcegroupstaggingapi'](**sync_args)
  File "/usr/local/lib/python3.9/site-packages/cartography/util.py", line 133, in timed
    return method(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/resourcegroupstaggingapi.py", line 234, in sync
    tag_data = get_tags(boto3_session, resource_type, region)
  File "/usr/local/lib/python3.9/site-packages/cartography/util.py", line 133, in timed
    return method(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/backoff/_sync.py", line 105, in retry
    ret = target(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/cartography/util.py", line 214, in inner_function
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/resourcegroupstaggingapi.py", line 129, in get_tags
    return get_role_tags(boto3_session)
  File "/usr/local/lib/python3.9/site-packages/cartography/util.py", line 133, in timed
    return method(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/cartography/intel/aws/iam.py", line 172, in get_role_tags
    name = role["RoleName"]
TypeError: string indices must be integers


*Env information:*:
>  - 0.71.0
>  - Python version: 3.9,3.10,3.11
>  In Docker
> neo4j:4.4.5-community



Discussion:

Patch:
diff --git a/cartography/intel/aws/iam.py b/cartography/intel/aws/iam.py
index 0e77cb0b..6e013388 100644
--- a/cartography/intel/aws/iam.py
+++ b/cartography/intel/aws/iam.py
@@ -165,7 +165,7 @@ def get_role_managed_policy_data(boto3_session: boto3.session.Session, role_list
 
 @timeit
 def get_role_tags(boto3_session: boto3.session.Session) -> List[Dict]:
-    role_list = get_role_list_data(boto3_session)
+    role_list = get_role_list_data(boto3_session)['Roles']
     resource_client = boto3_session.resource('iam')
     role_tag_data: List[Dict] = []
     for role in role_list:


Test patch:
diff --git a/tests/unit/cartography/intel/aws/iam/test_iam.py b/tests/unit/cartography/intel/aws/iam/test_iam.py
index 61d3a41c..5cd01ad0 100644
--- a/tests/unit/cartography/intel/aws/iam/test_iam.py
+++ b/tests/unit/cartography/intel/aws/iam/test_iam.py
@@ -39,12 +39,14 @@ def test_get_account_from_arn():
 
 def test__get_role_tags_valid_tags(mocker):
     mocker.patch(
-        'cartography.intel.aws.iam.get_role_list_data', return_value=[
-            {
-                'RoleName': 'test-role',
-                'Arn': 'test-arn',
-            },
-        ],
+        'cartography.intel.aws.iam.get_role_list_data', return_value={
+            'Roles': [
+                {
+                    'RoleName': 'test-role',
+                    'Arn': 'test-arn',
+                },
+            ],
+        },
     )
     mocker.patch('boto3.session.Session')
     mock_session = mocker.Mock()
@@ -72,12 +74,14 @@ def test__get_role_tags_valid_tags(mocker):
 
 def test__get_role_tags_no_tags(mocker):
     mocker.patch(
-        'cartography.intel.aws.iam.get_role_list_data', return_value=[
-            {
-                'RoleName': 'test-role',
-                'Arn': 'test-arn',
-            },
-        ],
+        'cartography.intel.aws.iam.get_role_list_data', return_value={
+            'Roles': [
+                {
+                    'RoleName': 'test-role',
+                    'Arn': 'test-arn',
+                },
+            ],
+        },
     )
     mocker.patch('boto3.session.Session')
     mock_session = mocker.Mock()