Repository: cloud-custodian/cloud-custodian

Issue:
Enable cross account resource lookups and actions
**Desired behavior:** 
Allow the specification of execution-options: assume_role to use account_id_from_event such as 
          assume_role: arn:aws:iam::{{account_id_from_event}}:role/target-role
so that an inbound event from a cross account delivery via https://aws.amazon.com/blogs/aws/new-cross-account-delivery-of-cloudwatch-events/ uses the event's "account" value are the variable seen above as {{account_id_from_event}}.

This role would then be used for the Describe of the resource and the terminate and/or mark actions.

**Benefits:** 
Enables multi-account scenarios to have one lambda to handle a policy rather a lambda for each account, thus reduce lambda sprawl management.

**Impact of not doing this:** 
Events delivered from other accounts, Custodian fails with the following error:
event ids not resolved: [u'i-0af1e58a1bd481072'] error:An error occurred (InvalidInstanceID.NotFound) when calling the DescribeInstances operation: The instance ID 'i-0af1e58a1bd481072' does not exist


Discussion:
i assume this is resolved with the merged pull request in #1416 
@FireballDWF did this get implemented as described in your initial comment?  I have a similar use case and i'm hoping to assume cross-account roles based on the account_id in the cloudwatch event.  Is this doable with a single lambda deployment or do I need multiple deployments, one for each account?

It would be fantastic to be able to use `{{account_id_from_event}}` in the execution-options for a lambda function to centralize rules.  Is there a better way to accomplish this?

Resolving patch:
diff --git a/c7n/policy.py b/c7n/policy.py
--- a/c7n/policy.py
+++ b/c7n/policy.py
@@ -399,7 +399,33 @@ def get_metrics(self, start, end, period):
             super(LambdaMode, self).get_metrics(start, end, period))
         return values
 
+    def get_member_account_id(self, event):
+        return event.get('account')
+
+    def get_member_region(self, event):
+        return event.get('region')
+
+    def assume_member(self, event):
+        # if a member role is defined we're being run out of the master, and we need
+        # to assume back into the member for policy execution.
+        member_role = self.policy.data['mode'].get('member-role')
+        member_id = self.get_member_account_id(event)
+        region = self.get_member_region(event)
+        if member_role and member_id and region:
+            # In the master account we might be multiplexing a hot lambda across
+            # multiple member accounts for each event/invocation.
+            member_role = member_role.format(account_id=member_id)
+            utils.reset_session_cache()
+            self.policy.options['account_id'] = member_id
+            self.policy.session_factory.region = region
+            self.policy.session_factory.assume_role = member_role
+            self.policy.log.info(
+                "Assuming member role: %s", member_role)
+            return True
+        return False
+
     def resolve_resources(self, event):
+        self.assume_member(event)
         mode = self.policy.data.get('mode', {})
         resource_ids = CloudWatchEvents.get_ids(event, mode)
         if resource_ids is None:
@@ -563,23 +589,8 @@ class GuardDutyMode(LambdaMode):
         'ec2': jmespath.compile('detail.resource.instanceDetails.instanceId'),
         'iam-user': jmespath.compile('detail.resource.accessKeyDetails.userName')}
 
-    def assume_member(self, event):
-        # if a member role is defined we're being run out of the master, and we need
-        # to assume back into the member for policy execution.
-        member_role = self.policy.data['mode'].get('member-role')
-        if member_role:
-            # In the master account we might be multiplexing a hot lambda across
-            # multiple member accounts for each event/invocation.
-            member_id = event['detail']['accountId']
-            member_role = member_role.format(account_id=member_id)
-            utils.reset_session_cache()
-            self.policy.options['account_id'] = member_id
-            self.policy.session_factory = SessionFactory(
-                assume_role=member_role)
-            self.policy.log.info(
-                "Guard duty assuming member role: %s", member_role)
-            return True
-        return False
+    def get_member_account_id(self, event):
+        return event['detail']['accountId']
 
     def resolve_resources(self, event):
         self.assume_member(event)


Test patch: