Category Archives: PowerShell

Automating a tedious task: Testing SharePoint page performance

Here’s a quick and dirty PowerShell script that hits a supplied SharePoint-related URL and outputs the correlation ID so you can check the ULS logs to further investigate why the page took longer than expected to load.

The script takes three parameters:

  1. SharePointUrl: The URL to test. Can be a SharePoint site collection, site, list, library, page. A resource in SharePoint.
  2. numTries: The number of times to try hitting the URL. Default is 1000
  3. unnaceptableTime – how long you deem is “too long” — milliseconds (i.e. 1000 = 1 second)

When you run the script, it will try connecting to the SharePoint site repeatedly and output attempts that take too long.

When SharePoint receives requests from users, it times how long it takes to process the request and returns this time in the SPRequestDuration HTTP header (in milliseconds). This script compares the returned SPRequestDuration to the unacceptableTime parameter and if the request took longer, it will output the SPRequestGUID which you can then use with Merge-SPLogFile to pull the ULS logs for the request.

param (
	
	$SharePointUrl = "https://yoursharepoint.example.com/demo/TypicalSharePointSite/SitePages/SomePageToTest.aspx",
	
	# Number of times to try hitting the page
	$numTries = 1000,
	
	# Number of milliseconds we'll say is too long for the page to return
	$unacceptableTime = 8000	
)

$timesTooLong = 0

# Repeat the number of times specified by $numTries
for ($i = 0; $i -lt $numTries; $i++) {

	# Build our URL to query SharePoint
	# We're going to abuse the rev parameter to trick our session into not caching the results from each query
	$requestUri = $SharePointUrl + "?rev=$i"
	
	# Issue our request to the fine SharePoint Server
	$Request = Invoke-WebRequest -Uri $requestUri -UseDefaultCredentials
	
	# The request took too long so send out the details
	if ([int]$Request.Headers["SPRequestDuration"] -gt $unacceptableTime) {
	
		Write-Output "$($Request.Headers["SPRequestGuid"]) $($Request.Headers["SPRequestDuration"])ms $requestUri"
		
		$timesTooLong++
	
	}
	
	
}

Write-Output "$timesTooLong request(s) took longer than $($unacceptableTime)ms"
Share Button

Clear the Configuration Cache in a SharePoint 2010, 2013, or 2016 farm

Here is a script that will clear the SharePoint Configuration Cache on all servers in the farm. This is a more compact script than the one I previously released.

Simply copy to one of the servers and run in an elevated PowerShell window. The script will determine where the configuration cache is stored on all servers in the farm, stop the timer services, clear the cache, reset the counter, and start the timer services again.

This script is provided as-is.

<#
.SYNOPSIS
Clear the Configuration Cache in a SharePoint 2010, 2013, or 2016 farm
  
.DESCRIPTION
Clear-SPConfigCache.ps1 will:
 1. Stop the SharePoint Timer Service on all servers in the farm
 2. Delete all xml files in the configuration cache folder on all servers in the farm
 3. Copy the existing cache.ini files as a backup
 4. Clear the cache.ini files and reset them to a value of 1
 5. Start the SharePoint Timer Service on all servers in the farm

Clear-SPConfigCache.ps1 will work in either single-server and multi-server farms.

Run in an elevated SharePoint Management Shell

Author: Jason Warren

.LINK
 http://jasonwarren.ca/ClearSPConfigCache ClearSPConfigCache.ps1
 
.EXAMPLE
 .\Clear-SPConfigCache.ps1
 
.INPUTS
None. Clear-SPConfigCache.ps1 does not take any input.

.OUTPUTS
Text output that describes the current task being performed.

#>


Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction Stop
$farm = Get-SPFarm
$ConfigDB = Get-SPDatabase | where {$_.Name -eq $Farm.Name}

# Configuration Cache is stored in %PROGRAMDATA\Microsoft\SharePoint\Config\[Config ID GUID]
# %PROGRAMDATA% is C:\ProgramData by default, it is assumed it's in the same location on all servers in the farm
#	i.e. if it's X:\ProgramData on one server, it will be X:\ProgramData on the others
# We'll be connecting via UNC paths, so we'll also change the returned DRIVE: to DRIVE$
$ConfigPath = "$(($env:PROGRAMDATA).Replace(':','$'))\Microsoft\SharePoint\Config\$($ConfigDB.Id.Guid)"

# Stop the timer service on all farm servers
$TimerServiceName = "SPTimerV4"
foreach ($server in $farm.TimerService.Instances.Server) {
	Write-Output "Stopping $TimerServiceName on $($server.Address)..."
	$service = Get-Service -ComputerName $server.Address -Name $TimerServiceName
	Stop-Service -InputObject $service -Verbose
} # Foreach server


$TimeStamp = Get-Date -Format "yyyymmddhhmmssms"

# Clear and reset the cache on each server in the farm
foreach ($server in $farm.TimerService.Instances.Server) {

	Write-Output $server.Address
	
	# build the UNC path e.g. \\server\X$\ProgramData\Microsoft\SharePoint\Config\00000000-0000-0000-0000-000000000000
	$ServerConfigPath = "\\$($server.Address)\$($ConfigPath)"
	
	# Delete the XML files
	Write-Output "Remove XML files: $ServerConfigPath..."
	Remove-Item -Path "$ServerConfigPath\*.xml"
	
	# Backup the old cache.ini
	Write-Output "Backup $ServerConfigPath\cache.ini..."
	Copy-Item -Path "$ServerConfigPath\cache.ini" -Destination "$ServerConfigPath\cache.ini.$TimeStamp"
	
	# Save the value of "1" to cache.ini
	Write-Output "Set cache.ini to '1'..."
	"1" | Out-File -PSPath "$ServerConfigPath\cache.ini"

	Write-Output ""
	
} #foreach server

#Start the timer service on all farm servers
foreach ($server in $farm.TimerService.Instances.Server) {
	Write-Output "Starting $TimerServiceName on $($server.Address)..."
	$service = Get-Service -ComputerName $server.Address -Name $TimerServiceName
	Start-Service -InputObject $service -Verbose
	
}
Share Button

Can’t create My Site because user is ‘Enqueued’

Symptom

A user’s personal site (i.e. their My Site) cannot be created. Looking at the ULS logs under the Personal Site Instantiation category, the creation failed because it is already queued.

[Enque rejected] MySiteInstantiationWorkItemJobDefinition::AddWorkItem: Not queuing Interactive MySite instantiation for: 'EXAMPLEDOMAIN\username' since the user is already pending in the queue.

(event id: ajjuq)

Root Cause

The user’s PersonalSiteInstantiationState user profile object property has been set to Enqueued. When the My Site Instantiation timer jobs run they ignore creating the site because it is already in the queue.

You can verify the value of the property with the following PowerShell script:

$SiteUrl = "https:/example"
$Username = "EXAMPLEDOMAIN\username"

Add-PSSnapIn Microsoft.SharePoint.PowerShell
$Site = Get-SPSite $SiteUrl
$ServiceContext = Get-SPServiceContext $Site
$UserProfileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($ServiceContext)
$UserProfile = $upm.GetUserProfile($UserName)
$UserProfile.PersonalSiteInstantiationState

The script will return the value of PersonalSiteInstantiationState. It will be set to Enqueued. For reference, see the MSDN for all possible values of the PersonalSiteInstantiationState enum

Resolution

Force the creation of the personal site using the CreatePersonalSite() method. https://msdn.microsoft.com/en-us/library/jj163964.aspx

$SiteUrl = "https:/example"
$Username = "EXAMPLE\username"

Add-PSSnapIn Microsoft.SharePoint.PowerShell
$Site = Get-SPSite $SiteUrl
$ServiceContext = Get-SPServiceContext $Site
$UserProfileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($ServiceContext)
$UserProfile = $upm.GetUserProfile($UserName)
$UserProfile.CreatePersonalSite()
Share Button

Which server or farm is SharePoint using for Office Web Apps?

If you find yourself with an unfamilar SharePoint 2013 farm with a problem with Office Web Apps the first step you’ll probably take is to figure out where Office Web Apps is running. Finding this out is simple!

Open an elevated SharePoint Management Shell on one of the SharePoint servers in the farm with an account with SPShellAdmin rights and run the following cmdlet:

Get-SPWOPIBinding

The output will list all the bindings by application/extension and zone. Find the combination that is causing you grief and look at the ServerName property. This is the public hostname used by Office Web Apps. If it’s a server name go connect to it. If it’s a DNS name that abstracts away a server name or load balancer you’ll need to do more digging but at least you’re heading in the right direction. ūüôā

Share Button

Get a list of SharePoint sites that allow anonymous authentication

For administrators who manage (usually) public-facing SharePoint farms, it’s good to keep tabs on the sites that allow anonymous users. Configuring anonymous access is a multi-step process — there are web application settings, site settings, and permissions. Although there is a lot of configuration, it’s easy to see which sites allow anonymous authentication using the SPWeb.AnonymousState site-level flag. Using this property we can write a short PowerShell script to list out all the sites in the farm and display whether or not they allow anonymous authentication.

SharePoint 2013 and SharePoint 2010

For SharePoint 2010, SharePoint 2013, and presumably future versions of SharePoint, getting this list is simple using the Get-SPSite cmdlet which can be used to list every site collection in the farm:

$sites = Get-SPSite -Limit All
foreach ($site in $sites) {
	$site.AllWebs | Select Url, AnonymousState
}

The output will look like something like this:

Url                                         AnonymousState
---                                         --------------
http://example.com                                 Disabled
http://example.com/searchcenter                    Disabled
http://example.net                                      On
http://example.net/About                                On
http://example.net/Careers                              On
http://example.net/Contact                              On
http://example.net/Search                               On

SharePoint 2007

If you have PowerShell installed on a server in the farm (you really should), you can use PowerShell to build this handy report just it takes a bit more effort:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$farm = [Microsoft.SharePoint.Administration.SPFarm]::Local
$websvcs = $farm.Services | where -FilterScript {$_.GetType() -eq [Microsoft.SharePoint.Administration.SPWebService]}
$webapps = @()
foreach ($websvc in $websvcs) {
	foreach ($webapp in $websvc.WebApplications) {		
		foreach ($site in $webapp.Sites) {
		
			$Site.AllWebs | Select Url, AnonymousState
		
		}
    }
}

The output will look look the same as with later versions of SharePoint:

Url                                          AnonymousState
---                                          --------------
http://example.com                                 Disabled
http://example.com/searchcenter                    Disabled
http://example.com/sites/Example                         On
http://example.com/sites/Example/Site1                   On
http://example.com/sites/Example/Site1/SubSite           On

(HT to Gary Lapointe for the code to get the web applications in a SharePoint 2007 farm using PowerShell: Getting an SPWebApplication object using PowerShell.

Share Button

Add and remove MIME types from SharePoint (PowerShell)

Petro Margaritis has a PowerShell script for adding a MIME type to a SharePoint web application: Setting Trusted IIS MIME Types In SharePoint using PowerShell. I modified it to allow you to remove the MIME type if it already exists:

Write-Host "This script will check if a particular MIME Type is excluded from the AllowedInlineDownloadedMimeTypes list when STRICT Browser File Handling Permissions are set on the Web Application" -foregroundcolor Darkcyan
$webAppRequest = Read-Host "What is the name of your Web Application? i.e. http://<serverName>"
$webApp = Get-SPWebApplication $webAppRequest
$mimeType = Read-Host "Which MIME Type would you like to confirm is included in the AllowedInlineDownloadedMimeTypes list for $webApp ? i.e. application/pdf"
If ($webApp.AllowedInlineDownloadedMimeTypes -notcontains "$mimeType")
{
    write-host "$mimeType does not exist in the AllowedInlineDownloadedMimeTypes list" -foregroundcolor Yellow
    $addResponse = Read-Host "Would you like to add it? (Yes/No)"
    if ($addResponse -contains "Yes")
    {
        $webApp.AllowedInlineDownloadedMimeTypes.Add("$mimeType")
        $webApp.Update()
        Write-Host "The MIME Type ' $mimeType ' has now been added" -foregroundcolor Green
        $iisresponse = Read-Host "This change requires an IIS Restart to take affect, do you want to RESET IIS now (Yes/No)"
        if ($iisResponse -contains "Yes")
        {
            IISRESET
            Write-Host "IIS has now been reset" -foregroundcolor Green
        }
        else
        {
            Write-Host "IIS has not been reset, please execute the IISRESET command at a later time" -foregroundcolor Yellow
        }
    }
    else
    {
        Write-Host "The MIME Type ' $mimeType ' was not added" -foregroundcolor Red
    }
}
else
{
    Write-Host "The MIME Type ' $mimeType ' already exists in the AllowedInlineDownloadedMimeTypes list for this Web Application" -foregroundcolor Yellow
	$addResponse = Read-Host "Would you like to remove it? (Yes/No)"
    if ($addResponse -contains "Yes")
    {
		$webApp.AllowedInlineDownloadedMimeTypes.Remove("$mimeType")
        $webApp.Update()
        Write-Host "The MIME Type ' $mimeType ' has now been removed" -foregroundcolor Green
        $iisresponse = Read-Host "This change requires an IIS Restart to take affect, do you want to RESET IIS now (Yes/No)"
        if ($iisResponse -contains "Yes")
        {
            IISRESET
            Write-Host "IIS has now been reset" -foregroundcolor Green
        }
        else
        {
            Write-Host "IIS has not been reset, please execute the IISRESET command at a later time" -foregroundcolor Yellow
        }
	}
    else
    {
        Write-Host "The MIME Type ' $mimeType ' was not removed" -foregroundcolor Red
    }

}
Share Button

Script: Clearing the SharePoint configuration cache on all servers in the farm

Need to clear the configuration cache on all of your SharePoint servers but don’t want to take the time to do it manually?

If you’ve configured PowerShell Remoting between the farm servers and are logged onto any server in the farm with an administrative account, you can run this script to automate this process.

Note the folder is for SharePoint 2010 and SharePoint 2013.

I borrowed the bulk of the clearing code from Thomas Bernhard’s blog post Clear SharePoint Config Cache with PowerShell and wrapped it in a script block so we can run it remotely.

Save the code below in a .ps1 and run it from one of the SharePoint servers in the farm in an elevated PowerShell window (you could also just paste it into the PowerShell window if you’re into that sort of thing).

#Script block for stopping the timer service
$sb_stop = {
	Write-Output "`t$($env:computername)"
	Stop-Service -Name "SPTimerV4"
}

#Script block for starting the timer service
$sb_start = {
	Write-Output "`t$($env:computername)"
	Start-Service -Name "SPTimerV4"
}

#Script block for finding and clearing the configuration cache
$sb_cache = {
	Write-Output "`t$($env:computername)"
	
	$folders = Get-ChildItem "C:\ProgramData\Microsoft\SharePoint\Config"
	
	foreach ($folder in $folders) {
		$items = Get-ChildItem $folder.Fullname -Recurse
		foreach ($item in $items) {
			if ($item.Name.ToLower() -eq "cache.ini") {
				$cachefolder = $folder.FullName
			} #if
		} #foreach item
	} #foreach folder
	
	Write-Output "`tDeleting xml..."	
	$cachefolderitems = Get-ChildItem $cachefolder -Recurse
	foreach ($cachefolderitem in $cachefolderitems) {
		if ($cachefolderitem -like "*.xml") {
			$cachefolderitem.Delete()
		} #if
	} #foreach
	
	Write-Output "`tResetting cache ini..."
	$a = Get-Content $cachefolder\cache.ini
	$a = 1
	Set-Content $a -Path $cachefolder\cache.ini

} # sb


Add-PSSnapin Microsoft.SharePoint.PowerShell
$servers = Get-SPServer | where {$_.Role -eq "Application"} | select -ExpandProperty Address
Write-Output "Servers in the farm: $servers"


#Stop timer
foreach ($server in $servers) {
	Write-Output "Stopping Timer Service on $server"
	Invoke-Command -Computername $server -ScriptBlock $sb_stop
	Write-Output " "
}

#Clear cache
foreach ($server in $servers) {
	Write-Output "Clearing configuration cache on $server"
	Invoke-Command -Computername $server -ScriptBlock $sb_cache
	Write-Output " "
}

#Start Timer
foreach ($server in $servers) {
	Write-Output "Starting Timer Service on $server"
	Invoke-Command -Computername $server -ScriptBlock $sb_start
	Write-Output " "
}

The output will look something like this:
clearconfigcache

Share Button

New-SPProfileServiceApplication -MySiteManagedPath documentation bug

Hot on the heels of my previous post, New-SPProfileServiceApplication -SiteNamingConflictResolution documentation bug, the -MySiteManagedPath parameter also has inconsistent documentation with the actual cmdlet in the wild.

[Note for reference this applies at the time of this writing to SharePoint Server 2013 with the March 2013 PU + December 2013 CU, farm buildversion 15.0.4551.1508]

TechNet defines the MySiteManagedPath parameter like this:

Parameter Required Type Description
MySiteManagedPath Optional System.String Specifies the managed path where personal sites will be created.The type must be a valid URL, in the form http://server_name.

And Get-Help New-SPProfileServiceApplication -Full like so:

-MySiteManagedPath <SPPrefixPipeBind>
    Specifies the managed path where personal sites will be created.


    The type must be a valid URL, in the form http://server_name.



    Required?                    false
    Position?                    Named
    Default value
    Accept pipeline input?       True
    Accept wildcard characters?  false

Both documentation references say you need to provide a valid URL. Of course, you’re providing a managed path so a better example would be something like “http://server_name/managedpath”.

When you specify a URL the cmdlet will throw a Microsoft.SharePoint.PowerShell.SPCmdletPipeBindException with the message:

Cannot find an SPPrefix object with Name: http://server_name/managedpath

What the cmdlet really wants is the actual name of the managed path. The cmdlet runs as expected when you give it a managed path name (that exists!) instead of a URL.

For example, say you have a My Sites web application http://mysites and you’ve created a Wildcard Inclusion managed path called “personal”.

The documentation says to do this (incorrect):

New-SPProfileServiceApplication ... -MySiteManagedPath "http://mysites/personal"

Instead give it the name of the managed path like this (correct):

New-SPProfileServiceApplication ... -MySiteManagedPath "personal"
Share Button

New-SPProfileServiceApplication -SiteNamingConflictResolution documentation bug

As of the writing of this post, there’s a weird discrepancy with the SharePoint 2013 New-SPProfileServiceApplication documentation and the actual expected values for the -SiteNamingConflictResolution parameter. This parameter allows you to specify the resolution method for naming personal sites when two users in different domains share the same account name.

[Note for reference this applies at the time of this writing to SharePoint Server 2013 with the March 2013 PU + December 2013 CU, farm buildversion 15.0.4551.1508]

From TechNet:

Parameter Required Type Description
SiteNamingConflictResolution Optional System.String Specifies the format to use to name personal sites.

Use one of the following integer values:

1   Personal site collections are to be based on user names without any conflict resolution. For example, http://portal_site/location/username/

2¬†¬†¬†Personal site collections are to be based on user names with conflict resolution by using domain names. For example, …/username/ or …/domain_username/

3   Personal site collections are to be named by using domain and user name always, to avoid any conflicts. For example, http://portal_site/location/domain_username/

The default value is 1 (do not resolve conflicts).

Please ignore for a moment that the type is a String and it says you need to specify an integer value. There’s a similar description from Get-Help New-SPProfileServiceApplication -Full:

-SiteNamingConflictResolution
    Specifies the format to use to name personal sites.

    Use one of the following integer values:

    1   Personal site collections are to be based on user names without any conflict resolution. For example,
    http://portal_site/location/username/

    2   Personal site collections are to be based on user names with conflict resolution by using domain names.
    For example, .../username/ or .../domain_username/

    3   Personal site collections are to be named by using domain and user name always, to avoid any conflicts.
    For example, http://portal_site/location/domain_username/

    The default value is 1 (do not resolve conflicts).

    Required?                    false
    Position?                    Named
    Default value
    Accept pipeline input?       False
    Accept wildcard characters?  false

Alright, so to specify the way I want to handle username conflicts for personal sites, I need to provide either a value of 1, 2, or 3 … or maybe “1”, “2”, or “3”. Of course, this blog post wouldn’t exist if this worked as written and both options are incorrect. ūüôā

Specifying any of these values throws a System.Management.Automation.ParameterBindingValidationException with the message:

Cannot validate argument on parameter ‘SiteNamingConflictResolution’. The argument “2” does not belong to the set “None,Resolve,Block” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again.

[Note: Whether you provide an integer (e.g. 2) or string (e.g. “2”) the exception message puts your value in quotes]

So the numbers don’t work because the parameter is validated against this set of values:

  • “None”
  • “Resolve”
  • “Block”

These values match up to the integers discussed in the documentation:

  1. “None” – no conflict resolution (prepare for fights!). Use this only if your users are in a single domain
  2. “Resolve” – add on the domain name for any conflicting accounts after the first exists. This is useful if most of your users come from one domain and there are some users in a different domain
  3. “Block” – block conflicts by always including the domain name. Use this if you have more than one domain and lots of users

Replace the numbers in your script with these values (1 -> “None”, 2 -> “Resolve”, 3 -> “Block”) and you should now be able to create your user profile service application.

Share Button

Are we using SharePoint Enterprise Features?

Here’s a quick PowerShell script (actually two scripts) that will go through your web applications, site collections, and sites and tell you which ones are using Enterprise features.

The first script is for SharePoint 2010 and SharePoint 2013:

Add-PSSnapin Microsoft.SharePoint.PowerShell
foreach ($webapp in Get-SPWebApplication) {

	foreach ($feature in $webapp.Features) {

		if ($feature.Definition.Displayname -eq "PremiumWebApplication") {
			Write-Output "$($Webapp.DisplayName) contains enterprise web application features"
		} # if enterprise web application feature

	} # foreach web application feature

	foreach ($site in $webapp.Sites) {

		foreach ($feature in $Site.Features) {

			if ($feature.Definition.Displayname -eq "PremiumSite") {
				Write-Output "$($Site.Url) contains enterprise site collection features"
			} # if enterprise site collection feature

		} # foreach site collection feature

		foreach ($web in $site.AllWebs) {

			foreach ($feature in $web.Features) {

				if ($feature.Definition.Displayname -eq "PremiumWeb") {
					Write-Output "$($web.Url) contains enterprise site features"
				} # if enterprise site feature

			} # foreach site feature

		} # foreach site

	} # foreach site collection

} # foreach web application

The second is for SharePoint 2007:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$farm = [Microsoft.SharePoint.Administration.SPFarm]::Local
$websvcs = $farm.Services | where -FilterScript {$_.GetType() -eq [Microsoft.SharePoint.Administration.SPWebService]}
$webapps = @()
foreach ($websvc in $websvcs) {
    foreach ($webapp in $websvc.WebApplications) {  

		foreach ($feature in $webapp.Features) {
						
			if ($feature.Definition.Displayname -eq "PremiumWebApplication") {
				Write-Output "$($Webapp.DisplayName) contains enterprise web application features"
			} # if enterprise web application feature
			
		} # foreach web application feature
			
        foreach ($site in $webapp.Sites) {
		
			foreach ($feature in $Site.Features) {
				
				if ($feature.Definition.Displayname -eq "PremiumSite") {
					Write-Output "$($Site.Url) contains enterprise site collection features"
				} # if enterprise site collection feature
				
			} # foreach site collection feature
	
			foreach ($web in $site.AllWebs) {
			
				foreach ($feature in $web.Features) {
				
					if ($feature.Definition.Displayname -eq "PremiumWeb") {
						Write-Output "$($web.Url) contains enterprise site features"
					} # if enterprise site feature
					
				} # foreach site feature
				
			} # foreach site
			
		} # foreach site collection
		
	} # foreach web application
						
} # foreach web service

The output will look something like this:

WebAppExample1 contains enterprise web application features
http://webappexample1/sites/Test contains enterprise site collection features
http://webappexample1/sites/Test contains enterprise site features
http://webappexample1/sites/Test/Site1 contains enterprise site features
http://webappexample1/sites/Test/Site1/SubSite contains enterprise site features
http://webappexample1/sites/testteamsite contains enterprise site collection features
http://webappexample1/sites/testteamsite contains enterprise site features
WebAppExample2 contains enterprise web application features
MySites contains enterprise web application features
SSPAdmin contains enterprise web application features
http://sspadmin/ssp/admin contains enterprise site collection features
http://sspadmin/ssp/admin/Content contains enterprise site features
Share Button