How to get a User Profile Image from the Microsoft Graph

I have been seeing a few people ask how to get a profile image from the Microsoft Graph…. They’re usually just 1 step away from figuring it out.

Most will use the following to get the photo:

The above returns the following information about the image but not the image itself:

"@odata.context": "$metadata#users('48d31887-5fad-4d73-a9f5-3c356e68a038')/photo/$entity",
"@odata.mediaContentType": "image/jpeg",
"@odata.mediaEtag": "\"2E24ABF6\"",
"id": "240X240",
"height": 240,
"width": 240

If you want the image, you need to add “$value” to the end.$value

The above will return your image.


Note: You need a work or school account for this to work.




Office 365, the Microsoft Bot Framework, & Cognitive Services: 50,000 Foot View

Over the summer, I started working with the Microsoft Bot Framework for three reasons:

  1. Bots are a cool technology that has become mainstream
  2. It provides awesome new possibilities in terms of productivity and customer engagement
  3. The company that I work for is making a clear push toward AI and Machine Learning

I’ve been sitting on this blog post for a while because I didn’t want to regurgitate the information that I was sourcing from, then I landed a project that kept me busy, then I agreed to do a presentation on the topic, and finally, I ended up having to build a proof of concept with it.  NOW… I have some time to talk about it.

Building a bot is not overly complicated but building a bot is a little underwhelming if that’s all you do with it.  Integrating artificial intelligence, to me, feels like a must because it provides that next level of interaction that makes it worth while.  AI is what will make your bot feel like you’re not interacting with a bot… at least until it tells you that it doesn’t understand what you’re asking when you say “hello”, but we just need to account for those things.

This post will try not to be a repeat of info that is readily available.  Instead, I’ll walk you through a bot that I built which was a help desk triage bot.  It’s a pure proof of concept that I wouldn’t necessarily put in a production environment, but it shows you what’s possible.

The bot, which I named ANA is made up of 3 major components.

  • First, (and obviously) is the Microsoft Bot Framework which will handle the message handling.
  • Second, I leveraged Microsoft’s Cognitive Services (specifically the Language Understanding Intelligent Service or LUIS) to provide a simple level of interaction.
  • Third, I used the Microsoft Graph to further integrate it into the Office 365 ecosystem.

Setting up ANA

I’ll go over the steps I took to set my bot up but there are a few other resources available for getting started so I won’t do my normal step by step.

First, you can visit the Bot Framework Documentation site which has plenty of good information on getting started.  Really check this out, the bot framework isn’t a simple message handling framework.  It has some cool features like the ability to enter “3 days from now” and it knowing how to parse that text to assign a date value equivalent to 3 days after the current date.

Another source that I saw recently was by @zimmergren (Tobias Zimmergen) who recently did a fairly comprehensive post on building a bot via the Azure Bot Service. I used the steps for creating the bot using the .NET SDK.  I haven’t tried the Azure Bot Service steps as of yet so I can’t recommend an option.


In order to create the bot, you will need Visual Studio 2017.  I tried using VS2015 (because that’s what I had at the time) but the Bot Application template wasn’t available for it.  The nuget package can be found here and here is the Bot Template for Visual Studio.

If you need anything else, you’ll likely find the relevant resources in the Start Building Bots page.

You’ll also want to install the BotFramework-Emulator so that you could connect to your bot without having to deploy.  For information about the emulator, visit the Debug bots with the Bot Framework Emulator page.  You can find the emulator setup file on github.

The Project Setup

Create a new Bot Application project in Visual Studio 2017


Solution Structure

When you create your new Bot Application, you’ll get 2 classes.  The Message Controller and a Root Dialog.  The Dialogs are meant to be a way to compartmentalize your various types of communications.  Your message controller takes the message received by the user and determines what to do with it.  When you create a new project, it’s setup to send it to the RootDialog which does the simple task of counting the number of characters in your message.

public async Task Post([FromBody]Activity activity)
if (activity.Type == ActivityTypes.Message)
//await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
await Conversation.SendAsync(activity, () => MakeDialog());
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;

Your web.config file also has 3 app settings that you’ll need when it’s time to deploy.

<!-- update these with your BotId, Microsoft App Id and your Microsoft App Password-->
<add key="BotId" value="YourBotId" />
<add key="MicrosoftAppId" value="" />
<add key="MicrosoftAppPassword" value="" />

Integrating the Language Understanding Intelligent Service (LUIS)

At this point, you have what amounts to as a Hello, World bot where you can see how things plug into each other.  I suggest you go over the Bot Framework documentation to get the details around creating dialogs and forms.  As you build your bot, you’ll find other things that you may want to incorporate.  For example, you may want to provide a Describe or Prompt attribute to change the way the Bot asks users for input.

If you want your bot to have some intelligence, you’ll want to start incorporating the Cognitive Services.  The Cognitive Services are so cool in my opinion.  They’re fairly simple to use and offer so much potential.

A very high level description of how they work is you feed it some input which will be different depending on which service you use and it returns a score.  That score is used to make ‘predictions’.  I know… that was maybe a bit too high level.  Let’s take a look at how I used it.

Ana has a few dialogs.  Some that I incorporated from demos, others that I created myself once I got the hang of it.  Two of those dialogs are the Greeting dialog and the HelpDesk dialog.

The Greeting dialog will receive a message like ‘hi’ or ‘hello’ and respond with a greeting of its own.  The Help Desk dialog was one that I developed once I got the hang of things.  Its job is to ask you questions about the kind of help desk support that you require and it takes that information and creates a task in Planner.  Again, we’re living in proof of concept land so this wasn’t intended for practical application.

Given that my solution accepts multiple dialogs or multiple types of conversations, a greeting and a help desk request, I need to be able to differentiate “hello” from “i need to submit a help desk ticket” and any variation of that.  This is where the Cognitive Services come in!

In the image below, you’ll see the list of Intents.  So when someone communicates with my bot, my bot will expect the intention to be a Greeting, a Help Desk request, or None which is where unknown requests go.  When this one is hit, that’s when you’re bot will throw it’s hands up… if it had hands.

LUIS Intents

Inside an intent is what is called an Utterance.  An utterance is the sentence that is associated with the intent and what the probability is that the sentence was intended to be.  If we look at the Greeting intent, we can see that there are 2 utterances associated with it.  ‘Hi’ has an 80% chance to be a greeting.  ‘Howdy’ has a 70% chance.

greeting utterance.PNG

Similarly, our help desk ticket has it’s own list of utterances.  In this case “I’d like to submit a help desk ticket” and “I’d like to submit a ticket” are both 100% intended to trigger my help desk ticket logic.

help desk utternance

The Suggested Utterances tab also stores a list of utterances that the service had no idea what to do with.  You can go in there and set the intent on the utterances so that those same words/sentences can also trigger the appropriate logic.

help desk suggested utterance

There’s more to it than just this but I highly recommend you read up on the Cognitive Services.  The site has some cool demos and there are other types of services like image recognition services and others.

LUIS and ANA Start Communicating

The following code starts with a couple of attributes.  LuisModel takes 2 guids and the text below shows you where you can get those.  The 2nd attribute is Serializable which is needed by the bot framework.

There are 3 methods which are also decorated with LuisIntent attributes.  The first has a blank value and this will be something that is caught in the None intent seen in the previous images.  The second intent is the Greeting intent.  This Greeting method will be called if the user enters Hi or Howdy as we saw in the Greeting intent.  Finally, we have the Help Desk Ticket, which is called… yep, you guessed it… when the Help Desk Intent is triggered.  There is also a HelpDeskTicket class that contains my the logic for submitting a ticket which uses the Microsoft Graph to create a task in Planner.

[LuisModel("<APP ID from LUIS Dashboard page>", "<key string from LUIS Publish App page>")]
public class LuisDialog : LuisDialog<object>

internal BuildFormDelegate<HelpDeskTicket> Ticket;

public LuisDialog()


public async Task None(IDialogContext context, LuisResult result)
await context.PostAsync("I'm sorry, I don't know what you mean.");

public async Task Greeting(IDialogContext context, LuisResult result)
context.Call(new GreetingDialog(), Callback);
public async Task Callback(IDialogContext context, IAwaitable<object> result)

public async Task HelpDeskTicket(IDialogContext context, LuisResult result)
if (this.Ticket is null)
this.Ticket = Models.HelpDeskTicket.BuildForm;
var helpDeskForm = new FormDialog<HelpDeskTicket>(new Models.HelpDeskTicket(), this.Ticket, FormOptions.PromptInStart);
context.Call<HelpDeskTicket>(helpDeskForm, Callback);


Data Access with the Microsoft Graph

The HelpDeskTicket class is where I kept my form logic.  Its fairly direct.  First it collects your help desk ticket information, then it authenticates, creates a Planner task object and assigns its values based on the user’s response, then it posts the task.

namespace Bots.Ana.Models
    public enum RequiredService


    public enum Severity

    public class HelpDeskTicket
		// the order of the variables determines the order of the questions
        public RequiredService ServiceRequired;
        public Severity IssueSeverity;
        [Prompt("Tell me about your issue.")]
        public String Description;

        public static IForm<HelpDeskTicket> BuildForm()
			// when the form is completed, run the submission logic
            OnCompletionAsyncDelegate<HelpDeskTicket> submission = async (context, state) =>
               await SubmitTicket(context, state);

            return new FormBuilder<HelpDeskTicket>()
                .Message("Welcome to the help desk ticketing bot!")

        private static async Task SubmitTicket(IDialogContext context, HelpDeskTicket state)
            string token = string.Empty;
            context.UserData.TryGetValue("AccessToken", out token);

            GraphServiceClient graphClient = SDKHelper.GetAuthenticatedClient(token); 

            GraphService service = new GraphService(context);
            var me = await service.GetCurrentUser();

            PlannerTask task = new PlannerTask();

            task.Title = $"{state.ServiceRequired} issue reported by {me.DisplayName}";
            task.PercentComplete = 0;
            task.PlanId = ConfigurationManager.AppSettings["HelpDeskPlanner"].ToString();

            // get the guid for the Planner Bucket from the web.config that matches the severity chosen
            switch (state.IssueSeverity)
                case Severity.High:
                    task.BucketId = ConfigurationManager.AppSettings["HighPriority"].ToString();
                case Severity.Medium:
                    task.BucketId = ConfigurationManager.AppSettings["MediumPriority"].ToString();
                case Severity.Low:
                    task.BucketId = ConfigurationManager.AppSettings["LowPriority"].ToString();
                case Severity.Emergency:
                    task.BucketId = ConfigurationManager.AppSettings["HighPriority"].ToString();
                    task.BucketId = ConfigurationManager.AppSettings["NoPriority"].ToString();

            PlannerTask planTask = await service.CreatePlan(task); 

            await context.PostAsync($"Your ticket has been submitted.  A technician will contact you soon.");


That was your 50,000 ft view of the application. I recently presented it at a user group where I went over the various integration points in Office 365. I showed the Bot running in Microsoft Teams and Skype. Here’s what the bot looks like in action.

First, a look at Microsoft Teams.

ana teams

Next, a look at Skype

ana skype

Issue Uploading Files to Teams

I was recently contacted about an odd issue occurring in Microsoft Teams where users were unable to upload files.  When users would click on the Files tab, they were met with a message “We are setting up your file directory.”   The message stuck around for a few days.

Teams File Dir

In order to fix the problem, you’ll need go to Groups, find the corresponding group for you Team (which will have the same name) and upload a file there.

groups file

After that, the message from the first screenshot will go away and your users will be able to upload files again.


Managing Multiple Copies of Files

Ok, ok, if you absolutely must have copies of the same file living in different libraries in SharePoint, this post will show how you’d manage that.  Keep in mind, you’ll have to maintain the updates which means you’ll need to remember that there are copies out there and you’ll need to follow the steps below to push the changes out to the other copies.

First, select the document that you want copies of and go to More > Properties



Next, click Manage Copies in the ribbon


Click the New Copy button

new copy

You’ll then get to a page that lets you specify the destination library and you can change the name of the file if you’d like.


You’ll then see a confirmation page.  Click OK



Now your documents are linked and if you make a change to the source, you can update the copies by following the same steps as above and instead of clicking New Copy, you’ll click Update Copies (screenshot #3).  That will take you to a page that will let you select which files you wish to update.




SharePoint Framework – Package and Deploy your Solution

In the previous 2 posts, I walked you through the setup of several required packages, talked about the SharePoint Workbench, and provided a script to create a public CDN in SharePoint Online.  I also avoided showing the solution.  The code itself isn’t important, but the structure is, so let’s take a look at that while we walk through bundling, packaging, and deploying.

The files that are important to packaging and deploying can be found in the config folder.


Bundle & Deploy

You’ll need to specify a cdn base path for the solution; otherwise, you’ll see that the webpart will try to reference files from the localhost instead of the destination location.  (In a previous post, I provide a script to create a CDN in SharePoint Online).  If you have a CDN in place, open the write-manifests.json file and provide the URL.


You can bundle the solution files with the following command:

gulp bundle --ship

Once you run the previous command, you’ll see a new Deploy folder was created inside temp. You’ll want to upload the files from the Deploy folder to the CDN that you’ve specified.
temp folder

So now you’ve told the solution where it’s going to get it’s content files and it’s time to deploy.

The solution package for an SPFx solution uses yet another new extension (sppkg).  In order to create the package, we invoke the package-solution.json. The file will contain a path where your solution will be deployed. In this case, my solution will be added to “SharePoint/Solution” and will be given the name sp-fx.sppkg.

To create that package, you’ll need the following PowerShell command:

gulp package-solution --ship

Next, take your sppkg file and upload it to your app catalog. You’ll notice that the CDN path appears in the modal to show you where the content for that solution will be referenced from.


Note: If you omit the “–ship” from the powershell commands above, you’ll create packages for debugging and not production. You’ll know that you did this if you see localhost as the content location instead of your CDN.

Now, your solution has been deployed and you’ll just need to add it. If you go to Site Contents > Add an App, you’ll find your web part. Finally, you can add your web part to the page the same way you’re used to adding web parts.Add a webpart

… And that’s pretty much it.

Setting up the Office 365 Public CDN in SharePoint Online

If you’re looking to set up the Office 365 Public CDN in SharePoint Online, you can use the following PowerShell scripts.  I installed the June 2017 Release of the SharePointPnP.PowerShell Commands for this post.

After you run the script, your changes will take affect within 15 minutes.  The files stored in the CDN can only be accessed within the context of SharePoint so if you try to browse to the CDN url for your asset, it will not work; however, if you reference the same url in SharePoint, it will work.

The CDN url will look like:

$spo = Read-Host 'Enter your SPO Service URL. (example https://<tenant>'
$origin = Read-Host 'Enter the url to the CDN.  (example https://<tenant>'

connect-sposervice $spo
$tenant = Get-SPOTenant

Set-SPOTenant -PublicCdnEnabled $true

$tenant = Get-SPOTenant
Write-host 'Public Cnd Enabled = ' $tenant.PublicCdnEnabled 

Write-host 'Allowed File Types = ' $t.PublicCdnAllowedFileTypes

New-SPOPublicCdnOrigin -url $origin

If you need to get the Origin ID, you can find it with the following script.

$tenant = Get-SPOTenant

Updated: February 13, 2018

The above PowerShell works with a previous version of the SharePoint Management Shell.  As of this update, we are on version 16.0.7317.1200.  There are some new commands.

$spo = Read-Host 'Enter your SPO Service URL. (example https://<tenant>'

$origin = Read-Host 'Enter the url to the CDN. (example sites/Media/CDNLibrary)'

connect-sposervice $spo

#This may take up to 15 minutes to update

Set-SPOTenantCdnEnabled -CdnType Public

#This may also take up to 15 minutes to update

Add-SPOTenantCdnOrigin -CdnType Public -OriginUrl $origin


Hello, SharePoint Framework (SPFx): Getting Started – Quick and Dirty

I know, I know… there’s github, pluralsight, PnP, and a plethora of videos and sites where you can find info on getting started with SPFx.  We’ll just add this one to the list.  This is a quick and dirty getting started guide.  I’ll also provide some references for you to dive deeply into related topics.


What do I need to install?

For starters, you need to install Node.js. Get yourself the LTS version.  nodejs

Once you have that installed, you can open up a command shell of your choice.  I’ll be using PowerShell.  You’re going to want to install the node package manager (npm) globally so you can use it from the command line for all of your projects.  In order to do that, you’ll just need to run the following command in PowerShell or whatever command shell you decided to use:

npm install npm -g

If the above powershell looks a little weird to you, what we’re saying in english is that we want to use the node package manager to install the latest node package manager globally.

Next up is the Yeoman Generator.  This is a handy little tool that creates your SharePoint project for you.  So we need to go back to your trusty npm and install yeoman globally.

 npm install yo -g 

Once you have the Yeoman generator, you’ll want the SharePoint template generator.

 npm install @microsoft/generator-sharepoint -g 

Next up, Gulp.  Now, gulp is a task automation tool.  We’re basically going to use it to run our solution.

 npm install gulp -g 

There’s one last package that needs to be installed and that’s webpack, but I’ll skip it for now.  We’ll set that up when it’s time to deploy our solution.

First Web Part

Now that we have (almost) everything that we need installed, we can start with our project.  The installs above were 1 time installs since we specified that they were each to be globally installed.

The first thing that we want to do is create the directory where our solution will live.  So in PowerShell, I go to the base directory where I store all of my solutions and then I type the following command to create a directory called “SPFx” (call it whatever you want):

 md SPFx 

The above is the mkdir command so you could type the following and it’s exactly the same:

 mkdir SPFx 

Then we want to go to that newly created directory and run our Yeoman generator to create the project files. (At this point, you should be in your project path in PowerShell)

 yo @microsoft/sharepoint

Next, the generator will start asking you for details like what do you want to call your solution, are you building a web part or an extension, names, descriptions, and what JavaScript library you want to use. (The version of the generator at the time of this post is Version Depending on which version you’re on, you may need to provide different inputs.

Here are my inputs. Once I hit enter, it starts installing a ton of stuff so you have time to check your email or maybe get a beverage of your choice.
sp yeoman

And this is what you’ll see when it’s done.  If you look inside the box in the image, it tells you what you need to “start” your solution.  You can go look at the 252 MB of SharePointy goodness that get’s created in the directory above.  (The size on disk is 297 MB).  I’ll let you inspect the files but for our purposes, we just want to run it.  This part I find kind of cool.  You DON’T need SharePoint installed on your machine for this.

sp yeoman end

Type the following to launch your solution inside the SharePoint Workbench!

 gulp serve 

There are a few things to note here.
First, this isn’t SharePoint but it functions like it in that you can click on the plus symbol to add a new web part. You’ll also see a few buttons at the top that will simulate what the page will look like on a mobile device or tablet.


Also, you may notice a certificate error and if you pay attention to the last bit of text outputted by the gulp serve command, you’ll see a warning regarding this. The workbench is using https and won’t load your scripts so you’ll want to run 1 more gulp command which we’ll do next, but first, I’ll add my web part to the page.

hello world workbench.PNG

Run the next command to fix the cert issue. The command will install a certificate. You’ll also get a prompt to confirm and the next time you run gulp serve, you won’t see the cert issue.

 gulp trust-dev-cert 

There’s something pretty cool about the workbench. If you have it running, you can go to your SharePoint Online and test out the page from there. Just go to your site and append /_layouts/15/workbench.aspx.  (Example: https://<tenant&gt;

That’s what you need to get started. To recap, we ran a few 1 time installs (nodejs, npm, gulp, yeoman, and the SharePoint generator). We created a solution using the yeoman generator, and we took a look at the SharePoint Workbench.

This was meant to be a quick and dirty starter guide (mainly for me). Below are additional resources if you want to start diving deeper into some of the concepts mentioned above. In another post, I’ll talk about webpack and deploy a solution.

Additional Resources