Friday, March 27, 2015

Deleted User Profile - How to Relink MySite (SharePoint 2010)

I've had a very interesting case on a SharePoint 2010 environment. A user's account was deleted accidentally by their internal AD team on Friday the 13th :) The account got recreated, but when the user tried to open MySite, they got "Access Denied" when clicking on "Contents".

I've made sure the user is the Site Collection Admin, checked every possible permission level and even re-added it just in case. No luck.

With the Farm Admin account, I was able to see the Contents of this user at all times.

I've come up with the following approach after a few hours of IIS / ULS logs reading that were simply showing 401 when this particular user tried to access their own MySite.

- Deleted the MySite (ensured we've got a fresh backup).
- The user then recreated a blank MySite.
- I've restored the backup with the Restore-SPSite command.

At this stage, I was able to see the contents with the Farm Admin account again, but when the user tried, he got the following error:

The attempted operation is prohibited because it exceeds the list view threshold enforced by the administrator.

And the respective ULS log entry:

The attempted operation is prohibited because it exceeds the list view threshold enforced by the administrator.
 at Microsoft.SharePoint.SPGlobal.HandleThrottleException(COMException comEx)    
 at Microsoft.SharePoint.Library.SPRequest.CrossListQuery(String bstrUrl, String bstrXmlWebs, String bstrXmlLists, String bstrXmlQuery, ISP2DSafeArrayWriter pCallback, Object& pvarColumns)    
 at Microsoft.SharePoint.SPWeb.GetSiteData(SPSiteDataQuery query)    
 at Microsoft.SharePoint.WebPartPages.AggregationWebPart.RunQuery(SPSiteDataQuery query)    
 at Microsoft.SharePoint.WebPartPages.UserDocsWebPart.GetEligibleItems()    
 at Microsoft.SharePoint.WebPartPages.AggregationWebPart.RenderWebPart(HtmlTextWriter writer) Inner Exception: The attempted operation is prohibited because it exceeds the list view threshold enforced by the administrator.  
 at Microsoft.SharePoint.Library.SPRequestInternalClass.CrossListQuery(String bstrUrl, String bstrXmlWebs, String bstrXmlLists, String bstrXmlQuery, ISP2DSafeArrayWriter pCallback, Object& pvarColumns)    
 at Microsoft.SharePoint.Library.SPRequest.CrossListQuery(String bstrUrl, String bstrXmlWebs, String bstrXmlLists, String bstrXmlQuery, ISP2DSafeArrayWriter pCallback, Object& pvarColumns)

Now... the list view treshold was set to 5000 for non-admins (default) and 20 000 for admins (again default). I've lifted it to 20 000 for non-admin users as well, just to find out it doesn't matter. No luck.

I've even decided to make the user a Farm Admin for a minute... again nothing changes.
Whenever he tried to access any of the libraries in "My Site", Unexpected error. The webpart "SharePoint Documents" still saying the stupid message about the List View Tresohld.

Maybe I should have mentioned that there are nowhere near 5000 items in that user's MySite in total.

So... next steps.

- Deleted the User Profile from SharePoint.
- Issued an Incremental Sync to import it - it didn't import.
- Issued a Full Sync - not imported again.
- Recreated the User Profile in SharePoint manually with all the properties

And still at the same stage...

Finally... I've decided to use the Export/Import instead of Backup and Restore and I did not use the -IncludeUserSecurity on purpose... as I am thinking that the old account is still referred to somewhere in the site permissions and that's causing all the headaches. Boom! All working fine now after the Import.

The only downside would be that the "SharePoint Documents" webpart which is the default one visible when you go to "Contents" under MySite will be showing no documents... as when I've used the Export method with the Farm Admin account now all documents show as modified by this account. Anyway once the user edits (or just check-out / check-in) a few documents, this webpart populates again and there's finally nothing more to worry about. Just be careful when deleting AD accounts :)

Monday, March 16, 2015

Yammer DirSync filtering by OU

This post might not be as helpful now as it would have been a year ago due to this improvement. Now Yammer DirSync could be used by organizations requiring Yammer SSO, previously it was used by each organization wanting to sync users from on-premise Active Directory to the Yammer network. More info on how to implement Yammer SSO, which is a different scenario can be found here.

I won't be trying to reinvent the wheel in this post, as there are plenty of good information sources on the Yammer DirSync implementation itself, like this one, but I'd rather share the limitations and issues I've faced when I had to meet a requirement to implement sophisticated filtering for the synchronization process.

The requirement seemed quite simple at a first sight - the customer wanted to synchronize users only from 5 specific OUs in their on-premise Active Directory domain.

Yammer DirSync (as well as the SharePoint User Profile Service) synchronization allows the use of LDAP queries. If you're new to those, you can read more about their syntax here.

The bad thing is, you have no option to query for a specific OU - and hence - no visible option to meet this requirement. The good thing is, there is a more advanced option to do this - partly a configuration cheat and partly modifications in the globalsettings.config.json file. 

The first part consists of adding more than one directory source, but if the OUs that you'd like to sync are in only one domain as in our scenario, you have two options:

- Sync from different domain controllers, add each one by hostname (better approach, straight forward)

You can't use a load-balanced name if you have created one (as per the installation guide).
- Sync from the same domain controller if you only have one (very rarely these days) or the requirement is to sync from one

In our scenario we needed to only sync from one DC, and the issue here arises from the fact that you can't add the same hostname in the configuration screen twice. It'll just tell you it already exists in the configuration.

The only ways to add the same DC again are to use the FQDN or the IP address in addition ot the NETBIOS hostname. Later on you can change that in the config files, but you have to go through the wizard first.

The configuration files you need then are stored in C:\ProgramData\Yammer\DirSync by default.

To locate the globalsettings.config.json file on your Yammer DirSync server, you need to go to the Yammer icon in the notifications tray and then click "About":


Then on the new pop-up window that you'll get, click on "Advanced Configuration":




Once you have the file open, you can implement some LDAP filters on each synchronization connection (I had to do this due to more specific requirements), but you still can't get the OU in the picture. The only way to do it is to use the "OverrideRootNamingContext" parameter which tells the sync tool which is the "root" container in the domain. So in our example, on each of the 5 synchronization connections that were created, we had to add the respective parameters. Let's say our domain name is contoso.local and we only want to sync users from the Test OU. We insert this in the first synchronization connection, and replace "Test" with the other OUs that we'd like to sync in the rest of the synchronization connections.


·   "OverrideRootNamingContext": "OU=Test,DC=contoso,DC=local”,

Friday, March 13, 2015

Cross-Farm Service Application Publishing

To start the process of cross farm service application publishing in SharePoint 2013, first look at the post about establishing STS trust between farms. You also need to have the farm IDs of the farms that are going to consume the service applications in hand.
So, let's assume the STS trust is established, now the steps to publish and then consume the services are:
1. On the source farm, go to Central Admin -> Application Management -> Manage service applications, select the one you want to publish (in this example Search) and click Publish from the top ribbon.
You could also achieve this with PowerShell:
Publish-SPServiceApplication -Identity <ServiceApplicationGUID>


If you do not know the GUID of the service application, you can use the following Windows PowerShell 3.0 cmdlet to list all service applications in the farm, together with their GUIDS:
Get-SPServiceApplication
2. Select the "Publish this Service Application to other farms" option. I recommend using https connection. 
Take a note of the Published URL and save it.
3. Grant permissions to the consuming farms on the source service application by using the consuming farm IDs. 
4. Now, go to the consuming farm(s) Central Admin -> Application Management -> Manage service applicatoins and click Connect. Now insert the address that you've saved in Step 2. The server hostname in the example screenshot is removed on purpose.
5. Now choose the service application that will be provided as a choice to you. In our example - Search Service.
Leave the option “Add this service application’s proxy to the farm’s default proxy list” ticked and click OK.
6. On the next screen, you can choose a name of the service application. Finally you'll get a confirmation that you've connected successfully.

If you're consuming remote Managed Metadata Service, there is an additional setting to be set that will prevent errors when trying to update some of the user profile properties through MySites:
Select this one as well (not related to the issue above), but needed if you want to map custom user profile properties to term sets:





Friday, February 27, 2015

"Object doesn't support property or method 'addEventListener'"

I've just been advised about an issue by two of our customers who are using a form that we've developed in InfoPath and deployed as a solution almost a year ago. It was all working smoothly.

What they're both getting today when trying to fill-out the form is:



I've tested on my dev environment and I could not reproduce the issue with Chrome or IE10 although I have the same version of the solution.

It turned out both customers have upgraded to IE11 and the issue did not exist in Chrome for them.

Both customers are still on SharePoint 2010, so I was eager to turn on the Compatibility View on IE11 and boom, it worked.

A quick research led me to this post and I was very surprised that it also applies to SharePoint 2013. Adding the site URL to the Compatibility  View is a quick and easy workaround, but that really shouldn't be the default behaviour, Microsoft. That breaks a lot of other things, for instance if you're using HTML5 on that site (which is very common nowadays), that would not work and it's a big trade-off. Hopefully that gets addressed within a hotfix anytime soon.

Thursday, February 19, 2015

Establishing STS Trust between SharePoint farms

You might come to a scenario where you have multiple farms and you want to manage some of the service applications centrally on one farm, publish them and consume them from one or more farms.
One of the prerequisites to succeed is to establish STS trust between the farms. This is what this post is all about. In one of my next post I'll write about the service publishing and consuming itself in more details.

So... imagine how it looks like (assume we have only 2 farms, could be n farms):


The steps needed to implement this topology are:

1Export the root certificate on the Services Farm

We will first need to export our Root certificate from the Services Farm. We will use the Get-SPCertificateAuthority cmdlet to export the certificate for our farm.

On the Services Farm, run the following in the SharePoint 2013 Management Shell:

$rootCert = (Get-SPCertificateAuthority).RootCertificate

$rootCert.Export("Cert") | Set-Content "C:\Cert\ServicesFarmRootCert.cer" -Encoding byte

2.  Create a Certificate on the Consuming Farm

On the Consumer Farm, we not only need to export the Root certificate, but also a Secure Token Service (STS) certificate as well. The later can be exported by using the Get-SPSecurityTokenServiceConfig cmdlet. To ease this process, we will also get the Farm ID for our Consuming Farms and create text files with it. The Farm ID will need to be added to the Publishing permissions on the Services Farm so that we can access our services later on.

Here's the PowerShell script you need to run to achieve that, on the first 2 variables you need to replace the values with your server hostnames:

$publisher = "ServicesFarmCAServer"
$consumer = "ConsumingFarmCAServer"
$path = "C:\Cert"
If ((test-path $path) -eq $false)
{
 [IO.Directory]::CreateDirectory("$path")
}
$rootCert = (Get-SPCertificateAuthority).RootCertificate
$rootCert.Export("Cert") | Set-Content "C:\Cert\ConsumingFarmRootCert.cer" -Encoding byte
$stsCert = (Get-SPSecurityTokenServiceConfig).LocalLoginProvider.SigningCertificate
$stsCert.Export("Cert") | Set-Content "C:\Cert\ConsumingFarmSTSCert.cer" -Encoding byte
$farmID = (Get-SPFarm).Id
New-Item C:\Cert\ConsumingFarmID.txt -type file -force -value "$farmID"
Copy-Item \\$consumer\c$\Cert\ConsumingFarmID.txt \\$publisher\c$\Cert

3,  Exchange the certificates between the Consuming and Services farms

Now we have all certificates that we need from the 2 farms. Remember, if you have more than one consuming farms, you need to repeat Step 2 for each of the farm. That's an easy copy-paste operation, however if you have more farms, it makes sense to script it.

$publisher = "ServicesFarmCAServer"
$consumer = "ConsumingFarmCAServer"
Copy-Item \\$publisher\c$\Cert\ServicesFarmRootCert.cer \\$cconsumer\c$\Cert
Copy-Item \\$cconsumer\c$\Cert\ConsumingFarmRootCert.cer \\$publisher\c$\Cert
Copy-Item \\$cconsumer\c$\Cert\ConsumingFarmSTSSTSCert.cer \\$publisher\c$\Cert

4.  Certificate Import on the Services farm

We now want to import all the Consuming farms certificates on the Services Farm and establish a trust. We are required to use the Farm ID to set up our permissions later on. We will rely on the text files we created a few steps back.

Replace ConsumingFarmName with the name you want to refer to the trusted provider/consumer and that's what will be visible later in the Trust section under Central Administration -> Security.

$trustCert = Get-PfxCertificate "C:\cert\ConsumingFarmRootCert.cer"
New-SPTrustedRootAuthority ConsumingFarmName -Certificate $trustCert
$stsCert = Get-PfxCertificate "c:\cert\ConsumingFarmSTSCert.cer"
New-SPTrustedServiceTokenIssuer ConsumingFarmName -Certificate $stsCert
$farmID = Get-Content C:\Cert\ConsumingFarmID.txt
$security = Get-SPTopologyServiceApplication | Get-SPServiceApplicationSecurity
$claimProvider = (Get-SPClaimProvider System).ClaimProvider
$principal = New-SPClaimsPrincipal -ClaimType "http://schemas.microsoft.com/sharepoint/2009/08/claims/farmid" -ClaimProvider $claimProvider -ClaimValue $farmID
Grant-SPObjectSecurity -Identity $security -Principal $principal -Rights "Full Control"
Get-SPTopologyServiceApplication | Set-SPServiceApplicationSecurity -ObjectSecurity $security

5. Certificate Import on the Consuming Farm

We have one final step to wrap up concerning our certificates. On the Consuming Farm(s), we will need to execute the following script to import the Services Farm Root Certificate only.

Replace ServicesFarmName with the name you want to refer to the trusted provider/consumer and that's what will be visible later in the Trust section under Central Administration -> Security.

$trustCert = Get-PfxCertificate "C:\Cert\ServicesFarmRootCert.cer"
New-SPTrustedRootAuthority ServicesFarmName -Certificate $trustCert

That should be it. Considering you've got your user profiles in sync, and you've done everything in this article properly, you are now ready to publish some of your service applications and consume them remotely. This works over WAN as well. As mentioned earlier, one of my next blog posts will focus on the publishing/consuming setup. 

Thursday, February 12, 2015

Free 100 GB OneDrive storage for 2 years, offer expires 28/02

I hope I caught your attention with the title, I just found out Microsoft have created this great offer, described here. I've already activated this on my personal OneDrive account. The only caveat is that you'd need to have U.S.-based IP address to sign up for Bing Rewards, and then you'll get a bit spam from Bing/OneDrive. You can unsubsribe from the mails with 1 click, though.

I think that after the 2 years, such offers will be standard, and I think this will be extended. But anyway, chances are you'll love it and continue the service even if they require something like $7 a month, which now gives you 1 TB and Office 365 Subscription.

Monday, February 9, 2015

Can't map a user proprety to a managed metadata term set

This will be a short one - my favorite type of blog posts :)

The scenario is the following:

Two on-prem SharePoint 2013 farms in remote locations
STS Trust established, tested and working
Farm A has Managed Metadata Service published
Farm B is connected to the Managed Metadata Service on Farm A

I am trying to map one custom user property to a managed metadata term set.
The user property is shown in the user profile and the users have permissions to add values in there.
When I go to add something in this field, I am able to add any term from the whole term store.

When I go in the User Properties in the User Profile Service Application, I can't map it to a specific term set, as the drop down is not visible (see the "Pick a Term Set for this property:" option).



Solution:

In the Service Applicatoins in Farm B, I had to edit the properties of the connection to the published service of Farm A and enable this option:


Now I can choose a Term Set of all available in that term store and map the proprety to it.
The issue and solution are valid even for a local Managed Metadata Service, just edit the properties of the Managed Metadata Service proxy.