Repository: cloud-custodian/cloud-custodian

Issue:
azure - event hub resources
Add event hub resource & implement firewall filter


Discussion:

Resolving patch:
diff --git a/tools/c7n_azure/c7n_azure/resources/event_hub.py b/tools/c7n_azure/c7n_azure/resources/event_hub.py
--- a/tools/c7n_azure/c7n_azure/resources/event_hub.py
+++ b/tools/c7n_azure/c7n_azure/resources/event_hub.py
@@ -12,8 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import logging
+
+from c7n_azure.filters import FirewallRulesFilter
 from c7n_azure.provider import resources
 from c7n_azure.resources.arm import ArmResourceManager
+from netaddr import IPSet
 
 
 @resources.register('eventhub')
@@ -22,13 +26,17 @@ class EventHub(ArmResourceManager):
 
     :example:
 
-    Finds all Event Hub resources in the subscription.
+    This policy will find all Event Hubs allowing traffic from 1.2.2.128/25 CIDR.
 
     .. code-block:: yaml
 
         policies:
-            - name: find-all-eventhubs
-              resource: azure.eventhub
+          - name: find-event-hub-allowing-subnet
+            resource: azure.eventhub
+            filters:
+              - type: firewall-rules
+                include:
+                    - '1.2.2.128/25'
 
     """
 
@@ -46,3 +54,29 @@ class resource_type(ArmResourceManager.resource_type):
             'properties.isAutoInflateEnabled'
         )
         resource_type = 'Microsoft.EventHub/namespaces'
+
+
+@EventHub.filter_registry.register('firewall-rules')
+class EventHubFirewallRulesFilter(FirewallRulesFilter):
+
+    def __init__(self, data, manager=None):
+        super(EventHubFirewallRulesFilter, self).__init__(data, manager)
+        self._log = logging.getLogger('custodian.azure.eventhub')
+        self.client = None
+
+    @property
+    def log(self):
+        return self._log
+
+    def process(self, resources, event=None):
+        self.client = self.manager.get_client()
+        return super(EventHubFirewallRulesFilter, self).process(resources, event)
+
+    def _query_rules(self, resource):
+        query = self.client.namespaces.get_network_rule_set(
+            resource['resourceGroup'],
+            resource['name'])
+
+        resource_rules = IPSet([r.ip_mask for r in query.ip_rules])
+
+        return resource_rules


Test patch:
diff --git a/tools/c7n_azure/tests/cassettes/EventHubTest.firewall.json b/tools/c7n_azure/tests/cassettes/EventHubTest.firewall.json
new file mode 100644
--- /dev/null
+++ b/tools/c7n_azure/tests/cassettes/EventHubTest.firewall.json
@@ -0,0 +1,118 @@
+{
+    "version": 1,
+    "interactions": [
+        {
+            "request": {
+                "method": "GET",
+                "uri": "https://management.azure.com/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/providers/Microsoft.EventHub/namespaces?api-version=2017-04-01",
+                "body": null,
+                "headers": {}
+            },
+            "response": {
+                "status": {
+                    "code": 200,
+                    "message": "OK"
+                },
+                "headers": {
+                    "content-type": [
+                        "application/json; charset=utf-8"
+                    ],
+                    "cache-control": [
+                        "no-cache"
+                    ],
+                    "server-sb": [
+                        "Service-Bus-Resource-Provider/CH3"
+                    ],
+                    "date": [
+                        "Wed, 07 Aug 2019 23:30:25 GMT"
+                    ],
+                    "content-length": [
+                        "731"
+                    ]
+                },
+                "body": {
+                    "data": {
+                        "value": [
+                            {
+                                "sku": {
+                                    "name": "Standard",
+                                    "tier": "Standard",
+                                    "capacity": 1
+                                },
+                                "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/test_eventhub/providers/Microsoft.EventHub/namespaces/offkxd3qjwh6a-cctesteventhubns",
+                                "name": "offkxd3qjwh6a-cctesteventhubns",
+                                "type": "Microsoft.EventHub/Namespaces",
+                                "location": "West US 2",
+                                "tags": {},
+                                "properties": {
+                                    "isAutoInflateEnabled": false,
+                                    "maximumThroughputUnits": 0,
+                                    "kafkaEnabled": false,
+                                    "provisioningState": "Succeeded",
+                                    "metricId": "aa98974b-5d2a-4d98-a78a-382f3715d07e:offkxd3qjwh6a-cctesteventhubns",
+                                    "createdAt": "2019-08-07T22:27:21.56Z",
+                                    "updatedAt": "2019-08-07T22:28:10.517Z",
+                                    "serviceBusEndpoint": "https://offkxd3qjwh6a-cctesteventhubns.servicebus.windows.net:443/",
+                                    "status": "Active"
+                                }
+                            }
+                        ]
+                    }
+                }
+            }
+        },
+        {
+            "request": {
+                "method": "GET",
+                "uri": "https://management.azure.com/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/test_eventhub/providers/Microsoft.EventHub/namespaces/offkxd3qjwh6a-cctesteventhubns/networkRuleSets/default?api-version=2017-04-01",
+                "body": null,
+                "headers": {}
+            },
+            "response": {
+                "status": {
+                    "code": 200,
+                    "message": "OK"
+                },
+                "headers": {
+                    "content-type": [
+                        "application/json; charset=utf-8"
+                    ],
+                    "cache-control": [
+                        "no-cache"
+                    ],
+                    "server-sb": [
+                        "Service-Bus-Resource-Provider/CH3"
+                    ],
+                    "date": [
+                        "Wed, 07 Aug 2019 23:30:26 GMT"
+                    ],
+                    "content-length": [
+                        "435"
+                    ]
+                },
+                "body": {
+                    "data": {
+                        "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/test_eventhub/providers/Microsoft.EventHub/namespaces/offkxd3qjwh6a-cctesteventhubns/networkRuleSets/default",
+                        "name": "default",
+                        "type": "Microsoft.EventHub/Namespaces/NetworkRuleSets",
+                        "location": "West US 2",
+                        "properties": {
+                            "defaultAction": "Deny",
+                            "virtualNetworkRules": [],
+                            "ipRules": [
+                                {
+                                    "ipMask": "10.1.1.1",
+                                    "action": "Allow"
+                                },
+                                {
+                                    "ipMask": "11.0.0.0/24",
+                                    "action": "Allow"
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/tools/c7n_azure/tests/templates/eventhub.json b/tools/c7n_azure/tests/templates/eventhub.json
--- a/tools/c7n_azure/tests/templates/eventhub.json
+++ b/tools/c7n_azure/tests/templates/eventhub.json
@@ -4,7 +4,8 @@
     "parameters": {},
     "variables": {
         "eventHubNamespaceName": "[concat(uniquestring(resourceGroup().id), '-cctesteventhubns')]",
-        "eventHubName": "cctesteventhub"
+        "eventHubName": "cctesteventhub",
+        "namespaceNetworkRuleSetName": "[concat(variables('eventhubNamespaceName'), concat('/', 'default'))]"
     },
     "resources": [
         {
@@ -13,8 +14,8 @@
             "name": "[variables('eventHubNamespaceName')]",
             "location": "westus2",
             "sku": {
-                "name": "Basic",
-                "tier": "Basic",
+                "name": "Standard",
+                "tier": "Standard",
                 "capacity": 1
             },
             "properties": {
@@ -34,6 +35,29 @@
                 "messageRetentionInDays": 1,
                 "partitionCount": 1
             }
+        },
+        {
+            "apiVersion": "2018-01-01-preview",
+            "name": "[variables('namespaceNetworkRuleSetName')]",
+            "type": "Microsoft.EventHub/namespaces/networkruleset",
+            "dependsOn": [
+                "[concat('Microsoft.EventHub/namespaces/', variables('eventhubNamespaceName'))]"
+            ],
+            "properties": {
+              "virtualNetworkRules": [],
+              "ipRules":
+              [
+                {
+                    "ipMask":"10.1.1.1",
+                    "action":"Allow"
+                },
+                {
+                    "ipMask":"11.0.0.0/24",
+                    "action":"Allow"
+                }
+              ],
+              "defaultAction": "Deny"
         }
+      }
     ]
 }
\ No newline at end of file
diff --git a/tools/c7n_azure/tests/test_event_hub.py b/tools/c7n_azure/tests/test_event_hub.py
--- a/tools/c7n_azure/tests/test_event_hub.py
+++ b/tools/c7n_azure/tests/test_event_hub.py
@@ -13,7 +13,7 @@
 # limitations under the License.
 from __future__ import absolute_import, division, print_function, unicode_literals
 
-from azure_common import BaseTest, arm_template
+from azure_common import BaseTest, arm_template, cassette_name
 
 
 class EventHubTest(BaseTest):
@@ -40,3 +40,105 @@ def test_find_by_name(self):
         })
         resources = p.run()
         self.assertEqual(len(resources), 1)
+
+    @cassette_name('firewall')
+    def test_firewall_rules_include_cidr(self):
+        p = self.load_policy({
+            'name': 'test-azure-eventhub',
+            'resource': 'azure.eventhub',
+            'filters': [
+                {'type': 'value',
+                 'key': 'name',
+                 'op': 'contains',
+                 'value_type': 'normalize',
+                 'value': '-cctesteventhubns'},
+                {'type': 'firewall-rules',
+                 'include': ['11.0.0.0/24']}],
+        })
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+
+    @cassette_name('firewall')
+    def test_firewall_rules_not_include_cidr(self):
+        p = self.load_policy({
+            'name': 'test-azure-eventhub',
+            'resource': 'azure.eventhub',
+            'filters': [
+                {'type': 'value',
+                 'key': 'name',
+                 'op': 'contains',
+                 'value_type': 'normalize',
+                 'value': '-cctesteventhubns'},
+                {'type': 'firewall-rules',
+                 'include': ['11.0.1.0/24']}],
+        })
+        resources = p.run()
+        self.assertEqual(len(resources), 0)
+
+    @cassette_name('firewall')
+    def test_firewall_rules_ranges(self):
+        p = self.load_policy({
+            'name': 'test-azure-eventhub',
+            'resource': 'azure.eventhub',
+            'filters': [
+                {'type': 'value',
+                 'key': 'name',
+                 'op': 'contains',
+                 'value_type': 'normalize',
+                 'value': '-cctesteventhubns'},
+                {'type': 'firewall-rules',
+                 'include': ['11.0.0.0-11.0.0.255']}],
+        }, validate=True)
+        resources = p.run()
+        self.assertEqual(1, len(resources))
+
+    @cassette_name('firewall')
+    def test_firewall_rules_not_ranges(self):
+        p = self.load_policy({
+            'name': 'test-azure-eventhub',
+            'resource': 'azure.eventhub',
+            'filters': [
+                {'type': 'value',
+                 'key': 'name',
+                 'op': 'contains',
+                 'value_type': 'normalize',
+                 'value': '-cctesteventhubns'},
+                {'type': 'firewall-rules',
+                 'include': ['11.0.1.0-11.0.1.255']}],
+        }, validate=True)
+        resources = p.run()
+        self.assertEqual(0, len(resources))
+
+    @cassette_name('firewall')
+    def test_firewall_rules_equal(self):
+        p = self.load_policy({
+            'name': 'test-azure-eventhub',
+            'resource': 'azure.eventhub',
+            'filters': [
+                {'type': 'value',
+                 'key': 'name',
+                 'op': 'contains',
+                 'value_type': 'normalize',
+                 'value': '-cctesteventhubns'},
+                {'type': 'firewall-rules',
+                 'equal': ['11.0.0.0/24', '10.1.1.1/32']}],
+        }, validate=True)
+        resources = p.run()
+        self.assertEqual(1, len(resources))
+
+    @cassette_name('firewall')
+    def test_firewall_rules_not_equal(self):
+        p = self.load_policy({
+            'name': 'test-azure-eventhub',
+            'resource': 'azure.eventhub',
+            'filters': [
+                {'type': 'value',
+                 'key': 'name',
+                 'op': 'contains',
+                 'value_type': 'normalize',
+                 'value': '-cctesteventhubns'},
+                {'type': 'firewall-rules',
+                 'equal': ['11.0.1.0/24', '10.1.1.1/32']}],
+        }, validate=True)
+        resources = p.run()
+        self.assertEqual(0, len(resources))