<|start_filename|>documentation/ServicePrincipal/package.json<|end_filename|>
{
  "name": "azure-sp",
  "author": "Microsoft Corporation",
  "contributors": [
    "<NAME> <<EMAIL>>"
  ],
  "private": true,
  "version": "0.1.0",
  "description": "Sample script for creating a service principal in Azure.",
  "tags": [
    "azure",
    "sdk"
  ],
  "keywords": [
    "node",
    "azure"
  ],
  "main": "./spCreate.js",
  "license": "MIT",
  "dependencies": {
    "ms-rest": "^1.15.0",
    "ms-rest-azure": "^1.15.0",
    "azure-graph": "1.1.1",
    "azure-arm-authorization": "3.1.1"
  },
  "homepage": "http://github.com/Azure/azure-sdk-for-node",
  "repository": {
    "type": "git",
    "url": "<EMAIL>:Azure/azure-sdk-for-node.git"
  },
  "bugs": {
    "url": "http://github.com/Azure/azure-sdk-for-node/issues"
  }
}

<|start_filename|>documentation/ServicePrincipal/spCreate.js<|end_filename|>
/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for
 * license information.
 * 
 */

'use strict';

// Different basic role types that can be assigned to a ServicePrincipal.

  ///////////////
 //Contributor//
///////////////                                         
//  Name             : Contributor
//  Id               : b24988ac-6180-42a0-ab88-20f7382dd24c <<<<<<
//  Description      : Lets you manage everything except access to resources.
//  AssignableScopes : 0=/
//  Actions          : 0=*
//  NotActions       : 0=Microsoft.Authorization/*/Delete, 1=Microsoft.Authorization/*/Write
//  IsCustom         : false

  /////////
 //Owner// 
/////////
//  Name             : Owner
//  Id               : 8e3af657-a8ff-443c-a75c-2fe8c4bcb635 <<<<<
//  Description      : Lets you manage everything, including access to resources.
//  AssignableScopes : 0=/
//  Actions          : 0=*
//  NotActions       :
//  IsCustom         : false

  ///////////
 // Reader//
///////////
//  Name             : Reader
//  Id               : acdd72a7-3385-48ef-bd42-f606fba81ae7 <<<<<
//  Description      : Lets you view everything, but not make any changes.
//  AssignableScopes : 0=/
//  Actions          : 0=*/read
//  NotActions       :
//  IsCustom         : false


  //////////
 //Steps://
//////////
// 1. Validating environment variables.
// 2. Creating an AD application
// 3. Creating a ServicePrincipal on top of the AD application
// 4. Assigning the "Contributor" role to the SP created in step 3 at the subscription scope.
// 5. Trying to login as the created SP. We have induced a timeout of 20 seconds. Still this may fail. 
//    However, that is fine. Executing the ServicePrincipal login in a separate script should work after a minute or so.

validate(function () {
  var msrestazure = require('ms-rest-azure');
  var graph = require('azure-graph');
  var authorization = require('azure-arm-authorization');
  var util = require('util');
  var moment = require('moment');

  var tenantId = process.env['AZURE_TENANT_ID'];
  var subscriptionId = process.env['AZURE_SUBSCRIPTION_ID'];
  var passwordForSp = process.env['AZURE_SP_SECRET'];
  var displayName = process.env['AZURE_SP_DISPLAY_NAME'];
  var homepage = 'http://' + displayName + ':8080';
  var identifierUris = [ homepage ];
  var roleId = 'b24988ac-6180-42a0-ab88-20f7382dd24c'; //that of a contributor
  var scope = '/subscriptions/' + subscriptionId; // we shall be assigning the sp, a 'contributor' role at the subscription level
  var roleDefinitionId = scope + '/providers/Microsoft.Authorization/roleDefinitions/' + roleId;
  var loginOptions = {
    domain: tenantId
  };
  msrestazure.interactiveLogin(loginOptions, function(err, creds) {
    if (err) {
      console.log('Error occured in interactive login: \n' + util.inspect(err, { depth: null }));
      return;
    }
    var options = {
      domain: tenantId,
      tokenAudience: 'graph',
      username: creds.username,
      tokenCache: creds.tokenCache,
      environment: creds.environment
    };
    var credsForGraph = new msrestazure.DeviceTokenCredentials(options);
    var graphClient = new graph(credsForGraph, tenantId);
    var startDate = new Date(Date.now());
    var endDate = new Date(startDate.toISOString());
    var m = moment(endDate);
    m.add(1, 'years');
    endDate = new Date(m.toISOString());
    var applicationCreateParameters = {
      availableToOtherTenants: false,
      displayName: displayName,
      homepage: homepage,
      identifierUris: identifierUris,
      passwordCredentials: [{
        startDate: startDate,
        endDate: endDate,
        keyId: msrestazure.generateUuid(),
        value: passwordForSp
      }]
    };
    graphClient.applications.create(applicationCreateParameters, function (err, application, req, res) {
      if (err) {
        console.log('Error occured while creating the application: \n' + util.inspect(err, { depth: null }));
        return;
      }
      
      var servicePrincipalCreateParameters = {
        appId: application.appId,
        accountEnabled: true
      };
      console.log('Underlying Application objectId: ' + application.objectId);
      graphClient.servicePrincipals.create(servicePrincipalCreateParameters, function (err, sp, req, res) {
        if (err) {
          console.log('Error occured while creating the servicePrincipal: \n' + util.inspect(err, { depth: null }));
          return;
        }
        var authzClient = new authorization(creds, subscriptionId, null);
        var assignmentGuid = msrestazure.generateUuid();
        var roleCreateParams = {
          properties: {
            principalId: sp.objectId,
            //have taken this from the comments made above
            roleDefinitionId: roleDefinitionId,
            scope: scope
          }
        };
        
        console.log('>>>>>>>>>>>\nSuccessfully created the servicePrincipal: \n' + util.inspect(sp, { depth: null }) + '\n');
        authzClient.roleAssignments.create(scope, assignmentGuid, roleCreateParams, function (err, roleAssignment, req, res) {
          if (err) {
            console.log('\nError occured while creating the roleAssignment: \n' + util.inspect(err, { depth: null }));
            return;
          }

          console.log('>>>>>>>>>>>\nSuccessfully created the role assignment for the servicePrincipal.\n');
          console.log('>>>>>>>>>>>\nIn future for login you will need the following info:');
          console.log('ServicePrincipal Id (SPN):    ' + sp.appId);
          console.log('ServicePincipal Password:    ' + passwordForSp);
          console.log('Tenant Id for ServicePrincipal:    ' + tenantId);
          console.log('>>>>>>>>>>>\n');
          console.log(util.format('\nTrying to login as the created servicePrincipal.\n\nWaiting for \'20 seconds\' for the changes to reflect. ' + 
            'If this call fails then  please try to login as the servicePrincipal by executing the following script:\n\n' + 
            'var msrestazure = require(\'ms-rest-azure\');\n\n' + 
            'msrestazure.loginWithServicePrincipalSecret(\'%s\', \'%s\', \'%s\', function(err, creds) { if (err) { console.log(err); ' + 
            '} else { console.log(creds); } });',
            sp.appId, passwordForSp, tenantId));
          setTimeout(function(appId, secret, tenantId, callback) {
            msrestazure.loginWithServicePrincipalSecret(sp.appId, passwordForSp, tenantId, callback);
          }, 20000, 
            sp.appId, passwordForSp, tenantId, function (err, spCreds, subs) {
            if (err) {
              console.log('\nError occured during servicePrincipal login: \n' + util.inspect(err, { depth: null }));
              return;
            }

            console.log('\nServicePrincipal login is successful.\n');
            return;
          });
        });
      });
    });
  });
});

function validate(callback) {
  var ids = [];
  var envs = ['AZURE_TENANT_ID', 'AZURE_SUBSCRIPTION_ID', 'AZURE_SP_SECRET', 'AZURE_SP_DISPLAY_NAME'];
  envs.forEach(function (item) {
    if (!process.env[item]) {
      ids.push(item);
    }
  });

  if (ids.length > 0) {
    console.log('Please set the following environment variables:\n\t' + ids.join(', '));
    process.exit();
  }
  callback();
}