A Master Key to Your Content

Once in a while I find myself in a situation where workflow hasn’t been completed but users have begun content entry or cleanup. Normally these users also have not been setup as site admins, but with some level of custom accesses. This causes the editors to spend time dealing with locking and unlocking items for editing. At times they’ll not even realize and they locked the item, and you start to get a trail of items edited then forgotten.

What’s an admin to do?

Without too much work you can turn-on the gutter icon for ‘Locked Items’, then drill through the tree visual identifying and jotting down what’s locked and who.

Locked Items Gutter Setting

But that’s tedious (and a bit error prone). As with all cool administrative tasks in Sitecore we are best to turn our attention to Sitecore PowerShell Extensions (SPE) in the marketplace

The SPE ships with function that gives us the ability to see and react to locked items on our site.


Step 1 – Import Function

By default the function Get-LockedChildItem is not callable. You must first ‘import’ it into your console or ISE session.

Import-Function Get-LockedChildItem

 Import-Function Get-LockedChildItem

If you don’t perform an import of the function, then you’ll receive a red error message like the following

Get-LockedChildItem : The term ‘Get-LockedChildItem’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

Step 2 – Get a Report

Before performing any sort of automation on our content tree, we should always produce a report that provides some context of what may get changed. In this case we want to get a listing of all items that are locked

Get-LockedChildItem -Recurse | Show-ListView

Here we are using the ‘Recurse’ flag to walk the entire tree. The collection of items locked are then piped to the very nice Show-ListView, allowing for easy review and export.

Get-LockedChildItem as a Report

We can also run the report for specific users via the -LockedBy parameter

Get-LockedChildItem -Recurse -LockedBy 'sitecore\editor' | Show-ListView

Step 3 – Time to Free the Content

Once we understand who has what locked we can start performing some mass freeing of content with the -Unlock parameter. To unlock everything, we run

Get-LockedChildItem -Recurse -Unlock

If we want to perform some hand picking of section of the site, we can either open the console to a certain node of the tree and run the above or define the start point with -Path parameter. This will unlock all the children of the item defined by path, even providing a nice output of what was unlocked.

Get-LockedChildItem -Recurse -Unlock -Path {85E0AF8C-ED9F-4CDA-BFB2-084015E17634}
Get-LockedChildItem -Recurse -Unlock -Path /sitecore/content/Coffeehouse/Home/About-Us

Unlock for a path

Finally, if we want to unlock for a specific individual we can re-use the -LockedBy parameter,

Get-LockedChildItem -Recurse -Unlock -LockedBy 'sitecore\editor'

Not a Replacement for workflow (i.e. the Disclaimer)

Being able to execute unlocking of content on a mass scale is helpful, but this shouldn’t be the replacement for properly planned and built workflow on your site.

SharePoint 2010 SP1 Not Installed

The past few weeks I have been working n scripting out the installation and configuration of SharePoint 2010 and all the services for a multi-server environment. I’ll be posting the details of the script in the coming weeks (okay most likely months….)

For the installation I have been using a slip-streamed set of media which includes SP1 and the June 2012 Cumulative Update. I farm will be using a SQL 2012 database which also provides the advantage of using the new BI features such as PowerView and SSRS as an actual service to SharePoint.

Everything seemed fine until I tried to configure PowerPivot and received a message saying “SharePoint 2010 SP1 is not installed on this machine.” This seemed odd as I’ve been able to configure everything up to this point….

After a few days of googling I finally got the correct search terms, which was “powerpivot error SharePoint 2010 SP1 is not installed”. This led me to a post by Jeff Jones blogger of www.spjeff.com. He has a create article which solved my problem called “Fixed -SharePoint 2010 SP1 is not installed on this computer.

The basic issue is that a registry key does not get updated with the new build version, causing PowerPivot to think everything is out dated. The registry key in question is HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\14.0\BuildVersion.

Since I like scripts here is a little PowerShell goodness to help you out. Be sure that the Microsoft.SharePoint.PowerShell snap-in has been loaded into your PowerShell session before running.

$currentKey = Get-ItemProperty "hklm:\software\microsoft\office server\14.0\" -Name BuildVersion
$farmBuild = (Get-SPFarm).BuildVersion
Write-Host "Registry Build is $($currentKey.BuildVersion)"
Write-Host "Farm Build is $farmBuild"
if($currentKey.BuildVersion -ne $farmBuild)
Write-Host " - Updating registry build to $farmBuild"
Set-ItemProperty "hklm:\software\microsoft\office server\14.0\" -Name BuildVersion -value $farmBuild

Requires Qualified Assembly Name, ugh!

You are plugging away at writing this super sweet class, to be used in your latest application, and then you hit the wall! Nothing builds because the configuration file requires a reference to the fully qualified assembly name…ugh!!!

The first thing you do is scratch your inner mind trying to recall what makes up this name. (At least that is what I end up doing every time.) It seems depending on what you are working on you will either need a four-part name or a ‘five-part name’.

The four-part fully qualified name consists of the following in the following order:

Assembly Name, Version=, Culture=culture code, PublicKeyToken=public key

(It is common for culture to be set to ‘neutral’ if there is nothing contained that is specific to a cultural style.)

What I consider the to be the five-part name is the four-part name with the namespace and class name that is being referenced prefixed.

Namespace.Class Name, Assembly Name, Version=, Culture=culture code, PublicKeyToken= public key

With the background details explained, how as a lazy programmer can we retrieve the fully qualified name without a lot of clicks? The old stand by use to be to fire up reflector, throw your assembly in and poof there was the four part assembly name, but any more finding a really good reflector is much more difficult (and there is a lot of extra clicking involved by using a second program.)

Visual Studio 2010 has a great feature in allowing you to set up single clicks from the menu bar and via hotkeys to external tools…which means a perfect fit for a simple PowerShell command.

  1. Go to Tools -> External Tools.
  2. The External Tools dialog box should open for you. Click Add.
  3. This will then generate a new value labeled [New Tool 1] and have it selected for you.
  4. In the lower half, provide a Tittle for the new tool such as "4-Part Name".
  5. In the Command, textbox you will enter the following
  6. In the Arguments, textbox enter the following
    -command "[System.Reflection.AssemblyName]::GetAssemblyName(\"$(TargetPath)\").FullName
  7. Select Use Output window to force the assembly name into the output window of Visual Studio.
  8. The completed dialog should resemble the following
  9. Click OK.
  10. Your command should now display under Tools.
  11. To get the four part name,
    1. Select the project or any file in the project from Solution Explorer or Solution Navigator
    2. Go to Tools –> 4-Part Name
    3. See the four part name displayed in the Output window

MSDN AssemblyName.GetAssemblyName()
MSDN Type.FullName
MSDN Details on Assemnly Names

All Together Now

This is the final part in my series on how to load users and groups into Active Directory (AD) using the magical powers found in PowerShell. ‘All Together Now’ is combining the different commands that have been reviewed in the earlier parts and how to load the users and group details from an XML file. There are a number of post out in the vast inter-webs which explain how to insert groups and users via a comma delimited file, but I have a slight bias towards the nice hierarchal form of XML, hence this final piece to the puzzle.

I am going to presume you understand the ins and outs of the other commands which were explained and demoed in the first series of post. If you haven’t read the first three parts here are the links to them for reference:

    The first step is to generate the XML. Here is the XML I will be referencing.

    1 <NewUserManagement> 2 <User> 3 <givenname></givenname> 4 <surname></surname> 5 <fullname></fullname> 6 <login></login> 7 <description></description> 8 <password></password> 9 <group></group> 10 <group></group> 11 </User> 12 <User> 13 <givenname></givenname> 14 <surname></surname> 15 <fullname></fullname> 16 <login></login> 17 <description></description> 18 <password></password> 19 <group></group> 20 <group></group> 21 </User> 22 </NewUserManagement>

You will need one ‘User’ node for each user that you will want to generate. Within this node, the ‘group’ node can be repeated for any number of groups that this user will be a member of. One thing that caused me a slight headache was specifically listing the Users domain group. What I learned is that users are automatically added to this group upon add, so no need to list it out.

Here is the definition f the other fields for reference:

     givenname – First Name

     surname – Last Name

     login – User login name for the domain

     description – Description of the user

     password – password in plain text for the user

     group – Name of an existing or new group the user should be a member

Hint 1: When writing multiple functions for a script, these functions must be defined before they are called.

Now that we have our XML written, let’s begin the script.

  1. For this script to be as flexible as possible, we will need to generate a single required parameter for the path to the XML file.

    1 param( 2 [Parameter(Mandatory=$true)] 3 [string] $pathToFile 4 )

  2. The first function that will be written will be the adding of new users to Active Directory.

    1 function addNewUser{ 2 param($uXML) 3 $loginName = $uXML.login; 4 $user = Get-ADUser -Filter {samAccountName -eq $loginName}; 5 if(!$user) 6 { 7 $securePassword = ConvertTo-SecureString $uXML.password -AsPlainText -Force; 8 $user = New-ADUser -SamAccountName $uXML.login -Name $uXML.login -AccountPassword $securePassword -CannotChangePassword $true -PasswordNeverExpires $true -Description $uXML.description -DisplayName $uXML.login -Enabled $true -EmailAddress ($uXML.login + "@rainfly.com") -Server "rainfly.com" -GivenName $uXml.givename -Surname $uXML.surname -PassThru; 9 Write-Host "User $user.SamAccountName was created."; 10 } 11 Sleep -Seconds 5; 12 return $user; 13 }

    The function has a single parameter, uXML, which is the XML object of the entire user node from the file. Line 4, performs a get on the user login name. If the user exists, then an AD user object is returned else it will be empty.

    Hint 2: Objects in PowerShell can be tested for null or nothing, by a simple if statement.


    1 if($obj) 2 { # $obj has a value; } 3 else 4 { # $obj is null or nothing; }

    The not version of this test is performed in Line 5 to determine if the user already exists. If the user does not exist they are added via the New-ADUser command. (When adding new users with a password the ConvertTo-SecureString is required.)

    Finally, the method returns the AD User object to the main script.

  3. The second function is to add the new users to groups.

    1 function addUserToGroup 2 { 3 param($uObj, $groupSamName) 4 $gObj = Get-ADGroup -Filter {samAccountName -eq $groupSamName}; 5 if(!$gObj)#check for null, true is null 6 { 7 $gObj = New-ADGroup -Name $groupSamName -GroupScope DomainLocal -DisplayName $groupSamName -SamAccountName $groupSamName -PassThru; 8 Write-Host "Group $groupSamName was created"; 9 } 10 if($gObj) 11 { 12 try{ 13 Add-ADGroupMember -Identity $gObj -Member $uObj; 14 Write-Host "Added user, $uObj.SamAccountName, to group $gObj.SamAccountName"; 15 }catch 16 { 17 Write-Host "Failed to add user, $uObj.SamAccountName , to $gObj.SamAccountName "; 18 } 19 } 20 else 21 { 22 Write-Host "User was not added to Group $groupSamName." 23 } 24 }

    This method accepts two parameters. The first, $uObj, is the AD user object that is either retrieved or returned after creation from addNewUser method. The second parameter, $groupSamName, is the name of the group as taken from the XML user node’s group element.

    Line 4 performs a retrieval from AD of the group, just as with the addNewUser method, a check is done for null/nothing to determine if the group already exists.

    Hint 3: Version 2.0 of PowerShell introduced try…catch blocks, that you can see beginning on line 12. Here it is used if to catch any issues that occur when adding the user to the group.

  4. Now that the basic methods we will be needing are in place, let’s begin to write the main script. The first line will be

    1 Import-Module active*;

    This line will automatically load the active directory modules for the PowerShell prompt being ran. This saves you the hassle of trying to remember to load the active directory modules, or run the script from the Active Directory PowerShell prompt.

  5. Finally, here is the heavy lifting portion of the script.

    1 if(Test-Path $pathToFile) 2 { 3 [xml]$xmlObj = Get-Content $pathToFile; 4 $allUsers = $xmlObj.NewUserManagement.ChildNodes; 5 if($allUsers) 6 { 7 foreach($userXML in $allUsers) 8 { 9 if($userXML.Name -ne "#comment") 10 { 11 $theUser = addNewUser -uXML $userXML; 12 foreach($userGroup in $userXML.group) 13 { 14 Write-Host "Procsessing Group: $userGroup"; 15 addUserToGroup -groupSamName $userGroup -uObj $theUser; 16 } 17 } 18 } 19 } 20 } 21 Else 22 { 23 Write-Host "The path to the XML file of users and groups is not valid."; 24 Write-Host $pathToFile; 25 }

    Line 1 kicks things off with a simple check to make sure the user XML file does exist. From there the file is parsed via a loop, to load each user and group. One of the great parts about using PowerShell to parse XML is that once the XML file has been loaded, you can use simple dot notation to get at each node, attribute, and element.

There you have it. The final piece to the puzzle, a script that will generate users and groups based on an XML file. I will note that there may be even more efficient ways to perform this task that I haven’t thought of, so please let me and everyone else know in the comments.

Time to Populate

This is part 3 of 4 in generating a script to populate users and groups in Active Directory. Part 3 is all about populating the groups generated with users. The first two parts are:
Part 1 Adding Users to AD Via PowerShell
Part 2 Making the Perfect Group

Step 0 – Import the Active Directory Modules:
Before we can even begin to try out commands we must make sure the Active Directory Modules have been imported. You can skip actually importing by running the predefined PowerShell prompt with Active Directory, which can be found amongst your Administrator Tools on the server. Or the old fashion way and enter the commands.
PS C:\> Import-Modules active*

Step 1 – Find out about the Command
The command which will be used is Add-ADGroupMember. The TechNet details can be found at http://TechNetMicrosoftt.com/en-us/library/ee617210.aspx.
This command allows for either groups, users, service accounts or computers to be added as members to the specified AD group. This cmdlet requires a ADGroup object, which can be returned via the -PassThru parameter when creating a new group(see part 2 for details on creating a new group) or retrieved from AD with the Get-ADGroup cmdlet.

Details on Get-ADGroup can be found on TechNet (http:TechNetMicrosoftoft.com/en-us/library/ee617196.aspx) but just like all the commands we will be needing in part 4, I am going to highlight a few of the key parameters.

Get-ADGroup returns either a single group or can return multiple groups from AD. To get more than one group returned you will have to use the Filter parameter, while to retrieve a single group you will need to provide the Distinguished Name (DN), GUID, security identifier (SID), security accounts manager (SAM) name, or the canonical name.

  • Identity
    • This parameter defines which group or groups will be returned.
    • This value can be a distinguished name, object GUID, Security Identifier (objectSID), or Security Accounts Manager Account Name (sAMAccountName)
    • This is a required parameter.
  • Properties
    • If you want additional properties outside the default set returned, these can be defined with this property.
    • This will be a comma separated list of the attributes.
    • Or you can specifyasteriskrick (*) to get all set attributes returned
    • This is an optional parameter.
    • Example: -Properties OfficePhone,Organization
  • Server
    • This defines the AD DS server which the group should be returned from.
    • Value can be Fully Qualified Domain Name, NetBIOS,Fully Qualified directory server name and port
    • If no server is given the following rules are applied to identify the server:
      1. Server value is taken from any passed in values
      2. Server from the associated with the Active Directory PowerShell provider drive
      3. The domain of the computer running PowerShell
    • This is an optional parameter

Once you hretrievedeved the AD Group object of the desired group (either via Get-ADGroup or using tPassThruThry parameter when creating a new group) it is time to begin adding members to the group. The command used to add members to a group is Add-ADGroupMember. Members that can be added are users, groups, computers, or service accounts.

New members can be identified via distinguished name (DN), GUID, security identifier (SID), SAM account name, or an AD object variable. If multiple members are to be added to the group use a coseparatedated list as the value of the Members parameter. Values parameterseres can not be submitted to Add-ADGroupMember via the pipeline,to do this use Add-ADPrincipalGroupMembership cmdlet.

The details for Add-ADGroupMember can be found on TechNet at httTechNetMicrosoftosoft.com/en-us/library/ee617210.aspx. As with the other commands, I have pulled out some of the key parameters needed for basic usage of the command.

  • Identity
    • Specifies the AD group that members will be added to.
    • This value can be a distinguished name, object GUID, Security Identifier (objectSID), or Security Accounts Manager Account Name (sAMAccountName)
    • In addition to the the above options, this value can be an AD Group object or be passed in via the pipeline.
    • This is a required parameter.
  • Members
    • A separatederated list of the members to be added to the group.
    • Members can be users, computers, groups, and security accounts.
    • This value can be a distinguished name, object GUID, Security Identifier (objectSID), Security Accounts Manager Account Name (sAMAccountName), or AD object variable.
    • This is a required parameter.
  • PassThru
    • Returns an object of the group that has just been modified.
    • By default the command returns nothing, unless this parameter is listed.
    • This is an optional parameter.
  • Server
    • This defines the AD DS server to connect to.
    • Value can be Fully Qualified Domain Name, NetBIOS,Fully Qualified directory server name and port
    • If no server is given the following rules are applied to identify the server:
      1. Server value is taken from any passed in values
      2. Server from the associated with the Active Directory PowerShell provider drive
      3. The domain of the computer running PowerShell
    • This is an optional parameter
  • Credential
    • The actions performed by the cmdlet by default use the credentials ocurrentlyrrenly logged in account running it.
    • This parameter allows for a specredentialsntials to be used to run the command. It accepts only PSCredential object
    • This is optional parameter.

Step 2 – Get and Populate a Group

Now that we have an understanding of the commands needed, lets try them out. First this to do is get the group we want to add members to.
PS C:\>$myGroup = Get-ADGroup -Identity theCoolKids
Check that you got the group
PS C:\>$myGroup = Get-ADGroup -Identity theCoolKids

With the group to add members to, it is time to add those members to the group.
PS C:\>Add-ADGroupMember -Identity $myGroup -Members myTest2Name,SQLUser,SQLAdminGroup

There we have it, how to get a preexisting group out of AD and then add new members to it. The final part of the series will be combining all of these commands into a single script to read an xml file to load Active Directory.

Making the Perfect Group

This is part 2 of my series on adding users and groups to Active Directory (AD) with PowerShell. Part 1 Adding Users to AD Via PowerShell explains the steps to add users. Users are only half the battle. I find that users are best dealt with when grouped into the perfect grouping.

Step 0 – Import the Active Directory Modules:
Before we can even begin to try out commands we must make sure the Active Directory Modules have been imported. You can skip actually importing by running the predefined PowerShell prompt with Active Directory, which can be found amongst your Administrator Tools on the server. Or the old fashion way and enter the commands.
PS C:\> Import-Modules active*

Step 1 – Learn the Command:
The command that will be used for creating groups is New-ADGroup. (A very creative name they gave it. I am slowly realizing that any task you want to perform finding the command isn’t difficult in PowerShell it is determining the correct module that is hard.) The specifics of the command can be found on TechNet (http://technet.microsoft.com/en-us/library/ee617258.aspx), but I will provide a high level review of the parameters most important to my task.

  • Name
    • The name for the group. This sets the AD Name property.
    • This is a required value.
  • GroupScope
    • Defines the scope of the group.
    • This is a required value.
    • Possible values are: DomainLocal or 0, Global or 1, Universal or 2.
    • Example: -GroupScope 1 or -GroupScope Global will both set the group scope value to global
  • Description
    • Provides a description for the group.
  • DisplayName
    • The text to be displayed for the group.
  • PassThru
    • New-ADGroup by default returns no value, this parameter causes the cmdlet to return an object of the newly created group.
  • SamAccountName
    • Defines the Security Account Manager (SAM) for the group.
  • Path
    • Defines the Organizational Unit (OU) which the group should be created in.
    • This expects a string in proper X.500 form.
    • If no value is specified the cmdlet uses the following rules to determine the OU. First, if using the AD PowerShell provider drive, the current path of the provider drive is used. Second, if the cmdlet has a default path, this will be used. Finally, if neither of these cases are true, the Path will default to the partition or naming context of the target domain.
    • -Path "OU=Users,DC=thecodeattic,DC=com"

Step 2 – Let’s make a group:
PS C:\>$myGroup = New-ADGroup -Name "theCoolKids" -GroupScope DomainLocal -Description "This is the coolest gorup of users around." -DisplayName "the very Cool Kids" -SamAccountName "theCoolKids" -PassThru

If you receive no error messages, then there is only one thing left to do. Confirm that the group was generated. Either look in AD for the group or from PowerShell enter

PS C:\$myGroup

Your result should be something similar to the following:

DistinguishedName : CN=theCoolKids,CN=Users,DC=rainfly,DC=com
GroupCategory : Security
GroupScope : DomainLocal
Name : theCoolKids
ObjectClass : group
ObjectGUID : 5cd2fe70-dbe7-4ed3-b996-546d792efd2c
SamAccountName : theCoolKids
SID : S-1-5-21-1333310011-458043100-2074871380-1138

That was fun. Part 3 will be a look at adding those users to groups.

Adding Users To AD Via PowerShell

A lot of people I am sure have written about PowerShell scripts to add new users and then automate the whole process. Many of these I have found revolve around the very nice Quest Software cmdlets, but I wanted to understand the inner workings plus generate a full script that will load users and group from a file.

This is part 1 of 4 in how to automate user creation. The steps for all four parts should work if you are directly on the server with the AD role or using Remote Server Administration Tools (RSAT) via Windows 7.

Step 1 – AD PowerShell Modules:

Before we can begin the script we need to confirm that the Active Directory modules have been loaded. At the PowerShell prompt enter: Get-Command –Module active* | Measure-Object

This command will query all loaded modules for the server which begin with ‘active’ and provide a count of what is found (| Measure-Object). Your result will look like the following if the module has not been loaded.


Your are looking for a count of 76. If you got a count of 76 or more skip on to Step 2. For the rest of us, we now need to load the AD Modules so we can write get to the fun part and write a script. At the PowerShell prompt enter: Import-Module active*
The wildcard is used again to get all those modules that are related, saves time and energy retyping each modules name. Depending on the speed of the machine you may see a green bar appear quickly at the top of the PowerShell window which shows the progress.

Perform another Get-Command –Module active* | Measure-Object to confirm that the AD Modules have loaded.


Now we are ready to begin the script.

Step 2 – Learn the Command:

The cmdlet used to add new users is New-ADUser. By using the Get-Help cmdlet you can learn the details or review the details on MSDN at http://technet.microsoft.com/en-us/library/ee617253.aspx.

Here are some of the highlights to the New-ADUser cmdlet I found most important for our script. Most of the common AD properties that you normally would set are available as parameters, any additional properties to be set can be included as part of the –OtherAttributes parameter.

As I just want some basic type users for development purposes we will not be concerned with this parameter. Parameters that we will be using are:

  • SamAccountName
    • This parameter is the Security Account Manager (SAM) value for the user created.
    • This is a required value
  • Name
    • String name to identify the new user by.
    • This is a required value.
  • AccountPassword
    • Provides the password for the new user.
    • Password setting can fail if the password does not meet the password policy restriction. The user account will still be created though.
    • This parameter requires a secure string value, this can be generated via the a separate object or entered via a prompt.
    • Example – via prompt: –AccountPassword (Read-Host –AsSecureString “password”)
    • Example – as object: $thePassword = ConvertTo-SecureString "password" -AsPlainText -Force;
  • CannotChangePassword
    • Use $false or $true
  • PasswordNeverExpires
    • Use $false or $true
    • Cannot be set true if ChangePasswordAtLogon is true
  • Description
    • Defines the description of the new user.
  • DisplayName
    • Defines the name displayed for the user.
  • Enabled
    • Use $false or $true
    • This defaults to false, so be sure to set it accordingly.
  • EmailAddress
    • The user’s email address.
  • Server
    • The domain server which should be connected to.
    • This will be defaulted when not supplied by the following: from the Server value from objects passed through the pipeline, server information with the AD PowerShell provider, domain of the computer running PowerShell
  • Path
    • This parameter is used to set the Organizational Unit (OU) or container the new user is to be added to.
    • If this parameter is not defined then the cmdlet will create the new user in the default user container for the domain.
  • PassThru
    • With this parameter the user object created is returned.

Step 3 – Tryout the Command:
Before I begin a script I like to write the single command once to make sure it behaves as I expect. So at the command prompt enter:

$thePassword = ConvertTo-SecureString "password" -AsPlainText -Force;

Then enter at command prompt:
New-ADUser -SamAccountName "myTest2SAM" -Name "myTest2Name" -AccountPassword $thePassword -CannotChangePassword $true -PasswordNeverExpires $true -Description "Test description" -DisplayName "myTest2DisplayName" -Enabled $true -EmailAddress "myTest2@rainfly.com" -Server "rainfly.com";