Tag Archives: SharePoint 2013

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

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-SPEnterpriseSearchServiceApplication : Value cannot be null.

Got this exception when creating a search service application in a multi-server farm:

New-SPEnterpriseSearchServiceApplication : Value cannot be null.

Parameter name: indexLocation

Found the resolution in this TechNet forum post: SharePoint 2013 (RTM) Powershell New-SPEnterpriseSearchApplicationService error about non-existent “indexlocation” parameter ??

The solution in my case wasn’t the accepted answer, rather the answer from iftvio suggesting to start the search and query service instances. This answer was counter-intuitive to me because I was running the script from a server that wasn’t part of the search topology.

Let me explain.

New-SPEnterpriseSearchServiceApplication

In this particular farm, I had the search components dedicated to a specific server. I was running the script from another server in the farm. In my scripts I start the search and query instances on the servers specified in a configuration file. Since the server I was running the scripts from wasn’t a search server it wasn’t in the list of servers on which to start the service instances. It turns out though, that New-SPEnterpriseSearchServiceApplication expects these service instances to be running on the current server.

To start the instances, use Start-SPEnterpriseSearchServiceInstance and Start-SPEnterpriseSearchQueryAndSiteSettingsServiceInstance

In the end my topology is what I expected, I just stopped the service instances once I was done configuring search.

Summary: When running New-SPEnterpriseSearchServiceApplication make sure the server is running the search and query service instances.

Share Button

Link Roundup 1

Here are some interesting links I’ve come across. Consider this a test of a future recurring feature.

A lot happened at the SharePoint Conference 2014 last week. I wasn’t there, though between Yammer and Twitter it felt like Vegas here in the -20C land that is Canada.

Looking for session slides from SharePoint Conference 2014? Check out the external Yammer’s uploaded files! Videos and demos should be realeased in about a month.

Lots of great videos from SPC14 over at channel9

Rackspace released the very addictive Fanatiblaster game based on their fantatical support. I know it’s totally based on real life because every SharePoint administrator carries a dart gun to shoot folders and SharePoint sites. One of the neat things about it is @ShareP911 will tweet out anytime someone beats the high score.

If you’re using CredSSP for PowerShell remoting, know that on Windows versions earlier than Windows Server 2012 R2 and Windows 8.1 that the server you connect to stores your username and password in plain text! Check out Accidental Sabotage: Beware of CredSSP over at PowerShell Magazine

Did you know you can now get SharePoint ULS logs from SharePoint Online using CSOM?

Creating a new Office Web Apps farm on Windows Server 2008 R2 SP1 throws an exception “New-OfficeWebAppsFarm : The operation failed. The server did not meet the following prerequisites: – Windows Update KB2592525 must be installed.” Then, when you try to install the update you get the error “This update is not applicable to your computer.” To resolve this wonderful trick to logic, check out this great post courtesy of Markus Nöbauer: Office Web Apps Server – KB2592525 Installation failed

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