Category Archives: PowerShell

User leaves the company and users mysite is no longer accesible

This has happened to me a few times….

A user leaves and his or her AD account is deleted.

Sharepoint catches wind and marks the mysite as abandoned and emails the manager it had in User profile service, then deletes the User profile.

Then a day later someone emails the SharePoint Admin (me) and says they aren’t actually that person’s manager.

The link in the email to the mysite doesn’t work and it’s easy to think that it just auto deleted.

Checking the site with powershell (get-spsite http://mysiteurl/person/userid) shows the site collection still exists.

I found the answer at Demand Prasad’s blog.

To see the contents of the mysite, you need to be one of the two site collection admins as assigned in Central admin, but you can’t use central admin, you have to use

stsadm -o siteowner -url http://mystite/person/userid -secondarylogin domain\userid

The orignal blog post does a great job of explaining it so head over and have a read.

http://demantprasad.wordpress.com/2013/05/03/my-site-cleanup-job-and-user-not-found-error-message-in-sharepoint-2010/

Customize the SP2013 blue bar

I don’t often do a ton of link spam, but this was a great article that came up on the SPYam discussion board the other day and it was worth jotting down so I wouldn’t loose track of it.

Ryan Dennis over at Sharepointblog.co.uk figured out that the html snippet used to display the word “SharePoint” is actually an editable property of the web application, named SuiteBarBrandingElementHTML

So this would let you see it and or update it:

$app = get-spwebapplication $url
$app.SuiteBarBrandingElementHTML

Ryan wrote a nice little wrapper function around this so please give his blog a visit!.

http://www.sharepointblog.co.uk/customizing-the-sharepoint-2013-suite-bar-branding-using-powershell

 – Jack

Troublshooting a 404 error related to moving a content database

I’ve had an odd issue come up a few times now….

For one reason or another, a content database needed to be detached and then re-attached to the SharePoint farm.

The most recent need for this was so a specific content DB could me moved to a different disk on the SQL server.

In theory, the operation is pretty easy:
  • Umount the Content DB –  I did this from Central administration, Manage content databases
  • have the DBA move the DB to a new disk.
  • Mount the content DB – this is where trouble started….
Here’s what happened:

The Site collections in that DB were available on one WFE, but not the other one.

Hmm, now for the troubleshooting….

Using PowerShell, I used the command below on each WFE:

get-spcontentdatabase -webapplication http://myurl.com

Oddly, on the working WFE, my “moved” content database showed  up in the list.

On the non-working WFE it was missing.

The fix:

I tried unmounting and remounting from Central admin – no luck. (though one time the “working” WFE flipped to the other WFE!)

Next I thought I would get clever and use powershell on the broken WFE to mount it there – this didn’t work- I got an error that said the Content DB was already attached.

I also tried things like restarting IIS, Restarting the SharePoint Timer Service and Restarting the SharePoint Admin Service – no luck.

I also tried clearing the SharePoint Timer Cache – no luck either!

So Finally I unmounted it from the farm (again from CA)

Then I ran the command below on ALL nodes (WFE and APP) to confirm that it wasn’t in the farm anywhere:

get-spcontentdatabase -webapplication http://myurl.com

Finally I ran the mount-spcontentdatabase command from the Central admin box:

Mount-SPContentDatabase -name mydbname -WebApplication http://myurl.com

Note that I only had to run this once – I confirmed the Content DB was visible on all 5 boxes by re-running the get-spcontentdatabase command on each of them and all was good!

As a side note, I was lucky – Because we moved the content database, I knew what site collection to validate. Also as a habit, I log into each WFE to validate – had I used the public URL and gone through the load balancer,  would have had a 50/50 chance of seeing the problem.

To make matters worse, we have 18 content Databases in this web application. The Content database we moved was NOT the root site collection! Meaning uncaught issues would have gone unnoticed for a long time. And when the issues were discovered, they’d only apply to a single site collection and only then on a specific WFE would there be a problem. These kinds of situations are nearly impossible to figure out down the road.

So if you find yourself detaching & re-attaching content databases, be sure to test the site collections in those databases on each WFE, it could save you hours down the road.

(SharePoint 2010 SP1)

Troubleshooting Bad AD accounts and people picker issues in Central admin while granting Site collection admin rights

I had a strange problem today – While trying to set a site collection admin via central administration on SharePoint 2010, The existing user ID’s were both underlined with a red squiggly line, and trying to pick new names in people picker resulted in no results.

image001

At first I suspected the CA box had maybe been dropped from the domain or some other AD related issue.

But further investigation showed that other web apps on the same farm were fine.

Additionally, From the web app itself, I was able to select users in the people picker and they worked just fine, So the problem was only with the people picker in central admin, and only for this one specific web application.

I asked around on the SPYam forum on Yammer, and also at SharePoint-Community.net.

I got some great dialog going within a few minutes –

Adam Larkin asked if I had been using IE 10 – He had seen issues caused by IE – I was using IE 10 but this didn’t turn out to be the issue in this case.

Jasit Chopra suggested that I check Authentication mechanism – but this was set to NTLM, and was the same between the working and non-working site,

Paul Choquette – suggested I compare web.configs – another great suggestion – It didn’t turn up anything however.

Vlad (one of the founders of SharePoint-Community.net) also chimed in.

So far so good, No solution yet, but narrowed it down quite a bit. I always appreciate any help troubleshooting – sometimes just talking about things leads to a resolution.

Over on Yammer I had asked the question as well, and Trevor Seward had the suggestion to check the people picker settings. Combining Trevor’s suggestion along with Pauls, I compared PeoplePicker settings between a working web app and the non-working web app and found that the “broken” one had something in the ActiveDirectoryCustomFilter.

Specifically, here’s the powershell I used:

$webapp = get-spwebapplication http://myurl.com
$webapp.peoplepickersettings
#this showed that there was something set in a sub property of peoplepickersettings called ActiveDirectoryCustomFilter.

#next step to fix this was to reset that property, the code below was used:
$webapp = get-spwebapplication http://myurl.com
$webapp.peoplepickersettings.ActiveDirectoryCustomFilter = $null
$webapp.update()

Whats really amazing is that Trevor’s reply on Yammer came from his phone, including the above PowerShell commands!

I think today there were several lessons learned:

1) I know a little more about the people picker settings

2) Reaching out to the community for help can be both engaging and rewarding.

Special thanks to everyone listed above for their help today!

Update: Trevor has a nice article on troubleshooting PeoplePicker related issues at http://thesharepointfarm.com/2014/01/people-picker-troubleshooting-tips/

ShareGate Choice List workaround Script

Update 3/6/2014

This script is no longer needed! ShareGate 4.5 was released and now copies these items natively- no workaround needed!

Hats off to the Share-Gate development team for this update!

The content below should be considered Archive/reference:

If you’re a user of Share-Gate, you may have run into an issue transferring SharePoint lists with Multiple choice fields.

The migrations work, so long as all the data in your list fits into the list of choices you have. But what if at one point the list of choices changed, and you now have data in your list that isn’t in the choice list?

The scenario is like this.
Say you have a choice field with “region”
It started out with North, South, East and West.
So you have data in the list that has those values in it.

Then someone says, “Hey lets restructure – going forward we want people to choose from: East, Central an West”

See what happened there?

The data that’s already in there could be North South East or West, but NEW data can only be West Central or East. This causes issues when the list is migrated.

The solution is to temporarily allow “Fill-in Choices:”

Field choices

 

Ideally, you would change the value “Allow ‘Fill-in’ choices:” above from “no” to “yes” before the migration, then set it back to no after the migration at the new location.

The trouble is,  a given web can have quite a few lists, and each list can have quite a few fields. Going through all of this manually, twice is not fun? What is fun you ask? I’m glad you ask – Running two PowerShell scripts…

I wrote this little PowerShell script…

Here’s what it does –

It enumerates though all the lists in your SP Web.
Then it checks each list to see if has any “Choice Fields” that are set to not allow fill-in choices.
Then it.. sets the Value to yes for “Allow Fill in Values”

How much would you pay for a timesaving script like this???

But wait! There’s more!

While it’s doing all this, its also generating a 2nd powershell script – filled with commands to turn these values back off.

$SourceURL = "https://yoururl.com/sitecollection/web"
$DestinationURL = $SourceURL #Feel free to replace this with the destination, or leave it the same for testing purposes 
$web = get-spweb $SourceURL
$actionweb = get-spweb $SourceURL

#build a "get back" script"
$script = ""
$script = '$web = get-spweb ' + $DestinationURL

foreach ($list in $web.lists)
{
    # get the list of fields with choices that are set to false (Assuming they exist)  
    $fields = $list.fields | where {$_.type -eq "choice" -and $_.FillInChoice -eq $false}
	if ($fields -ne $null)
	{
		write-host "Title: $($list.title)  ($($fields.count))" -foregroundcolor green
		foreach ($field in $fields)
		{
			$f = $actionweb.lists[$list.title].fields[$field.title] 
			$f.FillInChoice = $true
			$f.update()

			$line = '$field = $web.lists["' + $list.title + '"].fields["' + $field.title + '"]; $field.FillInChoice = $false; $field.Update()'
			write-host $line "#" $field.id
			if ($field.id -eq $null)
			{
				write-host "NULL FIELD! $($field.id)" -foregroundcolor yellow
				$field
				write-host "--------------------------------" -foregroundcolor yellow
			}
			$script = $script + "`r`n" + $line
		}#end foreach
	}#end if
}#end foreach

$script | out-file -filepath "Reset_$($DestinationURL)_FillInChoices_$(get-date -format 'yyyyMMdd_HHmmss').ps1"

Note in the above script – I needed two near identical $web objects – the reason for this is that as I went through the list in code, and did my .update() statements, it would invalidate the state of the $web object

Another upside is that the source and destination can be on different farms – just move the generated PS1 file to the destination and run it there.

If there’s a downside, it’s that this really only works for SP 2010 and SP 2013 On premise.

CRL, Loopback and things that can slow down code, and cause problems on servers with no internet access

I’ve run into a few issues in the past year or two that revolved around SSL certificates.

SSL certs usually get installed on your Web Front Ends.

As part of the whole grand scheme of how SSL’s work there is something called an CRL – a list of “bad” certificates that had to be “revoked”

If a hacker steals a Certificate for your bank and installs it on his server, and then directs traffic to his sever, your browser, all by itself, would not know the difference – this is where the CRL comes in – if the bank knows the certificate has been compromised, they can get it “Revoked” which puts it on the CRL, a sort of “black list” for bad certificates.

When the user at home opens his or her browser, part of the SSL authentication mechanism is that your browser checks the CRL list before allowing you to proceed.

This usually works just fine…

Unfortunately, with just the ‘right’ configuration, this can throw a nasty delay into network communications.

Here’s a scenario:

You have an SSL website.

Client PC’s connect to this SSL website from everywhere and everyone is happy.

A .Net developer writes some code to access content on the above server. Note here this is code running on one server (doesn’t have to be a web server) connecting to your SSL protected website on a different server.

What happens now is that the developers code “stalls” for about a minute as the server tries to access the CRL to see if the certificate presented by your SSL website has been revoked.

It stalls because the server the developer is running code on doesn’t have access to the internet (It can access your SSL website, because they are on the same internal network)

This can be an issue.

Ideally you’d fix the connectivity problem, but there may be times that’s out of your control (a demo laptop for example)

The next ideal fix would be a system wide fix like a global registry setting. This doesn’t exist.

Each app gets to decide how to deal with checking CRLS –

This article lists 14 different infrastructure pieces and how each is configured to disable CRL checking http://social.technet.microsoft.com/wiki/contents/articles/964.certificate-revocation-list-crl-verification-an-application-choice.aspx

One personal observation I’ve made is that Google Chome doesn’t seem to care about the CRL – (link) so if you think CRL is a problem on your server, and you can install Chrome, you could then compare how long IE takes to bring up your web page from scratch vs Chrome.

I’ve used the script below to turn off CRL checking for .net code:

#the following statement goes on one line
set-ItemProperty -path "HKCU:\Software\Microsoft\Windows\CurrentVersion\WinTrust\Trust Providers\Software Publishing" -name State -value 146944

#the following statement goes on one line also
set-ItemProperty -path "REGISTRY::\HKEY_USERS\.Default\Software\Microsoft\Windows\CurrentVersion\WinTrust\Trust Providers\Software Publishing" -name State -value 146944

get-ChildItem REGISTRY::HKEY_USERS | foreach-object {set-ItemProperty -ErrorAction silentlycontinue -path ($_.Name + "\Software\Microsoft\Windows\CurrentVersion\WinTrust\Trust Providers\Software Publishing")  -name State -value 146944}

Write-Host -ForegroundColor White " - Disabling Certificate Revocation List (CRL) check..."
ForEach($bitsize in ("","64")) 
{			
  $xml = [xml](Get-Content $env:windir\Microsoft.NET\Framework$bitsize\v2.0.50727\CONFIG\Machine.config)
  If (!$xml.DocumentElement.SelectSingleNode("runtime")) { 
    $runtime = $xml.CreateElement("runtime")
    $xml.DocumentElement.AppendChild($runtime) | Out-Null
  }
  If (!$xml.DocumentElement.SelectSingleNode("runtime/generatePublisherEvidence")) {
    $gpe = $xml.CreateElement("generatePublisherEvidence")
    $xml.DocumentElement.SelectSingleNode("runtime").AppendChild($gpe)  | Out-Null
  }
  $xml.DocumentElement.SelectSingleNode("runtime/generatePublisherEvidence").SetAttribute("enabled","false")  | Out-Null
  $xml.Save("$env:windir\Microsoft.NET\Framework$bitsize\v2.0.50727\CONFIG\Machine.config")
}

– Jack 

Schedule your PowerShell Scripts using the Windows Task Scheduler

I did a quick (<3min) video showing how to schedule a PowerShell script with the Windows Task Scheduler. Quick note, Assuming you are running a SharePoint related PowerShell, don't forget two things: One, you'll need to include

Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue

Also when Saving the script and entering a user account and password – don’t forget this account must have rights to SharePoint.

Powershell to build a mailing list of every site owner in SharePoint

If you have any reason to communicate with your site owners, this script might be handy.

A few uses could be:

  • Communicate policy changes as they relate to SharePoint Site Owners
  • Communicate outage notifications to your site owners
  • Communicate upcoming training opportunities to your site owners.
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue

$logfile = "c:\temp\SPOwnerEmailList_$(get-date -format `"yyyymmdd_hhmmtt`").txt"

$webapp = get-spwebapplication http://yourURL.com
$sites = $webapp.sites
$email = @("") #this sets up an empty array
foreach ($site in $sites)
{
   $weblist = $site.allwebs
   foreach ($web in $weblist)
   {
      $owners = $web.associatedownergroup
      foreach ($owner in $owners.users)
	  {
	     write-host "$($owner.email)  - $($web.AssociatedOwnerGroup.Name)"
	     $email = $email + $owner.email.ToLower() #this add's the current email address to the array
	  } 
	$web.dispose()	  
   } 
   $site.dispose()
}

#Sort the array and remove duplicates:
$email = $email | select -uniq | sort

foreach ($m in $email)
{
	"$m;" | out-file -filepath $logfile -append
}

#this just outputs how many email addresses there were to the screen:
$email | measure

The script creates a text file and it’s fairly easy to copy-> paste the results to Outlook.

ShareGate Can’t open a website – IN-017-006 The requested list or document library cannot be found. It may have been deleted.

I’ve run into this error a few times in ShareGate-

You try to open a Site Collection or web, and see this:

IN-017-006

SharePoint Designer is not much help either:

SPD

The solution, after much trial and error, was actually pretty simple!

But before I get to the solution, lets look at some things I tried that led me to the solution…

I searched the term “there are no items to show in this view” for SharePoint Designer on the internet.  This turned up a few possibilities – one being a corrupt /_vti_bin/Listdata.svc.

That wasn’t the case for me- some webs from the same site collection worked just fine – it was just one specific web that did not.

The other thing that turned up was the possibility of a corrupt list.  Often due to a missing feature needed by the list.

I turned to PowerShell and ran the following commands:

$web = get-spweb http://my.url.com/sites/mynoncooperatingweb
$web.lists

This started to return results, then errored out –

I was definitely on to something – there was something wrong with one of my lists.

Next I needed to figure out which one.

There are a few ways to do this – the easiest would likely have been to go to the website and choose “Site Actions”->”View all site content” and then just click on each one.

I didn’t do that.

Instead I mucked with PowerShell some more, I was able to tell from the error when I ran my above code which list was causing the problem.

My next step was to see if there was any data in the list.

Based on “Site Actions”->”View all site content” , it had an item count of zero, but I wasn’t sure I could trust this given the list was corrupt.

So I turned to SQL – I knew which Database it was in. (Get-SPContentDatabase -site $url will tell you this)

Select Top 1000 *
  From AllDocs
  Where 
     DirName like 'url/to/my/corrupt/document/library/%'
     Order By Dirname

This listed a few results,  but the results looked like what is usually found in the Forms folder – I didn’t for example, see anything at all that looked like end user data  or documentation.

Armed with this re-assurance that the list was not needed, it seemed the easiest way forward was just to delete the list – this would be easy – or so I thought….

I didn’t have any hopes of deleting the list through the UI, (though to be clear, it did show up under “Site Actions”->”View all site content” and clicking on that threw an error)

So I again turned to PowerShell:

I saw a delete method, so I called it without the ( ) to see what it was looking for, It needed a GUID.

I ran the command shown below to get a list of ID’s from my site – in my case, the second list was the corrupt one.

Then I put that guid in the delete function as shown: and got the error as shown:

powershell_delete_list

“List does not exists”

So that didn’t work.

Next I searched the internet for more information –

I found a post that mentioned the recycle bin. Ah Ha!

I looked at “Site Actions”->”View all site content” and then the recycle bin, which of course was empty.

So I went to Site Actions -> Site Settings -> then went to “Go To Top Level Site Settings” -> then  Site Collection Administration(heading) ->Recycle Bin.

After sorting by URL, I found the corrupt list there.

First I tried restoring the list, it threw an error.

Then I tried to delete the list – that worked, and put the item in the Second stage recycle bin.

Next I went to the 2nd Stage “Deleted from end user Recycle Bin” area and deleted it from there.

Back in Site actions -> “View all site content” the list no longer showed up.

I relaunched SharePoint Designer and I am again able to bring up the list of lists and libraries.

I again tried a migration in ShareGate and it’s purring along like a kitten.

So to make my long story short, the error in ShareGate was caused by a corrupt/broken list – this same error affected SharePoint Designer (they both presumably use the same web service interface to get information from SharePoint) and to a degree, it even affected looking at the lists in PowerShell.

I suspect there is a bug somewhere, but I suppose it’s possible the user hit delete at the exact instance the application pool restarted or something like that.

At the time this happened we were running  SP2010 SP1 + June 2011 CU – if you run across a similar situation, please leave a comment with your version of SharePoint, hopefully this is addressed in SP2!

– Jack

Simple PowerShell Script Logging

Here’s a very simple logging mechanism I’ve used before.

Disclaimer: I’m sure someone, somewhere has written a better one – if so please leave a link the comments.

Overview:  I wanted a simple logging function that would both display to the screen, and also write to disk.  I also wanted to be able to set the display color on output.

The logging function is simple and writes to disk and to the screen.

#PowerShell Logging Script
#SharePointJack.com

#Tip, if viewing on my blog, click the full screen icon in the toolbar above

# "Global" variables:
# the filename is scoped here
# this creates a log file with a date and time stamp
$logfile = "C:\YourLogFileNameGoesHere_$(get-date -format `"yyyyMMdd_hhmmsstt`").txt"

#this is our logging function, it must appear above the code where you are trying to use it.
#note there is a technique to get around needing this at the top, read the blog post to find out more...
function log($string, $color)
{
   if ($Color -eq $null) {$color = "white"}
   write-host $string -foregroundcolor $color
   $string | out-file -Filepath $logfile -append
}

# examples:
# log something
log "this is a simple output string, it will appear white"

# log with color on screen:
log "This string will appear yellow on screen" yellow
log "This will appear red" red

# powershell shortcuts useful for building strings:
$myvariable = "hello"
log "$myvariable world"

# include double quotes in your string:
log "`"this was quoted`""  #NOTE: This character is the tick (top left of a US keyboard) - it doesn't look like it comes across in this blog.

# use more than simple variables in a string:
$cmds = get-command
log "there are $($cmds.count) commands available"

 

Note: To make the above example easy and simple, I put the log function at the top.
In Powershell, functions must always come before the code that calls them.
An easy way to get around this is to wrap your code in a function and call that function at the very bottom of the ps1 file
Like this:

#Example of how to put functions below your own code.
#SharePointJack.com

$logfile = "C:\YourLogFileNameGoesHere_$(get-date -format `"yyyymmdd_hhmmtt`").txt"

Function Main()
{
   log "In Main Function" green
   #do stuff that calls a function here.
   $commands = MySpecialFunction
   log "$($commands.count) commands found!"
}

Function MySpecialFunction()
{
   log "I'm in a function" yellow
   $commands = get-command  
   return $commands
}

function log($string, $color)
{
   if ($Color -eq $null) {$color = "white"}
   write-host $string -foregroundcolor $color
   $string | out-file -Filepath $logfile -append
}

Main  #the last line of your script should call your Main function up top.

Note that the $logFile line was intentionally left on top, since it needs to be available globally.

I have a video that covers the above scripts, as well as Start-Transcript and Stop-Transcript