Repository: cloud-custodian/cloud-custodian

Issue:
azure - can not provide only 'count' on resize-plan action for app service plan
Current: 
Policy fails if only providing count for resize:
```
            resource: azure.appserviceplan
           actions:
              - type: resize-plan
                size:
                    count: 2
```

Expected:
Policy passes and can resize with only count field. 


Discussion:

Resolving patch:
diff --git a/tools/c7n_azure/c7n_azure/resources/appserviceplan.py b/tools/c7n_azure/c7n_azure/resources/appserviceplan.py
--- a/tools/c7n_azure/c7n_azure/resources/appserviceplan.py
+++ b/tools/c7n_azure/c7n_azure/resources/appserviceplan.py
@@ -12,13 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from c7n import utils
-
-from c7n_azure.provider import resources
-from c7n_azure.resources.arm import ArmResourceManager
-from c7n_azure.actions.base import AzureBaseAction
 from azure.mgmt.web import models
+from c7n_azure.actions.base import AzureBaseAction
 from c7n_azure.lookup import Lookup
+from c7n_azure.provider import resources
+from c7n_azure.resources.arm import ArmResourceManager
 
 
 @resources.register('appserviceplan')
@@ -63,7 +61,7 @@ class ResizePlan(AzureBaseAction):
 
     :example:
 
-    Resize App Service Plan to F1 plan with 1 instance.
+    Resize App Service Plan to B1 plan with 2 instance.
 
     .. code-block:: yaml
 
@@ -72,8 +70,8 @@ class ResizePlan(AzureBaseAction):
           resource: azure.appserviceplan
           actions:
            - type: resize-plan
-             size: F1
-             count: 1
+             size: B1
+             count: 2
 
 
     :example:
@@ -116,9 +114,14 @@ class ResizePlan(AzureBaseAction):
 
     """
 
-    schema = utils.type_schema(
-        'resize-plan',
-        **{
+    schema = {
+        'type': 'object',
+        'anyOf': [
+            {'required': ['size']},
+            {'required': ['count']}
+        ],
+        'properties': {
+            'type': {'enum': ['resize-plan']},
             'size': Lookup.lookup_type({'type': 'string',
                                         'enum': ['F1', 'B1', 'B2', 'B3', 'D1',
                                                  'S1', 'S2', 'S3', 'P1', 'P2',
@@ -126,8 +129,10 @@ class ResizePlan(AzureBaseAction):
                                                  'PC2', 'PC3', 'PC4']
                                         }),
             'count': Lookup.lookup_type({'type': 'integer'})
-        }
-    )
+        },
+        'additionalProperties': False
+    }
+    schema_alias = True
 
     def _prepare_processing(self):
         self.client = self.manager.get_client()  # type azure.mgmt.web.WebSiteManagementClient
@@ -143,14 +148,19 @@ def _process_resource(self, resource):
         if resource['kind'] == 'linux':
             model.reserved = True
 
+        size = Lookup.extract(self.data.get('size'), resource)
+
+        # get existing tier
+        model.sku = models.SkuDescription()
+        model.sku.tier = resource['sku']['tier']
+        model.sku.name = resource['sku']['name']
+
         if 'size' in self.data:
-            size = Lookup.extract(self.data.get('size'), resource)
-            model.sku = models.SkuDescription()
             model.sku.tier = ResizePlan.get_sku_name(size)
             model.sku.name = size
 
         if 'count' in self.data:
-            model.target_worker_count = Lookup.extract(self.data.get('count'), resource)
+            model.sku.capacity = Lookup.extract(self.data.get('count'), resource)
 
         try:
             self.client.app_service_plans.update(resource['resourceGroup'], resource['name'], model)


Test patch:
diff --git a/tests/test_policy.py b/tests/test_policy.py
--- a/tests/test_policy.py
+++ b/tests/test_policy.py
@@ -224,7 +224,7 @@ def visitor(e):
         names = self._visit_filters_and_actions(visitor)
         if names:
             self.fail(
-                "missing additionalProperties: Fallse on actions/filters\n %s" % (
+                "missing additionalProperties: False on actions/filters\n %s" % (
                     " \n".join(names)))
 
     def test_filter_action_type(self):
diff --git a/tools/c7n_azure/tests/cassettes/AppServicePlanTest.test_resize_plan_from_resource_tag.json b/tools/c7n_azure/tests/cassettes/AppServicePlanTest.test_resize_plan_from_resource_tag.json
deleted file mode 100644
--- a/tools/c7n_azure/tests/cassettes/AppServicePlanTest.test_resize_plan_from_resource_tag.json
+++ /dev/null
@@ -1,102 +0,0 @@
-{
-    "version": 1,
-    "interactions": [
-        {
-            "request": {
-                "method": "GET",
-                "uri": "https://management.azure.com/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/providers/Microsoft.Web/serverfarms?api-version=2018-02-01",
-                "body": null,
-                "headers": {}
-            },
-            "response": {
-                "status": {
-                    "code": 200,
-                    "message": "OK"
-                },
-                "headers": {
-                    "content-type": [
-                        "application/json"
-                    ],
-                    "date": [
-                        "Wed, 28 Aug 2019 06:18:05 GMT"
-                    ],
-                    "cache-control": [
-                        "no-cache"
-                    ],
-                    "content-length": [
-                        "9365"
-                    ]
-                },
-                "body": {
-                    "data": {
-                        "value": [
-                            {
-                                "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/test_appserviceplan/providers/Microsoft.Web/serverfarms/cctest-appserviceplan-win",
-                                "name": "cctest-appserviceplan-win",
-                                "type": "Microsoft.Web/serverfarms",
-                                "kind": "app",
-                                "location": "East US",
-                                "tags": {
-                                    "sku": "B1"
-                                },
-                                "properties": {
-                                    "serverFarmId": 13950228,
-                                    "name": "cctest-appserviceplan-win",
-                                    "workerSize": "Default",
-                                    "workerSizeId": 0,
-                                    "workerTierName": null,
-                                    "numberOfWorkers": 1,
-                                    "currentWorkerSize": null,
-                                    "currentWorkerSizeId": null,
-                                    "currentNumberOfWorkers": null,
-                                    "status": "Ready",
-                                    "webSpace": "test_appserviceplan-EastUSwebspace",
-                                    "subscription": null,
-                                    "adminSiteName": null,
-                                    "hostingEnvironment": null,
-                                    "hostingEnvironmentProfile": null,
-                                    "maximumNumberOfWorkers": 10,
-                                    "planName": "VirtualDedicatedPlan",
-                                    "adminRuntimeSiteName": null,
-                                    "computeMode": "Shared",
-                                    "siteMode": null,
-                                    "geoRegion": "East US",
-                                    "perSiteScaling": null,
-                                    "maximumElasticWorkerCount": null,
-                                    "numberOfSites": 0,
-                                    "hostingEnvironmentId": null,
-                                    "isSpot": null,
-                                    "spotExpirationTime": null,
-                                    "freeOfferExpirationTime": null,
-                                    "tags": {
-                                        "sku": "B1"
-                                    },
-                                    "kind": "app",
-                                    "resourceGroup": "test_appserviceplan",
-                                    "reserved": null,
-                                    "isXenon": null,
-                                    "hyperV": null,
-                                    "mdmId": null,
-                                    "targetWorkerCount": null,
-                                    "targetWorkerSizeId": null,
-                                    "provisioningState": null,
-                                    "webSiteId": null,
-                                    "existingServerFarmIds": null
-                                },
-                                "sku": {
-                                    "name": "S1",
-                                    "tier": "Standard",
-                                    "size": "S1",
-                                    "family": "S",
-                                    "capacity": 1
-                                }
-                            }
-                        ],
-                        "nextLink": null,
-                        "id": null
-                    }
-                }
-            }
-        }
-    ]
-}
\ No newline at end of file
diff --git a/tools/c7n_azure/tests/test_app_service_plan.py b/tools/c7n_azure/tests/test_app_service_plan.py
--- a/tools/c7n_azure/tests/test_app_service_plan.py
+++ b/tools/c7n_azure/tests/test_app_service_plan.py
@@ -14,8 +14,9 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
 
 from azure.mgmt.web import WebSiteManagementClient
-from azure_common import BaseTest, arm_template
+from azure_common import BaseTest, arm_template, cassette_name
 from c7n_azure.session import Session
+from jsonschema import ValidationError
 from mock import patch
 
 from c7n.utils import local_session
@@ -48,6 +49,16 @@ def test_app_service_plan_schema_validate(self):
             }, validate=True)
             self.assertTrue(p)
 
+        # size and count are missing
+        with self.assertRaises(ValidationError):
+            self.load_policy({
+                'name': 'test-azure-appserviceplan',
+                'resource': 'azure.appserviceplan',
+                'actions': [
+                    {'type': 'resize-plan'}
+                ]
+            }, validate=True)
+
     @patch('azure.mgmt.web.operations.app_service_plans_operations.'
            'AppServicePlansOperations.update')
     @arm_template('appserviceplan.json')
@@ -68,15 +79,17 @@ def test_resize_plan_win(self, update_mock):
             ],
             'actions': [
                 {'type': 'resize-plan',
-                 'size': 'F1'}]
-        })
+                 'size': 'B1',
+                 'count': 2}]
+        }, validate=True)
         resources = p.run()
         self.assertEqual(1, len(resources))
 
         name, args, kwargs = update_mock.mock_calls[0]
         self.assertEqual('cctest-appserviceplan-win', args[1])
-        self.assertEqual('F1', args[2].sku.name)
-        self.assertEqual('FREE', args[2].sku.tier)
+        self.assertEqual('B1', args[2].sku.name)
+        self.assertEqual('BASIC', args[2].sku.tier)
+        self.assertEqual(2, args[2].sku.capacity)
 
     @patch('azure.mgmt.web.operations.app_service_plans_operations.'
            'AppServicePlansOperations.update')
@@ -98,19 +111,22 @@ def test_resize_plan_linux(self, update_mock):
             ],
             'actions': [
                 {'type': 'resize-plan',
-                 'size': 'F1'}]
-        })
+                 'size': 'B1',
+                 'count': 3}]
+        }, validate=True)
         resources = p.run()
         self.assertEqual(1, len(resources))
 
         name, args, kwargs = update_mock.mock_calls[0]
         self.assertEqual('cctest-appserviceplan-linux', args[1])
-        self.assertEqual('F1', args[2].sku.name)
-        self.assertEqual('FREE', args[2].sku.tier)
+        self.assertEqual('B1', args[2].sku.name)
+        self.assertEqual('BASIC', args[2].sku.tier)
+        self.assertEqual(3, args[2].sku.capacity)
 
     @patch('azure.mgmt.web.operations.app_service_plans_operations.'
            'AppServicePlansOperations.update')
     @arm_template('appserviceplan.json')
+    @cassette_name('test_resize_plan_win')
     def test_resize_plan_from_resource_tag(self, update_mock):
         p = self.load_policy({
             'name': 'test-azure-appserviceplan',
@@ -128,7 +144,6 @@ def test_resize_plan_from_resource_tag(self, update_mock):
                      'key': 'tags.sku'
                  }}],
         })
-
         resources = p.run()
         self.assertEqual(1, len(resources))
 
@@ -153,7 +168,7 @@ def test_resize_consumption_win(self, logger):
             'actions': [
                 {'type': 'resize-plan',
                  'size': 'F1'}]
-        })
+        }, validate=True)
         p.run()
 
         logger.assert_called_once_with(
@@ -176,9 +191,41 @@ def test_resize_consumption_linux(self, logger):
             'actions': [
                 {'type': 'resize-plan',
                  'size': 'F1'}]
-        })
+        }, validate=True)
         p.run()
 
         logger.assert_called_once_with(
             'Skipping cctest-consumption-linux, '
             'because this App Service Plan is for Consumption Azure Functions.')
+
+    @patch('azure.mgmt.web.operations.app_service_plans_operations.'
+           'AppServicePlansOperations.update')
+    @arm_template('appserviceplan.json')
+    @cassette_name('test_resize_plan_win')
+    def test_resize_plan_win_only_count(self, update_mock):
+        p = self.load_policy({
+            'name': 'test-azure-appserviceplan-win',
+            'resource': 'azure.appserviceplan',
+            'filters': [
+                {'type': 'value',
+                 'key': 'name',
+                 'op': 'eq',
+                 'value_type': 'normalize',
+                 'value': 'cctest-appserviceplan-win'},
+                {'type': 'value',
+                 'key': 'sku.name',
+                 'op': 'eq',
+                 'value': 'S1'}
+            ],
+            'actions': [
+                {'type': 'resize-plan',
+                 'count': 3}]
+        }, validate=True)
+        resources = p.run()
+        self.assertEqual(1, len(resources))
+
+        name, args, kwargs = update_mock.mock_calls[0]
+        self.assertEqual('cctest-appserviceplan-win', args[1])
+        self.assertEqual('S1', args[2].sku.name)
+        self.assertEqual('Standard', args[2].sku.tier)
+        self.assertEqual(3, args[2].sku.capacity)