The main advantage of using Hyper-V with differencing disks is disk size. Building 2 or 3 separate machines (SP, AD and SQL) might take up to 140GB of disk space. And the SSDs are still expensive these days. Using differencing disks and 2 VM build (SP/SQL + AD) will use only up to 80GB. That’s right – with all the updates, SQL server and Visual Studio 2012 installed.

structure

The diagram above explains the structure of the virtual machines. Windows Server 2012 Parent VM acts as a parent virtual machine. SP 2013 and AD machines are created using that parent’s virtual disk with differencing option.

 

Parent VM

1. Create a new Windows Server 2012 Standard Virtual Machine. Select New in Hyper-V.

a1

2. Assign a name and set storage location.

2

3. Assign memory. Don’t use Dynamic Memory as it doesn’t work with SharePoint well.

s1

4. Configure networking to use external connection. Connections are managed in Virtual Switch Manager in Hyper-V.

4

5. Select Create a virtual disk. Set size to 60GB.

5

If you need to expand disk size later follow these steps:

  • Right click on virtual machine (Hyper-V). Select Settings…
  • Select Hard Drive. Click Edit…
  • Select Expand. Click Next…
  • Specify new size. Click Finish.
  • Start the virtual machine. Go to Administrative Tools -> Computer Management. Select Disk Management. Right click on C: drive. Select Extend Volume…. Specify new size and click Finish.

 6. Select Windows Server 2012 image and install the OS

6

7. Get it to a state that has all you need on all machines.

Common settings:

  • Configure and install Windows Updates.
  • Disable Windows Firewall.
  • Other desired configuration.
  • sysprep it. Run a command window and enter “C:\Windows\System32\Sysprep\sysprep.exe”. Select Generalize and Shutdown. Click OK.

7

Note: Do not start this machine. Ever. If you start this one all other VMs created from this one will stop working.

 

Active Directory VM

Skip these steps if you want to set up AD and SharePoint on the same VM. In that case make sure you install SharePoint and all its prerequisites before going with the AD installation and use dcpromo to install AD instead of using server manager.

1. Create a new Hard Disk. Select New in Hyper-V.

1

2. Select VHDX virtual disk type.

a2

3. Select Differencing.

a3

4. Specify Name and Location of the new disk.

a4

5. Specify a parent virtual disk created previously.

a5

6. Create a new virtual machine. Select New in Hyper-V.

7. Assign a name and set storage location.

a6

8. Assign memory. Don’t use Dynamic Memory.

a7

9. Set connection to Internal. Connections are managed in Virtual Switch Manager in Hyper-V.

a8

10. Select Use an existing virtual hard disk, select the child hard disk created earlier.

a9

11. Run the VM.

12. Launch Server Manager.

13. Click on Add roles and features.

14. Select Role-based or feature-based installation.

15. Add Active Directory Domain Services and DNS Server roles.

a12

16. In Active Directory Domain Services Configuration Wizard select Add a new forest. Specify a name for your domain.

a13

17. Select Next. Fill Domain Controller Options.

a14

18. Follow other steps and complete the installation.

 

 SharePoint VM

The steps are practically the same as creating the AD machine. Follow the previous steps and just change the name from AD to SP:

1. Create a new Hard Disk. Select New in Hyper-V.

2. Select VHDX virtual disk type.

3. Select Differencing.

4. Specify Name and Location of the new disk.

5. Specify a parent virtual disk created previously.

6. Create a new virtual machine. Select New in Hyper-V.

7. Assign a name and set storage location.

8. Assign memory. Don’t use Dynamic Memory.

s1

9. Configure networking to use external connection. Connections are managed in Virtual Switch Manager in Hyper-V.

s2

10. Select Use an existing virtual hard disk, select the child hard disk created earlier.

s3

11. Right Click on SharePoint VM and select Settings…

12. Select Add Hardware and add an internal connection switch.

13. Connect to SharePoint VM, set network configuration so the internal network adapter is in the same network as the AD machine’s adapter. Connect to the domain created on Active Directory VM. Proceed with SQL and SharePoint installation.

SharePoint installation will not be covered in this guide.

Hope this helps!

Paul.

Recently I’ve been having a hard time dealing with managed metadata default values. I’ve been trying to set default values with a piece of code from SharePoint 2010. I guess this is one out of few places where things got changed from previous version.

First things first – scenario: Users add an item to the list. Then an event receiver creates a site collection. 2 managed metadata fields in the newly created site need to have default values set.

This was doable with a fewer lines of code in SharePoint 2010:

1. Get the term and format a default value. Notice the “-1;#” in String.Format(). This is what causes problems in SharePoint 2013:

[..Some code omitted for briefness..]
var terms = placesTermSet.GetTerms(“Altis”, true);
var defaultTerm = terms[0];
var defaultValue = String.Format(“-1;#{0}{1}{2}”, defaultValue.Labels[0].Value, TaxonomyField.TaxonomyGuidLabelDelimiter, defaultValue.Id);

2. Assign it to the DefaultValue property of the field and update it:

field.DefaultValue = defaultValue;
field.Update(true);

SharePoint 2013 in this case doesn’t like the “-1;#” anymore. This should be replaced with a WssId – a unique term ID from TaxonomyHiddenList. This is unique for every site collection. We need to get that before assigning a default value:

1. Get the taxonomy field which will have the default value:

var field = targetSite.Fields.GetFieldByInternalName(“Places”) as TaxonomyField;
var fieldValue = new TaxonomyFieldValue(field);

2. Get the term which will be used as a default:

[..Some code omitted for briefness..]
var terms = placesTermSet.GetTerms(“Altis”, true);
var defaultTerm = terms[0];

3. Get the WssId, format a default value string, assign it and update the field:

fieldValue.TermGuid = Convert.ToString(defaultTerm.Id);
var dummy = fieldValue.ValidatedString;
if (dummy != null)
{
var defaultValue = String.Format(“{0};#{1}{2}{3}”, fieldValue.WssId, defaultValue.Labels[0].Value, TaxonomyField.TaxonomyGuidLabelDelimiter, defaultValue.Id);
field.DefaultValue = defaultValue;
field.Update(true);
}

Hope this helps!

Paul.

Permissions play an important role in SharePoint. Today in my first blog post I’d like to share a solution to a rare exception when SharePoint fails to assign permissions and you are left with an item that you can’t even delete. But first some background on the project which made me break the first rule of SharePoint – never mess with a SharePoint database directly.

The project that I finished working on half a year ago is a business process management system. Lots of lists, lots of libraries, over 30.000 sites (still counting) and of course the biggest pain for users (performance) and support team (issues) – item level permissions. These are assigned using event receivers (ItemAdded and ItemUpdated) based on a Department site column which was used in every site content type. This wouldn’t be an issue if we had kept everything according to Microsoft’s best practices and recommendations. The magic number of 5.000 was left far behind – after half year of usage some lists already had over 25.000 items with item level permissions. Almost every action (opening an item, saving it etc.) took from 3 to 10 seconds.

It was slow, but it worked until we hit about 15.000 items in a single list. Then every once in a while (1 in a 1000) an item was created without any permissions – we called them orphans. Too bad the exception was random and we couldn’t reproduce it. ULS logs would show this error:

System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint ‘Perms_PK’. Cannot insert duplicate key in object ‘dbo.Perms’. The duplicate key value is (9f07b6ef-25e1-4a7a-b06e-f60019e20255, 0x, ok/objektai/16855_.000).  The statement has been terminated.   

Users (except for site collection administrators) weren’t able to see these items in lists. They could open view or edit forms but couldn’t do anything that called an SPListItem.Update() or SPListItem.Delete(). Trying to view item permissions would result in an error:

Cannot complete this action

Luckily the URL contained ID of the SPListItem so we were able to check the properties of that item using PowerShell.

http://site/web/_layouts/User.aspx?obj={60782643-80D2-4F96-956F-C74A56263DC6},16855,LISTITEM&List={60782643-80D2-4F96-956F-C74A56263DC6

Usually items with unique permissions look like this:

HasUniqueRoleAssignments True
EffectiveBasePermissions FullMask
FirstUniqueAncestorSecurableObject Microsoft.SharePoint.SPListItem
ReusableAcl Microsoft.SharePoint.SPReusableAcl
RoleAssignments {Microsoft.SharePoint.SPRoleAssignment, Microsoft.SharePoint.SPRoleAssignment…}
AllRolesForCurrentUser {Full Control, Read, Limited Access, View Only…}
FirstUniqueAncestor Microsoft.SharePoint.SPListItem

but instead our items looked like this:

HasUniqueRoleAssignments
EffectiveBasePermissions FullMask
FirstUniqueAncestorSecurableObject
ReusableAcl
RoleAssignments
AllRolesForCurrentUser
FirstUniqueAncestor

Yes, all of these blank values were nulls. There were no possible ways of fixing these orphans either by code or using PowerShell – we couldn’t delete them (UnauthorizedAccessException) and SPListItem.ResetRoleInheritance() was returning an error System.Runtime.InteropServices.COMException (0x80004005): Cannot complete this action. The only way to do ResetRoleInheritance was to use a SharePoint database stored procedure proc_SecResetItemPerm. It takes SiteId, WebId, OldScopeId, Item Url and DocId as parameters so first we need to get them. PowerShell works great for that. Here is an example script with it’s output:

Add-PSSnapin Microsoft.Sharepoint.Powershell
$site = Get-SPSite http://site
$site.ID
#>>9f07b6ef-25e1-4a7a-b06e-f60019e20255

$web = Get-SPWeb http://site/web
$web.ID
#>>c3536be5-a419-4c27-88fd-269316c18757

$list = $web.Lists[“List”]
$item = $list.GetItemById(16855)
$item.Url
#>>list/16855_.000

$item.UniqueId
#>>fab7dc68-50ad-4ea0-9c99-230dfdbc0567

Now the last parameter that we need is OldScopeId. The way to get it is by querying Perms table:

USE [WSS_Content]
GO

SELECT TOP 1 [ScopeId]
FROM [WSS_Content].[dbo].[Perms]
WHERE ScopeUrl = ‘web/list/16855_.000’
GO

Since we have all the parameters we can execute proc_SecResetItemPerm stored procedure. We do this by writing a query against WSS_Content database:

USE [WSS_Content]
GO

GODECLARE @return_value int,
@NewScopeId uniqueidentifier,
@RequestGuid uniqueidentifier

EXEC @return_value = [dbo].[proc_SecResetItemPerm]
@SiteId = ‘9f07b6ef-25e1-4a7a-b06e-f60019e20255’,
@WebId = ‘c3536be5-a419-4c27-88fd-269316c18757’,
@OldScopeId = ‘B2D7B4C0-8E02-4E5F-A611-020BE5770A8F’,
@Url = N’web/list/16855_.000′,
@DocId = ‘fab7dc68-50ad-4ea0-9c99-230dfdbc0567′,
@NewScopeId = @NewScopeId OUTPUT,
@RequestGuid = @RequestGuid OUTPUT

SELECT @NewScopeId as N’@NewScopeId’,
@RequestGuid as N’@RequestGuid’

SELECT ‘Return Value’ = @return_value
GO

The query returns NewScopeId and we end up with broken role inheritance and no new roles assigned. Now we can assign new permissions or delete that item.

Hope it helps!

Paul.