Quantcast
Channel: Fabric Controller
Viewing all articles
Browse latest Browse all 18

Using ADAL and the Azure Resource Manager REST API from within a Webtask

$
0
0
Using ADAL and the  Azure Resource Manager REST API from within a Webtask

In my previous blog posts I already covered a few interesting use cases for the Webtask platform. The goal for this post is to lay a foundation that we can use in a next series of posts to do some cool things with Azure Resource Manager.

This is why I want to start by showing a simple sample that allows us to authenticate with ADAL and then call the Azure Resource Manager REST API to list a few resources.

Credentials for the Resource Manager REST API

The easiest way to authenticate is through a Service Principal. There are different ways to create one, but I'll go ahead and use the azure-cli.

First we'll need to authenticate in the cli (note that I'm using the --username parameter because I want to authenticate in my shell with an Azure AD user, not with a Microsoft Account)

npm install -g azure-cli  
azure config mode arm  
azure login --username something@something.onmicrosoft.com  

Then I'll list my accounts (in case I have multiple) and choose my current account. Also take note of the tenantId because you'll need it later on.

azure account list --json  
azure account set e5a1d0a0-c791-...  

Using ADAL and the  Azure Resource Manager REST API from within a Webtask

The next step is to create an Azure AD application for our Service Principal:

azure ad app create --name "Webtask Service Princpal" --home-page "http://mycompany.com" --identifier-uris "http://mycompany.com/webtask" --password A_STRONG_PASSWORD

info:    Executing command ad app create  
+ Creating application Webtask Service Princpal
data:    Application Id:          01199b2a-97a7-43ec-...  
data:    Application Object Id:   c24541a2-d2e1-41c6-...  
data:    Application Permissions:  
...
info:    ad app create command OK  

This command will return the Application Id which we'll need to create the Service Principal.

And now finally you can create the Service Principal and assign it the right permissions (eg: the Contributor role if you want it to make changes to your account).

azure ad sp create 01199b2a-97a7-...

azure role assignment create --spn "http://mycompany.com/webtask" --roleName "Contributor" --subscription e5a1d0a0-c791-...  
azure role assignment list --spn "http://mycompany.com/webtask"  

In order to proceed we'll need to gather the following information from the previous steps:

  • The Tenant Id (which you got from azure account list --json)
  • The Subscription Id (which you got from azure account list --json)
  • The Azure AD Client Id (the Application ID)
  • The Service Principal Password (the password when you created the Azure AD application)

A simple Webtask

So the goal is that we take this information and send it to a Webtask to have it do something with the Resource Manager API. In this sample we'll just send the info in the querystring, but if you look at my previous blog posts you'll also see how you can use the Encrypted Context of a Webtask to securely store the information.

And here is the code:

"use npm";
"use latest";

const _ = require('lodash');  
const adal = require('adal-node');  
const request = require('request');

const authenticate = (tenantId, clientId, servicePrincipalPassword, cb) => {  
  const context = new adal.AuthenticationContext(`https://login.windows.net/${tenantId}`);
  context.acquireTokenWithClientCredentials(`https://management.azure.com/`, clientId, servicePrincipalPassword, (err, res) => {
    if (err) {
      return cb(err);
    }

    return cb(null, res.accessToken);
  });
}

module.exports = (ctx, done) => {  
  let settings = ['AD_CLIENT_ID', 'AD_SERVICE_PRINCIPAL_PASSWORD', 'AD_TENANT_ID', 'AZURE_SUBSCRIPTION_ID'];
  let missing_settings = settings.filter((setting) => !ctx.data[setting]);
  if (missing_settings.length) {
    return done({ message: 'Missing settings: ' + missing_settings.join(', ') });
  }

  authenticate(ctx.data.AD_TENANT_ID, ctx.data.AD_CLIENT_ID, ctx.data.AD_SERVICE_PRINCIPAL_PASSWORD, (err, accessToken) => {
    if (err) {
      return done({
        message: 'Error authenticating.',
        err: err
      });
    }

    const options = {
      headers: {
        'Authorization': `Bearer ${accessToken}`
      },
      json: true,
      url: `https://management.azure.com/subscriptions/${ctx.data.AZURE_SUBSCRIPTION_ID}/resources?api-version=2015-01-01`
    };

    request.get(options, (err, res, body) => {
      if (err) {
        return done({ err: err });
      }

      if (res.statusCode !== 200) {
        return done({
          message: 'Error loading resources',
          status: res.statusCode,
          error: body
        });
      }

      // This contains all of your resources.
      return done(null, _.take(body.value, 5));
    });
  });
};

The full code is available on GitHub: https://github.com/sandrinodimattia/adal-azure-resource-manager-webtask

The authenticate method will use ADAL to get an accessToken which is then used to make a request to the Resource Manager REST API. We then return the first 5 resources we receive from that API call. That's it.

Now an important thing to note here is the "use npm"; directive, which allows us to require any NPM module out there. It will install any NPM module which is not available in the Webtask platform in real time. This does come with a performance hit, but still it allows us to use any module without having to wait for it to be available in Webtask.

Using the Webtask

So now I can just create the Webtask as follows:

npm i -g wt-cli  
wt init  
wt create https://raw.githubusercontent.com/sandrinodimattia/adal-azure-resource-manager-webtask/master/task.js --name azure-rm-sample  

This will return a url to which I can just append the required settings:

https://webtask.it.auth0.com/api/run/wt-MY_WEBTASK_ACCOUNT/azure-rm-sample?AD_CLIENT_ID=YOUR_CLIENT_ID&AD_SERVICE_PRINCIPAL_PASSWORD=YOUR_PASSWORD&AD_TENANT_ID=YOUR_TENANT_ID&AZURE_SUBSCRIPTION_ID=YOUR_SUBSCRIPTION_ID  

Finally when navigating to this page or calling it from Postman/curl/... I'll see my five first resources:

[
  {
    "id": "/subscriptions/e5a1d0a0-c791-xxxxxxxx/resourceGroups/a0-something/providers/Microsoft.ClassicCompute/domainNames/something-adfs",
    "name": "something-adfs",
    "type": "Microsoft.ClassicCompute/domainNames",
    "location": "westus"
  },
  {
    "id": "/subscriptions/e5a1d0a0-c791-xxxxxxxx/resourceGroups/a0-something/providers/Microsoft.ClassicCompute/domainNames/something-dc",
    "name": "something-dc",
    "type": "Microsoft.ClassicCompute/domainNames",
    "location": "westus"
  },
  {
    "id": "/subscriptions/e5a1d0a0-c791-xxxxxxxx/resourceGroups/a0-something/providers/Microsoft.ClassicCompute/domainNames/something-w5yobqsa",
    "name": "something-w5yobqsa",
    "type": "Microsoft.ClassicCompute/domainNames",
    "location": "westus"
  },
  {
    "id": "/subscriptions/e5a1d0a0-c791-xxxxxxxx/resourceGroups/a0-something/providers/Microsoft.ClassicCompute/virtualMachines/something-adfs",
    "name": "something-adfs",
    "type": "Microsoft.ClassicCompute/virtualMachines",
    "location": "westus"
  },
  {
    "id": "/subscriptions/e5a1d0a0-c791-xxxxxxxx/resourceGroups/a0-something/providers/Microsoft.ClassicCompute/virtualMachines/something-dc",
    "name": "something-dc",
    "type": "Microsoft.ClassicCompute/virtualMachines",
    "location": "westus"
  }
]

Why do this? Well there are many interesting use cases when we start to use web hooks and cron jobs.

Stay tuned for more!


Viewing all articles
Browse latest Browse all 18

Trending Articles