Repository: cloud-custodian/cloud-custodian

Issue:
Event Grid mode needs do resource type based filtering on the topic subscription.
- Event Grid will support filtering of the event body, this will help reduce the amount of data that flows through our EventGrid subscriptions
- https://docs.microsoft.com/en-us/azure/event-grid/event-filtering#advanced-filtering


Discussion:

Resolving patch:
diff --git a/tools/c7n_azure/c7n_azure/azure_events.py b/tools/c7n_azure/c7n_azure/azure_events.py
--- a/tools/c7n_azure/c7n_azure/azure_events.py
+++ b/tools/c7n_azure/c7n_azure/azure_events.py
@@ -145,8 +145,9 @@ def get_event_operations(cls, events):
 
 class AzureEventSubscription(object):
 
-    @classmethod
-    def create(cls, destination, name, session=None, event_filter=None):
+    @staticmethod
+    def create(destination, name, session=None, event_filter=None):
+
         s = session or local_session(Session)
         event_filter = event_filter or EventSubscriptionFilter()
 
diff --git a/tools/c7n_azure/c7n_azure/policy.py b/tools/c7n_azure/c7n_azure/policy.py
--- a/tools/c7n_azure/c7n_azure/policy.py
+++ b/tools/c7n_azure/c7n_azure/policy.py
@@ -18,7 +18,7 @@
 
 import six
 from azure.mgmt.eventgrid.models import \
-    StorageQueueEventSubscriptionDestination
+    StorageQueueEventSubscriptionDestination, StringInAdvancedFilter, EventSubscriptionFilter
 
 from c7n import utils
 from c7n.actions import EventAction
@@ -336,8 +336,14 @@ def _create_event_subscription(self, storage_account, queue_name, session):
         destination = StorageQueueEventSubscriptionDestination(resource_id=storage_account.id,
                                                                queue_name=queue_name)
 
+        # filter specific events
+        subscribed_events = AzureEvents.get_event_operations(
+            self.policy.data['mode'].get('events'))
+        advance_filter = StringInAdvancedFilter(key='Data.OperationName', values=subscribed_events)
+        event_filter = EventSubscriptionFilter(advanced_filters=[advance_filter])
+
         try:
-            AzureEventSubscription.create(destination, queue_name, session)
+            AzureEventSubscription.create(destination, queue_name, session, event_filter)
             self.log.info('Event grid subscription creation succeeded')
         except Exception as e:
             self.log.error('Event Subscription creation failed with error: %s' % e)


Test patch:
diff --git a/tools/c7n_azure/tests/test_policy.py b/tools/c7n_azure/tests/test_policy.py
--- a/tools/c7n_azure/tests/test_policy.py
+++ b/tools/c7n_azure/tests/test_policy.py
@@ -17,6 +17,12 @@
 from c7n_azure.azure_events import AzureEvents
 from c7n_azure.constants import FUNCTION_EVENT_TRIGGER_MODE, FUNCTION_TIME_TRIGGER_MODE
 from c7n_azure.policy import AzureEventGridMode, AzureFunctionMode
+from mock import mock
+
+
+# Mock of Azure StorageAccount class
+class MockStorageAccount(object):
+    id = 1
 
 
 class AzurePolicyModeTest(BaseTest):
@@ -132,7 +138,6 @@ def test_init_azure_function_mode_no_service_plan_name(self):
         self.assertTrue(params.function_app_name.startswith('test-azure-serverless-mode-'))
 
     def test_init_azure_function_mode_with_resource_ids(self):
-
         ai_id = '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups' \
                 '/testrg/providers/microsoft.insights/components/testai'
         sp_id = '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups' \
@@ -207,3 +212,54 @@ def test_event_mode_is_subscribed_to_event_false(self):
         }
         event_mode = AzureEventGridMode(p)
         self.assertFalse(event_mode._is_subscribed_to_event(event, subscribed_events))
+
+    def test_event_grid_mode_creates_advanced_filtered_subscription(self):
+        p = self.load_policy({
+            'name': 'test-azure-event',
+            'resource': 'azure.vm',
+            'mode':
+                {'type': FUNCTION_EVENT_TRIGGER_MODE,
+                 'events': ['VmWrite']},
+        })
+
+        with mock.patch('c7n_azure.azure_events.AzureEventSubscription.create') as mock_create:
+            storage_account = MockStorageAccount()
+            event_mode = AzureEventGridMode(p)
+            event_mode._create_event_subscription(storage_account, 'some_queue', None)
+
+            name, args, kwargs = mock_create.mock_calls[0]
+
+            # verify the advanced filter created
+            event_filter = args[3].advanced_filters[0]
+            self.assertEqual(event_filter.key, 'Data.OperationName')
+            self.assertEqual(event_filter.values, ['Microsoft.Compute/virtualMachines/write'])
+            self.assertEqual(event_filter.operator_type, 'StringIn')
+
+    def test_event_grid_mode_creates_advanced_filtered_subscription_with_multiple_events(self):
+        p = self.load_policy({
+            'name': 'test-azure-event',
+            'resource': 'azure.vm',
+            'mode':
+                {'type': FUNCTION_EVENT_TRIGGER_MODE,
+                 'events':
+                     ['VmWrite',
+                        {
+                            'resourceProvider': 'Microsoft.Resources/subscriptions/resourceGroups',
+                            'event': 'write'
+                        }]},
+        })
+
+        with mock.patch('c7n_azure.azure_events.AzureEventSubscription.create') as mock_create:
+            storage_account = MockStorageAccount()
+            event_mode = AzureEventGridMode(p)
+            event_mode._create_event_subscription(storage_account, 'some_queue', None)
+
+            name, args, kwargs = mock_create.mock_calls[0]
+
+            # verify the advanced filter created
+            event_filter = args[3].advanced_filters[0]
+            self.assertEqual(event_filter.key, 'Data.OperationName')
+            self.assertEqual(event_filter.values,
+                             ['Microsoft.Compute/virtualMachines/write',
+                              'Microsoft.Resources/subscriptions/resourceGroups/write'])
+            self.assertEqual(event_filter.operator_type, 'StringIn')