Second Life of a Hungarian SharePoint Geek

November 24, 2015

Recovering Passwords for SharePoint Managed Accounts

Filed under: Reflection, PowerShell, Security, SP 2013 — Tags: , , , — Peter Holpar @ 23:47

We have a SharePoint 2013 farm on a Windows 2008 R2 server. Recently we found this error in the Windows event logs in relation with the web application pool account:

Event ID 1511 – Windows cannot find the local profile and is logging you on with a temporary profile. Changes you make to this profile will be lost when you log off.

We tried to solve the issue based on the information we found in this post, but at the step below we faced the problem, that the password stored for the web application pool account (in this case we assumed domain\wa_pool_account) in our password repository does not work any more.

runas /u:domain\wa_pool_account /profile cmd

The web application pool account is registered as a managed account in SharePoint, at the original password has been already automatically changed by the system.

We could reset the password for the managed account as described in this article, but before changing the password I wanted to be sure there is no way to recover the current password from the system. I found a blog post and the related PowerShell code in TechNet Gallery, but I found the method described there (creating a new web application, and using an external tool, appcmd.exe) a bit overkill.

Instead of this I came up with an alternative solution that query the password directly from the SPManagedAccount object, via its private m_Password field (of type SPEncryptedString) that we can access by using Reflection. The public SecureStringValue property of the SPEncryptedString class returns an instance of the System.Security.SecureString class, and as illustrated here, we can “decode” its value to a simple string via Marshaling.

Using this approach, recovering the managed account password is so simple:

$ma = Get-SPManagedAccount domain\wa_pool_account
$maType = $ma.GetType()

$bindingFlags = [Reflection.BindingFlags]::NonPublic -bor [Reflection.BindingFlags]::Instance

$m_Password = $maType.GetField("m_Password", $bindingFlags)
$pwdEnc = $m_Password.GetValue($ma)

$ssv = $pwdEnc.SecureStringValue
$ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($ssv)

NullReferenceException in the Content Editor Web Part when Trying to Upload Images

Filed under: CEWP, SP 2013 — Tags: , — Peter Holpar @ 23:16

Recently a user complained, that when he tries to upload a photo in a Content Editor Web Part (CEWP)…


… an error is displayed.


We found the following relevant line in the ULS logs:

11/16/2015 12:46:45.29     w3wp.exe (0x21FFC)                          0x2185C    SharePoint Foundation             Runtime                           tkau    Unexpected    System.NullReferenceException: Object reference not set to an instance of an object.    at ASP._layouts_15_upload_aspx.__Render__control29(HtmlTextWriter __w, Control parameterContainer) in c:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\TEMPLATE\LAYOUTS\upload.aspx:line 81     at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)     at Microsoft.SharePoint.WebControls.ScriptBlock.Render(HtmlTextWriter writer)     at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)     at ASP._layouts_15_upload_aspx.__Render__control27(HtmlTextWriter __w, Control parameterContainer) in c:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\TEMPLATE\LAYOUTS\upload.aspx:line 61     at Sy…    b324429d-5597-507f-40b1-b3a2b68192e5

In upload.aspx at line 81 there is a reference for CurrentList.ID:

function LaunchOpenInExplorer()
    vCurrentListID = "<%= CurrentList.ID %>";
    vCurrentListUrlAsHTML = "<%= Web.Url + "/" + (CurrentList.RootFolder.Url.Length > 0 ? CurrentList.RootFolder.Url + "/" : "") %>";


We got the NullReferenceException, because the CurrentList object is null.

In our case, the CurrentList was null, because somebody has deleted all document libraries from the site where the page with the CEWP was located, so there was no place the user could upload the image.

However, we should not delete all of the libraries if you want to reproduce the issue. The GetActionableDocumentLibraries method of the Microsoft.SharePoint.ApplicationPages.UploadPage class is responsible for populating the list of document libraries of the upload page that are candidates for image upload. We can find the following loop and conditions in this method:

List<SPDocumentLibrary> list = new List<SPDocumentLibrary>();
foreach (SPList list2 in web.Lists)
  SPDocumentLibrary item = list2 as SPDocumentLibrary;
  if ((((item != null) && !item.Hidden) && ((SPBaseType.DocumentLibrary == item.BaseType) && (SPListTemplateType.WebPageLibrary != item.BaseTemplate))) && (!item.IsCatalog && (!requireAddListItemsPermissions || (requireAddListItemsPermissions && list2.DoesUserHavePermissions(SPBasePermissions.AddListItems, false)))))


As you can see, the library should not be a hidden one, should not be the Site Pages library or any of the standard catalog libraries (like Web Part Catalog), and the user must have the permission to add items to the list. For example, if there are no document libraries the user is allowed to add items to, we have the same issue with the CEWP.

Re-creating the deleted document library solved the issue for the user.

October 19, 2015

Displaying Notifications and Status Messages from a SharePoint-based AngularJS Application Including a FormController

Assume the following requirements: We should create a Single Page Application (SPA) (no SharePoint App!) that reads data from SharePoint using the JavaScript client object model, allows the user to edit the values, displays if a field value was changed (if it is “dirty” vs. “pristine”), performs data validations (if it is “valid” vs. “invalid”) and lets the user to save the changes. AngularJS was selected as the presentation framework for the SPA. On data save, we should give the users feedback on the progress (like “Saving changes…”, “Save completed” or “Error during the save operation”) via standard SharePoint notifications and status messages.

Challenge 1:  The FormController of the AngularJS framework is based on the form HTML element. That means, if we would like to use the features of the FormController, like dirty / pristine, validation, etc., we should include a form element in our HTML application. However, our SharePoint page is an ASP.NET page, that already contains a form element, and it does not like to include multiple ones.

Solution 1: Although there are tricks to bypass this limitation (like this or this one), I chose another way to go. I’ve included a Page Viewer Web Part that displays a “pure” HTML page that is stored in a document library in SharePoint, as well as any other non-standard artifacts of the application (.js and .css files, etc.). This HTML page – displayed in an IFRAME by the Page Viewer Web Part – contains the form element, that does not interfere with the form element on the ASP.NET page.

You can display a notification by calling the SP.UI.Notify.addNotification method, similarly a status message is displayed via the SP.UI.Status.addStatus method. Both of these Notify and Status classes are defined in the SP.UI namespace in the sp.js (and its debug version in sp.debug.js). This JavaScript file is typically referenced in the standard SharePoint pages, however you should add a reference to it in your custom pages, like in the case of our HTML page. If you forget to add the reference, you will get an error like this one:

TypeError: Unable to get property ‘addNotification’ of undefined or null reference

Challenge 2:  There is no notification / status message displayed, even if you add the reference to the sp.js. The reason of the problem is, that the HTML elements required by these methods are defined in the master page of the standard SharePoint sites. Obviously, these elements are not found in our custom page in the IFRAME, so the messages are not displayed.

Solution 2: I’ve found two similar blog posts (this one and this one) describing a similar issue with IFRAME and notification messages in the case of Client App Parts. The first of this two posts states that the problem is the IFRAME itself, that prohibits the communication between the parent page and the IFRAME. Of course, that is wrong. The real reason is the different domain names in the URL of the app part (IFRAME) and the host page, as correctly stated in the second post. If we have the same domain name (and we do have in this case), we do not need the rather complex approach described by the posts(that is still valid for the Client App Parts).  Displaying a notification / status message from the script included in the HTML page in the IFRAME in our case is so simple as to prepend the text ‘parent.’ before the method invocation, for example:

var notifyId = parent.SP.UI.Notify.addNotification("Saving…", true);

Of course, in this case you are using the JavaScript and HTML objects on the parent page, so you don’t need to reference the sp.js in your HTML page.

October 12, 2015

Waiting for Project Server Queue Operations in Server Side Code

Filed under: PS 2013 — Tags: — Peter Holpar @ 23:48

Recently we found a bug in our server-side Project Server code. Our goal was to set some project-related enterprise custom field values. Before setting the values, we tested, if the project is checked-out (to another user), and if so, we forced a a check-in, to be able to check out again to ourselves. After setting the values, we updated the project and published it, including check-in.

  1. if (proj.IsCheckedOut)
  2. {
  3.     proj.Draft.CheckIn(true);
  4. }
  5. DraftProject draftProj = proj.CheckOut();
  6. // set some custom field values
  7. draftProj.SetCustomFieldValue("customFieldName", "customFieldValue");
  8. draftProj.Update();
  9. draftProj.Publish(true);

If the project was not checked-out, the code worked as expected. However, if the project was checked-out to a use, we got an exception despite of the test on the line

DraftProject draftProj = proj.CheckOut();

The exception was:

Microsoft.ProjectServer.PJClientCallableException was unhandled
       at Microsoft.ProjectServer.PublishedProject.CheckOut()
       at SPGeneral.Program.Main(String[] args) in c:\projects\PSTest\Program.cs:line 37
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()

The message “CICOCheckedOutInOtherSession” suggested, that the project is not yet checked-in, although our traces showed that the line proj.Draft.CheckIn(true) was executed.

To understand the source of the problem, we should first understand how the CheckIn / CheckOut and Publish methods are executed:

Both of the CheckIn and Publish methods of the DraftProject class are executed asynchronously. These methods return a QueueJob object. The actual job is performed by the queue subsystem of Project Server.

The CheckOut method of the PublishedProject class is executed synchronously, it returns a DraftProject instance immediately.

In the above sample it means, that proj.CheckOut() is called, before the project would be effectively checked-in as a result of executing proj.Draft.CheckIn(true).

How to solve the problem, keeping our code readable if possible?

If you are working with client-side object model of Project Server, you know probably, that there is a solution for such issues: the WaitForQueue method of the ProjectContext object.Unfortunately, the equivalent of this method is not implemented in the server-side object model. It’s pretty strange, as usually its opposite is the case: methods and properties available on the server-side are many times missing on the client-side OM.

No problem, we can implement a similar method for ourselves!

The WaitForQueue method “wait for the specified queue job to complete, or for a maximum number of seconds”. It returns a JobState object, that should be JobState.Success if the queue job succeeded. If we have a look at the implementation of the method, we can see, that it calls the internal IsPendingJob method of the ProjectContext object to compare the current state of the job with the expected values, the refresh the job status by polling the server side objects. The wait time is decremented by 2 seconds in every iteration, the IsPendingJob method is responsible to sleep the thread for this two seconds.

Note: It means that the maximum wait time specified when calling WaitForQueue method is only an approximate value, as it does not include the send / response time of the server requests involved in the refreshing the job status, for example a one-minute wait time means 30 iterations, that is 30 requests / responses. So don’t be surprise if your wait times are considerably longer than specified if you have a slow network or busy server.

After this theory, lets see our own implementation:

  1. public static bool IsPending(this QueueJob job, out QueueConstants.JobState state)
  2. {
  3.         state = job.JobState;
  4.         switch (state)
  5.         {
  6.             case QueueConstants.JobState.Unknown:
  7.             case QueueConstants.JobState.ReadyForProcessing:
  8.             case QueueConstants.JobState.SendIncomplete:
  9.             case QueueConstants.JobState.Processing:
  10.             case QueueConstants.JobState.ProcessingDeferred:
  11.             case QueueConstants.JobState.OnHold:
  12.             case QueueConstants.JobState.Sleeping:
  13.             case QueueConstants.JobState.ReadyForLaunch:
  14.                 Thread.Sleep(new TimeSpan(0, 0, 2));
  15.                 return true;
  16.         }
  17.     state = QueueConstants.JobState.Success;
  18.     return false;
  19. }
  21. public static QueueConstants.JobState WaitToFinish(this QueueJob job, int timeoutSeconds)
  22. {
  23.     QueueConstants.JobState state = QueueConstants.JobState.Unknown;
  24.     while ((timeoutSeconds > 0) && job.IsPending(out state))
  25.     {
  26.         timeoutSeconds -= 2;
  27.     }
  28.     return state;
  29. }

As you can see, instead of extending the PSContext object with a WaitForQueue method, I decided to extend the QueueJob object itself with a WaitToFinish method. It seems to me simply more appropriate.

Note: As you probably know, and as I mentioned in my former posts, the server-side object model is based on the PSI infrastructure. It means, that the extra wait time mentioned above may apply to this solution as well.

Note 2: To be able to use the JobState enumeration value in your code, you should reference the Microsoft.Office.Project.Server.Library assembly in your project.

The modified logic in our application is displayed below:

  1. var timeOutInSec = 60; // wait max a minute
  2. bool canCheckOut = !proj.IsCheckedOut;
  3. if (!canCheckOut)
  4. {
  5.     // Project is checked out. Forcing check-in.
  6.     var job = proj.Draft.CheckIn(true);
  7.     var jobState = job.WaitToFinish(timeOutInSec);
  8.     if (jobState == QueueConstants.JobState.Success)
  9.     {
  10.         canCheckOut = true;
  11.     }
  12.     else
  13.     {
  14.         // WARNING Time-out on project check-in, or job failed
  15.     }
  16. }
  18. if (canCheckOut)
  19. {
  20.     DraftProject draftProj = proj.CheckOut();
  21.     // set some custom field values
  22.     draftProj.SetCustomFieldValue("customFieldName", "customFieldValue");
  23.     draftProj.Update();
  25.     // Publishing project (incl. check-in!)
  26.     var job = draftProj.Publish(true);
  27.     var jobState = job.WaitToFinish(Constants.DefaultPSJobTimeOut);
  28.     if (jobState == QueueConstants.JobState.Success)
  29.     {
  30.         // Project checked-in + published.
  31.     }
  32.     else
  33.     {
  34.         // WARNING Time-out on project publish / check-in or job failed
  35.     }
  36. }
  37. else
  38. {
  39.     // WARNING Project can not be checked-out / processed
  40. }

October 1, 2015

Change in the User Resolution in SharePoint 2013 People Picker

Filed under: Active Directory, PowerShell, SP 2013, People Picker — Tags: , , , — Peter Holpar @ 22:31

After a SharePoint 2010 to SharePoint 2013 migration our users complained, that in the multiple Active Directory domain environment they have (I wrote about it recently) the People Picker does not resolve the users the same way it did earlier. Only a subset of the users was resolved, users from a few domains were not included in the results at all.

The reason of this issue is a change in the GetTrustedDomains method of the Microsoft.SharePoint.Utilities.SPUserUtility class. Now (in SP 2013) it includes an extra condition, checking the value of  SPWebService.ContentService.PeoplePickerSearchInMultipleForests.

If you need the same behavior as in the SP 2010 version, you should set the value of  the PeoplePickerSearchInMultipleForests property to true.

You can achieve it using PowerShell:

$cs = [Microsoft.SharePoint.Administration.SPWebService]::ContentService
$cs.PeoplePickerSearchInMultipleForests = $true

or via C#:

SPWebService.ContentService.PeoplePickerSearchInMultipleForests = true;

The SharePoint Time Machine

Filed under: SP 2013, Tips & Tricks — Tags: , — Peter Holpar @ 22:06

Assume you have a SharePoint list with a lot of items. The list supports versioning and you should provide a snapshot of the items at a given time in the past.

As you know, the Versions property (of type SPListItemVersionCollection) of the SPListItem class contains the item versions. One can access a specific version via the indexer property of the collection, by the ID of the version (where the ID = 512 * major version number + minor version number), or by the version number (a.k.a. label, for example, 2.3), but there is no direct support to get the actual version at a specific time in the past.

To achieve my goal, I’ve implemented the GetVersionFromDate extension method, that iterates through the method, and returns the version we need based on its creation date:

  1. public static SPListItemVersion GetVersionFromDate(this SPListItemVersionCollection versions, DateTime localDate)
  2. {
  3.     SPListItemVersion result = null;
  5.     if (versions != null)
  6.     {
  7.         DateTime date = versions.ListItem.Web.RegionalSettings.TimeZone.LocalTimeToUTC(localDate);
  9.         SPListItemVersion prevVersion = null;
  11.         // versions[0] – current item version
  12.         // versions[versions.Count – 1] – first item version created
  13.         for (int i = versions.Count – 1; i >= 0; i–)
  14.         {
  15.             SPListItemVersion version = versions[i];
  16.             if (version.Created > date)
  17.             {
  18.                 result = prevVersion;
  19.                 break;
  20.             }
  21.             // if it is the last (actual) version and there is no result yet,
  22.             // then the date specified should be greater than the creation date of the last version
  23.             // we take the last version
  24.             else if (i == 0)
  25.             {
  26.                 result = version;
  27.             }
  29.             prevVersion = version;
  30.         }                
  32.     }
  34.     return result;
  35. }

Note, that the Created property stores the creation date as UTC time, that we should convert first.

Using this method accessing the specific version is so simple as:

  1. SPList list = web.Lists["Your List"];
  2. SPListItem item = list.Items.GetItemById(1);
  4. DateTime date = DateTime.Parse("2015/06/29 13:40");
  5. SPListItemVersion version = item.Versions.GetVersionFromDate(date);
  6. Console.WriteLine(version["APropertyName"]);

If you go through the items in the list and get the version of the specific time, you already have the required snapshot.

September 29, 2015

People Picker is very slow when searching users

Filed under: Active Directory, People Picker, SP 2010 — Tags: , , — Peter Holpar @ 22:12

The environment of a customer of us consists of several Active Directory domains, a few of them were recently migrated from former domains.

Users of the SharePoint sites complained that when they try to look up users via the People Picker, the result is displayed only after a delay of  30-40 seconds, instead of the former 3-5 seconds.

I’ve tried to catch the problem using Wireshark, filtering for the LDAP protocol, as described in this post. However, I found no problem with the requests / responses, except for a delay of about 30 seconds, although no request using this protocol was sent in this time lag. Obviously, the sender process waited for a response sent using another protocol.

Removing the LDAP filter in Wireshark, I found these retransmission attempts:

No.     Time            Source                        Destination   Protocol Length  Info
3241    44.218621000    IP of the SharePoint Server   IP of the DC    TCP    66    53607 > msft-gc [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
3360    47.217136000    IP of the SharePoint Server   IP of the DC    TCP    66    [TCP Retransmission] 53607 > msft-gc [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
3791    53.221414000    IP of the SharePoint Server   IP of the DC    TCP    62    [TCP Retransmission] 53607 > msft-gc [SYN] Seq=0 Win=8192 Len=0 MSS=1460 SACK_PERM=1

The msft-gc is an LDAP-like protocol used to query the Global Catalog (GC) in the Active Directory (uses port 3268). The retransmission timeout (RTO) value of the packet 3360 was 3 sec., the RTO of the packet 3791 was 9 sec., both causing delay in the user search process.

The source IP was the address of the SharePoint server, the IP address in the destination is the address of a former Domain Controller (DC). The server, that acted as DC of a domain that was already migrated was online, but the DC-role was already demoted on it . The IP address of the server was registered in DNS, so the server could be PINGed, but it did not respond to LDAP requests (including msft-gc) anymore.

The entries in the ULS logs has provided further evidence, that there is an issue with the Global Catalog in the AD forest (see the SearchFromGC method in the stack trace below):.

08/06/2015 13:26:34.08     w3wp.exe (0x66BC)                           0x9670    SharePoint Foundation             General                           72e9    Medium      Error in resolving user ‘UserName‘ : System.Runtime.InteropServices.COMException (0x8007203A): The server is not operational.       at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)     at System.DirectoryServices.DirectoryEntry.Bind()     at System.DirectoryServices.DirectoryEntry.get_AdsObject()     at System.DirectoryServices.DirectorySearcher.FindAll(Boolean findMoreThanOne)     at Microsoft.SharePoint.WebControls.PeopleEditor.SearchFromGC(SPActiveDirectoryDomain domain, String strFilter, String[] rgstrProp, Int32 nTimeout, Int32 nSizeLimit, SPUserCollection spUsers, ArrayList& rgResults)     at Microsoft.SharePoint.Utilities.SPUserUtility.ResolveAgainstAD(String input, Boolean inputIsEmailOnly, SPActiveDirectoryDomain globalCatalog, SPPrincipalType scopes, SPUserCo…    04482a74-c00f-4005-9cd3-11f765eca7a0
08/06/2015 13:26:34.08*    w3wp.exe (0x66BC)                           0x9670    SharePoint Foundation             General                           72e9    Medium      …llection usersContainer, TimeSpan searchTimeout, String customFilter)     at Microsoft.SharePoint.Utilities.SPActiveDirectoryPrincipalResolver.ResolvePrincipal(String input, Boolean inputIsEmailOnly, SPPrincipalType scopes, SPPrincipalSource sources, SPUserCollection usersContainer)     at Microsoft.SharePoint.Utilities.SPUtility.ResolvePrincipalInternal(SPWeb web, SPWebApplication webApp, Nullable`1 urlZone, String input, SPPrincipalType scopes, SPPrincipalSource sources, SPUserCollection usersContainer, Boolean inputIsEmailOnly, Boolean alwaysAddWindowsResolver).    04482a74-c00f-4005-9cd3-11f765eca7a0

Removing the orphaned DC entry from the AD  resolved the People Picker problem as well.

August 7, 2015

How to process the output of the stsadm EnumAllWebs operation

Filed under: PowerShell, SP 2010, Upgrade — Tags: , , — Peter Holpar @ 10:48

Recently I wrote about how to process the output of the Test-SPContentDatabase PowerShell cmdlet.

If you use the EnumAllWebs operation of the stsadm command, you have similar options as well. However, you have the output in XML format in this case, so we should use XPath expressions to access the information we need.

First, save the result into a text file:

stsadm -o EnumAllWebs -DatabaseName YourContentDB -IncludeFeatures -IncludeWebParts >> C:\Output.xml

then load its content into an XML object:

$reportXml = [Xml] (Get-Content C:\Output.xml)

To list the missing features:

Select-Xml -Xml $reportXml -XPath ‘//Site’ | % { $siteId = $_.Node.Id; Select-Xml -Xml $_.Node -XPath ‘Features/Feature[@Status="Missing"]/@Id’ | % { (Get-SPFeature -Site $siteId -Identity $_.Node.Value).DisplayName } }

Note, that site templates created by saving an existing site as a site template from the UI might be reported as missing features as explained here.

To get an overview of the site templates:

Select-Xml -Xml $reportXml -XPath ‘//Web/@TemplateName’ | % { $_.Node.Value } | Group { $_ }


To get an overview of the missing web-scoped features:

Select-Xml -Xml $reportXml -XPath ‘//Web/Features/Feature[@Status="Missing"]/@Id’ | % { $_.Node.Value } | Group { $_ } | % { Write-Host $_.Name – Count is $_.Count }

Finally, to get an overview of the missing web parts:

Select-Xml -Xml $reportXml -XPath ‘//Web/WebParts/WebPart[@Status="Missing"]/@Id’ | % { $_.Node.Value } | Group { $_ } | % { Write-Host $_.Name – Count is $_.Count }

Computing Hash of SharePoint Files

Filed under: PowerShell, SP 2010 — Tags: , — Peter Holpar @ 10:46

Recently we had to compare master pages of hundreds of sites to find out, which ones contain customizations. Comparing the content of files means in practice typically to compare the hash values calculated based on the file content.

But how to compute the hash of a file stored in SharePoint?

If you had the SharePoint sites mapped to the Windows File System via WebDAV, you could use the Get-FileHash cmdlet from PowerShell 4.0 (or above). In PowerShell 5.0 the Get-FileHash cmdlet is able to compute the hash of input streams as well, so we could use SPFile.OpenBinaryStream method to access the file content, and then compute its hash value.

Since I have only PowerShell 2.0 on my SharePoint server, I had to create my own hashing solution, the Get-SPFileHash function:

function Get-SPFileHash($fileUrl) 

  $site = New-Object Microsoft.SharePoint.SPSite $fileUrl
  $web = $site.OpenWeb()
  $file = $web.GetFile($fileUrl)
  $bytes = $file.OpenBinary()
  $md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
  $hash = [System.BitConverter]::ToString($md5.ComputeHash($bytes))
  return $hash

Having this method, it is easy to create a summary of all sites, their master pages, and the hash values of the master page content:

$siteUrl = "http://YourSharePointSite&quot;

$site = Get-SPSite $siteUrl

$site.AllWebs | % {
  $obj = New-Object PSObject
  $obj | Add-Member NoteProperty Url($_.Url)
  $obj | Add-Member NoteProperty MasterUrl($_.MasterUrl)
  $obj | Add-Member NoteProperty FileHash(Get-SPFileHash ($siteUrl + $_.MasterUrl))
  Write-Output $obj
} | Export-CSV "C:\data\MasterPageHash.csv"

You can import the resulting CSV file into Excel and process its content as you wish.

Note: If you simply double-click on the CSV file created by the former script in Windows Explorer, it is not opened in the format you probably wish: values separated into columns. Instead of that, the first column would contain the entire line. You should first prepare the file: open it in Notepad, optionally remove the first header line, and save the file again, changing the encoding from ANSI to Unicode. Next, start Excel, and open the CSV file from Excel, setting the separator character to Comma on the second page of the Text Import Wizard.

August 2, 2015

PowerShell Scripts around the SID

Filed under: SP 2010, Active Directory, PowerShell, Migration — Tags: , , , — Peter Holpar @ 23:38

If you ever migrated SharePoint users you should be familiar either with the Move-SPUser cmdlet or its predecessor, the migrateuser stsadm operation:

$sourceURL = ";
$web = Get-SPWeb $sourceURL
$user = $web.SiteUsers["domain\jdoe"]
Move-SPUser -Identity $user -NewAlias "newDomain\john.doe" –IgnoreSID


stsadm -o migrateuser –oldlogin "domain\jdoe" -newlogin "newDomain\john.doe" -ignoresidhistory

As you see, both method relies on the SID (or on its ignorance), but what is this SID and how can we read its value for our SharePoint or Active Directory users?

Each user in the Active Directory (AD) has a security identifier (SID) that is a unique, immutable identifier, allowing the user to be renamed without affecting its other properties.

Reading the SID of a SharePoint user from PowerShell is so simple as:

$web = Get-SPWeb
$user = $web.AllUsers["domain\LoginName"]

To be able to work with Active Directory from PowerShell, you need of course the Active Directory cmdlets. If your machine has no role in AD, you should install this PowerShell module using the steps described in this post.

Once you have this module installed, and you imported it via “Import-Module ActiveDirectory”, you can read the SID of a user in AD:

$user = Get-ADUser UserLoginNameWithoutDomain -Server

Where UserLoginNameWithoutDomain is the login name of the user without the domain name, like jdoe in case of domain\jdoe, and is your DC responsible for the domain of your user.

If you need the SID history from AD as well, it’s a bit complicated. In this case I suggest you to read this writing as well.

$ADQuery = Get-ADObject –Server`
        -LDAPFilter "(samAccountName=UserLoginNameWithoutDomain )" `
        -Property objectClass, samAccountName, DisplayName, `
        objectSid, sIDHistory, distinguishedname, description, whenCreated |
        Select-Object * -ExpandProperty sIDHistory
$ADQuery | % { 
  Write-Host $_.samAccountName
  Write-Host Domain $_.AccountDomainSid.Value 
  Write-Host SID History
  $_.sIDHistory | % {
  Write-Host ——————–

Older Posts »

The Shocking Blue Green Theme. Blog at


Get every new post delivered to your Inbox.

Join 60 other followers