From db59819253883dc5686230e552c20ce6d1464fae Mon Sep 17 00:00:00 2001 From: Wallace Breza Date: Fri, 21 Jun 2019 12:47:42 -0700 Subject: [PATCH] set resource template defaults --- src/armTemplates/ase.ts | 12 +++- src/armTemplates/consumption.ts | 5 +- src/armTemplates/premium.ts | 12 +++- src/armTemplates/resources/functionApp.ts | 8 +-- src/armTemplates/resources/storageAccount.ts | 4 +- src/plugins/deploy/azureDeployPlugin.ts | 5 -- src/services/apimService.ts | 64 +++++++++++--------- src/services/armService.test.ts | 10 +++ src/services/armService.ts | 25 ++++++-- src/services/baseService.test.ts | 6 +- src/services/baseService.ts | 43 +++++++------ src/services/functionAppService.test.ts | 6 +- src/services/functionAppService.ts | 19 +++--- 13 files changed, 129 insertions(+), 90 deletions(-) diff --git a/src/armTemplates/ase.ts b/src/armTemplates/ase.ts index a9b31ed1..89a4aa9d 100644 --- a/src/armTemplates/ase.ts +++ b/src/armTemplates/ase.ts @@ -4,7 +4,7 @@ import { StorageAccountResource } from "./resources/storageAccount"; import { AppServicePlanResource } from "./resources/appServicePlan"; import { HostingEnvironmentResource } from "./resources/hostingEnvironment"; import { VirtualNetworkResource } from "./resources/virtualNetwork"; -import { ArmResourceTemplateGenerator } from "../services/armService.js"; +import { ArmResourceTemplateGenerator, ArmResourceTemplate } from "../services/armService.js"; import { ServerlessAzureConfig } from "../models/serverless.js"; const resources: ArmResourceTemplateGenerator[] = [ @@ -18,7 +18,7 @@ const resources: ArmResourceTemplateGenerator[] = [ const AseTemplate: ArmResourceTemplateGenerator = { getTemplate: () => { - const template: any = { + const template: ArmResourceTemplate = { "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": {}, @@ -41,6 +41,13 @@ const AseTemplate: ArmResourceTemplateGenerator = { template.parameters.appServicePlanSkuName.defaultValue = "I1"; template.parameters.appServicePlanSkuTier.defaultValue = "Isolated"; + // Update the functionApp resource to include the app service plan references + const app: any = template.resources.find((resource: any) => resource.type === "Microsoft.Web/sites"); + if (app) { + app.properties.serverFarmId = "[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]"; + app.dependsOn.push("[concat('Microsoft.Web/serverfarms/', parameters('appServicePlanName'))]") + } + return template; }, @@ -50,6 +57,7 @@ const AseTemplate: ArmResourceTemplateGenerator = { parameters = { ...parameters, ...resource.getParameters(config), + location: config.provider.region, } }); diff --git a/src/armTemplates/consumption.ts b/src/armTemplates/consumption.ts index 4b62decb..5f71883e 100644 --- a/src/armTemplates/consumption.ts +++ b/src/armTemplates/consumption.ts @@ -1,7 +1,7 @@ import { FunctionAppResource } from "./resources/functionApp"; import { AppInsightsResource } from "./resources/appInsights"; import { StorageAccountResource } from "./resources/storageAccount"; -import { ArmResourceTemplateGenerator } from "../services/armService.js"; +import { ArmResourceTemplateGenerator, ArmResourceTemplate } from "../services/armService.js"; import { ServerlessAzureConfig } from "../models/serverless"; const resources: ArmResourceTemplateGenerator[] = [ @@ -12,7 +12,7 @@ const resources: ArmResourceTemplateGenerator[] = [ const ConsumptionTemplate: ArmResourceTemplateGenerator = { getTemplate: () => { - const template: any = { + const template: ArmResourceTemplate = { "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": {}, @@ -41,6 +41,7 @@ const ConsumptionTemplate: ArmResourceTemplateGenerator = { parameters = { ...parameters, ...resource.getParameters(config), + location: config.provider.region, } }); diff --git a/src/armTemplates/premium.ts b/src/armTemplates/premium.ts index c496abb0..2bb84c56 100644 --- a/src/armTemplates/premium.ts +++ b/src/armTemplates/premium.ts @@ -2,7 +2,7 @@ import { FunctionAppResource } from "./resources/functionApp"; import { AppInsightsResource } from "./resources/appInsights"; import { StorageAccountResource } from "./resources/storageAccount"; import { AppServicePlanResource } from "./resources/appServicePlan"; -import { ArmResourceTemplateGenerator } from "../services/armService.js"; +import { ArmResourceTemplateGenerator, ArmResourceTemplate } from "../services/armService.js"; import { ServerlessAzureConfig } from "../models/serverless"; const resources: ArmResourceTemplateGenerator[] = [ @@ -14,7 +14,7 @@ const resources: ArmResourceTemplateGenerator[] = [ const PremiumTemplate: ArmResourceTemplateGenerator = { getTemplate: () => { - const template: any = { + const template: ArmResourceTemplate = { "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": {}, @@ -37,6 +37,13 @@ const PremiumTemplate: ArmResourceTemplateGenerator = { template.parameters.appServicePlanSkuName.defaultValue = "EP1"; template.parameters.appServicePlanSkuTier.defaultValue = "ElasticPremium"; + // Update the functionApp resource to include the app service plan references + const app: any = template.resources.find((resource: any) => resource.type === "Microsoft.Web/sites"); + if (app) { + app.properties.serverFarmId = "[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]"; + app.dependsOn.push("[concat('Microsoft.Web/serverfarms/', parameters('appServicePlanName'))]") + } + return template; }, @@ -46,6 +53,7 @@ const PremiumTemplate: ArmResourceTemplateGenerator = { parameters = { ...parameters, ...resource.getParameters(config), + location: config.provider.region, } }); diff --git a/src/armTemplates/resources/functionApp.ts b/src/armTemplates/resources/functionApp.ts index 7d701b7e..492b6bf3 100644 --- a/src/armTemplates/resources/functionApp.ts +++ b/src/armTemplates/resources/functionApp.ts @@ -11,10 +11,6 @@ export const FunctionAppResource: ArmResourceTemplateGenerator = { "defaultValue": "", "type": "String" }, - "appServicePlanName": { - "defaultValue": "", - "type": "String" - }, "storageAccountName": { "defaultValue": "", "type": "String" @@ -36,7 +32,6 @@ export const FunctionAppResource: ArmResourceTemplateGenerator = { "name": "[parameters('functionAppName')]", "location": "[parameters('location')]", "dependsOn": [ - "[concat('Microsoft.Web/serverfarms/', parameters('appServicePlanName'))]", "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]", "[concat('microsoft.insights/components/', parameters('appInsightsName'))]" ], @@ -80,7 +75,6 @@ export const FunctionAppResource: ArmResourceTemplateGenerator = { }, "name": "[parameters('functionAppName')]", "clientAffinityEnabled": false, - "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]", "hostingEnvironment": "" } } @@ -90,7 +84,7 @@ export const FunctionAppResource: ArmResourceTemplateGenerator = { getParameters: (config: ServerlessAzureConfig) => { const resourceConfig: ResourceConfig = { - name: `${config.provider.prefix}-${config.provider.region}-${config.provider.stage}-functionapp`, + name: `${config.provider.prefix}-${config.provider.region}-${config.provider.stage}-${config.service}`, ...config.provider.functionApp, }; diff --git a/src/armTemplates/resources/storageAccount.ts b/src/armTemplates/resources/storageAccount.ts index 2a9688d7..6b08dc02 100644 --- a/src/armTemplates/resources/storageAccount.ts +++ b/src/armTemplates/resources/storageAccount.ts @@ -43,10 +43,10 @@ export const StorageAccountResource: ArmResourceTemplateGenerator = { ] } }, - + getParameters: (config: ServerlessAzureConfig) => { const resourceConfig: ResourceConfig = { - name: `${config.provider.prefix}${config.provider.region}${config.provider.stage}-sa`.toLocaleLowerCase(), + name: `${config.provider.prefix}${config.provider.region.substr(0,3)}${config.provider.stage.substr(0,3)}sa`.replace("-", "").toLocaleLowerCase(), sku: {}, ...config.provider.storageAccount, }; diff --git a/src/plugins/deploy/azureDeployPlugin.ts b/src/plugins/deploy/azureDeployPlugin.ts index 3d06d44a..76c88ebc 100644 --- a/src/plugins/deploy/azureDeployPlugin.ts +++ b/src/plugins/deploy/azureDeployPlugin.ts @@ -47,11 +47,6 @@ export class AzureDeployPlugin { } private async deploy() { - this.serverless.cli.log("OPTIONS"); - this.serverless.cli.log(JSON.stringify(this.options, null, 4)); - this.serverless.cli.log("PROVIDER REGION"); - this.serverless.cli.log(this.serverless.service.provider.region); - const resourceService = new ResourceService(this.serverless, this.options); await resourceService.deployResourceGroup(); diff --git a/src/services/apimService.ts b/src/services/apimService.ts index a6452f95..6126a70c 100644 --- a/src/services/apimService.ts +++ b/src/services/apimService.ts @@ -17,18 +17,22 @@ import { Guard } from "../shared/guard"; export class ApimService extends BaseService { private apimClient: ApiManagementClient; private functionAppService: FunctionAppService; - private config: ApiManagementConfig; + private apimConfig: ApiManagementConfig; public constructor(serverless: Serverless, options?: Serverless.Options) { super(serverless, options); - this.config = this.serverless.service.provider["apim"]; - if (!this.config) { + this.apimConfig = this.config.provider.apim; + if (!this.apimConfig) { return; } - if (!this.config.backend) { - this.config.backend = {} as any; + if (!this.apimConfig.name) { + this.apimConfig.name = `${this.config.provider.prefix}-${this.config.provider.region}-${this.config.provider.stage}-apim` + } + + if (!this.apimConfig.backend) { + this.apimConfig.backend = {} as any; } this.apimClient = new ApiManagementClient(this.credentials, this.subscriptionId); @@ -39,24 +43,24 @@ export class ApimService extends BaseService { * Gets the configured APIM resource */ public async get(): Promise { - if (!(this.config && this.config.name)) { + if (!(this.apimConfig && this.apimConfig.name)) { return null; } try { - return await this.apimClient.apiManagementService.get(this.resourceGroup, this.config.name); + return await this.apimClient.apiManagementService.get(this.resourceGroup, this.apimConfig.name); } catch (err) { return null; } } public async getApi(): Promise { - if (!(this.config && this.config.api && this.config.api.name)) { + if (!(this.apimConfig && this.apimConfig.api && this.apimConfig.api.name)) { return null; } try { - return await this.apimClient.api.get(this.resourceGroup, this.config.name, this.config.api.name); + return await this.apimClient.api.get(this.resourceGroup, this.apimConfig.name, this.apimConfig.api.name); } catch (err) { return null; } @@ -66,7 +70,7 @@ export class ApimService extends BaseService { * Deploys the APIM top level api */ public async deployApi() { - if (!(this.config && this.config.name)) { + if (!(this.apimConfig && this.apimConfig.name)) { return null; } @@ -86,7 +90,7 @@ export class ApimService extends BaseService { Guard.null(service); Guard.null(api); - if (!(this.config && this.config.name)) { + if (!(this.apimConfig && this.apimConfig.name)) { return null; } @@ -131,21 +135,21 @@ export class ApimService extends BaseService { this.log("-> Deploying API"); try { - const api = await this.apimClient.api.createOrUpdate(this.resourceGroup, this.config.name, this.config.api.name, { + const api = await this.apimClient.api.createOrUpdate(this.resourceGroup, this.apimConfig.name, this.apimConfig.api.name, { isCurrent: true, - subscriptionRequired: this.config.api.subscriptionRequired, - displayName: this.config.api.displayName, - description: this.config.api.description, - path: this.config.api.path, - protocols: this.config.api.protocols, + subscriptionRequired: this.apimConfig.api.subscriptionRequired, + displayName: this.apimConfig.api.displayName, + description: this.apimConfig.api.description, + path: this.apimConfig.api.path, + protocols: this.apimConfig.api.protocols, }); - if (this.config.cors) { + if (this.apimConfig.cors) { this.log("-> Deploying CORS policy"); - await this.apimClient.apiPolicy.createOrUpdate(this.resourceGroup, this.config.name, this.config.api.name, { + await this.apimClient.apiPolicy.createOrUpdate(this.resourceGroup, this.apimConfig.name, this.apimConfig.api.name, { format: "rawxml", - value: this.createCorsXmlPolicy(this.config.cors) + value: this.createCorsXmlPolicy(this.apimConfig.cors) }); } @@ -168,17 +172,17 @@ export class ApimService extends BaseService { try { const functionAppResourceId = `https://management.azure.com${functionApp.id}`; - return await this.apimClient.backend.createOrUpdate(this.resourceGroup, this.config.name, this.serviceName, { + return await this.apimClient.backend.createOrUpdate(this.resourceGroup, this.apimConfig.name, this.serviceName, { credentials: { header: { "x-functions-key": [`{{${this.serviceName}-key}}`], }, }, - title: this.config.backend.title || functionApp.name, - tls: this.config.backend.tls, - proxy: this.config.backend.proxy, - description: this.config.backend.description, - protocol: this.config.backend.protocol || "http", + title: this.apimConfig.backend.title || functionApp.name, + tls: this.apimConfig.backend.tls, + proxy: this.apimConfig.backend.proxy, + description: this.apimConfig.backend.description, + protocol: this.apimConfig.backend.protocol || "http", resourceId: functionAppResourceId, url: backendUrl, }); @@ -216,13 +220,13 @@ export class ApimService extends BaseService { const operation = await client.apiOperation.createOrUpdate( this.resourceGroup, - this.config.name, - this.config.api.name, + this.apimConfig.name, + this.apimConfig.api.name, options.function, operationConfig, ); - await client.apiOperationPolicy.createOrUpdate(this.resourceGroup, this.config.name, this.config.api.name, options.function, { + await client.apiOperationPolicy.createOrUpdate(this.resourceGroup, this.apimConfig.name, this.apimConfig.api.name, options.function, { format: "rawxml", value: this.createApiOperationXmlPolicy(), }); @@ -245,7 +249,7 @@ export class ApimService extends BaseService { const masterKey = await this.functionAppService.getMasterKey(functionApp); const keyName = `${this.serviceName}-key`; - return await this.apimClient.property.createOrUpdate(this.resourceGroup, this.config.name, keyName, { + return await this.apimClient.property.createOrUpdate(this.resourceGroup, this.apimConfig.name, keyName, { displayName: keyName, secret: true, value: masterKey, diff --git a/src/services/armService.test.ts b/src/services/armService.test.ts index 600c7fc5..71772ce7 100644 --- a/src/services/armService.test.ts +++ b/src/services/armService.test.ts @@ -63,6 +63,11 @@ describe("Arm Service", () => { expect(deployment.template.resources.find((resource) => resource.type === "Microsoft.Web/sites")).not.toBeNull(); expect(deployment.template.resources.find((resource) => resource.type === "Microsoft.Storage/storageAccounts")).not.toBeNull(); expect(deployment.template.resources.find((resource) => resource.type === "microsoft.insights/components")).not.toBeNull(); + + // Verify the ARM template includes the linkage to the correct server farm + const functionApp = deployment.template.resources.find((res) => res.type === "Microsoft.Web/sites"); + expect(functionApp.dependsOn).toContain("[concat('Microsoft.Web/serverfarms/', parameters('appServicePlanName'))]"); + expect(functionApp.properties.serverFarmId).toEqual("[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]"); }); it("ASE template includes correct resources", async () => { @@ -77,6 +82,11 @@ describe("Arm Service", () => { expect(deployment.template.resources.find((resource) => resource.type === "Microsoft.Web/sites")).not.toBeNull(); expect(deployment.template.resources.find((resource) => resource.type === "Microsoft.Storage/storageAccounts")).not.toBeNull(); expect(deployment.template.resources.find((resource) => resource.type === "microsoft.insights/components")).not.toBeNull(); + + // Verify the ARM template includes the linkage to the correct server farm + const functionApp = deployment.template.resources.find((res) => res.type === "Microsoft.Web/sites"); + expect(functionApp.dependsOn).toContain("[concat('Microsoft.Web/serverfarms/', parameters('appServicePlanName'))]"); + expect(functionApp.properties.serverFarmId).toEqual("[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]"); }); it("Consumption template includes correct resources", async () => { diff --git a/src/services/armService.ts b/src/services/armService.ts index 695213d3..1d8a5829 100644 --- a/src/services/armService.ts +++ b/src/services/armService.ts @@ -1,9 +1,10 @@ import Serverless from "serverless"; -import { Deployment, DeploymentsCreateOrUpdateResponse, DeploymentExtended } from "@azure/arm-resources/esm/models"; +import { Deployment, DeploymentExtended } from "@azure/arm-resources/esm/models"; import { BaseService } from "./baseService"; import { ResourceManagementClient } from "@azure/arm-resources"; import { Guard } from "../shared/guard"; import { ServerlessAzureConfig } from "../models/serverless"; +import fs from "fs"; export interface ArmResourceTemplateGenerator { getTemplate(): ArmResourceTemplate; @@ -50,7 +51,7 @@ export class ArmService extends BaseService { const mergedTemplate = template.getTemplate(); let parameters = template.getParameters(azureConfig); - if (this.serverless.service.provider["apim"]) { + if (this.config.provider.apim) { const apimTemplate = ApimResource.getTemplate(); const apimParameters = ApimResource.getParameters(azureConfig); @@ -78,15 +79,29 @@ export class ArmService extends BaseService { public async deployTemplate(deployment: ArmDeployment): Promise { Guard.null(deployment); - const deploymentParameters: Deployment = { + const deploymentParameters = {}; + Object.keys(deployment.parameters).forEach((key) => { + const parameterValue = deployment.parameters[key]; + if (parameterValue) { + deploymentParameters[key] = { value: deployment.parameters[key] }; + } + }); + + this.serverless.cli.log(JSON.stringify(deploymentParameters, null, 4)); + fs.writeFileSync(".serverless/arm-template.json", JSON.stringify(deployment.template, null, 4)); + + const armDeployment: Deployment = { properties: { mode: "Incremental", template: deployment.template, - parameters: deployment.parameters, + parameters: deploymentParameters, } }; // Deploy ARM template - return await this.resourceClient.deployments.createOrUpdate(this.resourceGroup, this.deploymentName, deploymentParameters); + const result = await this.resourceClient.deployments.createOrUpdate(this.resourceGroup, this.deploymentName, armDeployment); + this.serverless.cli.log("ARM deployment complete"); + + return result; } } \ No newline at end of file diff --git a/src/services/baseService.test.ts b/src/services/baseService.test.ts index 2ea8af60..1a522e91 100644 --- a/src/services/baseService.test.ts +++ b/src/services/baseService.test.ts @@ -85,7 +85,7 @@ describe("Base Service", () => { }); it("Sets default region and stage values if not defined", () => { - const testService = new TestService(sls); + const testService = new MockService(sls); expect(testService).not.toBeNull(); expect(sls.service.provider.region).toEqual("westus"); @@ -97,7 +97,7 @@ describe("Base Service", () => { stage: "prod", region: "eastus2" }; - const testService = new TestService(sls, cliOptions); + const testService = new MockService(sls, cliOptions); expect(testService.getSlsRegion()).toEqual(cliOptions.region); expect(testService.getSlsStage()).toEqual(cliOptions.stage); @@ -136,7 +136,7 @@ describe("Base Service", () => { const region = testService.getSlsRegion(); const stage = testService.getSlsStage(); - expect(resourceGroupName).toEqual(`${sls.service["service"]}-${region}-${stage}-rg`); + expect(resourceGroupName).toEqual(`sls-${region}-${stage}-${sls.service["service"]}-rg`); }); it("Fails if credentials have not been set in serverless config", () => { diff --git a/src/services/baseService.ts b/src/services/baseService.ts index 4a81638e..a7556eb7 100644 --- a/src/services/baseService.ts +++ b/src/services/baseService.ts @@ -3,6 +3,7 @@ import fs from "fs"; import request from "request"; import Serverless from "serverless"; import { Guard } from "../shared/guard"; +import { ServerlessAzureConfig } from "../models/serverless"; export abstract class BaseService { protected baseUrl: string; @@ -11,6 +12,7 @@ export abstract class BaseService { protected subscriptionId: string; protected resourceGroup: string; protected deploymentName: string; + protected config: ServerlessAzureConfig; protected constructor( protected serverless: Serverless, @@ -19,24 +21,41 @@ export abstract class BaseService { ) { Guard.null(serverless); + this.setDefaultValues(); + this.baseUrl = "https://management.azure.com"; + this.config = serverless.service as any; this.serviceName = serverless.service["service"]; this.credentials = serverless.variables["azureCredentials"]; this.subscriptionId = serverless.variables["subscriptionId"]; this.resourceGroup = this.getResourceGroupName(); this.deploymentName = serverless.service.provider["deploymentName"] || `${this.resourceGroup}-deployment`; - this.setDefaultValues(); - if (!this.credentials && authenticate) { throw new Error(`Azure Credentials has not been set in ${this.constructor.name}`); } } - public getResourceGroup(): string { - return this.resourceGroup; + public getRegion(): string { + return this.options.region || this.serverless.service.provider.region; } + public getStage(): string { + return this.options.stage || this.serverless.service.provider.stage; + } + + public getFunctionAppName(): string { + return this.config.provider.functionApp + ? this.config.provider.functionApp.name + : `${this.config.provider.prefix}-${this.config.provider.region}-${this.config.provider.stage}-${this.config.service}`; + } + + public getResourceGroupName(): string { + return this.options["resourceGroup"] + || this.serverless.service.provider["resourceGroup"] + || `${this.config.provider.prefix}-${this.getRegion()}-${this.getStage()}-${this.serviceName}-rg`; + } + /** * Sends an API request using axios HTTP library * @param method The HTTP method @@ -105,19 +124,9 @@ export abstract class BaseService { if (!this.serverless.service.provider.stage) { this.serverless.service.provider.stage = "dev"; } - } - - protected getRegion(): string { - return this.options.region || this.serverless.service.provider.region; - } - protected getStage(): string { - return this.options.stage || this.serverless.service.provider.stage; - } - - protected getResourceGroupName(): string { - return this.options["resourceGroup"] - || this.serverless.service.provider["resourceGroup"] - || `${this.serviceName}-${this.getRegion()}-${this.getStage()}-rg`; + if (!this.serverless.service.provider["prefix"]) { + this.serverless.service.provider["prefix"] = "sls"; + } } } diff --git a/src/services/functionAppService.test.ts b/src/services/functionAppService.test.ts index fce61cb8..a37426f5 100644 --- a/src/services/functionAppService.test.ts +++ b/src/services/functionAppService.test.ts @@ -33,7 +33,7 @@ describe("Function App Service", () => { beforeAll(() => { - // TODO: How to spy on defaul exported function? + // TODO: How to spy on default exported function? const axiosMock = new MockAdapter(axios); // Master Key @@ -86,7 +86,7 @@ describe("Function App Service", () => { const service = createService(); const result = await service.get(); expect(WebSiteManagementClient.prototype.webApps.get) - .toBeCalledWith(provider.resourceGroup, slsService["service"]); + .toBeCalledWith(provider.resourceGroup, service.getFunctionAppName()); expect(result).toEqual(app) }); @@ -98,7 +98,7 @@ describe("Function App Service", () => { } as any; const result = await service.get(); expect(WebSiteManagementClient.prototype.webApps.get) - .toBeCalledWith(provider.resourceGroup, slsService["service"]); + .toBeCalledWith(provider.resourceGroup, service.getFunctionAppName()); expect(result).toBeNull(); }); diff --git a/src/services/functionAppService.ts b/src/services/functionAppService.ts index 4b1eb687..1a9740b6 100644 --- a/src/services/functionAppService.ts +++ b/src/services/functionAppService.ts @@ -1,8 +1,6 @@ import fs from "fs"; import path from "path"; import { WebSiteManagementClient } from "@azure/arm-appservice"; -import { ResourceManagementClient } from "@azure/arm-resources"; -import { Deployment } from "@azure/arm-resources/esm/models"; import jsonpath from "jsonpath"; import _ from "lodash"; import Serverless from "serverless"; @@ -13,18 +11,15 @@ import { Guard } from "../shared/guard"; import { ArmService, ArmDeployment } from "./armService"; export class FunctionAppService extends BaseService { - private resourceClient: ResourceManagementClient; private webClient: WebSiteManagementClient; public constructor(serverless: Serverless, options: Serverless.Options) { super(serverless, options); - - this.resourceClient = new ResourceManagementClient(this.credentials, this.subscriptionId); this.webClient = new WebSiteManagementClient(this.credentials, this.subscriptionId); } public async get(): Promise { - const response: any = await this.webClient.webApps.get(this.resourceGroup, this.serviceName); + const response: any = await this.webClient.webApps.get(this.resourceGroup, this.getFunctionAppName()); if (response.error && (response.error.code === "ResourceNotFound" || response.error.code === "ResourceGroupNotFound")) { return null; } @@ -112,7 +107,7 @@ export class FunctionAppService extends BaseService { } public async uploadFunctions(functionApp: Site): Promise { - Guard.null(functionApp); + Guard.null(functionApp, "functionApp"); this.log("Deploying serverless functions..."); await this.zipDeploy(functionApp); @@ -128,10 +123,10 @@ export class FunctionAppService extends BaseService { const armService = new ArmService(this.serverless); let deployment: ArmDeployment; - if (this.serverless.service.provider["armTemplate"]) { - this.log(`-> Deploying custom ARM template: ${this.serverless.service.provider["armTemplate"].file}`); - const templateFilePath = path.join(this.serverless.config.servicePath, this.serverless.service.provider["armTemplate"].file); - const userParameters = this.serverless.service.provider["armTemplate"].parameters; + if (this.config.provider.armTemplate) { + this.log(`-> Deploying custom ARM template: ${this.config.provider.armTemplate.file}`); + const templateFilePath = path.join(this.serverless.config.servicePath, this.config.provider.armTemplate.file); + const userParameters = this.config.provider.armTemplate.parameters; const userParametersKeys = Object.keys(userParameters); let template = JSON.parse(fs.readFileSync(templateFilePath, "utf8")); let parameters: any; @@ -145,7 +140,7 @@ export class FunctionAppService extends BaseService { deployment = { template, parameters }; } else { - deployment = await armService.createDeployment(this.serverless.service.provider["type"] || "consumption"); + deployment = await armService.createDeployment(this.config.provider.type || "consumption"); } // Check if there are custom environment variables defined that need to be