Repository: cloud-custodian/cloud-custodian

Issue:
iam-user resource arn's generated with region, but not valid per spec
Invoking self.manager.get_arns(), for example in securityhub.py's OtherResourcePostFindings.format_resource(), when resource type=iam-[user|group|profile|role|policy|certificate], results in an arn returned which has the current region included, which is not compliant with the arn standard per https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html

Next step: unit test with policy showing the bug

Other resources not to spec: 
1. ami - account currently included
2. snapshot - account currently included


Discussion:

Resolving patch:
diff --git a/c7n/query.py b/c7n/query.py
--- a/c7n/query.py
+++ b/c7n/query.py
@@ -495,9 +495,16 @@ def region(self):
 
     def get_arns(self, resources):
         arns = []
+
+        m = self.get_model()
+        arn_key = getattr(m, 'arn', None)
+        id_key = m.id
+
         for r in resources:
-            _id = r[self.get_model().id]
-            if 'arn' in _id[:3]:
+            _id = r[id_key]
+            if arn_key:
+                arns.append(r[arn_key])
+            elif 'arn' in _id[:3]:
                 arns.append(_id)
             else:
                 arns.append(self.generate_arn(_id))
diff --git a/c7n/resources/iam.py b/c7n/resources/iam.py
--- a/c7n/resources/iam.py
+++ b/c7n/resources/iam.py
@@ -55,6 +55,7 @@ class resource_type(object):
         config_type = "AWS::IAM::Group"
         # Denotes this resource type exists across regions
         global_resource = True
+        arn = 'Arn'
 
     def get_resources(self, resource_ids, cache=True):
         """For IAM Groups on events, resource ids are Group Names."""
@@ -86,6 +87,7 @@ class resource_type(object):
         config_type = "AWS::IAM::Role"
         # Denotes this resource type exists across regions
         global_resource = True
+        arn = 'Arn'
 
     def get_resources(self, resource_ids, cache=True):
         """For IAM Roles on events, resource ids are role names."""
@@ -113,6 +115,7 @@ class resource_type(object):
         config_type = "AWS::IAM::User"
         # Denotes this resource type exists across regions
         global_resource = True
+        arn = 'Arn'
 
     def get_source(self, source_type):
         if source_type == 'describe':
@@ -150,6 +153,7 @@ class resource_type(object):
         filter_name = None
         # Denotes this resource type exists across regions
         global_resource = True
+        arn = 'Arn'
 
     arn_path_prefix = "aws:policy/"
 
@@ -188,6 +192,7 @@ class resource_type(object):
         dimension = None
         # Denotes this resource type exists across regions
         global_resource = True
+        arn = 'Arn'
 
 
 @resources.register('iam-certificate')


Test patch:
diff --git a/tests/test_iam.py b/tests/test_iam.py
--- a/tests/test_iam.py
+++ b/tests/test_iam.py
@@ -110,6 +110,9 @@ def test_credential_access_key_multifilter_delete(self):
         self.assertEqual(len(keys), 1)
         dt = parser.parse(resources[0]['c7n:matched-keys'][0]['last_rotated'])
         self.assertNotEqual(keys[0]['CreateDate'], dt)
+        self.assertEqual(
+            p.resource_manager.get_arns(resources),
+            ["arn:aws:iam::644160558196:user/kapil"])
 
     def test_access_key_last_service(self):
         # Note we're reusing the old console users flight records
@@ -252,6 +255,9 @@ def test_iam_role_unused(self):
         )
         resources = p.run()
         self.assertEqual(len(resources), 1)
+        self.assertEqual(
+            p.resource_manager.get_arns(resources),
+            ['arn:aws:iam::644160558196:role/service-role/AmazonSageMaker-ExecutionRole-20180108T122369']) # NOQA
 
     def test_iam_role_get_resources(self):
         session_factory = self.replay_flight_data("test_iam_role_get_resource")
@@ -423,6 +429,9 @@ def test_iam_instance_profile_inuse(self):
         )
         resources = p.run()
         self.assertEqual(len(resources), 1)
+        self.assertEqual(
+            p.resource_manager.get_arns(resources),
+            ['arn:aws:iam::644160558196:instance-profile/root_joshua'])
 
     def test_iam_instance_profile_unused(self):
         session_factory = self.replay_flight_data("test_iam_instance_profile_unused")
@@ -521,6 +530,10 @@ def test_iam_group_used_users(self):
         )
         resources = p.run()
         self.assertEqual(len(resources), 2)
+        self.assertEqual(
+            p.resource_manager.get_arns(resources),
+            ['arn:aws:iam::644160558196:group/Admins',
+             'arn:aws:iam::644160558196:group/powerusers'])
 
     def test_iam_group_unused_users(self):
         session_factory = self.replay_flight_data("test_iam_group_unused_users")