Thursday, 29 September 2016

Read User Profile Properties From SharePoint Online Using CSOM

We may required to retrieve SharePoint Online user profile properties, such as display name, email, title, manager and other business and personal information. In this article, I am going write C# code to get user profile properties programmatically using CSOM. The SharePoint Online SDK assembly Microsoft.SharePoint.Client.UserProfiles.dll includes the class PeopleManager and the PeopleManager class includes the following methods.
  • GetMyProperties - This method returns current user properties.
  • GetPropertiesFor - This method returns all profile properties for a specific person. 
  • GetUserProfilePropertiesFor -This method returns only required properties that you specify
Before proceed, you need to add the assembly reference Microsoft.SharePoint.Client.UserProfiles.dll and include following namespace
using Microsoft.SharePoint.Client; 
using Microsoft.SharePoint.Client.UserProfiles;

Read Current User Profile Properties

First initialize the PeopleManager object and use GetMyProperties() method to retrieve current user profile properties.
public static void GetCurrentUserProfileProperties() 
{ 
    string siteUrl = "https://spotenant-admin.sharepoint.com"; 
 
    // Connect to the sharepoint site client context. 
    ClientContext clientContext = new ClientContext(siteUrl); 
    //clientContext.Credentials = credentials
 
    // Get the PeopleManager object and then get the current user's properties. 
    PeopleManager peopleManager = new PeopleManager(clientContext); 
    PersonProperties myProperties = peopleManager.GetMyProperties(); 
             
    // This request load the AccountName and UserProfileProperties 
    clientContext.Load(myProperties, p => p.AccountName, p => p.UserProfileProperties); 
    clientContext.ExecuteQuery(); 
 
    foreach (var property in myProperties.UserProfileProperties) 
    { 
        Console.WriteLine(string.Format("{0}: {1}", 
            property.Key.ToString(), property.Value.ToString())); 
    } 
}

Get Specific User Profile Properties

To get the specified user profile information, we can use GetPropertiesFor() method by passing target user's account id. The account of the user, should formatted either as a login name, or as a claims identity, e.g. i:0#.f|membership|admin@spotenant.onmicrosoft.com;
public static void GetTargetUserProfileProperties() 
{ 
    string siteUrl = "https://spotenant-admin.sharepoint.com"; 
    string targetUser = "i:0#.f|membership|alexd@spotenant.onmicrosoft.com"; 
 
    // Connect to the sharepoint site client context. 
    ClientContext clientContext = new ClientContext(siteUrl); 
    // clientContext.Credentials = your credentials
 
    // Get the PeopleManager object and then get the target user's properties. 
    PeopleManager peopleManager = new PeopleManager(clientContext); 
    PersonProperties userProperties = peopleManager.GetPropertiesFor(targetUser); 
 
    // This request load the AccountName and user's all other Profile Properties 
    clientContext.Load(userProperties, p => p.AccountName, p => p.UserProfileProperties); 
    clientContext.ExecuteQuery(); 
 
    foreach (var property in userProperties.UserProfileProperties) 
    { 
        Console.WriteLine(string.Format("{0}: {1}", 
            property.Key.ToString(), property.Value.ToString())); 
    } 
}

Get Only Required Profile Properties

You can use the following C# code to get only specific set of user properties.
public static void GetSpecificProfileProperties() 
{ 
    string siteUrl = "https://spotenant-admin.sharepoint.com"; 
    string targetUser = "i:0#.f|membership|alexd@spotenant.onmicrosoft.com"; 
 
    // Connect to the sharepoint site client context. 
    ClientContext clientContext = new ClientContext(siteUrl); 
    // clientContext.Credentials = your credentials
 
    // Get the PeopleManager object. 
    PeopleManager peopleManager = new PeopleManager(clientContext); 
    // Retrieve specific properties by using the GetUserProfilePropertiesFor method.  
    string[] profilePropertyNames = new string[] { "Manager", "Department", "Title" }; 
    UserProfilePropertiesForUser profilePropertiesForUser = new UserProfilePropertiesForUser( 
        clientContext, targetUser, profilePropertyNames); 

    IEnumerable<string> profilePropertyValues = peopleManager.GetUserProfilePropertiesFor(profilePropertiesForUser); 
  
    // Load the request for the set of properties. 
    clientContext.Load(profilePropertiesForUser); 
    clientContext.ExecuteQuery(); 
 
    // Returned collection contains only property values 
    foreach (var value in profilePropertyValues) 
    { 
        Console.WriteLine(value); 
    } 
}

Wednesday, 28 September 2016

Find Last Logon Time for Office 365 Users using Powershell

Getting last logon date of all Office 365 Mailbox enabled users is one of the important task to track user logon activity and find inactive users to calculate the Exchange Online license usage. We can use the Exchange Online powershell cmdlet Get-MailboxStatistics to get last logon time, mailbox size, and other mailbox related statistics data.

Before proceed, first we need to connect Remote Exchange Online powershel module by running below command:
$LiveCred = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $LiveCred -Authentication Basic -AllowRedirection
Import-PSSession $Session
Run the following command to get mailbox statistics for a single Office 365 user
Get-MailboxStatistics -Identity <name or upn of user>
To find last logon time for all the Office 365 users, first we need to get all mailboxes by using Get-Mailbox cmdlet and pipe the results to Get-MailboxStatistics.
Get-Mailbox -ResultSize Unlimited | Get-MailboxStatistics | Select-Object DisplayName,LastLogonTime
Using above script, you can get output only from the cmdlet Get-MailboxStatistics and you can't fetch any result from Get-Mailbox. If you want to read detail from Get-Mailbox command like UserPrincipalName, you need to merge output of two cmdlets.
$mailboxes = Get-Mailbox -ResultSize Unlimited
$mailboxes | ForEach-Object {
$mbx = $_
$mbs = Get-MailboxStatistics -Identity $mbx.UserPrincipalName | Select LastLogonTime
if ($mbs.LastLogonTime -eq $null){
$lt = "Never Logged In"
}else{
$lt = $mbs.LastLogonTime }

New-Object -TypeName PSObject -Property @{ 
UserPrincipalName = $mbx.UserPrincipalName
LastLogonTime = $lt }
}

Export Last Logon Time of Office 365 Users to CSV file:

You can also export all Exchange Online user's last logon time to csv file by using below script
$Result=@() 
$mailboxes = Get-Mailbox -ResultSize Unlimited
$totalmbx = $mailboxes.Count
$i = 1 
$mailboxes | ForEach-Object {
$i++
$mbx = $_
$mbs = Get-MailboxStatistics -Identity $mbx.UserPrincipalName | Select LastLogonTime
if ($mbs.LastLogonTime -eq $null){
$lt = "Never Logged In"
}else{
$lt = $mbs.LastLogonTime }

Write-Progress -activity "Processing $mbx" -status "$i out of $totalmbx completed"

$Result += New-Object PSObject -property @{ 
Name = $mbx.DisplayName
UserPrincipalName = $mbx.UserPrincipalName
LastLogonTime = $lt }
}

$Result | Export-CSV "C:\\O365-LastLogon-Info.csv" -NoTypeInformation -Encoding UTF8

Tuesday, 27 September 2016

Invite external users to SharePoint site using CSOM

We may required to automate the process of inviting external users to SharePoint site or document. In this article, I am going write CSOM based C# code to send invitation for bulk external users to site or document.

The Microsoft SharePoint Online SDK assembly Microsoft.SharePoint.Client.dll includes the namespace Microsoft.SharePoint.Client.Sharing in which there are two useful classes called WebSharingManager and DocumentSharingManager. We can use the class WebSharingManager to share a SharePoint site with external users.
private static void InviteExternalUsersForSite() 
{ 
    // Create Connection to SharePoint Online Site 
    using (var context = new ClientContext("https://spotenant.sharepoint.com/sites/contosobeta")) 
    { 
        context.Credentials = new SharePointOnlineCredentials("admin@spotenant.com", securePwd); 
        // Create invitation request list to multiple users 
        var users = new List<string>() { "admin@extDomain.com", "alexd@extDomain.com"}; 
        var userRoles = new List<UserRoleAssignment>(); 
        foreach (var user in users) 
        { 
            UserRoleAssignment role = new UserRoleAssignment(); 
            role.UserId = user; 
            role.Role = Role.View; 
            userRoles.Add(role); 
        } 
        string message = "Please accept this invite to access our SharePoint Site."; 
        // Send invitation requests to external users 
        WebSharingManager.UpdateWebSharingInformation(context, context.Web, userRoles, true, message, true, true); 
        context.ExecuteQuery(); 
    } 
}
We need to use the class DocumentSharingManager to programmtically share a document with external users.
string absoluteFileUrl = "https://spotenant.sharepoint.com/sites/contosobeta/Shared%20Documents/Document.docx"; 
DocumentSharingManager.UpdateDocumentSharingInfo(context, absoluteFileUrl, userRoles, true, true, true, customMsg, true, true);
Note: Here, I have used list to send invitation for multiple users, but you can alternatively import users from csv file and share site or document with bulk users.

Friday, 23 September 2016

Azure AD Import-Module – Could not load file or assembly

Problem:

You will receive the following error when try to run the Windows Azure Active Directory Module or run the command Import-Module MSOnline on a Windows 7 or 2008 R2 machine.
Import-Module : Could not load file or assembly ‘file:///C:\Windows\system32\WindowsPowerShell\v1.0\Modules\MSOnline\Microsoft.Online.Administration.Automation.PSModule.dll’ or one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded.
At line:1 char:14
+ Import-Module <<<<  MSOnline
    + CategoryInfo          : InvalidOperation: (:) [Import-Module], BadImageFormatException
    + FullyQualifiedErrorId : FormatXmlUpateException,Microsoft.PowerShell.Com   mands.ImportModuleCommand

Cause:

This problem will occur due to PowerShell built against the .NET framework in a specific version. You can check it by the PowerShell variable: $PSVersionTable.

Fix/Solution:

For me the problem was solved after installing Windows Management Framework 3.0.

For more detail, you can refer this article: Azure AD Module – This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded

Powershell - Connect-MsolService Error : Method not found

Problem:

After installing Azure Active Directory Powershell module, you will get the following error when you connect Azure AD using the cmdlet Connect-MsolService.
Connect-MsolService : Method not found: 'Void
System.Runtime.InteropServices.Marshal.PtrToStructure(IntPtr, !!0)'.
At line:1 char:1
+ Connect-MsolService -Credential $cred
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (:) [Connect-MsolService], Mis
   singMethodException
    + FullyQualifiedErrorId : System.MissingMethodException,Microsoft.Online.A
   dministration.Automation.ConnectMsolService

Fix/Solution:

For me the issue was fixed after update my .NET to 4.5.2.

You can also try other solutions if the above update does not fix the problem for you. Check this Microsoft forum for more info: Problem to Connect MsolService

Fix - Office 365 Implicit Remoting Broken in PowerShell

Problem:

You will get the following error when you connect to Exchange Online via PowerShell after the Windows Client Update KB3176934.
Import-PSSession : Could not load type ‘System.Management.Automation.SecuritySupport’ from assembly
‘System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’.

Cause:

On August 23, Microsoft released the Windows Client update KB3176934. Due to a missing .MOF file in the build package, the update breaks DSC. All DSC operations will result in an “Invalid Property” error. This is a known issue and it is documented under Known Issues section in https://support.microsoft.com/en-in/kb/3176934

Fix/Solution:

Microsoft released fix for this issue in next cumulative Windows update https://support.microsoft.com/en-us/kb/3176938.

Thursday, 22 September 2016

Create SharePoint List from Custom Template using CSOM

In this post, I am going write C# code sample to create a new list based on existing list template (.stp uploaded to the list template gallery) with managed Client Object Model. Along with creating new sharepoint list or document library, this process creates new content type, site columns , list view and contents (files or list items) if exists in custom list template.

This process includes following steps:

1. Initialize site context by giving site url where we want to create list or document library.
2. Get all custom list templates.
3. Initialize ListCreationInformation with new list or document library name and custom list template.
4. Finally, creates new list with custom template.
private static void CreateLibraryUsingCustomTemplate() 
{ 
    using (var context = new ClientContext("https://spo-tenant.sharepoint.com/sites/teamsite")) 
    { 
        context.Credentials = new SharePointOnlineCredentials("username", securePwd); 
        // Load the custom templates from site collection 
        ListTemplateCollection templates = context.Site.GetCustomListTemplates(context.Web); 
        context.Load(templates); 
        context.ExecuteQuery(); 
        // Initialize list or library creation info 
        var listCreationInfo = new ListCreationInformation 
        { 
            Title = "Test Custom Library", 
            Description = "Test Custom Library" 
        }; 
  
        ListTemplate listTemplate = templates.First(listTemp => listTemp.Name == "<template name>"); 

        listCreationInfo.ListTemplate = listTemplate; 
        listCreationInfo.TemplateFeatureId = listTemplate.FeatureId; 
        listCreationInfo.TemplateType = listTemplate.ListTemplateTypeKind;  
        // Add Document Library to site 
        context.Web.Lists.Add(listCreationInfo); 
        context.ExecuteQuery(); 
    } 
} 

Friday, 16 September 2016

Rename a document in Sharepoint Online using CSOM

There is no direct function to rename a uploaded document in SharePoint document library using the client side object model (csom), as a work-around, we need to use File.MoveTo method to rename a file. Here we need to actually move the file in same location with different name.

Use the below powershell script to rename a specific sharepoint document file.
#Add required references to SharePoint client assembly to use CSOM 
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")  
 
$siteUrl = "https://spotenant.sharepoint.com/sites/contosobeta" 
$UserName = "admin@spotenant.onmicrosoft.com" 
$SecPwd = $(ConvertTo-SecureString 'adminPassword' -asplaintext -force) 
 
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl) 
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName,$SecPwd) 
$ctx.credentials = $credentials 
 
#Rename a file 
$fileUrl ="/sites/contosobeta/Shared Documents/test.csv" 
$newfileUrl="/sites/contosobeta/Shared Documents/test_rename.csv" 

$file = $ctx.Web.GetFileByServerRelativeUrl($fileUrl) 
$file.MoveTo($newfileUrl, [Microsoft.SharePoint.Client.MoveOperations]::Overwrite) 
$ctx.ExecuteQuery() 

Rename a file using Office Dev PnP

You can also use below powershell code to rename a document stored inside a SharePoint Online document library using Office Dev PnP.
Connect-SPOnline -url [yoururl]
$ctx = Get-SPOContext
$web = Get-SPOWeb
$fileUrl ="/sites/contosobeta/Shared Documents/test.csv" 
$newfileUrl="/sites/contosobeta/Shared Documents/test_rename.csv" 
$file = $web.GetFileByServerRelativeUrl("$fileUrl")
$file.MoveTo("$newfileUrl", 'Overwrite')
$ctx.ExecuteQuery()
 

Rename all uploaded document files in a SharePoint List

You can also use the below powershell script to rename all the files in a document library.
$siteUrl = "https://spotenant.sharepoint.com/sites/contosobeta" 
$UserName = "admin@spotenant.onmicrosoft.com" 
$SecPwd = $(ConvertTo-SecureString 'adminPassword' -asplaintext -force) 
 
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl) 
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName,$SecPwd) 
$ctx.credentials = $credentials 
  
#Load items 
$list = $ctx.Web.Lists.GetByTitle("Documents") 
$query = [Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery() 
$items = $list.GetItems($query) 
$ctx.Load($items) 
$ctx.ExecuteQuery() 
 
#Rename File(s) 
foreach ($item in $items){ 
if($item.FileSystemObjectType -eq [Microsoft.SharePoint.Client.FileSystemObjectType ]::File) {  
$destFileUrl = $item["FileRef"].ToString().Replace("test","test_rename") 
$item.File.MoveTo($destFileUrl, [Microsoft.SharePoint.Client.MoveOperations]::Overwrite) 
$ctx.ExecuteQuery() 
} 
}

Thursday, 15 September 2016

Guest access feature indroduced for Office 365 Groups

The introduction of Office 365 groups gives the rich collaboration within a team inside an organization. There will be a situation you need to give access to outside of organisation when you work closely with customers, partners, and others outside organizations. The importance of collaboration with external users is inevitable in various business needs, to resolve this, Microsoft finally announced the external user (Guest) access feature: Introducing guest access for Office 365 Groups


Note: Microsoft announced the guest access feature works only for email accounts including corporate and consumer domains (such as Outlook.com or Gmail.com). If the guest email identity is associated with a Microsoft account (such as Office 365 or Outlook.com accounts, for instance), the user is directed to a sign-in page to identify themselves. If the guest doesn’t have a Microsoft account, they will be directed to a sign-up page to create an account.

You can also read this great article https://www.petri.com/external-access-office-365-groups (by Tony Redmond) to know more about how to add guest users for Office 365 Groups and how an external user invitation works for guest users.

Wednesday, 14 September 2016

Get all Documents from a SharePoint List using CSOM

In this post, I am going to write C# code to list all documents in a SharePoint list and get all files from SharePoint Site using Client Object Model (CSOM).

List all documents in a SharePoint Document Library

The below C# code find and list all files in the particular document library. You can replace your own list name to get required documents.
public static void GetAllDcoumentsInaList() 
{    
    string sitrUrl = "https://SPTenant.sharepoint.com/sites/contosobeta"; 
    using (var ctx = new ClientContext(sitrUrl)) 
    { 
        //ctx.Credentials = Your Credentials
        ctx.Load(ctx.Web, a => a.Lists); 
        ctx.ExecuteQuery(); 

        List list = ctx.Web.Lists.GetByTitle("Documents"); 
        var items = list.GetItems(new CamlQuery() { ViewXml = "<View Scope=\"RecursiveAll\"><Query><Where><IsNotNull><FieldRef Name=\"File_x0020_Type\" /></IsNotNull></Where></Query></View>" }); 
        ctx.Load(items); 
        ctx.ExecuteQuery(); 
        foreach (var doc in items) 
        { 
            Console.WriteLine(doc["FileRef"].ToString().Split('/').LastOrDefault() + 
                " (" + doc["File_x0020_Size"].ToString() + " bytes)"); 
        } 
    } 
} 

Get all documents in a SharePoint Site

To retrieve items from entire site, first, we need to fetch all the lists in a site and add condition check BaseType in list to filter only document libraries.
public static void GetAllDcoumentsInaSite() 
{ 
    string sitrUrl = "https://SPTenant.sharepoint.com/sites/contosobeta"; 
    using (var ctx = new ClientContext(sitrUrl)) 
    { 
        //ctx.Credentials = Your Credentials
        ctx.Load(ctx.Web, a => a.Lists); 
        ctx.ExecuteQuery(); 
 
        foreach (List list in ctx.Web.Lists) 
        { 
            if (list.BaseType == BaseType.DocumentLibrary) 
            { 
                Console.WriteLine("List: " + list.Title); 
                Console.WriteLine("-----------------------"); 
                var items = list.GetItems(new CamlQuery() { ViewXml = "<View Scope=\"RecursiveAll\"><Query><Where><IsNotNull><FieldRef Name=\"File_x0020_Type\" /></IsNotNull></Where></Query></View>" }); 
                ctx.Load(items); 
                ctx.ExecuteQuery(); 
                foreach (var doc in items) 
                { 
                    Console.WriteLine(doc["FileRef"].ToString().Split('/').LastOrDefault() + 
                " (" + doc["File_x0020_Size"].ToString() + " bytes)"); 
                } 
            } 
        } 
    } 
} 

Thursday, 8 September 2016

CSOM : The request uses too many resources - SharePoint Online

I have the following c# csom (client side object model) code to get sub sites and nested sub sites of a site collection. This code runs perfectly for most site collections, but it not working for a single site collection which has a lot of sub-sites and nested sub sites.
private static List<Web> RecursiveSites(ClientContext siteCtx, Web site)
{
    var allSites = new List<Web>();
    allSites.Add(site);
    if (site.Webs.Count > 0)
    {
        siteCtx.Load(site.Webs, w => w.Include(a => a.Title, a => a.Url, a => a.Webs));
        siteCtx.ExecuteQuery();
        foreach (Web web in site.Webs)
        {
            allSites.AddRange(RecursiveSites(siteCtx, web));
        }
    }
    return allSites;
}
I am receiving below error:
Microsoft.SharePoint.Client.ServerException: The request uses too many resources
After googled sometime, I've found an article in MSDN that explains the request limits of the csom. You can refer this under the heading Request limits of the CSOM at bottom of this article https://msdn.microsoft.com/en-us/library/office/jj163082.aspx
The CSOM in Project Server 2013 is built on the CSOM implementation in SharePoint Server 2013 and inherits the limits 
for the maximum size of a request. SharePoint has a 2 MB limit for an operations request, and a 50 MB limit for the size of a
submitted binary object. The request size is limited to protect the server from excessively long queues of operations and
from processing delays for large binary objects.
.

Solution:

In SharePoint On-Premise, you can resolve this issue by setting the maxObjectPaths in WebApplication to a higher value using Powershell:
#Get the value:
Get-SPWebApplication | %{$_.ClientCallableSettings}

#Set the Value:
Add-PSSnapin Microsoft.SharePoint.PowerShell
$webApp = Get-SPWebApplication "http://myspwebapp/"
$webApp.ClientCallableSettings.MaxObjectPaths = 2500
$webApp.Update()
But in SharePoint Online, there is no way to access the ClientCallableSettings.MaxObjectPaths property. so we need to re-write the code to reduce request size. I have changed the code in a way to send request for one site instead of all sub sites at the same time, now the code execute the siteCtx.ExecuteQuery() inside the loop instead of outside the loop.
private static List<Web> RecursiveSites(ClientContext siteCtx, Web site)
{
    var allSites = new List<Web>();
    allSites.Add(site);
    if (site.Webs.Count > 0)
    {     
        foreach (Web web in site.Webs)
        {
            siteCtx.Load(web , w => w.Title, w => w.Url, w => w.Webs);
            siteCtx.ExecuteQuery();
            allSites.AddRange(RecursiveSites(siteCtx, web));
        }
    }
    return allSites;
}