All posts in PowerShell

SharePoint Custom Solution Crashes IIS Worker Process (w3wp.exe) – Part 1

Had a doozy of an issue the other day.  All of a sudden, a SharePoint farm that has been chugging along with no changes suddenly started having some weird issues.  Users could open, view, edit documents, but as soon as they attempted a save or an upload of a new document things started to go bad.  If they were using Windows Explorer they received the error: “The specified network name is no longer available”

SharePoint Custom Solution Crashes IIS Worker Proces - Windows Explorer Error

If they were using the GUI the upload form hung for a while and eventually reverted to “The Page Cannot be Displayed”

At the same time, we were getting reports of users in other areas of the farm getting a very slow response within SharePoint.  What was really confusing about this was that the issue was happening to just a single site collection in the farm.

Errors Received

Windows Event Log

We were receiving a number of errors besides those at the end user level.  The server event log indicated our app pool was crashing.  The error received was actually a warning (to me if an app pool is crashing, it should be an error) with the msg:

A process serving application pool ‘SharePoint Web Apps’ suffered a fatal communication error with the Windows Process Activation Service. The process id was ‘6292’. The data field contains the error number.

SharePoint Custom Solution Crashes IIS Worker Proces - EventLogError

In the multiple WFE environment it was happening back and forth between the two serves indicating the load balance was doing its job.  It also indicated why people were seeing slow response.  Each time the app pool failed, it had to restart and then reload the SharePoint environment (like you see after an IIS Reset).

ULS Logs

The ULS logs were something else.  In this particular environment our logs usually range from 5MB-40MB in size for a 30 min period.  When I ran a one minute log export using “Merge-SPLogFIle” the exported file was 1.3 GB.  Nothing screamed error at me, however there were a couple of things standing out.

06/21/2017 10:30:38.00        w3wp.exe (0x112C)        0x1E58        SharePoint Foundation        Performance        naqx        Monitorable        Potentially excessive number of SPRequest objects (16) currently unreleased on thread 46.  Ensure that this object or its parent (such as an SPWeb or SPSite) is being properly disposed. This object is holding on to a separate native heap. Allocation Id for this object: {C3DC973B-90B4-4974-A33D-A5A05A722DF7} Stack trace of current allocation:    at Microsoft.SharePoint.SPGlobal.CreateSPRequestAndSetIdentity(SPSite site, String name, Boolean bNotGlobalAdminCode, String strUrl, Boolean bNotAddToContext, Byte[] UserToken, SPAppPrincipalToken appPrincipalToken, String userName, Boolean bIgnoreTokenTimeout, Boolean bAsAnonymous)     at Microsoft.SharePoint.SPWeb.InitializeSPRequest()     at Microsoft.SharePoint.SPWeb.EnsureSPRequest()     at Microsoft.SharePoint.SPSite.OpenWeb(String strUrl, Int32 mondoHint)     at Microsoft.SharePoint.SPSite.OpenWeb(Guid gWebId, Int32 mondoHint)     at Microsoft.SharePoint.SPSite.OpenWeb(Guid gWebId)….

06/21/2017 10:31:21.09        w3wp.exe (0x112C)        0x1E58        SharePoint Foundation        General        8m90        Medium        1045 heaps created, above warning threshold of 128. Check for excessive SPWeb or SPSite usage.        a8dafd9d-9faa-70d5-b0e7-8c1711386713

So this screamed of some custom code (which we do have) running that is not disposing of the SPSite or SPWeb objects properly.  Why it suddenly became a problem I don’t know.  We did have security patches happen on the server over the weekend.  I didn’t think it likely to be the cause as the environment had been used for a day and a half with no issues.  We backed out of the patch anyways, but didn’t affect the issue occurring.  What was also confusing was this issue was also occurring in our Pre-Prod environment.  The silver lining is now I could really do some troubleshooting without affecting sites that were functioning or production data.

I finally tracked down the issue to an event receiver we have running in our environment.  The project sites all of the same structure and it was decided that code would be used to enforce this structure.  To that end, event receivers were built to ensure folders at certain levels (library root, root +1 level and root +2 levels) were not deleted nor files or folders at those levels were added.  I took a guess that these event receivers were causing the issues.  Using PowerShell I removed the event receivers from a library being affected.  In case you need this for something else the code to remove a list event receiver is:

$siteColURL = "<Site URL>";

$spSite = Get-SPSite $siteColURL

foreach($spWeb in $spSite.AllWebs)
{
    $spList = $spWeb.Lists["<LIBRARY NAME"];
    
    try 
    {
        for($i = $splist.EventReceivers.Count - 1; $i -ge 0; $i--)
        {
            $eventReceiver = $spList.EventReceivers[$i];
            if($eventReceiver.Class -eq "<EVENT RECEIVER CLASS>")
            {
                $eventReceiver.Delete();
            }
        }

        $spList.Update();
        
        Write-Host("Updated library for site: {0}" -f $spWeb.URL);
    }
    catch 
    {
        $ErrorMessage = $_.Exception.Message;
        Write-Host ("{0}: An error occurred deleting the event receiver for site: {1}.  Error received: {2}" -f `
                        (get-date).ToString("yyyy-MM-dd HH:mm:ss"), $spWeb.URL, $ErrorMessage) -ForegroundColor Red
    }    
}

In the above code (which removes the event receivers from ALL specified libraies in ALL subsites) I used the event receiver class to find the items I wanted to remove.  You can also use .Name and .Assembly if you wish. I used Class simply because when the sites were created and the receivers attached, no names were given.  With the event receivers removed, users were now able to upload and save documents.  So I had indeed found the culprit.  Now to determine why.

I’ll cover the review of the code and the final determination of the cause of the issue in Part 2 of this series.

 

Thanks for reading!

How to Delete a SharePoint List Item Attachment with PowerShell

Every now and then a problem pops up in that a user is unable to delete an attachment on a list item.  The system goes through the motions of deleting the item, but when you get back to the list item the attachment is still there.  No errors are received and no reason is given for the item not deleting.  I haven’t seen this happen very often but it happened recently in one of our team site lists.  There was nothing special about the list item or the attachment, but for whatever reason, it wouldn’t delete the attachment.  Like so many things I come across I figure if it happened to me, it could happen to someone else.  So that brought about this blog post.  I want to show you how to delete a list item’s attachment with PowerShell.

How to Delete a SharePoint List Item Attachment with PowerShell

So to start off I have a simple list with an item that has two attachments.  I want to delete the one called “AutoSPInstallerFolderStructure.txt”.  The process for deleting it is pretty straightforward.  You simply access the list, get the item we are looking for in the list and then loop through the attachments until we find the one we want to delete and remove it.

Building the script:

#############################################################################################
#Date			Who					Comment													#
#-------------	------------------	------------------------------------------------------	#
#01May2017		David Drever		Deletes an attachment from a list item                  #
#############################################################################################

$webURL = "http://teams.drevsp2016.com";
$listName = "AttachmentList";
$attachmentName = "AutoSPInstallerFolderStructure.txt";

$spWeb = Get-SPWeb $webURL;
$spList = $spWeb.Lists[$listName];
$spListItems = $spList.Items;  

foreach($spListItem in $spListItems)
{

    #Need to do a reverse loop as we are deleting from the collection and a forward loop will fail
    for($i=$spListItem.Attachments.count-1; $i -ge 0; $i--) 
    { 
        if($spListItem.Attachments[$i].Contains($attachmentName))
        {
            write-host "Deleting file:" $spListItem.Attachments[$i]
            $spListItem.Attachments.Delete($spListItem.Attachments[$i])
            $spListItem.SystemUpdate()
        }    
    }
}

Before the Script:

How to Delete a SharePoint List Item Attachment with PowerShell - BeforeScript

Running the Script:

How to Delete a SharePoint List Item Attachment with PowerShell - Running The Script

After the Script:

How to Delete a SharePoint List Item Attachment with PowerShell - After the Script

And that’s it.  You can also modify the script to delete attachments in bulk if the need is there.

 

Thanks for reading!

SharePoint Saturday Vancouver 2017

This week I had the distinct privilege to present at SharePoint Saturday Vancouver.  I presented a search based session with Joanne Klein and a new presentation I just finished about using  PowerShell to make your day-to-day administration\usage of SharePoint so much easier.  As always, I had a great time presenting.  Had great interaction with the attendees of my sessions.

A huge thank you to the sponsors and to the Vancouver SharePoint Users Group.

As promised here are the slide decks for the two presentations:

Finding the Needle in a Haystack SharePoint Style: Search Concepts and Content Quality

Stop Clicking Your Mouse Button, We have a Script for That!

Start SharePoint 2013 Workflows with PowerShell

Recently was working on a small project that ended up requiring me to start SharePoint 2013 workflows with PowerShell scripts.  This turned out to be a necessity because of a few things.  I was finishing off some development recently on a smaller project that required a significant amount of JavaScript injection code.  To be terribly honest, had I known at the time it would have taken as much as it did, I would have rewritten it as a full Add-In, but that’s a story for another day.  To make a long story short because of the way the form had been written by another developer we were saving attachments to the list manually from the form (not allowing SharePoint to do it for us).  Because we had workflows running on item added, we were having save conflicts because the items could not be added until after the form data was initially inserted into the list.  Basically getting a kind of race condition between the workflows and the list item update.

My first thought was to simply disable the workflows from starting automatically and instead initiating them from the form using JSOM.  This however wasn’t going to work because while the code I wrote worked perfectly fine (I’ll blog on that later), I forgot the solution also had a mobile app feeding into it.  The mobile app wasn’t able to use the same model as I could and SharePoint 2013 workflows is one of the few places REST needs to be vastly improved.  In theory it can be done simply by using the ExecuteProcess endpoint and enter the necessary information in the body of the transmission.  However, I had never done this and didn’t have a lot of time left to figure it out (I’ll post something here if I get around to working it out).  So that left a PowerShell Script and scheduled tasks.

Start SharePoint 2013 Workflows with PowerShell

So now let’s get to why you’re here.  How to initiate SharePoint 2013 workflows from PowerShell.  The gist of my solution was this:

  1. Build a search query for every item created in the past 10 minutes (scheduled task will run every 5 min so there will be plenty of overlap).
  2. Loop through each item returned and check to see if the workflows have run based on the workflow status field created.
  3. If no workflow for that item, initiate the workflow.

Search Query

The query is pretty straight forward:

#############################################################################################
#Function Name: BuildItemsCreatedMinutesInPastQuery			     							#
#Parameters: $minutesInPast - Number of minutes to subtract from current time				#
#                                                                                           #
#Return Value: SPQuery object																#
#Purpose: Creates an SPQuery object that will check against the Created Date subracting the #
#         number of minutes passed in to the function          								#
#############################################################################################

function BuildItemsCreatedMinutesInPastQuery([Int]$minutesInPast)
{
    $setMinInPast = $minutesInPast * -1;
    $currentTime = (Get-Date).AddMinutes($setMinInPast);
    $formatTime = $currentTime.ToString('s');
    
    $spQuery = New-Object Microsoft.SharePoint.SPQuery;
    $spQuery.Query =
       "<Where>
            <Geq>
                <FieldRef Name='Created' />
                <Value IncludeTimeValue='TRUE' Type='DateTime'>$formatTime</Value>                
            </Geq>
        </Where>"

    return $spQuery;
}

The script only takes in the number of minutes you want to look into the past.  You get the current date and time, back it up the number of minutes you want, convert it to ISO 8601 format and build it into your query to find any items later than that time.  If you aren’t up on CAML query, the code basically translates to “Get all items, where the field ‘Created’ (of type DateTime) is greater than or equal (<geq>) to the date I want in ISO 8601 format.

Initiate the Workflow

When initiating the workflow there are a couple of steps you need to do first:

  1. Connect to the SharePoint Workflow Services and create a Workflow Services Manager object
  2. Connect to the subscription service
  3. Get the workflow subscription you are looking for (searching an enumerated list using the workflow name)
    • You are checking all the workflow associations (definitions) within the site and grabbing the one you are looking for
  4. Build an empty dictionary.  This is used to hold the parameters of the workflow if there was an initiate form gathering information.
  5. Creating an object of the workflow instance service and initiating the workflow against the item in question.
#build the WF Manager and Subscription Services objects to manipulate the WFs in the site
$spWFMgr = New-Object Microsoft.SharePoint.WorkflowServices.WorkflowServicesManager($spWeb);
$spWFSubSvc = $spWFMgr.GetWorkflowSubscriptionService();

#try to find the WF to initiate
$spWorkflow = $spWFSubSvc.EnumerateSubscriptions() | ?{$_.Name -eq $wfName}

if($spWorkflow)
{
    #connect to the Instance service, build empty parameter dictionary and initiate the WF.
    $spWFInstanceSvc = $spWFMgr.GetWorkflowInstanceService();
    $wfParams  = New-Object 'system.collections.generic.dictionary[string,object]'

    $wfGUID = $spWFInstanceSvc.StartWorkflowOnListItem($spWorkflow, $spListItem.ID, $wfParams);
}
else
{
    $returnMessage = ("Workflow: {0} not found" -f $wfName);
}

That’s it.  These few lines, find the workflow you need, create an instance of it against the item you wish and then start the workflow.

You next step is to build it all together and then toss it into a scheduled task to run every 5 minutes or however often you need it to run.

Thanks for reading!

Quickly Build a Multi-Server SharePoint 2013 Developer Environment (Part 4) – Preparing SharePoint Servers

Welcome to part four of my series on how to “Quickly Build a Multi-Server SharePoint 2013 Developer Environment”.  In this section I am going discuss how to configure SharePoint servers and prepare our last two VMs.  We’ll be using a series of tools to accomplish this, including AutoSPSourceBuilder and AutoSPInstaller (which if you followed the prep from part two you already have downloaded and ready to go).  The other thing we are going to do that has been different than the steps done so far is we are going to configure both SP servers and then install SharePoint and configure the farm all at once.  To do this we will be using the remote installation option that AutoSPInstaller gives us.

Posts in this series:

Read more