Repository: serverless/serverless-azure-functions
Pull request #325 (2019-09-09 20:54:06)

fix: Exclude ARM params with null values
- [x] Eliminate explicit `type` from valued paramaters
- [x] Remove any parameters whose value is `null` before deployment
- [x] Make `type` optional on `ArmParameters`


Patch:
diff --git a/src/armTemplates/compositeArmTemplate.ts b/src/armTemplates/compositeArmTemplate.ts
index d79c084..199ba04 100644
--- a/src/armTemplates/compositeArmTemplate.ts
+++ b/src/armTemplates/compositeArmTemplate.ts
@@ -41,7 +41,6 @@ export class CompositeArmTemplate implements ArmResourceTemplateGenerator {
         ...parameters,
         ...resource.getParameters(config),
         location: {
-          type: ArmParamType.String,
           value: AzureNamingService.getNormalizedRegionName(config.provider.region)
         }
       };
diff --git a/src/armTemplates/resources/apim.ts b/src/armTemplates/resources/apim.ts
index 3544fba..2576046 100644
--- a/src/armTemplates/resources/apim.ts
+++ b/src/armTemplates/resources/apim.ts
@@ -74,23 +74,18 @@ export class ApimResource implements ArmResourceTemplateGenerator {
 
     return {
       apiManagementName: {
-        type: ArmParamType.String,
         value: ApimResource.getResourceName(config),
       },
       apimSkuName: {
-        type: ArmParamType.String,
         value: apimConfig.sku.name,
       },
       apimSkuCapacity: {
-        type: ArmParamType.Int,
         value: apimConfig.sku.capacity,
       },
       apimPublisherEmail: {
-        type: ArmParamType.String,
         value: apimConfig.publisherEmail,
       },
       apimPublisherName: {
-        type: ArmParamType.String,
         value: apimConfig.publisherName,
       }
     };
diff --git a/src/armTemplates/resources/appInsights.ts b/src/armTemplates/resources/appInsights.ts
index bba0480..0a4498a 100644
--- a/src/armTemplates/resources/appInsights.ts
+++ b/src/armTemplates/resources/appInsights.ts
@@ -46,7 +46,6 @@ export class AppInsightsResource implements ArmResourceTemplateGenerator {
   public getParameters(config: ServerlessAzureConfig): ArmParameters {
     return {
       appInsightsName: {
-        type: ArmParamType.String,
         value: AppInsightsResource.getResourceName(config),
       }
     };
diff --git a/src/armTemplates/resources/appServicePlan.ts b/src/armTemplates/resources/appServicePlan.ts
index 05406f4..670aee7 100644
--- a/src/armTemplates/resources/appServicePlan.ts
+++ b/src/armTemplates/resources/appServicePlan.ts
@@ -65,15 +65,12 @@ export class AppServicePlanResource implements ArmResourceTemplateGenerator {
 
     return {
       appServicePlanName: {
-        type: ArmParamType.String,
         value: AppServicePlanResource.getResourceName(config),
       },
       appServicePlanSkuName: {
-        type: ArmParamType.String,
         value: resourceConfig.sku.name,
       },
       appServicePlanSkuTier: {
-        type: ArmParamType.String,
         value: resourceConfig.sku.tier,
       }
     }
diff --git a/src/armTemplates/resources/functionApp.ts b/src/armTemplates/resources/functionApp.ts
index 1ce2985..9d16299 100644
--- a/src/armTemplates/resources/functionApp.ts
+++ b/src/armTemplates/resources/functionApp.ts
@@ -126,19 +126,15 @@ export class FunctionAppResource implements ArmResourceTemplateGenerator {
 
     return {
       functionAppName: {
-        type: ArmParamType.String,
         value: FunctionAppResource.getResourceName(config),
       },
       functionAppNodeVersion: {
-        type: ArmParamType.String,
         value: resourceConfig.nodeVersion,
       },
       functionAppWorkerRuntime: {
-        type: ArmParamType.String,
         value: resourceConfig.workerRuntime,
       },
       functionAppExtensionVersion: {
-        type: ArmParamType.String,
         value: resourceConfig.extensionVersion,
       }
     };
diff --git a/src/armTemplates/resources/hostingEnvironment.ts b/src/armTemplates/resources/hostingEnvironment.ts
index f2b650f..06a08c9 100644
--- a/src/armTemplates/resources/hostingEnvironment.ts
+++ b/src/armTemplates/resources/hostingEnvironment.ts
@@ -68,7 +68,6 @@ export class HostingEnvironmentResource implements ArmResourceTemplateGenerator
   public getParameters(config: ServerlessAzureConfig): ArmParameters {
     return {
       hostingEnvironmentName: {
-        type: ArmParamType.String,
         value: HostingEnvironmentResource.getResourceName(config)
       }
     }
diff --git a/src/armTemplates/resources/storageAccount.ts b/src/armTemplates/resources/storageAccount.ts
index 734fd60..cf8ea2f 100644
--- a/src/armTemplates/resources/storageAccount.ts
+++ b/src/armTemplates/resources/storageAccount.ts
@@ -66,15 +66,12 @@ export class StorageAccountResource implements ArmResourceTemplateGenerator {
 
     return {
       storageAccountName: {
-        type: ArmParamType.String,
         value: StorageAccountResource.getResourceName(config),
       },
       storageAccountSkuName: {
-        type: ArmParamType.String,
         value: resourceConfig.sku.name,
       },
       storageAccoutSkuTier: {
-        type: ArmParamType.String,
         value: resourceConfig.sku.tier,
       }
     };
diff --git a/src/armTemplates/resources/virtualNetwork.ts b/src/armTemplates/resources/virtualNetwork.ts
index 9092eb8..921d658 100644
--- a/src/armTemplates/resources/virtualNetwork.ts
+++ b/src/armTemplates/resources/virtualNetwork.ts
@@ -86,7 +86,6 @@ export class VirtualNetworkResource implements ArmResourceTemplateGenerator {
   public getParameters(config: ServerlessAzureConfig): ArmParameters {
     return {
       virtualNetworkName: {
-        type: ArmParamType.String,
         value: VirtualNetworkResource.getResourceName(config),
       }
     }
diff --git a/src/models/armTemplates.ts b/src/models/armTemplates.ts
index 778d666..6f10135 100644
--- a/src/models/armTemplates.ts
+++ b/src/models/armTemplates.ts
@@ -46,7 +46,7 @@ export interface ArmResourceTemplate {
 
 export interface ArmParameters {
   [key: string]: {
-    type: ArmParamType;
+    type?: ArmParamType;
     value?: string | number;
     defaultValue?: string | number;
   };
diff --git a/src/services/armService.ts b/src/services/armService.ts
index 9a06782..cde93a4 100644
--- a/src/services/armService.ts
+++ b/src/services/armService.ts
@@ -102,6 +102,12 @@ export class ArmService extends BaseService {
       return;
     }
 
+    for (const key of Object.keys(deployment.parameters)) {
+      if (!deployment.parameters[key].value) {
+        delete deployment.parameters[key];
+      }
+    }
+
     // Construct deployment object
     const armDeployment: Deployment = {
       properties: {


Test patch:
diff --git a/src/services/armService.test.ts b/src/services/armService.test.ts
index 361f17c..a926ac1 100644
--- a/src/services/armService.test.ts
+++ b/src/services/armService.test.ts
@@ -366,5 +366,40 @@ describe("Arm Service", () => {
           new RegExp(`.*${errorPattern}.*`,"s")
         );
     });
+
+    it("Does not try to include paramaters with a value that is undefined", async () => {
+      sls.service.provider.runtime = "nodejs10.x";
+      const deployment = await service.createDeploymentFromType(ArmTemplateType.Consumption);
+
+      expect(deployment.parameters.functionAppExtensionVersion).not.toBeUndefined();
+      expect(deployment.parameters.functionAppExtensionVersion.value).toBeUndefined();
+
+      await service.deployTemplate(deployment);
+
+      expect(deployment.parameters.functionAppExtensionVersion).toBeUndefined();
+
+      const paramKeys = Object.keys(deployment.parameters);
+      paramKeys.forEach((key) => {
+        const paramValue = deployment.parameters[key];
+        if (paramValue) {
+          expect(paramValue.value).not.toBeUndefined();
+        }
+      })
+
+      const expectedResourceGroup = sls.service.provider["resourceGroup"];
+      const expectedDeploymentName = sls.service.provider["deploymentName"] || `${this.resourceGroup}-deployment`;
+      const expectedDeploymentNameRegex = new RegExp(expectedDeploymentName + "-t([0-9]+)")
+      const expectedDeployment: Deployment = {
+        properties: {
+          mode: "Incremental",
+          ...deployment
+        },
+      };
+
+      const call = (Deployments.prototype.createOrUpdate as any).mock.calls[0];
+      expect(call[0]).toEqual(expectedResourceGroup);
+      expect(call[1]).toMatch(expectedDeploymentNameRegex);
+      expect(call[2]).toEqual(expectedDeployment);
+    });
   });
 });