Synching AlertSite Devices List with a SharePoint List via AlertSite’s REST API + PowerShell
December 30th, 2011 § Leave a Comment
This one’s a little bit more obscure than usual as I’m not sure how many people use SmartBear Software’s AlertSite service or not. If not, then this should be a good demonstration of how we can use PowerShell to connect to remote REST APIs and then sync the parsed XML response/information to a custom SharePoint List
. On the otherhand, if you don’t care about SharePoint then this demo would also be a good code sample for the .NET crowd as well since AlertSite only has sample code using Perl.
This script assumes that there is a custom SharePoint List created with the following fields:
Title – Single Line of Text (default)
MonitorInterval – Number
BeingMonitored – Yes/No
NotifyOnError – Yes/No
Traceroute – Yes/No
MonitorTimeout – Number
Once you create the fields using the names above, feel free to change the display names. The script will work with the internal field names that are static.
Click here to download the PS1.
PowerShell Script pasted below for search purposes.
You may need to highlight and copy the script below into notepad to be able to read all of the characters that were chopped off by this blog’s layout.
######################################################################################################
# This script will log into AlertSite's REST API and sync the account's devices with a SharePoint List
#
# Author: Henry Ong
#
# AlertSite Documentation:
# http://help.alertsite.com/AlertSite/RestAPI?skin=clean.nat%2cnat#5_1_Example_of_Report_API
######################################################################################################
############# Start Variables ################
$RESTServer = "https://www.alertsite.com/restapi"
$login = "YourEmailAddress"
$password = "YourPassword"
$postLogin = "<Login><Login>$login</Login><Password>$password</Password></Login>"
$devicesRequest = "<List><APIVersion>1.1</APIVersion><TxnHeader><Request><Login>$login</Login><SessionID>$sessionID</SessionID></Request></TxnHeader><Source></Source></List>"
$SPWebURL = "YourSharePointSiteURL"
$SPListName = "AlertSite Devices"
############# End Variables ##################
[System.Reflection.Assembly]::LoadWithPartialName("System.Net")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
##############################################
## Setup user agent and login request
##############################################
$encode = [System.Text.Encoding]::UTF8
$loginBytes = $encode.GetBytes($postLogin)
## Creating and posting a request to AlertSite's REST API Login page
$req = [System.Net.WebRequest]::Create($RESTServer + "/user/login")
$req.Method = "POST"
$req.ContentType = "text/xml"
$req.ContentLength = $loginBytes.Length
$req.UserAgent = "AlertSite REST Client/1.0"
$reqStream = $req.GetRequestStream()
$reqStream.Write($loginBytes, 0, $loginBytes.Length)
$reqStream.Close()
## Working with the response sent back from AlertSite
$resp = $req.GetResponse()
$cookie = $resp.Headers["Set-Cookie"]
$responseStream = $resp.GetResponseStream()
$respReader = (New-Object System.IO.StreamReader($responseStream))
$XMLResponse = $respReader.ReadToEnd()
$responseStream.Close()
# Save Session ID and Customer Object ID for subsequent API calls
$XMLEncoded = [xml]$XMLResponse
$sessionID = $XMLEncoded.Response.SessionID
$objCust = $XMLEncoded.Response.ObjCust
$resp.Close()
##############################################
## Getting the list of devices from AlertSite
##############################################
$encode = [System.Text.Encoding]::UTF8
$listDeviceBytes = $encode.GetBytes($devicesRequest)
$req = [System.Net.WebRequest]::Create($RESTServer + "/devices/list")
$req.Method = "POST"
$req.ContentType = "text/xml"
$req.ContentLength = $listDeviceBytes.Length
$req.UserAgent = "AlertSite REST Client/1.0"
$req.Headers.Add("Cookie", $cookie)
$reqStream = $req.GetRequestStream()
$reqStream.Write($listDeviceBytes, 0, $listDeviceBytes.Length)
$reqStream.Close()
$resp = $req.GetResponse()
$responseStream = $resp.GetResponseStream()
$respReader = (New-Object System.IO.StreamReader($responseStream))
$XMLResponse = $respReader.ReadToEnd()
$responseStream.Close()
$XMLEncoded = [xml]$XMLResponse
$resp.Close()
###########################################
# Update SharePoint List
###########################################
$site = New-Object Microsoft.SharePoint.SPSite($SPWebURL)
$web = $site.OpenWeb()
$deviceList = $web.Lists[$SPListName]
foreach($device in $XMLEncoded.Response.TxnList.Txn)
{
$deviceName = InjectSpaces $device.TxnName
$interval = $device.TxnDetail.GetAttribute("Interval")
$monitored = ReturnBoolFromLetter $device.TxnDetail.GetAttribute("Monitor")
$notify = ReturnBoolFromLetter $device.TxnDetail.GetAttribute("Notify")
$traceRoute = ReturnBoolFromLetter $device.TxnDetail.GetAttribute("TraceError")
$timeout = $device.TxnDetail.GetAttribute("TimeOut")
$listItem = FindDeviceInSharePoint $deviceName
if($listItem -eq $null)
{
CreateNewListItem $deviceList $deviceName $interval $monitored $notify $traceRoute $timeout
}
elseif($listItem -ne $null)
{
UpdateListItem $listItem $deviceName $interval $monitored $notify $traceRoute $timeout
}
}
$spweb.dispose()
$spsite.dispose()
####################################################################################################
## This function will replace the "%20" characters in the string with spaces.
####################################################################################################
function InjectSpaces($string)
{
$newString = $string.replace("%20", " ")
return $newString
}
####################################################################################################
## This function will return the SharePoint List Item that represents the device if it exists.
####################################################################################################
function FindDeviceInSharePoint($device)
{
$listQuery = New-Object Microsoft.SharePoint.SPQuery
$listQuery.Query = "<Where><Eq><FieldRef Name='Title' /><Value Type='Text'>" + $device + "</Value></Eq></Where>"
$itemCollection = $deviceList.GetItems($listQuery)
if($itemCollection.count -eq 1)
{
return $itemCollection[0]
}
else
{
return $null
}
}
####################################################################################################
## This function will create a new SharePoint List Item to represent the AlertSite Device if
## it doesn't exist.
####################################################################################################
function CreateNewListItem($spList, $deviceName, $monitorInterval, $beingMonitored, $notifyOnError, $traceRouteOnError, $monitorTimeout)
{
$listItem = $spList.Items.Add()
$listItem["Title"] = $deviceName
$listItem["MonitorInterval"] = $monitorInterval
$listItem["BeingMonitored"] = $beingMonitored
$listItem["NotifyOnError"] = $notifyOnError
$listItem["Traceroute"] = $traceRouteOnError
$listItem["MonitorTimeout"] = $monitorTimeout
$listItem.update()
}
####################################################################################################
## This function will update an existing SharePoint List Item that represents an AlertSite Device.
####################################################################################################
function UpdateListItem($listItem, $deviceName, $monitorInterval, $beingMonitored, $notifyOnError, $traceRouteOnError, $monitorTimeout)
{
$listItem["MonitorInterval"] = $monitorInterval
$listItem["BeingMonitored"] = $beingMonitored
$listItem["NotifyOnError"] = $notifyOnError
$listItem["Traceroute"] = $traceRouteOnError
$listItem["MonitorTimeout"] = $monitorTimeout
$listItem.update()
}
####################################################################################################
## This function will convert the AlertSite attributes for SharePoint consumption.
####################################################################################################
function ReturnBoolFromLetter($letter)
{
if($letter -eq "y")
{
return $true
}
elseif($letter -eq "n")
{
return $false
}
}
SharePoint Web Page/Valid Site Monitoring with Email Alerts PowerShell Script
November 30th, 2011 § 4 Comments
Two scripts in two days? Am I out of my mind?! Yes, no, yes, maybe… Yesterday’s SharePoint Diagnostic Log Monitor + Email Alert PowerShell Script worked so well that I was getting bombarded with too many emails! Yes, they were all critical as far as SharePoint was concerned and I was able to resolve a great majority of them. But some of them just seemed to be inherent to SharePoint and didn’t really affect the end-user experience therefore the criticality of these alerts became less of a priority.
So today I went about bootlegging a different type of monitoring system that would be more indicative of an end user’s experience with the site. With this script, I wanted to connect to the SharePoint sites and see if I get a valid response or valid content back from the server. If I can’t download the page with the System.Net.WebClient.DownloadString() method, then it’ll throw an exception and that’s how I’ll know whether the site is accessible by an end-user or not. If it’s not accessible, then an email will be generated and sent to me. Pretty cool eh?
This is extra useful when used in an environment with load balanced front ends and you want to know exactly which server is having issues. If you want to use this script in this manner, then make sure you create the scheduled tasks for each server.
Sample Email Output:
URL: https://url1
Server: ALW01
Exception: System.Net.WebException: The remote server returned an error: (500) Internal Server Error. at System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request) at System.Net.WebClient.DownloadString(Uri address) at DownloadString(Object , Object[] ) at System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(Object target, Object[] arguments, MethodInformation methodInformation, Object[] originalArguments)
===================================
URL: https://url2
Server: ALW02
Exception: System.Net.WebException: The remote server returned an error: (500) Internal Server Error. at System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request) at System.Net.WebClient.DownloadString(Uri address) at DownloadString(Object , Object[] ) at System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(Object target, Object[] arguments, MethodInformation methodInformation, Object[] originalArguments)
===================================
You may need to highlight and copy the script below into notepad to be able to read all of the characters that were chopped off by this blog’s layout.
############# Start Variables ################
$urls = @("https://url1", "https://url2")
$emailFrom = "SharePoint.Automation@email.com"
$emailTo = @("email1","email2")
$subject = "SharePoint Down!"
$smtpserver = "SMTPServer"
$server = "ALW01"
# When used in a load-balanced environment where
# each server has host entries to itself, this can help you
# identify which server is having issues.
############# End Variables ##################
[System.Reflection.Assembly]::LoadWithPartialName("System.Net")
$wc = New-Object System.Net.WebClient
$wc.UseDefaultCredentials = $true
$body = ""
foreach($url in $urls)
{
try
{
$page = $wc.DownloadString($url);
if($page.Contains("An unexpected error has occurred.") -or $page.Contains("Cannot connect to the configuration database"))
{
$body += "<b>URL:</b> " + $url + "<br><br>"
$body += "<b>Server:</b> " + $server + "<br><br>"
$body += "<b>Exception:</b> " + "Getting a nasty error. Please help me." + "<br><br>"
$body += "===================================<br><br>"
}
}
catch [Exception]
{
$body += "<b>URL:</b> " + $url + "<br><br>"
$body += "<b>Server:</b> " + $server + "<br><br>"
$body += "<b>Exception:</b> " + $_.Exception + "<br><br>"
$body += "===================================<br><br>"
}
}
if($body -ne "")
{
Send-MailMessage -To $emailTo -Subject $subject -Body $body -SmtpServer $smtpserver -From $emailFrom -BodyAsHtml
}
SharePoint Diagnostic Log Monitor + Email Alert PowerShell Script
November 29th, 2011 § 2 Comments
Looking for a bootleg SharePoint monitoring and alert system? Well, you’ve come to the right place! I wrote the following PowerShell script to be run as a scheduled task against SharePoint’s Diagnostic Logs. It will not only find the latest log file to analyze, but it will slice and dice through that current log file for the most critical “Critical” events. Once it finds those Critical events that are usually indicative of something really wrong with your environment, it’ll package up all of the messages in an HTML email and send it your way.
You may need to highlight and copy the script below into notepad to be able to read all of the characters that were chopped off by this blog’s layout.
############# Start Variables ################
$logDirectory = "D:\Logs\Diagnostic Logs\*.log"
$emailFrom = "FromEmailAddress"
$emailTo = @("Email1","Email2")
$subject = "SharePoint Diagnostics Critical Alert"
$smtpserver = "EmailServer"
############# End Variables ##################
$latestLogFile = get-childitem $logDirectory | sort LastWriteTime -desc | select -first 1
$criticalItems = Select-String $latestLogFile.FullName -Pattern "Critical"
if($criticalItems -ne $null)
{
$body = ""
foreach($criticalItem in $criticalItems)
{
$body += "<b>Error:</b> " + $criticalItem.Line + "<br><br>"
$body += "<b>Line Number:</b> " + $criticalItem.LineNumber + "<br><br>"
$body += "<b>File Path:</b> " + $criticalItem.Path + "<br><br>"
$body += "===================================<br><br>"
}
Send-MailMessage -To $emailTo -Subject $subject -Body $body -SmtpServer $smtpserver -From $emailFrom -BodyAsHtml
}
SharePoint 2010 Intermittent Search Errors & Web Services Round Robin Service Load Balancer Event: EndpointFailure
October 18th, 2011 § 3 Comments
Environment:
SharePoint 2010 Enterprise
3 WFE’s each with Query Roles
2 App Servers with 1 Dedicated Indexer
Error in Event Viewer:
Event ID: 8313
SharePoint Web Services Round Robin Service Load Balancer Event: EndpointFailure
Process Name: w3wp
Process ID: 5948
AppDomain Name: /LM/W3SVC/303076968/ROOT-1-129634027688317155
AppDomain ID: 2
Service Application Uri: urn:schemas-microsoft-com:sharepoint:service:767f7d712e064db3bb8945d9d42d0e12#authority=urn:uuid:f7625c7a33924a9981c0206f8bf0054a&authority=https://applicationserver:32844/Topology/topology.svc
Active Endpoints: 3
Failed Endpoints:1
Affected Endpoint: http://applicationserver:32843/767f7d712e064db3bb8945d9d42d0e12/SearchService.svc
Error in Diagnostic Logs:
Internal server error exception: System.ServiceModel.ServerTooBusyException: The HTTP service located at http://applicationserver:32843/767f7d712e064db3bb8945d9d42d0e12/SearchService.svc is too busy. —> System.Net.WebException: The remote server returned an error: (503) Server Unavailable. at System.Net.HttpWebRequest.GetResponse() at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
Troubleshooting Steps:
I tried to access the web service that SharePoint was trying to get to via the browser and noticed that it was reporting a 503 error as reported in the Diagnostic Logs. Then I hopped onto IIS on the affected servers and looked at the SharePoint Web Services web application (IIS) in search for a corresponding Service Application GUID that matched. The application didn’t exist on either of the application servers so that would cause the EndpointFailure as reported in the Event Viewer logs. I then double checked the Services on Server page for the application servers to make sure that the search services were turned on as having these turned on would provision the web services. They were all started which threw a little confusion my way but with some hints from this forum post, I went ahead and stopped/started the Search Query and Site Settings Service. After a little while, assuming that some timer jobs had to run, the Service Application with the missing GUID showed up in IIS and the error stopped appearing in the Event logs.
Summarized Solution:
1. Go to your Services on Server page in Central Administration.
2. Stop and Start the Search Query and Site Settings Service on each affected server as reported in the Event Viewer or SharePoint Diagnostic Logs.
SharePoint Governance for Document Library Versioning
September 28th, 2011 § 5 Comments
SharePoint Governance… a topic beaten to death right? Luckily for you that’s not what I’m here to talk about today. Today, I’ll be talking about how we can run reports that can actually help us analyze and potentially enforce governance rules upon your awesome user community that unwittingly turned on versioning for all of their Document Libraries and conveniently forgot to include a version limit number while they continue to upload 50 MB PowerPoint slide decks with the same file name over and over again, creating a bajillion versions that go back 5 years… phew. Yes that was meant to be a run-on sentence. But it’s quite a common problem isn’t it? So how do we deal with this…
Since I work in an environment with too many Site Collections and Document Libraries to troll through manually, I’ve written a handy dandy PowerShell script to do all the work for me. This script will give me a report with the following information:
My Report Output Header |
Description |
| ListTitle | The title of the SharePoint list with BaseType = DocumentLibrary |
| ListURL | The server relative URL of the SharePoint list. This script assumes you’re working with one Site Collection at a time. |
| ListAuthor | The person that created the list. |
| ListItemCount | The number of items in the list. |
| ListVersioningMajor | Whether or not major versioning is enabled for this list. |
| ListVersioningMinor | Whether or not minor versioning is enabled for this list. |
| MajorVersionLimit | The number of major versions to keep if limited. |
| MinorVersionLimit | The number of minor versions to keep if limited. |
| ListItemTitle | The name of the list item/document being reported on. |
| ListItemAuthor | The last person that modified the list item/document being reported on. |
| ListItemLastMod | The last modified time stamp for the list item/document being reported on. |
| ListItemVersions | The number of versions (major and minor) associated with the list item/document being reported on. |
| ListItemFileSize | The file size for the current version of the list item/document being reported on. |
| ListItemFileSizeCorpus | The aggregated storage space being used by all versions. Note: This is not an exact number but a simple estimate of the size of the current version multiplied by the number of versions associated with the list item/document being reported on. |
The PowerShell Script (Download Link)
# This script will generate a report of lists of type "DocumentLibrary" # and will give details on all of the files and their versioning attributes. # # Author: Henry Ong ######################## Start Variables ######################## $siteURL = "http://SiteCollection" #URL to the Site Collection you want to analyze. $filePath = "D:\PowerShellScripts\AllVersionedLibraries.csv" ######################## End Variables ######################## if(Test-Path $filePath) { Remove-Item $filePath } Clear-Host [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint") # Creates an object that represents a SharePoint List function CreateNewListObject { $listObject = New-Object system.Object $listObject | Add-Member -type NoteProperty -Name ListTitle -Value $list.Title $listObject | Add-Member -type NoteProperty -Name ListURL -Value $list.DefaultViewURL $listObject | Add-Member -type NoteProperty -Name ListAuthor -Value $list.Author $listObject | Add-Member -type NoteProperty -Name ListItemCount -Value $list.ItemCount $listObject | Add-Member -type NoteProperty -Name ListVersioningMajor -Value $list.EnableVersioning $listObject | Add-Member -type NoteProperty -Name ListVersioningMinor -Value $list.EnableMinorVersions $listObject | Add-Member -type NoteProperty -Name MajorVersionLimit -Value $list.MajorVersionLimit $listObject | Add-Member -type NoteProperty -Name MinorVersionLimit -Value $list.MajorWithMinorVersionsLimit return $listObject } # Creates an object that represents a SharePoint List Item function CreateNewListItemObject { $listItemObject = New-Object system.Object $listItemObject | Add-Member -type NoteProperty -Name ListTitle -Value $list.Title $listItemObject | Add-Member -type NoteProperty -Name ListURL -Value $list.DefaultViewURL $listItemObject | Add-Member -type NoteProperty -Name ListAuthor -Value $list.Author $listItemObject | Add-Member -type NoteProperty -Name ListItemCount -Value $list.ItemCount $listItemObject | Add-Member -type NoteProperty -Name ListVersioningMajor -Value $list.EnableVersioning $listItemObject | Add-Member -type NoteProperty -Name ListVersioningMinor -Value $list.EnableMinorVersions $listItemObject | Add-Member -type NoteProperty -Name MajorVersionLimit -Value $list.MajorVersionLimit $listItemObject | Add-Member -type NoteProperty -Name MinorVersionLimit -Value $list.MajorWithMinorVersionsLimit $listItemObject | Add-Member -type NoteProperty -Name ListItemTitle -Value $item.Name $listItemObject | Add-Member -type NoteProperty -Name ListItemAuthor -Value $item["Modified By"] $listItemObject | Add-Member -type NoteProperty -Name ListItemLastMod -Value $item["Modified"] $listItemObject | Add-Member -type NoteProperty -Name ListItemVersions -Value $versions $listItemObject | Add-Member -type NoteProperty -Name ListItemFileSize -Value $fileSize $listItemObject | Add-Member -type NoteProperty -Name ListItemFileSizeCorpus -Value $fileSizeTotal return $listItemObject } $site = new-object microsoft.sharepoint.spsite($siteURL) $customListObjects =@() $allWebs = $site.AllWebs foreach ($web in $allWebs) { foreach ($list in $web.Lists) { if($list.BaseType -eq "DocumentLibrary") { $customListObject = CreateNewListObject $customListObjects += $customListObject if($customListObject.ListVersioningMajor -eq $true -or $customListObject.ListVersioningMinor -eq $true) { foreach($item in $list.items) { $versions = $item.versions.count $itemID = $item.UniqueId $file = $web.GetFile($itemID) $fileSize = $file.TotalLength/1000000 $fileSizeTotal = $fileSize*$versions $customListItemObject = CreateNewListItemObject $customListObjects += $customListItemObject } } Write-Host $list.Title Write-Host $web.title } } $web.Dispose() } $site.dispose() # Exporting the data to a CSV file $customListObjects | Select-Object ListTitle,ListURL,ListAuthor,ListItemCount,ListVersioningMajor,ListVersioningMinor,MajorVersionLimit,MinorVersionLimit,ListItemTitle,ListItemAuthor,ListItemLastMod,ListItemVersions,ListItemFileSize,ListItemFileSizeCorpus | Export-Csv $filePath write-host "Done"
Analyzing the Report
So once the report is run, I typically delete the first line with junk data that says “#TYPE Selected.System.Object” (anyone know how to not have that outputted?) and then convert everything into a table. That’s done by selecting all of the data (CTRL-A) and then going to the Insert tab of the Excel Ribbon and then selecting Table and include headers. Now with this information in a sortable/filterable format, I can go through any of the columns to better determine which Document Libraries or documents are potential problems. For example:
1. I’ll first filter on “ListVersioningMajor=TRUE” to figure out which ones have versioning turned on.
2. Then I can filter on “MajorVersionLimit” to figure out which ones don’t have a limit in place.
3. And then I’ll sort the “ListItemVersions” column to figure out which documents have a ton of versions.
Now that’s just one way to look at this report to figure out next steps, I’m sure you’ll find other pivots on this data that you’ll find useful.
Key Take Aways
Just having a governance plan in place is not good enough. Your governance plan needs to be actionable and enforceable. Using scripts like this to provide both proactive and reactive reporting can help you and your Site Owners by providing data-driven firepower to effectively manage your SharePoint environments. What am I doing with this data? Depending on the scenario I can:
1. Send the report to the Site Owner to mull over and figure out where and how much they want to limit the versioning or…
2. I can say hey, it looks like you’re not using versioning at all. Mind if I turn it off? And that’s a post for next time – How to automatically change the versioning settings for all Document Libraries in a Site Collection.
Basic Page Document Libraries upgraded to SharePoint 2010 and an error message: “Cannot save your changes”
September 14th, 2011 § 2 Comments
This one was quite an anomally so I thought I’d share…
Scenario:
I have a SharePoint sub-site with multiple Document Libraries created with the Basic Page Document Template. These libraries were upgraded from MOSS 2007 to SharePoint 2010. Users with Full Control were able to modify/edit the pages just fine but users with Contributor rights were not able to save changes made to existing content pages. When Contributors tried to save changes made with the WYSIWYG editor, which seems to be a variation of the Content Editor Web Part, they get an browser popup error message that says “Cannot save your changes“.
Troubleshooting Steps:
I went up, down and all around all of the permission configurations on the site, library, parents, etc. but couldn’t see why a user with Contributor rights was not able to save their changes. Also of note: the content rendered ok in IE7 but didn’t render at all for the user in Firefox. I finally ended up deleting the page and tried to recreate it with the affected user’s session using Firefox. That’s when I was given a much better clue. The Content Editor Web Part on the page returned: “A Web Part or Web Form Control on this Page cannot be displayed or imported. You don’t have Add and Customize Pages permissions required to perform this action.”
Solution:
With that clue, I trekked up to the root site and modified the permission levels for Contributors. Site Actions > Site Permissions > Permission Levels (in the ribbon) > Clicked on Contribute and then checked the “Add and Customize Pages” option. And that did it!
SharePoint 2010 Converting From Classic to Claims Authentication Lessons Learned
August 18th, 2011 § 14 Comments
Environment
2 x SharePoint Server 2010 Web Front Ends
2 x SharePoint Server 2010 Application Servers
1 x SQL Server 2008 R2 DB Server
SSL and load-balanced enabled web application
What we’re trying to do
After upgrading a SharePoint 2007 content database to a SharePoint 2010 farm, we wanted to convert to Claims Authentication. Following the TechNet documentation on this, we came across an error message after trying to set the authentication provider as shown below:
Set-SPwebApplication $wa -AuthenticationProvider (New-SPAuthenticationProvider) -Zone Default
Error Message
Set-SPWebApplication: An update conflict has occurred, and you must re-try this action. The object SPWebApplication Name=SharePoint Sites was updated by domain\serviceAccount, in the OWSTIMER process, on machine SPServer. View the tracing log for more information about the conflict.
After spending over 20 hours going through a multitude of workarounds and scenarios including those that are commonly known like deleting the file cache, I think I’ve come up with a series of steps that is repeatable and reliable (at least in my environment).
Steps
1. Perform all actions below using the farm account.
2. Create your web app in classic authentication mode, attach content DB, etc. as appropriate. In my environment the base line web application included host headers, SSL and load-balancing already configured. We also had the appropriate content database attached and upgraded. One lesson learned here was that you’ll want to do a backup of your content database after a successful mount-spcontentdatabase and upgrade (if you’re upgrading from SharePoint 2007) prior to performing any more actions on it. Depending on how large your content database is, it will behoove you to back it up since the upgrade process could be very time consuming on large content databases.
3. Log on to each non-database server in the farm and stop the “SharePoint 2010 Timer” service. You can get to this by going to Start > Run > Services.msc
4. Pre-emptively clear the file cache on each of the non-database servers. The file cache can typically be found on each server here: C:\ProgramData\Microsoft\SharePoint\Config. If you don’t see some files/folders, you may have to unhide file extensions and hidden files/folders. Now you’ll notice that there may be more than 1 folder with GUIDs. You’ll want to open them and find the one that contains nothing but XML files and one “cache.ini” file. Go ahead and delete all the XML files. With the lonely cache.ini file that’s left over, open it up, delete all the contents and replace it with “1″. Make sure you do this on all non-database servers. You’ll know that you correctly stopped the SharePoint Timer Service if the XML files don’t come back.
5. Now let’s move forward with the claims authentication conversion. You can follow all of the instructions on TechNet here. To summarize, you’ll want to perform the following commands in Powershell via the SharePoint 2010 Management Shell:
$WebAppName = "http://yourWebAppUrl"
$account = "yourDomain\farmaccount"
$wa = get-SPWebApplication $WebAppName
Set-SPwebApplication $wa -AuthenticationProvider (New-SPAuthenticationProvider) -Zone Default
Press 'Y' for Yes when prompted
$wa = get-SPWebApplication $WebAppName
$account = (New-SPClaimsPrincipal -identity $account -identitytype 1).ToEncodedString()
$zp = $wa.ZonePolicies("Default")
$p = $zp.Add($account,"PSPolicy")
$fc=$wa.PolicyRoles.GetSpecialRole("FullControl")
$p.PolicyRoleBindings.Add($fc)
$wa.Update()
$wa = get-SPWebApplication $WebAppName
$wa.MigrateUsers($true)
6. What the above commands will do is convert the authentication mechanism for the web application to claims-based as well as convert all the user objects in your content database to the claims format (“i:0#.w|domain\account”). Now a thing to note here is that through experimentation, I found that even when/if I get the failure/error message (in red above) after the “Set-SPwebApplication $wa -AuthenticationProvider (New-SPAuthenticationProvider) -Zone Default” command, my content database is not corrupted. So no need to restore/re-upgrade. It seems to have performed the proper claims updates to the content database and then tries to update the web application. If the error occurs, you’ll want to dismount the content database, delete the web application, re-create the web application in classic authentication mode and then start over with Step 1 above. If you don’t get any error messages, then continue below.
7. Time to update the Portal Super User and Super Reader Accounts using the same Powershell session as above. If you had closed it already, you’ll need to grab the web application object again before performing the commands below:
$wa.Properties["portalsuperuseraccount"] = "i:0#.w|domain\yourSuperuserAccount" $wa.Properties["portalsuperreaderaccount"] = "i:0#.w|domain\yourSuperReaderAccount" $wa.Update()
8. Time to start all of the SharePoint Timer Jobs (services.msc). When you perform this action, you’ll notice that all of the cache files you had deleted previously will be recreated and the number in the cache.ini file will show a number other than 1. After a few minutes, the timer service will propagate all of the changes that we’ve been performing to all of the web front end servers. You can monitor this by checking on the status of the timer jobs @ http://centraladmin/_admin/Timer.aspx.
9. Now you should be able to login to your site and everything should work! You should not get any Access Denied messages. If you do, you probably didn’t do Step 7 properly. If you look at the permissions and users in your claims-based SharePoint site, you’ll notice that almost all users will be in the claims format (“i:0#.w|domain\account”). If they are not, they are most likely users that no longer exist in Active Directory. If they are valid users and for some reason didn’t have their login name changed then you have some other problems that I haven’t come across yet. Another place to review to make sure that claims is working properly is to look at the user policy for your web app. There’s a “User Policy” button in the ribbon of this Central Admin page: http://centraladmin/_admin/WebApplicationList.aspx.
Happy claiming!
Reporting on All Sites for a SharePoint Web Application
June 30th, 2011 § 6 Comments
Hey everyone! I’ve been knee deep in a migration project the last month and just now got a breather to post some of the goodies I’ve come up with. So in analyzing our current SharePoint farm we wanted to generate a report of what kind of SharePoint sites exist in our environment. We were looking for these attributes:
- When was the site created?
- When was the site last modified?
- Who owns the site?
- Site name
- Site URL
- The name of the template being used
- The template ID
- Whether or not it’s a Fab40 site
- How many items are in the site?
- Whether or not the site is empty
How did we use this script?
I originally wrote this script to detect all instances of Fab40 sites so that we could deal with them (get rid of them prior to upgrading to SharePoint 2010), but then extended it to report on all site templates. This gave me the information I needed to reach out to all the site owners to clean up empty and/or low usage sites (we don’t have automatic site deletion turned on).
The Script
# This script will generate a report of all sites in the web app
# along with how many items are within the site.
#
# Author: Henry Ong
######################## Start Variables ########################
$siteURL = “http://siteurl” #URL to any site in the web application.
$filePath = “D:\PowerShellScripts\AllSites.csv”
######################## End Variables ########################
if(Test-Path $filePath)
{
Remove-Item $filePath
}
Clear-Host
[System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint”)
# Creates an object that represents a SharePoint site.
function CreateNewObject
{
$customObject = New-Object system.Object
$customObject | Add-Member -type NoteProperty -Name WebCreated -Value $web.Created.ToShortDateString()
$customObject | Add-Member -type NoteProperty -Name WebModified -Value $web.LastItemModifiedDate.ToShortDateString()
$customObject | Add-Member -type NoteProperty -Name RequestAccessEmail -Value $web.RequestAccessEmail
$customObject | Add-Member -type NoteProperty -Name WebTitle -Value $web.Title
$customObject | Add-Member -type NoteProperty -Name WebURL -Value $web.URL
$customObject | Add-Member -type NoteProperty -Name WebTemplateName -Value $web.WebTemplate
$customObject | Add-Member -type NoteProperty -Name WebTemplateID -Value $web.WebTemplateId
$customObject | Add-Member -type NoteProperty -Name Fab40Site -Value $false
$customObject | Add-Member -type NoteProperty -Name ItemCount -Value “”
$customObject | Add-Member -type NoteProperty -Name SiteAdmins -Value “”
$customObject | Add-Member -type NoteProperty -Name GreaterThanZeroItems -Value $false
return $customObject
}
$site = new-object microsoft.sharepoint.spsite($siteURL)
$webApp = $site.webapplication
$allSites = $webApp.sites
$customObjectsList =@()
foreach
($site in $allSites)
{
$allWebs = $site.AllWebs
foreach ($web in $allWebs)
{
$itemCount = 0;
$customWebObject = CreateNewObject
$templateID = $web.WebTemplateID.ToString()
if($templateID.startswith(“758″))
{
$customWebObject.Fab40Site = $true
}
foreach ($list in $web.Lists)
{
if(($list.ItemCount -gt 0) -and ($list.Hidden -ne $true))
{
$customWebObject.GreaterThanZeroItems = $true
$itemCount += $list.ItemCount;
}
}
$customWebObject.ItemCount = $itemCount.ToString()
$customObjectsList += $customWebObject
foreach($user in $web.SiteAdministrators)
{
$customWebObject.SiteAdmins += “$user “
}
Write-Host $web.title
$web.Dispose()
}
$site.dispose()
}
# Exporting the data to a CSV file
$customObjectsList | Select-Object WebCreated,WebModified,WebTitle,WebURL,WebTemplateName,WebTemplateID,Fab40Site,ItemCount,GreaterThanZeroItems,SiteAdmins,RequestAccessEmail | Export-Csv $filePath
write-host “Done”
Don’t forget to use PowerGUI! PowerGUI is your friend – http://powergui.org/index.jspa
SharePoint Link Reporter using PowerShell
May 20th, 2011 § 4 Comments
Hey gang! So here’s what I’ve been cooking up… I was trying to figure out a way to generate a report of all (almost all) links within a SharePoint web application so that I could analyze them for potential broken links come a migration project. Details and the PowerShell script are below. Or you can download the script here.
Scenario:
In preps for a SharePoint 2007 to SharePoint 2010 migration project, I wanted to do a prescan of all the links in the:
- Quick Launch
- Top/Global Navigation
- Links Lists
Why? Well, since we have an environment with a bajillion collaborative Site Collections, we all know it’s rare for an end-user to use relative links when manually creating links to things within the SharePoint sites. More often than not, absolute links will be used which could be quite a quandary in a migration situation in which URLs can change for any number of reasons. With this report, I could scan through it very quickly and notify site owners about links that may need to be updated in the future.
Solution Synopsis:
Loop through every SharePoint Site and Site Collection in a web application, every link in the Quick Launch menus of each site, every Top/Global Nav link of each site, and every Links list of each site. Take all that and dump into a spreadsheet. Visually scan spreadsheet for suspect links. This script will work with both publishing and non-publishing sites. *Have not tested on SharePoint 2010 environments.
*Modified script 5/23/2011 to also report all links starting with “http://” contained within the body of the default page for each SPWeb object. This is useful in case end-users manually put links into Content Editor Web Parts or Page Content Controls that link back to site content using absolute links. Thanks to the Hey, Scripting Guy! Blog for the inspiration.
Sample Output:
Click here for a sample output CSV.
PowerShell Script (feel free to leave constructive feedback if you see something that could be done differently/better!):
# This script will print out a list of all links in the Quick Launch, Links Lists, and default # page for each SPWeb object to a text file located in the directory specified in the variables section. # # Author: Henry Ong ######################## Start Variables ######################## $siteURL = "http://alvhong2" #URL to any site in the web application. $filePath = "C:\PowerShellScripts\Links.csv" $PublishingFeatureGUID = "94c94ca6-b32f-4da9-a9e3-1f3d343d7ecb" ######################## End Variables ######################## if(Test-Path $filePath) { Remove-Item $filePath } Clear-Host [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint") [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Publishing") [System.Reflection.Assembly]::LoadWithPartialName("System.Net.WebClient") # Creates an object that represents an SPWeb's Title and URL function CreateNewWebObject { $linkObject = New-Object system.Object $linkObject | Add-Member -type NoteProperty -Name WebTitle -Value $web.Title $linkObject | Add-Member -type NoteProperty -Name WebURL -Value $web.URL return $linkObject } # Creates an object that represents the header links of the Quick Launch function CreateNewLinkHeaderObject { $linkObject = New-Object system.Object $linkObject | Add-Member -type NoteProperty -Name WebTitle -Value $prevWebTitle $linkObject | Add-Member -type NoteProperty -Name WebURL -Value $prevWebURL $linkObject | Add-Member -type NoteProperty -Name QLHeaderTitle -Value $node.Title $linkObject | Add-Member -type NoteProperty -Name QLHeaderLink -Value $node.Url return $linkObject } # Creates an object that represents to the links in the Top Link bar function CreateNewTopLinkObject { $linkObject = New-Object system.Object $linkObject | Add-Member -type NoteProperty -Name WebTitle -Value $prevWebTitle $linkObject | Add-Member -type NoteProperty -Name WebURL -Value $prevWebURL $linkObject | Add-Member -type NoteProperty -Name TopLinkTitle -Value $node.Title $linkObject | Add-Member -type NoteProperty -Name TopLinkURL -Value $node.Url $linkObject | Add-Member -type NoteProperty -Name TopNavLink -Value $true return $linkObject } # Creates an object that represents the links of in the Quick Launch (underneath the headers) function CreateNewLinkChildObject { $linkObject = New-Object system.Object $linkObject | Add-Member -type NoteProperty -Name WebTitle -Value $prevWebTitle $linkObject | Add-Member -type NoteProperty -Name WebURL -Value $prevWebURL $linkObject | Add-Member -type NoteProperty -Name QLHeaderTitle -Value $prevHeaderTitle $linkObject | Add-Member -type NoteProperty -Name QLHeaderLink -Value $prevHeaderLink $linkObject | Add-Member -type NoteProperty -Name QLChildLinkTitle -Value $childNode.Title $linkObject | Add-Member -type NoteProperty -Name QLChildLink -Value $childNode.URL return $linkObject } ## Creates an object that represents items in a Links list. function CreateNewLinkItemObject { $linkObject = New-Object system.Object $linkObject | Add-Member -type NoteProperty -Name WebTitle -Value $prevWebTitle $linkObject | Add-Member -type NoteProperty -Name WebURL -Value $prevWebURL $linkObject | Add-Member -type NoteProperty -Name ListName -Value $list.Title $spFieldURLValue = New-Object microsoft.sharepoint.spfieldurlvalue($item["URL"]) $linkObject | Add-Member -type NoteProperty -Name ItemTitle -Value $spFieldURLValue.Description $linkObject | Add-Member -type NoteProperty -Name ItemURL -Value $spFieldURLValue.Url return $linkObject } # Determines whether or not the passed in Feature is activated on the site or not. function FeatureIsActivated {param($FeatureID, $Web) return $web.Features[$FeatureID] -ne $null } # Creates an object that represents a link within the body of a content page. function CreateNewPageContentLinkObject { $linkObject = New-Object system.Object $linkObject | Add-Member -type NoteProperty -Name WebTitle -Value $prevWebTitle $linkObject | Add-Member -type NoteProperty -Name WebURL -Value $prevWebURL $linkObject | Add-Member -type NoteProperty -Name PageContentLink -Value $link return $linkObject } $wc = New-Object System.Net.WebClient $wc.UseDefaultCredentials = $true $pattern = "(((f|ht){1}tp://)[-a-zA-Z0-9@:%_\+.~#?&//=]+)" $site = new-object microsoft.sharepoint.spsite($siteURL) $webApp = $site.webapplication $allSites = $webApp.sites $customLinkObjects =@()foreach ($site in $allSites) { $allWebs = $site.AllWebs foreach ($web in $allWebs) { ## If the web has the publishing feature turned OFF, use this method if((FeatureIsActivated $PublishingFeatureGUID $web) -ne $true) { $quickLaunch = $web.Navigation.QuickLaunch $customLinkObject = CreateNewWebObject $customLinkObjects += $customLinkObject $prevWebTitle = $customLinkObject.WebTitle $prevWebURL = $customLinkObject.WebURL # First level of the Quick Launch (Headers) foreach ($node in $quickLaunch) { $customLinkObject = CreateNewLinkHeaderObject $customLinkObjects += $customLinkObject $prevHeaderTitle = $node.Title $prevHeaderLink = $node.Url # Second level of the Quick Launch (Links) foreach ($childNode in $node.Children) { $customLinkObject = CreateNewLinkChildObject $customLinkObjects += $customLinkObject } } # Get all the links in the Top Link bar $topLinks = $web.Navigation.TopNavigationBar foreach ($node in $topLinks) { $customLinkObject = CreateNewTopLinkObject $customLinkObjects += $customLinkObject $prevHeaderTitle = $node.Title $prevHeaderLink = $node.Url } } ## If the web has the publishing feature turned ON, use this method else { $publishingWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web) $quickLaunch = $publishingWeb.CurrentNavigationNodes $customLinkObject = CreateNewWebObject $customLinkObjects += $customLinkObject $prevWebTitle = $customLinkObject.WebTitle $prevWebURL = $customLinkObject.WebURL # First level of the Quick Launch (Headers) foreach ($node in $quickLaunch) { $customLinkObject = CreateNewLinkHeaderObject $customLinkObjects += $customLinkObject $prevHeaderTitle = $node.Title $prevHeaderLink = $node.Url # Second level of the Quick Launch (Links) foreach ($childNode in $node.Children) { $customLinkObject = CreateNewLinkChildObject $customLinkObjects += $customLinkObject } } # Get all the links in the Top Link bar $topLinks = $web.Navigation.TopNavigationBar foreach ($node in $topLinks) { $customLinkObject = CreateNewTopLinkObject $customLinkObjects += $customLinkObject $prevHeaderTitle = $node.Title $prevHeaderLink = $node.Url } } #Looking for lists of type Links $lists = $web.Lists foreach ($list in $lists) { if($list.BaseTemplate -eq "Links") { $prevWebTitle = $customLinkObject.WebTitle $prevWebURL = $customLinkObject.WebURL # Going through all the links in a Links List foreach ($item in $list.Items) { $customLinkObject = CreateNewLinkItemObject $customLinkObjects += $customLinkObject } Write-Host $list.Title } } #Looking at the default page for each web for links embedded within the content areas $htmlContent = $wc.DownloadString($web.URL) $result = $htmlContent | Select-String -Pattern $pattern -AllMatches $links = $result.Matches | ForEach-Object {$_.Groups[1].Value} foreach ($link in $links) { $customLinkObject = CreateNewPageContentLinkObject $customLinkObjects += $customLinkObject } Write-Host $web.Title $web.Dispose() } $site.dispose() } # Exporting the data to a CSV file $customLinkObjects | Select-Object WebTitle,WebURL,TopNavLink,TopLinkTitle,TopLinkURL,QLHeaderTitle,QLHeaderLink,QLChildLinkTitle,QLChildLink,ListName,ItemTitle,ItemURL,PageContentLink | Export-Csv $filePath write-host "Done"





