How to use Microsoft Graph to get office groups listed in a native (Console) application

To get groups from using Microsoft Graph you have to do the following things:

  • Register an application using Azure AD and give required privileges to the application
  • Request a token for your application using the client ID and client secret key
  • Make the API call to using REST by specifying that your realm where to get the groups from

Register your application through Azure AD

Go to https://portal.azure.com/ or https://manage.windowsazure.com

Next from the Azure navigation go to your active directory. Next you should see different options in the top area of the your AAD, such as:

Users, Groups, Applications, Domain and so on.

Select the Applications section.

aad1

Next you should get a listing of applications configured in your AAD.

Next from the AAD UI select ADD to add a new application.

aad2

Select what type of an application it is. You can use both option. I used the configurations of an MVC application which allowed me to work just fine. What matters is the client ID, client secret key and the privileges. The redirect URL is not necessary for a Native Application, you can type in anything as long as it is a URL.

aad4aad5

 

After your application is created go to the configure section.

aad6

Here find the client ID and copy store it somewhere to be used later.

Then go to the keys section and add a new key(client secret)

aad7

Then scroll down and go the applications section and add the Microsoft Graph application and select the needed privileges. In this case, you would like at least to be able to read groups.

aad8aad9

That is it, next is some code.

Request a token for your application

This is the piece of code that will get the token from your application. The constants and parameters will be explained soon.

public static string GetTokenForApplication(String realm, String clientId, String clientSecret)
 {
 AuthenticationContext authenticationContext = new AuthenticationContext(Constants.UnifiedApiConnect.AzureADAuthority + realm, false);
 // Config for OAuth client credentials 
 ClientCredential clientCred = new ClientCredential(clientId, clientSecret);

 AuthenticationResult authenticationResult = authenticationContext.AcquireToken(Constants.UnifiedApiConnect.O365UnifiedAPIResource,
 clientCred);
 string token = authenticationResult.AccessToken;
 return token;
 }

Make the API Call to get the groups

This is the code that will get the token and request data from the Graph API


TokenHelper.Token = Program.GetTokenForApplication(realm, clientId, clientSecret);
 List<Group> groups = new List<Group>();
 string APIURL = Constants.UnifiedApiConnect.O365UnifiedAPIResource + "v1.0/" + realm + "/groups?$filter=groupTypes/any(c:c+eq+'Unified')";
 try
 {
 groups = GroupsHttpHelper.GetGroups(APIURL);
 foreach (var group in groups)
 {
 groupsSite.Add(new SiteDirectoryEntity() { Title = group.displayName, URL = String.Format(Program.O365OutlookMailGroupURL, realm, realmLCID, group.mail) });
 }
 }
 catch (Exception ex)
 {
 Logger.Error("Error in processing O365 groups through MS Graph: " + ex.Message + "\n" + ex.StackTrace);
 }

This is the code that will do the request to the Graph API, notice that the data is deserialized into objects.

 


public class GroupsHttpHelper
 {
 public static List<Group> GetGroups(string apiUrl)
 {
 if (String.IsNullOrEmpty(apiUrl)) { throw new ArgumentNullException("apiUrl"); }

 List<Group> groups = new List<Group>();

 string responseContent = GroupsHttpHelper.GetHttpResource(apiUrl);
 var responseObject = JsonConvert.DeserializeObject<GraphResponse<Group>>(responseContent);
 foreach (var item in responseObject.value)
 {
 groups.Add(item);
 }

 return groups;

 }

 public static string GetHttpResource(string url)
 {
 string responseContent = String.Empty;

 string token = TokenHelper.Token;

 var request = (HttpWebRequest)HttpWebRequest.Create(url);

 request.Method = "GET";
 request.Accept = "application/json";
 request.Headers.Add("Authorization", "Bearer " + token);

 var response = request.GetResponse();
 using (var reader = new StreamReader(response.GetResponseStream()))
 {
 responseContent = reader.ReadToEnd();
 }

 return responseContent;
 }
 }

public class Group
 {
 public string accessType { get; set; }

 public bool? allowExternalSenders { get; set; }

 public bool? autoSubscribeNewMembers { get; set; }
 public string description { get; set; }
 public string displayName { get; set; }
 public string[] groupTypes { get; set; }
 public string id { get; set; } // identifier

 public bool isSubscribedByMail { get; set; }
 public string mail { get; set; }

 public bool? mailEnabled { get; set; }
 public string mailNickname { get; set; }
 public string onPremisesLastSyncDateTime { get; set; } //timestamp
 public string onPremisesSecurityIdentifier { get; set; }

 public bool? onPremisesSyncEnabled { get; set; }
 public string[] proxyAddresses { get; set; }

 public bool? securityEnabled { get; set; }
 public int unseenCount { get; set; }
 public string visibility { get; set; }
 }

Namespaces needed in the code:

using Microsoft.Online.SharePoint.TenantAdministration;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Newtonsoft.Json;

NuGet Packages needed:

Active Directory Authentication Library – Id: Microsoft.IdentityModel.Clients.ActiveDirectory
Json.NET – Id: Newtonsoft.Json

WebConfig Values and parameters in the code

 

<add key=”MSGraphGourps_Realm” value=”yourrealm.com” />

<add key=”ClientId” value=”your client ID in the Azure AD application” />
<add key=”ClientSecret” value=”your client secret key in the Azure AD application” />

The URL format for the authentication context request is something like this: https://login.microsoftonline.com/yourrealm.com

To acquire the token: https://graph.microsoft.com/

To get the groups the URL is: https://graph.microsoft.com/1.0/yourrealm.com/groups

If you are not sure where to get your realm from there are two ways:

  1. Get it from the URL in your browser bar when you are viewing an O365 outlook mail box or group: https://outlook.office.com/owa/?realm=yourrealm.com
  2. The other option is to use the Azure Management UI and again in the url your should find it: https://manage.windowsazure.com/yourrealm.com

 

Getting other information from Groups

One Drive:

https://graph.microsoft.com/v1.0/your realm name/groups/{your group guid}/drive/root

Owners:

https://graph.microsoft.com/v1.0/your realm name/groups/{your group guid}/owners

Group Logo:

 

How to set O365 PageLayout Associated Content Type field

Here is a code snippet to set your page layouts associated content type. This one was a bit weird when I first tried to set it.

 private static void SetPageLayoutMetadata(Web web, File uploadFile, string title, string publishingAssociatedContentType)
{
// Speficy that the page layout is a page layout and not a master page
var parentContentTypeId = ""0x01010007FF3E057FA8AB4AA42FCB67B453FFC100E214EEE741181F4E9F7ACC43278EE811""; //Page Layout
var gallery = web.GetCatalog(116);
web.Context.Load(gallery, g => g.ContentTypes);
web.Context.ExecuteQuery();

var contentTypeId = gallery.ContentTypes.FirstOrDefault(ct => ct.StringId.StartsWith(parentContentTypeId)).StringId;
var item = uploadFile.ListItemAllFields;
web.Context.Load(item);

item["ContentTypeId"] = contentTypeId;
item["Title"] = title;
item["PublishingAssociatedContentType"] = publishingAssociatedContentType;

item.Update();
web.Context.ExecuteQuery();
} 

Two important things:

  • parentContentTypeId: This is the ID that tells O365 that this page layout is actually page layout, this is a static id which is defined in the code
  • publishingAssociatedContentType: This is your actual content type to which you want to associate your page layout to: Example of the format:;#my content type name;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D00A3428808D7DE6A4RABF83921AFB25424;#
    • So the format should be in the following way:

public static String BuildPublishingAssociativeContentTypeId(ContentType contentType)
{
String contentTypeAssociativeID = null;
if(contentType != null)
{
contentTypeAssociativeID = String.Format(";#{0};#{1};#", contentType.Name, contentType.StringId);
}

return contentTypeAssociativeID;
}

O365 OfficeDev PnP CSOM tools installation

This post will assume you have Visual Studio installed with Microsoft Office 365 API tools for Visual Studio.

  • Create a new Visual Studio project, lets say a console application.
  • Press on your project the second mouse button and select “Manage NuGet Packages…”
  • In the new window search for online packages and type in pnp or officedevpnpcore or similar. Then install the OfficeDevPnp.Core.Online package.

OfficePnp1

  • Wait for the installation to complete. After this you are ready to use CSOM to operate on your desired O365 site.

OfficePnp2

As you can see from the image above the nuget package will install all of what you need. You do not have to worry about much. Easy, right :)?

Links to know:

https://github.com/OfficeDev/PnP

https://channel9.msdn.com/blogs/OfficeDevPnP/Getting-Started-with-PnP-Provisioning-Engine

https://github.com/OfficeDev/PnP-Partner-Pack

http://dev.office.com/patterns-and-practices-resources

http://dev.office.com/blogs/new-sharepoint-csom-version-released-for-Office-365

https://www.nuget.org/packages/Microsoft.SharePointOnline.CSOM

https://lionadi.wordpress.com/2015/06/22/office-365-o365-client-application-registration-authentication-and-authorization/

https://lionadi.wordpress.com/2015/07/02/o365-csom-client-side-object-model-examples/

https://lionadi.wordpress.com/2015/09/16/o365-csom-getting-user-profile-information-without-possible-errors/

https://lionadi.wordpress.com/tag/o365/

SharePoint 2013 display templates for content webparts do not work

This problem may occur when you have a custom display template which you are modifying at some stage and updating it into SharePoint. If for some reason your template stops working what you need to do is go to the display template and modify its properties.

There you will find several options where you want this template to be used, such as search, content webparts etc.

So go and check your display templates properties and make sure you have selected where the template is to be used.

Corrupted SharePoint Blog Post.aspx page and how to fix it

This is one possible solution which can also be applied to O365(possibly).

The solution is to re-activate two features related to Blogs site. These features should re-create the broken post.aspx page.

BlogContent $Resources:core,blogContentFeatureTitle; Web 0d1c50f7-0309-431c-adfb-b777d5473a65 $Resources:core,blogContentFeatureDesc;
BlogHomePage Blog Home Page Web e4639bb7-6e95-4e2f-b562-03b832dd4793 Creates the default.aspx page for the a Blog s

O365 – CSOM Check if a user does not exist in your tenant anymore

I had a situation where I needed to make sure if a user does not exist anymore in the O365 system. I had to use CSOM for this solution and no matter how I tried I could not get any indication that a user has been deleted from the system.

I tried the IsActive field and Deleted field of an user object but no luck. No matter what I did even if the user was deleted and the expiration date of 30 days expired the user was still active.

My solution was that I checked to see if CSOM could retrieve a user profile data. If no data could be retrieved then this is an indication that the user profile has been deleted, hence the user has been deleted from the system. That is it. I used a field like account name since I know it must exist for all users.

There may be a “proper” solution for this but I just could not find one no matter how hard I tried(or maybe I just looked in the wrong place 🙂 ). Anyway see my previous post on how to get user profile information on how to do this workaround:

https://lionadi.wordpress.com/2015/09/16/o365-csom-getting-user-profile-information-without-possible-errors/

O365 Add-In(App) Error: “Sorry, only tenant administrators can add or give access to this app.”

On the web there are many answers to this problem mine was simply related to the permission requests in the APP and the user account privileges in O365.

In my case I accidentally requested permission on tenant level while I needed only Site Collection level permission for my app. This lead to situations where my app was visible to be added by someone whom can manage apps in a site BUT did not have permissions to approve permission requests at Tenant level.

So lesson learned: Check your app and user permissions/privileges.