Repository: cloud-custodian/cloud-custodian

Issue:
azure - app service plan with metrics filter raise exception
The metrics filter does not appear to work on App Service Plans.

"Operation return an invalid status code 'Not Found'

```
policies:
  - name: app-service-plan
    resource: azure.appserviceplan
    filters:
      - type: metric
        metric: CpuPercentage
        aggregation: average
        op: gt
        threshold: 0
        timeframe: 168
```


Discussion:
@stefangordon I will work on this one

Resolving patch:
diff --git a/tools/c7n_azure/c7n_azure/filters.py b/tools/c7n_azure/c7n_azure/filters.py
--- a/tools/c7n_azure/c7n_azure/filters.py
+++ b/tools/c7n_azure/c7n_azure/filters.py
@@ -25,6 +25,7 @@
 from azure.mgmt.policyinsights import PolicyInsightsClient
 from dateutil import tz as tzutils
 from dateutil.parser import parse
+from msrest.exceptions import HttpOperationError
 
 from c7n.filters import Filter, FilterValidationError, ValueFilter
 from c7n.filters.core import PolicyValidationError
@@ -120,7 +121,10 @@ class MetricFilter(Filter):
 
     aggregation_funcs = {
         'average': Math.mean,
-        'total': Math.sum
+        'total': Math.sum,
+        'count': Math.sum,
+        'minimum': Math.max,
+        'maximum': Math.min
     }
 
     schema = {
@@ -135,7 +139,7 @@ class MetricFilter(Filter):
             'timeframe': {'type': 'number'},
             'interval': {'enum': [
                 'PT1M', 'PT5M', 'PT15M', 'PT30M', 'PT1H', 'PT6H', 'PT12H', 'P1D']},
-            'aggregation': {'enum': ['total', 'average']},
+            'aggregation': {'enum': ['total', 'average', 'count', 'minimum', 'maximum']},
             'no_data_action': {'enum': ['include', 'exclude']},
             'filter': {'type': 'string'}
         }
@@ -184,15 +188,19 @@ def get_metric_data(self, resource):
         cached_metric_data = self._get_cached_metric_data(resource)
         if cached_metric_data:
             return cached_metric_data['measurement']
-
-        metrics_data = self.client.metrics.list(
-            resource['id'],
-            timespan=self.timespan,
-            interval=self.interval,
-            metricnames=self.metric,
-            aggregation=self.aggregation,
-            filter=self.filter
-        )
+        try:
+            metrics_data = self.client.metrics.list(
+                resource['id'],
+                timespan=self.timespan,
+                interval=self.interval,
+                metricnames=self.metric,
+                aggregation=self.aggregation,
+                filter=self.filter
+            )
+        except HttpOperationError as e:
+            self.log.error("could not get metric:%s on %s. Full error: %s" % (
+                self.metric, resource['id'], str(e)))
+            return None
 
         if len(metrics_data.value) > 0 and len(metrics_data.value[0].timeseries) > 0:
             m = [getattr(item, self.aggregation)
diff --git a/tools/c7n_azure/c7n_azure/utils.py b/tools/c7n_azure/c7n_azure/utils.py
--- a/tools/c7n_azure/c7n_azure/utils.py
+++ b/tools/c7n_azure/c7n_azure/utils.py
@@ -213,6 +213,16 @@ def sum(numbers):
         clean_numbers = [e for e in numbers if e is not None]
         return float(sum(clean_numbers))
 
+    @staticmethod
+    def max(numbers):
+        clean_numbers = [e for e in numbers if e is not None]
+        return float(max(clean_numbers))
+
+    @staticmethod
+    def min(numbers):
+        clean_numbers = [e for e in numbers if e is not None]
+        return float(min(clean_numbers))
+
 
 class GraphHelper(object):
     log = logging.getLogger('custodian.azure.utils.GraphHelper')


Test patch:
diff --git a/tools/c7n_azure/tests/cassettes/ArmResourceTest.test_metric_filter_invalid_metric.json b/tools/c7n_azure/tests/cassettes/ArmResourceTest.test_metric_filter_invalid_metric.json
new file mode 100644
--- /dev/null
+++ b/tools/c7n_azure/tests/cassettes/ArmResourceTest.test_metric_filter_invalid_metric.json
@@ -0,0 +1,226 @@
+{
+    "version": 1,
+    "interactions": [
+        {
+            "request": {
+                "method": "GET",
+                "uri": "https://management.azure.com/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/providers/Microsoft.Compute/virtualMachines?api-version=2019-03-01",
+                "body": null,
+                "headers": {}
+            },
+            "response": {
+                "status": {
+                    "code": 200,
+                    "message": "OK"
+                },
+                "headers": {
+                    "cache-control": [
+                        "no-cache"
+                    ],
+                    "content-type": [
+                        "application/json; charset=utf-8"
+                    ],
+                    "date": [
+                        "Wed, 28 Aug 2019 23:19:02 GMT"
+                    ],
+                    "content-length": [
+                        "4673"
+                    ],
+                    "x-ms-original-request-ids": [
+                        "92fb254d-fa8d-407c-b1d2-022b5aad40d4",
+                        "28cd531c-8de3-4244-a7e2-40cc093d6725"
+                    ]
+                },
+                "body": {
+                    "data": {
+                        "value": [
+                            {
+                                "name": "cctestvm",
+                                "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/TEST_VM/providers/Microsoft.Compute/virtualMachines/cctestvm",
+                                "type": "Microsoft.Compute/virtualMachines",
+                                "location": "southcentralus",
+                                "tags": {
+                                    "testtag": "testvalue",
+                                    "schedule": "on=(M-U,8);off=(M-U,18);tz=pt"
+                                },
+                                "properties": {
+                                    "vmId": "987cd274-2230-4d4e-a648-ff5bff230cc3",
+                                    "hardwareProfile": {
+                                        "vmSize": "Basic_A0"
+                                    },
+                                    "storageProfile": {
+                                        "imageReference": {
+                                            "publisher": "Canonical",
+                                            "offer": "UbuntuServer",
+                                            "sku": "16.04.0-LTS",
+                                            "version": "latest"
+                                        },
+                                        "osDisk": {
+                                            "osType": "Linux",
+                                            "name": "cctestvm_OsDisk_1_281576b3adf340ad93b48ed4ce32c7b7",
+                                            "createOption": "FromImage",
+                                            "caching": "ReadWrite",
+                                            "managedDisk": {
+                                                "storageAccountType": "Standard_LRS",
+                                                "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/test_vm/providers/Microsoft.Compute/disks/cctestvm_OsDisk_1_281576b3adf340ad93b48ed4ce32c7b7"
+                                            },
+                                            "diskSizeGB": 30
+                                        },
+                                        "dataDisks": [
+                                            {
+                                                "lun": 0,
+                                                "name": "cctestvm_disk2_faa8d572b4424b709b634f3ab154bc4b",
+                                                "createOption": "Empty",
+                                                "caching": "None",
+                                                "managedDisk": {
+                                                    "storageAccountType": "Standard_LRS",
+                                                    "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/test_vm/providers/Microsoft.Compute/disks/cctestvm_disk2_faa8d572b4424b709b634f3ab154bc4b"
+                                                },
+                                                "diskSizeGB": 1023,
+                                                "toBeDetached": false
+                                            }
+                                        ]
+                                    },
+                                    "osProfile": {
+                                        "computerName": "vmpmvqwjwl57ofs",
+                                        "adminUsername": "testuser",
+                                        "linuxConfiguration": {
+                                            "disablePasswordAuthentication": false
+                                        },
+                                        "secrets": []
+                                    },
+                                    "networkProfile": {
+                                        "networkInterfaces": [
+                                            {
+                                                "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/test_vm/providers/Microsoft.Network/networkInterfaces/myVMNic"
+                                            }
+                                        ]
+                                    },
+                                    "diagnosticsProfile": {
+                                        "bootDiagnostics": {
+                                            "enabled": true,
+                                            "storageUri": "https://pmvqwjwl57ofssalinuxvm.blob.core.windows.net/"
+                                        }
+                                    },
+                                    "provisioningState": "Creating"
+                                }
+                            },
+                            {
+                                "name": "aks-default-21042115-0",
+                                "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/MC_AZURE-SINGLE-KEYVAULT-RG_AZURE-SINGLE-KEYVAULT_WESTUS2/providers/Microsoft.Compute/virtualMachines/aks-default-21042115-0",
+                                "type": "Microsoft.Compute/virtualMachines",
+                                "location": "westus2",
+                                "tags": {
+                                    "aksEngineVersion": "v0.38.4-aks",
+                                    "creationSource": "aks-aks-default-21042115-0",
+                                    "orchestrator": "Kubernetes:1.13.7",
+                                    "poolName": "default",
+                                    "resourceNameSuffix": "21042115",
+                                    "Hello": "World"
+                                },
+                                "properties": {
+                                    "vmId": "6fe8a810-f7cd-482b-a7a7-564d153301b2",
+                                    "availabilitySet": {
+                                        "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/MC_AZURE-SINGLE-KEYVAULT-RG_AZURE-SINGLE-KEYVAULT_WESTUS2/providers/Microsoft.Compute/availabilitySets/DEFAULT-AVAILABILITYSET-21042115"
+                                    },
+                                    "hardwareProfile": {
+                                        "vmSize": "Standard_B2s"
+                                    },
+                                    "storageProfile": {
+                                        "imageReference": {
+                                            "publisher": "microsoft-aks",
+                                            "offer": "aks",
+                                            "sku": "aks-ubuntu-1604-201907",
+                                            "version": "2019.07.10"
+                                        },
+                                        "osDisk": {
+                                            "osType": "Linux",
+                                            "name": "aks-default-21042115-0_OsDisk_1_3288027384f443f0aa7336e03b33db04",
+                                            "createOption": "FromImage",
+                                            "caching": "ReadWrite",
+                                            "managedDisk": {
+                                                "storageAccountType": "Premium_LRS",
+                                                "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/MC_azure-single-keyvault-rg_azure-single-keyvault_westus2/providers/Microsoft.Compute/disks/aks-default-21042115-0_OsDisk_1_3288027384f443f0aa7336e03b33db04"
+                                            },
+                                            "diskSizeGB": 30
+                                        },
+                                        "dataDisks": []
+                                    },
+                                    "osProfile": {
+                                        "computerName": "aks-default-21042115-0",
+                                        "adminUsername": "k8sadmin",
+                                        "linuxConfiguration": {
+                                            "disablePasswordAuthentication": true,
+                                            "ssh": {
+                                                "publicKeys": [
+                                                    {
+                                                        "path": "/home/k8sadmin/.ssh/authorized_keys",
+                                                        "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCYADDuGxunu3KlrYLXAzhdDegjL42RL5lrDUqvSHs4GfntHna9s5nVTgcPwla/QDhxgGHDvEjxjpoi1peWwbgiksZ+dGKv0RpkR/UfgiCjop/9zXDy8u3s8i39cObYO8+ED4MAugRtPMWj2ORcHiAEs/cxt8U/socm8cqtWzKeVzs06fFfmOZC6R5EcWMyIZClbBqXw5VoBthbio/e3lPCSxFbZgAcr7OLe7xyZa1ICy5wKJsc9vZESs31sZHvIx65V/WrZCBe5vjO7iQt42iPVGabKmohwFdZHpwLhta6Z6N4oKymTnLX8KpjBW4lDETvWQE2yXyDZXLWqafYGpqV jomalsan@duck1.local"
+                                                    }
+                                                ]
+                                            },
+                                            "provisionVMAgent": true
+                                        },
+                                        "secrets": [],
+                                        "allowExtensionOperations": true,
+                                        "requireGuestProvisionSignal": true
+                                    },
+                                    "networkProfile": {
+                                        "networkInterfaces": [
+                                            {
+                                                "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/MC_azure-single-keyvault-rg_azure-single-keyvault_westus2/providers/Microsoft.Network/networkInterfaces/aks-default-21042115-nic-0"
+                                            }
+                                        ]
+                                    },
+                                    "provisioningState": "Succeeded"
+                                },
+                                "resources": [
+                                    {
+                                        "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/MC_AZURE-SINGLE-KEYVAULT-RG_AZURE-SINGLE-KEYVAULT_WESTUS2/providers/Microsoft.Compute/virtualMachines/aks-default-21042115-0/extensions/computeAksLinuxBilling"
+                                    },
+                                    {
+                                        "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/MC_AZURE-SINGLE-KEYVAULT-RG_AZURE-SINGLE-KEYVAULT_WESTUS2/providers/Microsoft.Compute/virtualMachines/aks-default-21042115-0/extensions/cse-agent-0"
+                                    }
+                                ]
+                            }
+                        ]
+                    }
+                }
+            }
+        },
+        {
+            "request": {
+                "method": "GET",
+                "uri": "https://management.azure.com/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/TEST_VM/providers/Microsoft.Compute/virtualMachines/cctestvm/providers/microsoft.insights/metrics?timespan=2019-08-27%2023%3A59%3A59%2F2019-08-28%2023%3A59%3A59&interval=P1D&metricnames=InvalidMetric&aggregation=average&api-version=2018-01-01",
+                "body": null,
+                "headers": {}
+            },
+            "response": {
+                "status": {
+                    "code": 400,
+                    "message": "Failed to find metric configuration for provider: Microsoft.Compute, resource Type: virtualMachines, metric: InvalidMetric, Valid metrics: Percentage CPU,Network In,Network Out,Disk Read Bytes,Disk Write Bytes,Disk Read Operations/Sec,Disk Write Operations/Sec,CPU Credits Remaining,CPU Credits Consumed,Per Disk Read Bytes/sec,Per Disk Write Bytes/sec,Per Disk Read Operations/Sec,Per Disk Write Operations/Sec,Per Disk QD,OS Per Disk Read Bytes/sec,OS Per Disk Write Bytes/sec,OS Per Disk Read Operations/Sec,O"
+                },
+                "headers": {
+                    "cache-control": [
+                        "no-cache"
+                    ],
+                    "content-type": [
+                        "application/json"
+                    ],
+                    "date": [
+                        "Wed, 28 Aug 2019 23:19:02 GMT"
+                    ],
+                    "content-length": [
+                        "1117"
+                    ]
+                },
+                "body": {
+                    "data": {
+                        "code": "BadRequest",
+                        "message": "Failed to find metric configuration for provider: Microsoft.Compute, resource Type: virtualMachines, metric: InvalidMetric, Valid metrics: Percentage CPU,Network In,Network Out,Disk Read Bytes,Disk Write Bytes,Disk Read Operations/Sec,Disk Write Operations/Sec,CPU Credits Remaining,CPU Credits Consumed,Per Disk Read Bytes/sec,Per Disk Write Bytes/sec,Per Disk Read Operations/Sec,Per Disk Write Operations/Sec,Per Disk QD,OS Per Disk Read Bytes/sec,OS Per Disk Write Bytes/sec,OS Per Disk Read Operations/Sec,OS Per Disk Write Operations/Sec,OS Per Disk QD,Data Disk Read Bytes/sec,Data Disk Write Bytes/sec,Data Disk Read Operations/Sec,Data Disk Write Operations/Sec,Data Disk Queue Depth,OS Disk Read Bytes/sec,OS Disk Write Bytes/sec,OS Disk Read Operations/Sec,OS Disk Write Operations/Sec,OS Disk Queue Depth,Inbound Flows,Outbound Flows,Inbound Flows Maximum Creation Rate,Outbound Flows Maximum Creation Rate,Premium Data Disk Cache Read Hit,Premium Data Disk Cache Read Miss,Premium OS Disk Cache Read Hit,Premium OS Disk Cache Read Miss,Network In Total,Network Out Total"
+                    }
+                }
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/tools/c7n_azure/tests/test_armresource.py b/tools/c7n_azure/tests/test_armresource.py
--- a/tools/c7n_azure/tests/test_armresource.py
+++ b/tools/c7n_azure/tests/test_armresource.py
@@ -126,6 +126,26 @@ def test_metric_filter_not_find_average(self):
         resources = p.run()
         self.assertEqual(len(resources), 0)
 
+    @arm_template('vm.json')
+    def test_metric_filter_invalid_metric(self):
+        p = self.load_policy({
+            'name': 'test-azure-metric',
+            'resource': 'azure.vm',
+            'filters': [
+                {'type': 'value',
+                 'key': 'name',
+                 'op': 'eq',
+                 'value_type': 'normalize',
+                 'value': 'cctestvm'},
+                {'type': 'metric',
+                 'metric': 'InvalidMetric',
+                 'aggregation': 'average',
+                 'op': 'gte',
+                 'threshold': 0}],
+        })
+        resources = p.run()
+        self.assertEqual(0, len(resources))
+
     def test_metric_filter_invalid_missing_metric(self):
         policy = {
             'name': 'test-azure-metric',
diff --git a/tools/c7n_azure/tests/test_utils_math.py b/tools/c7n_azure/tests/test_utils_math.py
new file mode 100644
--- /dev/null
+++ b/tools/c7n_azure/tests/test_utils_math.py
@@ -0,0 +1,80 @@
+# Copyright 2019 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+from azure_common import BaseTest
+from c7n_azure.utils import Math
+
+
+class UtilsMathTest(BaseTest):
+
+    def test_mean_single_value(self):
+        data = [10]
+        actual = Math.mean(data)
+        self.assertEqual(data[0], actual)
+
+    def test_mean_multi_value(self):
+        data = [10, 20, 30, 40, 50]
+        actual = Math.mean(data)
+        self.assertEqual(30, actual)
+
+    def test_mean_multi_value_with_null(self):
+        data = [10, 20, None, 30, 40, 50]
+        actual = Math.mean(data)
+        self.assertEqual(30, actual)
+
+    def test_sum_single_value(self):
+        data = [10]
+        actual = Math.sum(data)
+        self.assertEqual(data[0], actual)
+
+    def test_sum_multi_value(self):
+        data = [10, 20, 30, 40, 50]
+        actual = Math.sum(data)
+        self.assertEqual(150, actual)
+
+    def test_sum_multi_value_with_null(self):
+        data = [10, 20, None, 30, 40, 50]
+        actual = Math.sum(data)
+        self.assertEqual(150, actual)
+
+    def test_min_single_value(self):
+        data = [10]
+        actual = Math.min(data)
+        self.assertEqual(data[0], actual)
+
+    def test_min_multi_value(self):
+        data = [10, 20, 30, 40, 50]
+        actual = Math.min(data)
+        self.assertEqual(10, actual)
+
+    def test_min_multi_value_with_null(self):
+        data = [10, 20, None, 30, 40, 50]
+        actual = Math.min(data)
+        self.assertEqual(10, actual)
+
+    def test_max_single_value(self):
+        data = [10]
+        actual = Math.max(data)
+        self.assertEqual(data[0], actual)
+
+    def test_max_multi_value(self):
+        data = [10, 20, 30, 40, 50]
+        actual = Math.max(data)
+        self.assertEqual(50, actual)
+
+    def test_max_multi_value_with_null(self):
+        data = [10, 20, None, 30, 40, 50]
+        actual = Math.max(data)
+        self.assertEqual(50, actual)