How to Fix Navigational Links After Relocating a Site Collection

This post is going to discuss how to fix navigational links after relocating a site collection within the same web application. Relocating a site collection to a new URL is a relatively common task. One reason you may relocate a site collection is if a certain area within your organization is suddenly renamed as part of an organizational change. Hopefully this blog post will help you overcome this type of situation with relative ease.  Though it is possible to fix up the quick launch navigation as well, the sample script I'm providing only fixes top link navigation links.


I was recently working with a customer that was going through an organizational change. Part of that organizational change required that the URL of a few site collections be changed. In order to accommodate the required URL change, the site collection was backed up and restored to a new URL. Scattered throughout the environment was hundreds of links that pointed to the previous URL for the site collection. Rather than manually browse the entire organizational structure to make sure that all navigational structures were updated to the new URL of the site collection, we decided to leverage PowerShell. This proved to be a significant reduction in total effort required.


The overall approach to this solution is pretty straightforward. What we want to do is discover any links that are used in the site navigation which contain a reference to the old URL, and update them with a reference to the new URL. Based on my particulars needs in my situation, I started at the web application level. I could have easily started at the farm level, or even at the site collection level in a relatively simple manner. Of course, the navigational elements are actually scoped at the web level, so we need to find a way to get all the way down to enumerating all of the webs, having provided only a web application. That doesn't sound too hard, right? So first we'll start with Get-SPSite –WebApplication $WebapplicationURL. This will return us with a list of all SPSites. From here we can basically loop through all of these site collections, enumerating all webs. This can be accomplished by using $SPSite.allwebs. Now we have a collection of webs. For each of these webs we'll have to evaluate the navigation nodes, which we will look at in the solution part of this post.


Now that we know what our approach is, let's take a look at how to put all of that together. The first step I've taken is to get a list of all site collections within the scope of the supplied web application. I gave away the answer up top, but here's what that looks like:

We all do this every day, so there's nothing new here. The next thing we have to do, is create several nested foreach loops. Since everything is nested, it's hard to show how all that works in a way that's easy to follow. So first I'm going to explain how foreach works, and then I'll explain the individual parts that are in each nested foreach loop. A foreach loop basically takes a collection of objects, and executes your PowerShell script block against each of the elements in the collection. The syntax you use is as follows:

Foreach($Object in $Collection)
PowerShell script block in here

The following screenshot illustrates that very concept:

Hopefully I haven't lost anybody yet. Getting back to where we were, we have a list of site collections, now we need a list of webs. How do we do that? We'll want to make a foreach loop for each site collection. We want to return all webs in all site collections. This is simple enough. Inside our foreach block, we'll do the following:

$TargetWebs = $TargetSite.allwebs

Now we have a collection of webs, however each web has its own set of navigational controls. This includes the global navigation (top links) and the current navigation (quick launch). This means we have one more foreach loop which will enumerate through all webs in $TargetWebs. In that loop, we'll want to get the navigation control. We can do this by getting the navigation property of the web. Returning the navigation object isn't that exciting.

And now we're getting to the moment we've all been waiting for, accessing the actual links! This we can do getting the TopNavigationBar or QuickLaunch properties of the TargetNav object. That should look like this:

The next thing we need to do is do a foreach loop to enumerate through all of the elements in the TopNavigationBar or QuickLaunch. In this loop, we're going to be converting the URL returned to a string object, and using split against it to split each string at each "/". We can then use another foreach loop. When we encounter a chunk of the string that contains the old URL, we replace it with the new URL, and then rebuild the string. Once we've completed this, we update the url property of the object with the new string. Since relative links start with a leading slash, this will cause our new link to start with a double forward slash. For that reason, we use substring(1), to start at the second position in the array – or simply remove the first leading slash.

The next step is just a simple update to the property using PowerShell.

This can also be verified in the UI

Download This Script

Download UpdateTopLinks.ps1 (zipped)


This script accepts four parameters. Once is the web application url, one is the old URL chunk, one is the new URL chunk, and the last is the logging directory. Simply update these four parameters and run the script.


As always, if you have any questions or feedback, let me know. If you have any ideas to optimize the script, I'd like to hear that too. Thanks for reading!

You can also follow me on Twitter: