SharePoint 2013 Workflow Instance Size Error

January 6, 2015

While working on a recent project I got the chance to setup and use SharePoint 2013 workflows. This is a major step forward for the product and has a lot of benefits, however it does introduce a few complications.

As the workflows are now all stored and processed outside of SharePoint all communication is done via WCF services. This is great as it extracts the processing out of SharePoint and thus reduces the load on SharePoint.

The downside of this is it introduces additional areas where something can go wrong and this is what leads me onto the topic of this blog post.

The workflow I was working on was used to handle the approval of data on a SharePoint list item. In order for the workflow to move onto the next stage it required 5 groups of users to approve the data. The workflow had to wait until all 5 groups of users had completed and approved the data and once approved the workflow would move onto the next stage and send some emails.

The UI to handle the approval locked down the form so only users in certain groups could actually approve each section so in most cases a user would come in and approve 1 out of the 5 sections and this worked fine. There was also an admin group who could approve all 5 sections at one time. It was in this case where an error was being thrown by the workflow causing it to be terminated, see figure 1

image

Figure 1

When clicking on the workflow I could see the internal status was ‘Terminated’ and when clicking on the information icon I could see the error was “System.Activities.Statements.WorkflowTerminatedException: The workflow instance was too large to persist (over 5120 kilobytes). The maximum persistable workflow instance size is 5120 kilobytes.”, see figure 2.

image

Figure 2

For SharePoint errors this is actually reasonably detailed as it supplies a message explaining what the issue is instead of the usual something went wrong error. As this was my first real experience of SharePoint 2013 workflows I didn’t really know where to start debugging this so I had a Google around.

There are a few articles out there but nothing which matched what I was looking for. The closest I came was an article which suggested changing a server configuration property called ‘WorkflowServiceMaxActivityXamlSizeInBytes’.

I had a look into this and discovered this property can be updated by using a PowerShell command called ‘Set-WFServiceConfiguration’, see MSDN article. I tried to increase this value but unfortunately it didn’t seem to make any difference. I looked over the additional properties which can be set and I found one called ‘WorkflowServiceMaxInstanceSizeKB’ which seemed to match the error I was getting in terms of the description of the error but also the max value limit on the error message seemed to match the default value on the MSDN article. I tried changing this using the PowerShell command but again the workflow was still failing with the same error.

Given these properties were exposed so Microsoft obviously expected these might need to be changed in certain circumstances and the fact that the ‘WorkflowServiceMaxInstanceSizeKB’ property matched so closely to the error I was encountering I done some investigation into this and found there was another property called ‘WorkflowServiceMaxInstanceCompressedSizeKB’ which also had the same default value of 5120.

Unfortunately all articles referring to this were people adding a row directly into a table in one of the workflow databases. Anyone who knows SharePoint knows touching any SharePoint databases is not supported so I was reluctant to do this, however since it was a development environment I tried it but I still encountered the same error.

It wasn’t until I came across an article which was describing a slightly different error that on a I noticed down at the bottom of the article it said you have to restart the ‘Workflow Manager Backend’ windows service in order for settings changes to be picked up and after I done this the workflow started working.

I then had to do some testing to check which of the properties I had changed was the one which fixed the error and in my case it turns out I only needed the ‘WorkflowServiceMaxInstanceSizeKB’ property which could be set via PowerShell, see figure 3 for the exact script, so I didn’t have to worry about adding a value directly into the workflow database.

Set-WFServiceConfiguration -ServiceUri http://workflowmanagerurl:12291/ -Name “WorkflowServiceMaxInstanceSizeKB” -Value 30720

Figure 3

Overall the process of changing the workflow settings is very easy using the available PowerShell functions the key is to remember and re-start the ‘Workflow Manager Backend’ windows service to get these updated values picked up.


Error editing publishing page in SharePoint

November 25, 2013

While working on a recent project I was navigating our development site and when I tried to edit a page I got the standard SharePoint error screen, see figure 1. There was very little customisations on the site as it was still in the earlier stages of the development process so I was slightly confused as to what the issue could be.

image

Figure 1

As with most SharePoint errors the easiest way to get to the bottom of the issue is to check the ULS logs so I copied the correlation ID and opened up ULS Viewer on the server. I opened the latest ULS log file and filtered by the correlation ID. Looking through the log file I finally found the details of the error, see Figure 2.

Application error when access /Pages/default.aspx, Error=Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
at Microsoft.SharePoint.SPFieldMultiColumnValue.get_Item(Int32 index)

System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
at Microsoft.SharePoint.SPFieldMultiColumnValue.get_Item(Int32 index)

Getting Error Message for Exception System.Web.HttpUnhandledException (0x80004005): Exception of type ‘System.Web.HttpUnhandledException’ was thrown. —> System.InvalidOperationException: Failed to compare two elements in the array. —> System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
at Microsoft.SharePoint.SPFieldMultiColumnValue.get_Item(Int32 index)

Figure 2

Looking over the details it wasn’t obvious what the actual error was but it seemed to point to an issue with the page layouts so I decided to review all custom ones to see if there was anything obviously wrong. I opened the site in the browser and navigated to the master page gallery. When I tried to edit one of the custom page layouts I got an error, see figure 3.

image

Figure 3

The error seemed to be highlighting an issue with the content type the page layout was associated with so I returned to the master page gallery and hovered over the associated content type link, see RHS column on Figure 4, and I could see the URL on the bottom of the page was “_layouts/15/ManageContentType.aspx?ctype=#VALUE!” whereas it should contain the ID of the content type.

 

screenshot2

Figure 4

Normally this would not be an issue with page layouts uploaded via the browser or SharePoint designer, however in my case the custom page layouts had been uploaded into SharePoint via PowerShell.

I checked the PowerShell script and I could see the original version uploaded a page layout into the correct location, however it was setting the associated content type property of the page layout to be a string, see figure 5. I knew from previous experience this needed to be a concatenated string but I couldn’t remember the exact format so I quickly put together a test script which got the value of an OOTB page layout. Using this script I could see the value actually had to be a concatenated string of the content type name and ID, see figure 6 for updated PowerShell.

Incorrect version
  1. $newFile.Item["PublishingAssociatedContentType"] = "Article Page"

Figure 5
Correct version
  1. $newFile.Item["PublishingAssociatedContentType"] = ";#Article Page;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D;#"

Figure 6

As the site was still in the early stages of development I was able to easily delete all pages which used the custom page layouts, delete the page layouts and re-upload them with the corrected script. I then double checked the associated content link in the master page gallery, see figure 7, and it was correctly populated. After that I was able to edit pages and content within the site.

screenshot 
Figure 7

Hopefully this saves some other people some time and I suppose the key lesson would be, as always, be careful with PowerShell and double check its actually doing what you expect.


Generate WSP and coy to another location on post build event in Visual Studio 2010

April 19, 2012

When working on all SharePoint 2010 projects deployments are generally done via PowerShell scripts. I usually have a set PowerShell script which I copy and alter updating items such as the site URL, solution name and feature ID and I save this in a deployment folder under where the solution file is located. I then create a solution folder in Visual Studio called ‘Deployment files’ and add my PowerShell scripts to this. This way the deployment files are part of the solution and should be added into our code repository as well.

In my PowerShell scripts I don’t hard coded the WSP location I find the current directory from which the script is getting executed and then append the WSP name, see below. This means the WSP has to be in the same folder as the scripts which isn’t generally an issue but it means I have to package the project in Visual Studio and then copy the WSP from the build folder to my scripts folder. While this only takes a few minutes you need to do it each time which can get a bit repetitive.

Find Solution Location
  1. $scriptpath = $MyInvocation.MyCommand.Path
  2. $dir = Split-Path $scriptpath
  3. $solutionName=”WSPNAME.wsp”
  4. $solutionPath = $dir + “\” + $solutionName

 

I decided to look into using post build commands to see if I could generate the WSP then copy it to the folder where my scripts are located. Since I have done it several times I first looked at copying the WSP file to another location. This can be done by

  1. Right click on the project and select properties

ProjectPropertiesWindow

  1. Next select the Build Events option on the RHS
  2. In the post-build event command line window enter the script below

copy $(TargetDir)$(TargetName).wsp $(SolutionDir)DeploymentFiles

    1. The TargetDir should be the full path to the build folder where the WSP will be created
    2. The TargetName should be the same name as the project so in my case if the project name was TestProject I would be looking for a file called TestProject.wsp
    3. The SolutionDir should be the full path to the folder where the solution file is located.
    4. \DeploymentFiles is simply the name of the folder located under the main solution folder where I keep my PowerShell scripts

3. Save the file and build the project

If it has worked you should get a message in the Output screen indicating the file has been copied.

This was fine except this simply copies the WSP from the bin folder to my deployment scripts folder but what I need is to ensure I am getting the latest version of the WSP so I need to generate the WSP before copying the file. As this was not something I had done before I done some research and it seems the only way to do this is to edit the project file.

I opened my project file in Notepad++, might be worth taking a backup of this first, and found the section at the end, see below, and added the suggested XML, see below.

Section After which I need to add my addition XML
<Import Project=”$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\SharePointTools\Microsoft.VisualStudio.SharePoint.targets” />

Required XML
<PropertyGroup>
<BuildDependsOn>$(BuildDependsOn);CreatePackage</BuildDependsOn>
</PropertyGroup>”

What I found was as I had already added my post build event to copy the WSP there was already a section in the project file called “PropertyGroup”, see below.

PostBuildCopyProjectXML

At this point I wasn’t sure if I was supposed to add a new PropertyGroup element or add my BuildDependsOn section to the existing property group. I looked around on Google but there wasn’t much details on this so added the section to generate the WSP as another PropertyGroup element but I added this before my copy build event in case the order was important. When I opened Visual Studio, or if you already had it opened you will need to reload the project, and build it I found it did copy the file and it did build the WSP it did them in the wrong order. It first copied the WSP then it built a new version of the WSP.

Obviously this isn’t much use as I would always end up with an old version of the WSP. I tried adding the post build code which moves the file into the same property group, see below, but I still ended up with the same outcome where the file was moved first then a new version created.

XML with generate new WSP and move file in one property group in project file
PostBuildAndGenerateWSPProjectXML

I spent some time researching this issue on Google and found an article on how to generate a WSP in post build command and noticed that I had “<BuildDependsOn>” in my version but in the article above it was “<PostBuildEventDependsOn>”. As soon as I changed this, see new project XML below, things executed in the correct order and it first built my WSP then copied it.

Final version of project file XML

<PropertyGroup>  <PostBuildEventDependsOn>$(PostBuildEventDependsOn);CreatePackage</PostBuildEventDependsOn>
<PostBuildEvent>copy “$(TargetDir)$(TargetName).wsp” “$(SolutionDir)DeploymentFiles”</PostBuildEvent>
</PropertyGroup>

I hope this will help others as it took me a while to get this right. As always please be careful with making changes to the project file as this can cause issues.


PowerShell script for updating workflows

December 22, 2011

The other day when working on a project I needed to update some existing Visual Studio workflows. I had a Visual Studio project which contained a variety of workflows which were used across several SharePoint sites. While keeping all the workflows in one solution works it is not ideal for deployment as you can’t auto associate the workflows as part of the deployment.

I decided to try and automate the deployment so started thinking what my options were. Being a developer my first thought was code in a feature receiver on activated and deactivated, however I decided that this would be another opportunity to practice some PowerShell. This gave me the flexibility of changing the script if need be without having to alter code.

Before starting to write the script I went through in my head what it need to do and came up with the following:

  1. Get the list to which the workflow should be deployed
  2. Check to see if the workflow was already deployed
    1. If it was already deployed check to see if there were any running instances
      1. If there were running instances set the workflow to not allow any new instances but allow existing ones to finish.
      2. If not remove the workflow and go to step 3
    2. If not move on to step 3
  3. Associate the new version of the workflow to the list

 

With these steps in mind I started to create my script. I will cover each of the above and go through the script required to achieve the goal.

Point 1

First I wanted to get the list the workflow was associated with. This is very easy and can be accomplished by getting the web object then using that to get the list object much the same way you would if you were writing C#

Get Workflow List
  1. Write-Host 'Get web'
  2. $hrWeb = Get-SPWeb $siteURL
  3.  
  4. Write-Host 'Get Lists'
  5. $list = $hrWeb.Lists[$listName]
  6. $taskList = $hrWeb.Lists[$taskListName]
  7. $workflowHistoryList = $hrWeb.Lists[$historyListName]

 

Point 2

With the list the next task was to see if the workflow is currently deployed to the list. This presented a problem as my plan was to automatically deploy the workflow with a name and the date of the deployment i.e. “Workflow 22/12/2011’. This caused problems as I wouldn’t know the last deployment date so I had to use a like comparison on the name. I wasn’t comfortable just using a like comparison as I felt this wasn’t always going to get me what I wanted so I added an additional check to validate the name of the workflow but also if it was of the correct base workflow type.

Try and find workflow
  1. Write-Host 'Get base template'
  2. $basetemplate = $hrWeb.WorkflowTemplates.GetTemplateByName($workflowTemplateName,$culture);
  3. Write-Host $basetemplate.Id
  4.  
  5. #set up variable to hold workflow instance if we find it
  6. $existingWorkflow = $null
  7.  
  8. Write-Host 'Get workflow association'
  9. #loop through all associations on list
  10. foreach($tempWorkflowAssociation in $list.WorkflowAssociations)
  11. {
  12. #check if the base template id matches the base template of the current WF associaton
  13. #in additon check the name of the current WF association against the one we are interested in
  14. if($tempWorkflowAssociation.BaseTemplate.Id -eq $basetemplate.Id -and $tempWorkflowAssociation.Name -like $workflowName +"*" )
  15. {
  16. $existingWorkflow = $tempWorkflowAssociation
  17. break
  18. }
  19. }
  20. #check we have a workflow
  21. if($existingWorkflow -ne $null)
  22. {

 

Point 2.1

As you can see the last line in the previous snippet makes sure we have a workflow returned so at this point we know there is a version of the workflow we are interested in currently deployed to the list. Now I need to know if there are any running instances for this.

Check running instances
  1. Write-Host 'Got workflow associated with list'
  2. if($existingWorkflow.RunningInstances -ge 0)
  3. {

 

Point 2.1.1

If there are running instances these must continue working until they are complete but not allow any new instances of this workflow to be initiated. This can be done by setting the ‘No new instances’ option through the workflow settings in the UI or by the following command.

No new instances
  1. Write-Host 'There are running instances so set to allow no new running instances'
  2. $existingWorkflow.set_Enabled($false)
  3. $list.UpdateWorkflowAssociation($existingWorkflow)

 

Point 2.1.2

If there are no running instances then we want to remove the current version. This can be done by the command below

Remove workflow
  1. Write-Host 'No running instances so remove'
  2. $list.RemoveWorkflowAssociation($existingWorkflow)

 

Point 3

We are now in a position where we can try and associate the new version of the workflow to the list. As I mentioned above when attaching the new workflow I am adding it with the name and the date so I first build up the name then create an new workflow association object the add it to the list.

Associate new workflow
  1. Write-Host 'Create workflow association details'
  2. $date = Get-Date
  3. $workflowName = $workflowName + " " + $date.ToShortDateString()
  4. $newWorkflow=[Microsoft.SharePoint.Workflow.SPWorkflowAssociation]::CreateListAssociation($basetemplate, $workflowName,$taskList,$workflowHistoryList)  
  5.  
  6. $newWorkflow.AllowManual = $allowManualStart
  7. $newWorkflow.AutoStartChange = $autoStartChange
  8. $newWorkflow.AutoStartCreate = $autoStartCreate
  9.  
  10. Write-Host 'Add workflow to list'
  11. $list.AddWorkflowAssociation($newWorkflow)

At this point the new version of the workflow should be attached to the list. I started creating this with some hardcoded values but then extracted it out into a reusable function. The full function is below.

Fully assocaition function
  1. function AssocaiteWorkflow([string]$siteURL, [string]$listName, [string]$taskListName, [string]$historyListName,
  2.     [string]$workflowTemplateName, [string]$workflowName, [bool]$allowManualStart, [bool]$autoStartChange, [bool]$autoStartCreate)
  3. {
  4.  
  5. Write-Host 'Start WF assocaition for ' $workflowName ' on list ' $listName ' and site ' $siteURL
  6.  
  7. #get culture
  8. $culture= Get-Culture
  9.  
  10. Write-Host 'Get web'
  11. $hrWeb = Get-SPWeb $siteURL
  12.  
  13. Write-Host 'Get Lists'
  14. $list = $hrWeb.Lists[$listName]
  15. $taskList = $hrWeb.Lists[$taskListName]
  16. $workflowHistoryList = $hrWeb.Lists[$historyListName]
  17.  
  18. Write-Host 'Get base template'
  19. $basetemplate = $hrWeb.WorkflowTemplates.GetTemplateByName($workflowTemplateName,$culture);
  20. Write-Host $basetemplate.Id
  21.  
  22. #set up variable to hold workflow instance if we find it
  23. $existingWorkflow = $null
  24.  
  25. Write-Host 'Get workflow association'
  26. #loop through all associations on list
  27. foreach($tempWorkflowAssociation in $list.WorkflowAssociations)
  28. {
  29. #check if the base template id matches the base template of the current WF associaton
  30. #in additon check the name of the current WF association against the one we are interested in
  31. if($tempWorkflowAssociation.BaseTemplate.Id -eq $basetemplate.Id -and $tempWorkflowAssociation.Name -like $workflowName +"*" )
  32. {
  33. $existingWorkflow = $tempWorkflowAssociation
  34. break
  35. }
  36. }
  37. #check we have a workflow
  38. if($existingWorkflow -ne $null)
  39. {
  40. Write-Host 'Got workflow associated with list'
  41. if($existingWorkflow.RunningInstances -ge 0)
  42. {
  43. Write-Host 'There are running instances so set to allow no new running instances'
  44. $existingWorkflow.set_Enabled($false)
  45. $list.UpdateWorkflowAssociation($existingWorkflow)
  46. }
  47. else
  48. {
  49. Write-Host 'No running instances so remove'
  50. $list.RemoveWorkflowAssociation($existingWorkflow)
  51. }
  52. }
  53. else
  54. {
  55. Write-Host 'No workflow associated with list'
  56. }
  57.  
  58. Write-Host 'Create workflow association details'
  59. $date = Get-Date
  60. $workflowName = $workflowName + " " + $date.ToShortDateString()
  61. $newWorkflow=[Microsoft.SharePoint.Workflow.SPWorkflowAssociation]::CreateListAssociation($basetemplate, $workflowName,$taskList,$workflowHistoryList)  
  62.  
  63. $newWorkflow.AllowManual = $allowManualStart
  64. $newWorkflow.AutoStartChange = $autoStartChange
  65. $newWorkflow.AutoStartCreate = $autoStartCreate
  66.  
  67. Write-Host 'Add workflow to list'
  68. $list.AddWorkflowAssociation($newWorkflow)
  69. }

 

You can then call this in the standard way i.e.

Call function
  1. AssocaiteWorkflow "http://sharepoint&quot; "Test List Name" "Tasks List Name" "Workflow History List Name" "Visual Studio Workflow Name" "Workflow Instance Name" $true $true $true

 

As with all PowerShell scripts while they are very useful please always test them on a development environment before running them on live. As always I can’t be responsible for any issues that arise so if you use this you do so at your own risk.


My first Powershell command

December 6, 2011

The other day I finally decided it was time to stop using STSADM and start learning PowerShell. I decided a good way to do this was to rewrite existing STSADM commands in PowerShell.

The first thing I decided to do was to have a quick look at the basics so after a Google I found a video by Todd Klindt called Windows PowerShell for SharePoint Server 2010 Administrators. I found this very useful as a starting point assuming no knowledge and it allowed me to get quickly up and running by searching for commands and getting examples on how to use certain commands.

After watching this I then decided to have a look on TechNet to review some of the commands I would need to know in order to deploy/update a solution package. I managed to find a list of exactly what I was looking for a full list of all PowerShell commands for features and solutions.

Using the commands for features and solutions linked above I started to plan what steps I need to take to as part of my PowerShell script. The feature and solution I was updating contained a Module which loads the a custom JS file to the web.

I think it is always good practice to manually write down the steps you want to accomplish before actually getting started writing any script/code as this allows you to plan out what you are trying to achieve and gives you something to refer to at a later date. With this in mind I worked out the deployment steps should be as follows:

  1. Deactivate feature on each web
  2. Uninstall solution
  3. Remove solution
  4. Add solution
  5. Deploy solution
  6. Activate feature on each web

 

Before starting my PowerShell script I first done some research to see if there was a way to detect if a certain command had finished running. The reason I investigated this was I have had experience before when writing STSADM commands which would not run a timer job was currently in progress and another command was initiated. After some looking around I found the ‘Wait For Command To Complete’ script, see below, on the MSDN site. This meant I was able to kick off a command such as uninstall then call my wait function so the next section of the script had to wait until the previous command completed.

Wait For Command To Complete
  1. # Wait for the SharePoint timer job name that contains "solution-deployment".
  2. function Wait4Timer
  3. {    
  4.     Write-Host -NoNewLine "`nFinding timer job"
  5.     
  6.     # The language-dependent display name of the timer job contains "Solution Retraction".
  7.     # while (($jd = Get-SPTimerJob | ?{ $_.DisplayName -like "*Solution Retraction*"+$fileName+"*" }) -eq $null)
  8.     while (($jd = Get-SPTimerJob | ?{ $_.Name -like "*solution-deployment*" + $fileName + "*" }) -eq $null)
  9.     {
  10.         Write-Host -NoNewLine .
  11.         Start-Sleep -Seconds 1
  12.     }
  13.     $jdName = $jd.Name
  14.     Write-Host "`njob: $jdName"
  15.     Write-Host -NoNewLine Waiting to finish
  16.     
  17.     while ((Get-SPTimerJob $jdName) -ne $null)
  18.     {
  19.        Write-Host -NoNewLine .
  20.        Start-Sleep -Seconds 1
  21.     }
  22.     
  23.     Write-Host
  24.     $jd.HistoryEntries | %{ Write-Host job history: $_.Status }
  25.     Write-Host
  26. } # End of function Wait4Timer.

 

With the wait in place I was then able to go about writing the rest of the script. Referring back to my 6 steps documented above I then proceed to flesh these out and add in the actual PowerShell script. After a few iterations I ended up with the script below. With me still being a novice in this area if there are any issues with this please let me know but I have tested this and it all seems to work ok.

PLEASE NOTE: If you take a copy of this script please run it on a development environment first as while this should be generic and should run on any farm it is always best practice to never run these scripts directly on live without first being tested elsewhere.

  1. # Wait for the SharePoint timer job name that contains "solution-deployment".
  2. function Wait4Timer
  3. {    
  4.     Write-Host -NoNewLine "`nFinding timer job"
  5.     
  6.     # The language-dependent display name of the timer job contains "Solution Retraction".
  7.     # while (($jd = Get-SPTimerJob | ?{ $_.DisplayName -like "*Solution Retraction*"+$fileName+"*" }) -eq $null)
  8.     while (($jd = Get-SPTimerJob | ?{ $_.Name -like "*solution-deployment*" + $fileName + "*" }) -eq $null)
  9.     {
  10.         Write-Host -NoNewLine .
  11.         Start-Sleep -Seconds 1
  12.     }
  13.     $jdName = $jd.Name
  14.     Write-Host "`njob: $jdName"
  15.     Write-Host -NoNewLine Waiting to finish
  16.     
  17.     while ((Get-SPTimerJob $jdName) -ne $null)
  18.     {
  19.        Write-Host -NoNewLine .
  20.        Start-Sleep -Seconds 1
  21.     }
  22.     
  23.     Write-Host
  24.     $jd.HistoryEntries | %{ Write-Host job history: $_.Status }
  25.     Write-Host
  26. } # End of function Wait4Timer.
  27.  
  28. Add-PSSnapin Microsoft.SharePoint.PowerShell
  29.  
  30. #get the current directory
  31. $scriptpath = $MyInvocation.MyCommand.Path
  32. $dir = Split-Path $scriptpath
  33.  
  34. #set the solution name
  35. $solutionName="SolutionName.wsp"
  36. #build up the full path to the file
  37. $solutionPath = $dir + "\" + $solutionName
  38.  
  39. #set the feature ID
  40. $featureID = "6208eb36-05cb-4109-a9d2-b620d3b1b34d"
  41. Write-Host 'Start to disable feature in each site'
  42. #please change the SharePoint site URL as appropriate
  43. $site = Get-SPsite http://sharepoint
  44. foreach($web in $site.AllWebs) {
  45. Write-Host "Deactivate feature for Web " $web.Url
  46. disable-spfeature -identity $featureID -confirm:$false -url $web.Url
  47. }
  48.  
  49. Write-Host 'Start to uninstall solution'
  50. Uninstall-SPSolution -identity $solutionName -confirm:$true
  51.  
  52. Write-Host 'Waiting for unistall job to finish'
  53. Wait4Timer
  54.  
  55. Write-Host 'Start to remove solution'
  56. Remove-SPSolution ?Identity $solutionName -confirm:$true
  57. Write-Host 'Start to add solution'
  58. Add-SPSolution -LiteralPath $solutionPath -confirm:$true
  59.  
  60. Write-Host 'Start to install solution to all web applications'
  61. Install-SPSolution ?Identity $solutionName -GACDeployment -confirm:$true
  62.  
  63. Write-Host 'Waiting for install job to finish'
  64. Wait4Timer
  65.  
  66. Write-Host 'start to enable Feature'
  67. foreach($web in $site.AllWebs) {
  68. Write-Host "Activate featur for Web " $web.Url
  69. Enable-spfeature -identity $featureID -confirm:$false -url $web.Url
  70. }
  71.  
  72. Remove-PSSnapin Microsoft.SharePoint.PowerShell


%d bloggers like this: