Repository: cloud-custodian/cloud-custodian

Issue:
mugc.py does not have the ability to act on all regions
Cloud-Custodian has the ability to use `-r all` and act on all regions.

mugc.py can only act on a specific region, eg: `-r us-west-1` - And also does not have the ability to take in multiple regions, eg: `-r us-west-1 -r us-east-1` as Cloud Custodian does.




Discussion:

Resolving patch:
diff --git a/c7n/policy.py b/c7n/policy.py
--- a/c7n/policy.py
+++ b/c7n/policy.py
@@ -746,9 +746,12 @@ def tags(self):
     def get_cache(self):
         return self.resource_manager._cache
 
+    @property
+    def execution_mode(self):
+        return self.data.get('mode', {'type': 'pull'})['type']
+
     def get_execution_mode(self):
-        exec_mode_type = self.data.get('mode', {'type': 'pull'}).get('type')
-        exec_mode = execution[exec_mode_type]
+        exec_mode = execution[self.execution_mode]
         if exec_mode is None:
             return None
         return exec_mode(self)
diff --git a/tools/ops/mugc.py b/tools/ops/mugc.py
--- a/tools/ops/mugc.py
+++ b/tools/ops/mugc.py
@@ -1,4 +1,4 @@
-# Copyright 2016-2017 Capital One Services, LLC
+# Copyright 2016-2018 Capital One Services, LLC
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -19,38 +19,41 @@
 import sys
 
 from c7n.credentials import SessionFactory
-from c7n.policy import load as policy_load
-from c7n import mu, resources
+from c7n.config import Config
+from c7n.policy import load as policy_load, PolicyCollection
+from c7n import mu
 
+# TODO: mugc has alot of aws assumptions
+
+from c7n.resources.aws import AWS
+import boto3
 from botocore.exceptions import ClientError
 
-log = logging.getLogger('resources')
+
+log = logging.getLogger('mugc')
 
 
-def load_policies(options):
-    policies = []
+def load_policies(options, config):
+    policies = PolicyCollection([], config)
     for f in options.config_files:
-        collection = policy_load(options, f)
-        policies.extend(collection.filter(options.policy_filter))
+        policies += policy_load(config, f).filter(options.policy_filter)
     return policies
 
 
-def resources_gc_prefix(options, policy_collection):
-    """Garbage collect old custodian policies based on prefix.
+def region_gc(options, region, policy_config, policies):
 
-    We attempt to introspect to find the event sources for a policy
-    but without the old configuration this is implicit.
-    """
     session_factory = SessionFactory(
-        options.region, options.profile, options.assume_role)
+        region=region,
+        assume_role=policy_config.assume_role,
+        profile=policy_config.profile,
+        external_id=policy_config.external_id)
 
     manager = mu.LambdaManager(session_factory)
     funcs = list(manager.list_functions(options.prefix))
-
     client = session_factory().client('lambda')
 
     remove = []
-    current_policies = [p.name for p in policy_collection]
+    current_policies = [p.name for p in policies]
     for f in funcs:
         pn = f['FunctionName'].split('-', 1)[1]
         if pn not in current_policies:
@@ -62,11 +65,13 @@ def resources_gc_prefix(options, policy_collection):
             result = client.get_policy(FunctionName=n['FunctionName'])
         except ClientError as e:
             if e.response['Error']['Code'] == 'ResourceNotFoundException':
-                log.warn("Lambda Function or Access Policy Statement missing: {}".
-                    format(n['FunctionName']))
+                log.warning(
+                    "Region:%s Lambda Function or Access Policy Statement missing: %s",
+                    region, n['FunctionName'])
             else:
-                log.warn("Unexpected error: {} for function {}".
-                    format(e, n['FunctionName']))
+                log.warning(
+                    "Region:%s Unexpected error: %s for function %s",
+                    region, e, n['FunctionName'])
 
             # Continue on with next function instead of raising an exception
             continue
@@ -97,12 +102,41 @@ def resources_gc_prefix(options, policy_collection):
             'runtime': n['Runtime'],
             'events': events}, None)
 
-        log.info("Removing %s" % n['FunctionName'])
+        log.info("Region:%s Removing %s", region, n['FunctionName'])
         if options.dryrun:
             log.info("Dryrun skipping removal")
             continue
         manager.remove(f)
-        log.info("Removed %s" % n['FunctionName'])
+        log.info("Region:%s Removed %s", region, n['FunctionName'])
+
+
+def resources_gc_prefix(options, policy_config, policy_collection):
+    """Garbage collect old custodian policies based on prefix.
+
+    We attempt to introspect to find the event sources for a policy
+    but without the old configuration this is implicit.
+    """
+
+    # Classify policies by region
+    policy_regions = {}
+    for p in policy_collection:
+        if p.execution_mode == 'poll':
+            continue
+        policy_regions.setdefault(p.options.region, []).append(p)
+
+    regions = get_gc_regions(options.regions)
+    for r in regions:
+        region_gc(options, r, policy_config, policy_regions.get(r, []))
+
+
+def get_gc_regions(regions):
+    if 'all' in regions:
+        session = boto3.Session(
+            region_name='us-east-1',
+            aws_access_key_id='never',
+            aws_secret_access_key='found')
+        return session.get_available_regions('s3')
+    return regions
 
 
 def setup_parser():
@@ -112,8 +146,8 @@ def setup_parser():
         '-c', '--config', dest="config_files", nargs="*", action='append',
         help="Policy configuration files(s)")
     parser.add_argument(
-        '-r', '--region', default=os.environ.get(
-            'AWS_DEFAULT_REGION', 'us-east-1'))
+        '-r', '--region', action='append', dest='regions', metavar='REGION',
+        help="AWS Region to target. Can be used multiple times, also supports `all`")
     parser.add_argument('--dryrun', action="store_true", default=False)
     parser.add_argument(
         "--profile", default=os.environ.get('AWS_PROFILE'),
@@ -121,6 +155,8 @@ def setup_parser():
     parser.add_argument(
         "--prefix", default="custodian-",
         help="The Lambda name prefix to use for clean-up")
+    parser.add_argument("-p", "--policies", default=None, dest='policy_filter',
+                        help="Only use named/matched policies")
     parser.add_argument(
         "--assume", default=None, dest="assume_role",
         help="Role to assume")
@@ -134,11 +170,6 @@ def main():
     parser = setup_parser()
     options = parser.parse_args()
 
-    options.policy_filter = None
-    options.log_group = None
-    options.external_id = None
-    options.cache_period = 0
-    options.cache = None
     log_level = logging.INFO
     if options.verbose:
         log_level = logging.DEBUG
@@ -148,18 +179,28 @@ def main():
     logging.getLogger('botocore').setLevel(logging.ERROR)
     logging.getLogger('c7n.cache').setLevel(logging.WARNING)
 
+    if not options.regions:
+        options.regions = [os.environ.get('AWS_DEFAULT_REGION', 'us-east-1')]
+
     files = []
     files.extend(itertools.chain(*options.config_files))
     files.extend(options.configs)
     options.config_files = files
+
     if not files:
         parser.print_help()
         sys.exit(1)
 
-    resources.load_resources()
+    policy_config = Config.empty(
+        regions=options.regions,
+        profile=options.profile,
+        assume_role=options.assume_role)
+
+    # use cloud provider to initialize policies to get region expansion
+    policies = AWS().initialize_policies(
+        load_policies(options, policy_config), policy_config)
 
-    policies = load_policies(options)
-    resources_gc_prefix(options, policies)
+    resources_gc_prefix(options, policy_config, policies)
 
 
 if __name__ == '__main__':


Test patch: