Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port to m161: AzureResourceGroupDeployment: Improve docs/error logs to surface SPN details to user #11909

Merged
merged 4 commits into from
Dec 6, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -191,5 +191,7 @@
"loc.messages.LogDeploymentName": "Deployment name is %s",
"loc.messages.ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired.",
"loc.messages.DeploymentGroupConfigurationNotSucceeded": "Deployment group configuration did not succeed on one or more Virtual Machine(s): %s",
"loc.messages.DeploymentGroupConfigurationFailedOnVM": "Failure for Virtual Machine '%s': %s"
"loc.messages.DeploymentGroupConfigurationFailedOnVM": "Failure for Virtual Machine '%s': %s",
"loc.messages.ServicePrincipalRoleAssignmentDetails": "Please make sure the Service Principal with name %s is assigned the right roles for the Resource Group %s. Follow the link for more details: https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal",
"loc.messages.ServicePrincipalFetchFailed": "Error while fetching Service Principal details: %s"
}
2 changes: 2 additions & 0 deletions Tasks/AzureResourceGroupDeploymentV2/Tests/EnablePrereq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_SERVICEPRINCIPALKEY"] = "key";
process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_TENANTID"] = "tenant";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONID"] = "sId";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONNAME"] = "sName";
process.env["ENDPOINT_DATA_AzureRM_GRAPHURL"] = "https://graph.windows.net/";
process.env["ENDPOINT_URL_AzureRM"] = "https://management.azure.com/";
process.env["ENDPOINT_DATA_AzureRM_ENVIRONMENTAUTHORITYURL"] = "https://login.windows.net/";

Expand All @@ -47,4 +48,5 @@ tr.registerMock('azure-arm-rest-v2/webRequestUtility', require('./mock_node_modu
tr.registerMock('azure-arm-rest-v2/azure-arm-compute', require('./mock_node_modules/azure-arm-compute'));
tr.registerMock('azure-arm-rest-v2/azure-arm-network', require('./mock_node_modules/azure-arm-network'));
tr.registerMock('azure-arm-rest-v2/azure-arm-resource', require('./mock_node_modules/azure-arm-resource'));
tr.registerMock('azure-arm-rest-v2/azure-graph', require('./mock_node_modules/azure-graph/azure-graph'));
tr.run();
2 changes: 2 additions & 0 deletions Tasks/AzureResourceGroupDeploymentV2/Tests/VMOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_SERVICEPRINCIPALKEY"] = "key";
process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_TENANTID"] = "tenant";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONID"] = "sId";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONNAME"] = "sName";
process.env["ENDPOINT_DATA_AzureRM_GRAPHURL"] = "https://graph.windows.net/";
process.env["ENDPOINT_URL_AzureRM"] = "https://management.azure.com/";
process.env["ENDPOINT_DATA_AzureRM_ENVIRONMENTAUTHORITYURL"] = "https://login.windows.net/";
process.env["ENDPOINT_DATA_AzureRM_ACTIVEDIRECTORYSERVICEENDPOINTRESOURCEID"] = "https://management.azure.com";

tr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-task-lib/mock-toolrunner'));
tr.registerMock('azure-arm-rest-v2/azure-arm-compute', require('./mock_node_modules/azure-arm-compute'));
tr.registerMock('azure-arm-rest-v2/azure-graph', require('./mock_node_modules/azure-graph/azure-graph'));
tr.run();
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_SERVICEPRINCIPALKEY"] = "key";
process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_TENANTID"] = "tenant";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONID"] = "sId";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONNAME"] = "sName";
process.env["ENDPOINT_DATA_AzureRM_GRAPHURL"] = "https://graph.windows.net/";;
process.env["ENDPOINT_URL_AzureRM"] = "https://management.azure.com/";
process.env["ENDPOINT_DATA_AzureRM_ENVIRONMENTAUTHORITYURL"] = "https://login.windows.net/";
process.env["ENDPOINT_URL_PatEndpoint"] = "https://testking123.visualstudio.com";
Expand Down Expand Up @@ -55,6 +56,7 @@ tr.registerMock('azure-arm-rest-v2/webRequestUtility', require('./mock_node_modu
tr.registerMock('azure-arm-rest-v2/azure-arm-network', require('./mock_node_modules/azure-arm-network'));
tr.registerMock('azure-arm-rest-v2/azure-arm-resource', require('./mock_node_modules/azure-arm-resource'));
tr.registerMock('azure-arm-rest-v2/azure-arm-compute', require('./mock_node_modules/azure-arm-compute'));
tr.registerMock('azure-arm-rest-v2/azure-graph', require('./mock_node_modules/azure-graph/azure-graph'));


tr.run();
2 changes: 2 additions & 0 deletions Tasks/AzureResourceGroupDeploymentV2/Tests/createOrUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_SERVICEPRINCIPALKEY"] = "key";
process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_TENANTID"] = "tenant";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONID"] = "sId";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONNAME"] = "sName";
process.env["ENDPOINT_DATA_AzureRM_GRAPHURL"] = "https://graph.windows.net/";
process.env["ENDPOINT_URL_AzureRM"] = "https://management.azure.com/";
process.env["ENDPOINT_DATA_AzureRM_ENVIRONMENTAUTHORITYURL"] = "https://login.windows.net/";
process.env["ENDPOINT_DATA_AzureRM_ACTIVEDIRECTORYSERVICEENDPOINTRESOURCEID"] = "https://management.azure.com";
Expand All @@ -49,4 +50,5 @@ tr.setAnswers(a);

tr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-task-lib/mock-toolrunner'));
tr.registerMock('azure-arm-rest-v2/azure-arm-resource', require('./mock_node_modules/azure-arm-resource'));
tr.registerMock('azure-arm-rest-v2/azure-graph', require('./mock_node_modules/azure-graph/azure-graph'));
tr.run();
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_SERVICEPRINCIPALKEY"] = "key";
process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_TENANTID"] = "tenant";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONID"] = "sId";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONNAME"] = "sName";
process.env["ENDPOINT_DATA_AzureRM_GRAPHURL"] = "https://graph.windows.net/";
process.env["ENDPOINT_URL_AzureRM"] = "https://management.azure.com/";
process.env["ENDPOINT_DATA_AzureRM_ENVIRONMENTAUTHORITYURL"] = "https://login.windows.net/";
process.env["ENDPOINT_DATA_AzureRM_ACTIVEDIRECTORYSERVICEENDPOINTRESOURCEID"] = "https://management.azure.com";

tr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-task-lib/mock-toolrunner'));
tr.registerMock('azure-arm-rest-v2/azure-arm-resource', require('./mock_node_modules/azure-arm-resource'));
tr.registerMock('azure-arm-rest-v2/azure-graph', require('./mock_node_modules/azure-graph/azure-graph'));
tr.run();
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_SERVICEPRINCIPALKEY"] = "key";
process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_TENANTID"] = "tenant";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONID"] = "sId";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONNAME"] = "sName";
process.env["ENDPOINT_DATA_AzureRM_GRAPHURL"] = "https://graph.windows.net/";
process.env["ENDPOINT_URL_AzureRM"] = "https://management.azure.com/";
process.env["ENDPOINT_DATA_AzureRM_ENVIRONMENTAUTHORITYURL"] = "https://login.windows.net/";
process.env["ENDPOINT_DATA_AzureRM_ACTIVEDIRECTORYSERVICEENDPOINTRESOURCEID"] = "https://management.azure.com";
Expand All @@ -25,4 +26,5 @@ tr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-
tr.registerMock('azure-arm-rest-v2/azure-arm-resource', require('./mock_node_modules/azure-arm-resource'));
tr.registerMock('azure-arm-rest-v2/azure-arm-compute', require('./mock_node_modules/azure-arm-compute'));
tr.registerMock('azure-arm-rest-v2/azure-arm-network', require('./mock_node_modules/azure-arm-network'));
tr.registerMock('azure-arm-rest-v2/azure-graph', require('./mock_node_modules/azure-graph/azure-graph'));
tr.run();

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_SERVICEPRINCIPALKEY"] = "key";
process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_TENANTID"] = "tenant";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONID"] = "sId";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONNAME"] = "sName";
process.env["ENDPOINT_DATA_AzureRM_GRAPHURL"] = "https://graph.windows.net/";
process.env["ENDPOINT_URL_AzureRM"] = "https://management.azure.com/";
process.env["ENDPOINT_DATA_AzureRM_ENVIRONMENTAUTHORITYURL"] = "https://login.windows.net/";
process.env["ENDPOINT_DATA_AzureRM_ACTIVEDIRECTORYSERVICEENDPOINTRESOURCEID"] = "https://management.azure.com";
Expand All @@ -25,4 +26,5 @@ tr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-
tr.registerMock('azure-arm-rest-v2/azure-arm-resource', require('./mock_node_modules/azure-arm-resource'));
tr.registerMock('azure-arm-rest-v2/azure-arm-compute', require('./mock_node_modules/azure-arm-compute'));
tr.registerMock('azure-arm-rest-v2/azure-arm-network', require('./mock_node_modules/azure-arm-network'));
tr.registerMock('azure-arm-rest-v2/azure-graph', require('./mock_node_modules/azure-graph/azure-graph'));
tr.run();
14 changes: 14 additions & 0 deletions Tasks/AzureResourceGroupDeploymentV2/models/DeployAzureRG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class AzureRGTaskParameters {
public deploymentName: string;
public deploymentMode: string;
public credentials: msRestAzure.ApplicationTokenCredentials;
public graphCredentials: msRestAzure.ApplicationTokenCredentials;
public deploymentGroupProjectName = "";
public tokenCredentials: TokenCredentials;
public deploymentOutputs: string;
Expand Down Expand Up @@ -95,6 +96,18 @@ export class AzureRGTaskParameters {
throw (msg);
}
}

private _getAzureADGraphCredentials(connectedService: string): msRestAzure.ApplicationTokenCredentials {
var servicePrincipalId: string = tl.getEndpointAuthorizationParameter(connectedService, "serviceprincipalid", false);
var servicePrincipalKey: string = tl.getEndpointAuthorizationParameter(connectedService, "serviceprincipalkey", false);
var tenantId: string = tl.getEndpointAuthorizationParameter(connectedService, "tenantid", false);
var envAuthorityUrl: string = tl.getEndpointDataParameter(connectedService, 'environmentauthorityurl', false);
envAuthorityUrl = (envAuthorityUrl != null) ? envAuthorityUrl : "https://login.windows.net/";
var activeDirectoryResourceId: string = tl.getEndpointDataParameter(connectedService, 'graphUrl', false);
activeDirectoryResourceId = (activeDirectoryResourceId != null) ? activeDirectoryResourceId : "https://graph.windows.net/";
var credentials = new msRestAzure.ApplicationTokenCredentials(servicePrincipalId, tenantId, servicePrincipalKey, activeDirectoryResourceId, envAuthorityUrl, activeDirectoryResourceId, false);
return credentials;
}

private async getARMCredentials(connectedService: string): Promise<msRestAzure.ApplicationTokenCredentials> {
var azureEndpoint = await new AzureRMEndpoint(connectedService).getEndpoint();
Expand Down Expand Up @@ -140,6 +153,7 @@ export class AzureRGTaskParameters {
this.deploymentName = tl.getInput("deploymentName");
this.deploymentMode = tl.getInput("deploymentMode");
this.credentials = await this.getARMCredentials(this.connectedService);
this.graphCredentials = this._getAzureADGraphCredentials(this.connectedService);
this.deploymentGroupProjectName = tl.getInput("project");
this.deploymentOutputs = tl.getInput("deploymentOutputs");
this.addSpnToEnvironment = tl.getBoolInput("addSpnToEnvironment", false);
Expand Down
27 changes: 21 additions & 6 deletions Tasks/AzureResourceGroupDeploymentV2/operations/ResourceGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path = require("path");
import tl = require("azure-pipelines-task-lib/task");
import fs = require("fs");
import util = require("util");

import azureGraph = require("azure-arm-rest-v2/azure-graph");
import env = require("./Environment");
import deployAzureRG = require("../models/DeployAzureRG");
import armResource = require("azure-arm-rest-v2/azure-arm-resource");
Expand Down Expand Up @@ -523,7 +523,7 @@ export class ResourceGroup {
});
}

private async performAzureDeployment(armClient: armResource.ResourceManagementClient, deployment: Deployment, retryCount = 0): Promise<void> {
private async performAzureDeployment(armClient: armResource.ResourceManagementClient, deployment: Deployment, retryCount = 0, spnName: string): Promise<void> {
if (deployment.properties["mode"] === "Validation") {
return this.validateDeployment(armClient, deployment);
} else {
Expand All @@ -534,9 +534,13 @@ export class ResourceGroup {
armClient.deployments.createOrUpdate(this.taskParameters.deploymentName, deployment, (error, result, request, response) => {
if (error) {
if(error.code == "ResourceGroupNotFound" && retryCount > 0){
return this.waitAndPerformAzureDeployment(armClient, deployment, retryCount);
return this.waitAndPerformAzureDeployment(armClient, deployment, retryCount, spnName);
}
this.writeDeploymentErrors(error);
if(error.statusCode == 403) {
tl.error(tl.loc("ServicePrincipalRoleAssignmentDetails", spnName, this.taskParameters.resourceGroupName));
}

return reject(tl.loc("CreateTemplateDeploymentFailed"));
}
if (result && result["properties"] && result["properties"]["outputs"] && utils.isNonEmpty(this.taskParameters.deploymentOutputs)) {
Expand All @@ -551,9 +555,9 @@ export class ResourceGroup {
}
}

private async waitAndPerformAzureDeployment(armClient: armResource.ResourceManagementClient, deployment: Deployment, retryCount): Promise<void> {
private async waitAndPerformAzureDeployment(armClient: armResource.ResourceManagementClient, deployment: Deployment, retryCount, spnName: string): Promise<void> {
await sleepFor(3);
return this.performAzureDeployment(armClient, deployment, retryCount - 1);
return this.performAzureDeployment(armClient, deployment, retryCount - 1, spnName);
}

private async createTemplateDeployment(armClient: armResource.ResourceManagementClient) {
Expand All @@ -566,7 +570,18 @@ export class ResourceGroup {
} else {
throw new Error(tl.loc("InvalidTemplateLocation"));
}
await this.performAzureDeployment(armClient, deployment, 3);
await this.performAzureDeployment(armClient, deployment, 3, await this.getServicePrincipalName());
}

protected async getServicePrincipalName(): Promise<string> {
try {
var graphClient: azureGraph.GraphManagementClient = new azureGraph.GraphManagementClient(this.taskParameters.graphCredentials);
var servicePrincipalObject = await graphClient.servicePrincipals.GetServicePrincipal(null);
return !!servicePrincipalObject ? servicePrincipalObject.appDisplayName : "";
} catch (error) {
tl.debug(tl.loc("ServicePrincipalFetchFailed", error));
return "";
}
}

private escapeBlockCharacters(str: string): string {
Expand Down
6 changes: 4 additions & 2 deletions Tasks/AzureResourceGroupDeploymentV2/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"version": {
"Major": 2,
"Minor": 157,
"Patch": 7
"Patch": 13
},
"demands": [],
"minimumAgentVersion": "2.119.1",
Expand Down Expand Up @@ -480,6 +480,8 @@
"LogDeploymentName": "Deployment name is %s",
"ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired.",
"DeploymentGroupConfigurationNotSucceeded": "Deployment group configuration did not succeed on one or more Virtual Machine(s): %s",
"DeploymentGroupConfigurationFailedOnVM": "Failure for Virtual Machine '%s': %s"
"DeploymentGroupConfigurationFailedOnVM": "Failure for Virtual Machine '%s': %s",
"ServicePrincipalRoleAssignmentDetails": "Please make sure the Service Principal with name %s is assigned the right roles for the Resource Group %s. Follow the link for more details: https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal",
"ServicePrincipalFetchFailed": "Error while fetching Service Principal details: %s"
}
}
6 changes: 4 additions & 2 deletions Tasks/AzureResourceGroupDeploymentV2/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"version": {
"Major": 2,
"Minor": 157,
"Patch": 7
"Patch": 13
},
"demands": [],
"minimumAgentVersion": "2.119.1",
Expand Down Expand Up @@ -480,6 +480,8 @@
"LogDeploymentName": "ms-resource:loc.messages.LogDeploymentName",
"ExpiredServicePrincipal": "ms-resource:loc.messages.ExpiredServicePrincipal",
"DeploymentGroupConfigurationNotSucceeded": "ms-resource:loc.messages.DeploymentGroupConfigurationNotSucceeded",
"DeploymentGroupConfigurationFailedOnVM": "ms-resource:loc.messages.DeploymentGroupConfigurationFailedOnVM"
"DeploymentGroupConfigurationFailedOnVM": "ms-resource:loc.messages.DeploymentGroupConfigurationFailedOnVM",
"ServicePrincipalRoleAssignmentDetails": "ms-resource:loc.messages.ServicePrincipalRoleAssignmentDetails",
"ServicePrincipalFetchFailed": "ms-resource:loc.messages.ServicePrincipalFetchFailed"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,7 @@
"loc.messages.ResourceGroupNameNotProvided": "Resource Group name should be provided",
"loc.messages.LocationNotProvided": "Location is required for deployment",
"loc.messages.ARMServiceConnectionScope": "ARM Service Conection deployment scope - %s",
"loc.messages.CompleteDeploymentModeNotSupported": "Deployment mode 'Complete' is not supported for deployment at '%s' scope"
"loc.messages.CompleteDeploymentModeNotSupported": "Deployment mode 'Complete' is not supported for deployment at '%s' scope",
"loc.messages.ServicePrincipalRoleAssignmentDetails": "Please make sure the Service Principal with name %s is assigned the right roles for the entity %s. Follow the link for more details: https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal",
"loc.messages.ServicePrincipalFetchFailed": "Error while fetching Service Principal details: %s"
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_SERVICEPRINCIPALKEY"] = "key";
process.env["ENDPOINT_AUTH_PARAMETER_AzureRM_TENANTID"] = "tenant";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONID"] = "sId";
process.env["ENDPOINT_DATA_AzureRM_SUBSCRIPTIONNAME"] = "sName";
process.env["ENDPOINT_DATA_AzureRM_GRAPHURL"] = "https://graph.windows.net/";
process.env["ENDPOINT_URL_AzureRM"] = "https://management.azure.com/";
process.env["ENDPOINT_DATA_AzureRM_ENVIRONMENTAUTHORITYURL"] = "https://login.windows.net/";
process.env["ENDPOINT_DATA_AzureRM_ACTIVEDIRECTORYSERVICEENDPOINTRESOURCEID"] = "https://management.azure.com";
Expand All @@ -49,4 +50,5 @@ tr.setAnswers(a);

tr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-task-lib/mock-toolrunner'));
tr.registerMock('azure-arm-rest-v2/azure-arm-resource', require('./mock_node_modules/azure-arm-resource'));
tr.registerMock('azure-arm-rest-v2/azure-graph', require('./mock_node_modules/azure-graph/azure-graph'));
tr.run();
Loading