Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.

Commit 1c6eb50

Browse files
authored
feat: More detailed error message upon ARM deployment failure (#306)
Logs a more detailed error message when an ARM template deployment fails
1 parent 0130622 commit 1c6eb50

File tree

3 files changed

+82
-7
lines changed

3 files changed

+82
-7
lines changed

src/models/azureProvider.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,9 @@ export interface FunctionMetadata {
1818
handler: string;
1919
events: FunctionEvent[];
2020
}
21+
22+
export interface DeploymentExtendedError {
23+
code: string;
24+
message: string;
25+
details?: DeploymentExtendedError[];
26+
}

src/services/armService.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import jsonpath from "jsonpath";
88
import { Deployments } from "@azure/arm-resources";
99
import { Deployment, DeploymentExtended } from "@azure/arm-resources/esm/models";
1010
import { ResourceService } from "./resourceService";
11+
import { DeploymentExtendedError } from "../models/azureProvider";
1112

1213
describe("Arm Service", () => {
1314
let sls: Serverless
@@ -311,5 +312,47 @@ describe("Arm Service", () => {
311312
expect(call[1]).toMatch(expectedDeploymentNameRegex);
312313
expect(call[2]).toEqual(expectedDeployment);
313314
});
315+
316+
it("Throws more detailed error message upon failed ARM deployment", async () => {
317+
Deployments.prototype.createOrUpdate = jest.fn(() => Promise.reject(null));
318+
const lastDeploymentError: DeploymentExtendedError = {
319+
code: "DeploymentFailed",
320+
message: "At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.",
321+
details: [
322+
{
323+
code: "ServiceAlreadyExists",
324+
message: "Api service already exists: abc-123-apim"
325+
},
326+
{
327+
code: "StorageAccountAlreadyTaken",
328+
message: "The storage account named ABC123 is already taken."
329+
}
330+
]
331+
}
332+
ResourceService.prototype.getLastDeployment = jest.fn(() => Promise.resolve({
333+
properties: {
334+
error: lastDeploymentError
335+
}
336+
})) as any;
337+
const deployment: ArmDeployment = {
338+
parameters: MockFactory.createTestParameters(false),
339+
template: MockFactory.createTestArmTemplate()
340+
};
341+
deployment.parameters.param1 = "3"
342+
const { code, message, details } = lastDeploymentError;
343+
let errorPattern = [
344+
code,
345+
message,
346+
details[0].code,
347+
details[0].message,
348+
details[1].code,
349+
details[1].message
350+
].join(".*")
351+
await expect(service.deployTemplate(deployment))
352+
.rejects
353+
.toThrowError(
354+
new RegExp(`.*${errorPattern}.*`,"s")
355+
);
356+
});
314357
});
315358
});

src/services/armService.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Guard } from "../shared/guard";
1010
import { BaseService } from "./baseService";
1111
import { ResourceService } from "./resourceService"
1212
import deepEqual from "deep-equal";
13+
import { DeploymentExtendedError } from "../models/azureProvider";
1314

1415
export class ArmService extends BaseService {
1516
private resourceClient: ResourceManagementClient;
@@ -130,14 +131,39 @@ export class ArmService extends BaseService {
130131
this.log(`---> Resource Group: ${this.resourceGroup}`)
131132
this.log(`---> Deployment Name: ${this.deploymentName}`)
132133

133-
const result = await this.resourceClient.deployments.createOrUpdate(
134-
this.resourceGroup,
135-
this.deploymentName,
136-
armDeployment
137-
);
138-
this.log("-> ARM deployment complete");
134+
try {
135+
const result = await this.resourceClient.deployments.createOrUpdate(
136+
this.resourceGroup,
137+
this.deploymentName,
138+
armDeployment
139+
);
140+
this.log("-> ARM deployment complete");
141+
return result;
142+
} catch (err) {
143+
const lastDeployment = await resourceService.getLastDeployment();
144+
const errorDetails: DeploymentExtendedError = lastDeployment.properties["error"];
145+
if (errorDetails) {
146+
throw new Error(this.deploymentErrorToString(errorDetails));
147+
}
148+
}
149+
}
139150

140-
return result;
151+
private deploymentErrorToString(deploymentError: DeploymentExtendedError) {
152+
if (!deploymentError.code || !deploymentError.message) {
153+
return JSON.stringify(deploymentError);
154+
}
155+
let errorString = `${deploymentError.code} - ${deploymentError.message}`;
156+
if (deploymentError.details) {
157+
158+
errorString += `
159+
------------------------
160+
DEPLOYMENT ERROR DETAILS
161+
------------------------\n`
162+
deploymentError.details.forEach((childError) => {
163+
errorString += `\n${this.deploymentErrorToString(childError)}`
164+
})
165+
}
166+
return errorString;
141167
}
142168

143169
/**

0 commit comments

Comments
 (0)