Workflow failed on start (retrying) when anonymous users start workflows


Here is an interesting issue and subsequent workaround – anonymous users can create list items (if granted the appropriate permissions), but workflows will not start for anonymous users.

If you do some research on this topic, you’ll see lots of people trying to allow this, so that emails can be sent for example – but the answer is almost always “here is some code you can deploy.” That’s an unfortunate answer, as not all of us have the ability or skillset to write and deploy Visual Studio code to our environments or our client’s environments.

However, after lots of trial and error I came up with a solution using PowerShell.

Essentially, we need to do the following:

  1. Grant anonymous users the ability to add items
  2. Setup a SharePoint Designer workflow to do the e-mailing, or what have you – set the workflow to start when items are changed, not when items are created…
  3. Configure a PowerShell script to run against the site or sites so that the items get updated if they have not yet had their Workflow Status updated to “Complete”

The script is currently configured to iterate through a single site collection and all of it’s subwebs, looking for any “GenericList” (Custom List) that has items which have not yet been marked as “Completed” by the workflow engine…

Here is my PowerShell Script to update items:

Start-SPAssignment -Global
$Site = Get-SPSite http://siteurl
$Webs = $Site.AllWebs
	foreach ($Web in $Webs) {
		foreach ($list in $Web.Lists | where {$_.BaseType -eq "GenericList"}){
			foreach ($item in $list.Items | where {$_.Workflows.InternalState -ne "Completed"}) {
			$item.Update()
			}
		$list.Update()	
		}
	$Web.Dispose()		
	}
$Site.Dispose()
Stop-SPAssignment -Global

Setup a Windows Scheduled Task to run this every half-hour or so, and it will iterate through your site – kicking off any yet-to-be-started workflows under the context of whatever account you use in the Scheduled Task.

Disclaimer: This may cause heavy utilization of server resources if you have a large site collection, lots of lists, big lists, or if you run this against multiple web applications. I’m sure there are other (perhaps better) solutions, but this works for me. No warranty or guarantee is implied. 🙂

Enjoy!
RD

Fix access denied errors for Claims-based authentication sites for users with permissions


Wow this was a fun issue. A web application using Claims-based authentication started giving access denied errors to ALL users after setting the values of the PortalSuperUserAccount and PortalSuperReaderAccount properties of the web application.

I won’t write much because it’s all explained in this excellent blog post by Andras Gaal.

Essentially, if you setup the accounts and give them permission via Web App Policy – you have to assign the account values using the Claims ID (i:0#.w|domain\user), NOT the (domain\user) NTLM ID.

To set the properties, use PowerShell!

$webapp = Get-SPWebApplication http://webappurl
$webapp.Properties["portalsuperuseraccount"] = "<claimsId>
$webapp.Properties["portalsuperreaderaccount"] = <claimsId>
$webapp.Update()

Configure Web.Config Custom Errors using PowerShell


It seems like every time I go to disable custom errors for the purposes of troubleshooting, I have to turn to the interwebs to remember the exact settings I need to change and what I need to change them to. Of course it’s all done in the web.config file for the web application with which you are working – and it’s really just two settings that need tweaked…

Instead of doing this manually, I thought “What if I could create two PowerShell Functions to manipulate these values?”

Well, lucky for me I had already done this to enable and disable SP Blob Caching – so it was pretty easy to modify that code to change the customErrors and CallStack settings in the web.config.

Essentially the hardest part is making sure you grab the right configuration path from the web.config, once you’ve got that, it’s just a matter of creating a new object (Microsoft.SharePoint.Administration.SPWebConfigModification) and then setting values of that object.

Once you’ve created your 2 $configMod objects, you call the $webapp.WebConfigModifications.Add method for each mod – and then call the Update() method to apply. Hit your page again and you should see an uglier, yet more informational error message.

Once you have dot-sourced the function, here is how you disable the SharePoint (custom) Errors:

Disable-SPCustomErrors -WebApplication http://webappurl

And here is how you re-enable the SharePoint (custom) Errors:

Enable-SPCustomErrors -WebApplication http://webappurl

And without further ado, here is the code containing two functions – Enable-SPCustomErrors and Disable-SPCustomErrors:

# Disable-SPCustomErrors Function
function Disable-SPCustomErrors { 
param( 
	[Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]
	[Microsoft.SharePoint.PowerShell.SPWebApplicationPipeBind] 
	$WebApplication
) 

process { 
    $WebApp = $WebApplication.Read() 
    # SPWebConfigModification to enable/disable CustomErrors
    $configMod1 = New-Object Microsoft.SharePoint.Administration.SPWebConfigModification 
    $configMod1.Path = "configuration/system.web/customErrors" 
    $configMod1.Name = "mode" 
    $configMod1.Sequence = 0 
    $configMod1.Owner = "CustomErrorsMod" 
    ## SPWebConfigModificationType.EnsureChildNode -> 0 
    ## SPWebConfigModificationType.EnsureAttribute -> 1 
    ## SPWebConfigModificationType.EnsureSection -> 2 
    $configMod1.Type = 1 
    $configMod1.Value = "Off"
		
    ######################################################################
	
	# SPWebConfigModification to enable/disable CustomErrors 
    $configMod2 = New-Object Microsoft.SharePoint.Administration.SPWebConfigModification 
    $configMod2.Path = "configuration/SharePoint/SafeMode" 
    $configMod2.Name = "CallStack" 
    $configMod2.Sequence = 0 
    $configMod2.Owner = "CustomErrorsMod" 
    ## SPWebConfigModificationType.EnsureChildNode -> 0 
    ## SPWebConfigModificationType.EnsureAttribute -> 1 
    ## SPWebConfigModificationType.EnsureSection -> 2 
    $configMod2.Type = 1 
    $configMod2.Value = "true" 
    # Add mods, update, and apply 
    $WebApp.WebConfigModifications.Add( $configMod1 ) 
    $WebApp.WebConfigModifications.Add( $configMod2 )
    $WebApp.Update() 
    $WebApp.Parent.ApplyWebConfigModifications() 
} 
}



# Enable-SPCustomErrors Function
function Enable-SPCustomErrors { 
param( 
    [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)] 
    [Microsoft.SharePoint.PowerShell.SPWebApplicationPipeBind] 
    $WebApplication 
) 

process { 
    $WebApp = $WebApplication.Read() 
    $mods = @() 
    foreach ( $mod in $WebApp.WebConfigModifications ) { 
        if ( $mod.Owner -eq "CustomErrorsMod" ) { 
            $mods += $mod 
        } 
} 
    foreach ( $mod in $mods ) { 
        [void] $WebApp.WebConfigModifications.Remove( $mod ) 
    } 
    $WebApp.Update() 
    $WebApp.Parent.ApplyWebConfigModifications() 
} 
}

Get a Web, List and Library Inventory using PowerShell


A colleague of mine recently asked me “Ryan, can you give me some PowerShell code that can give me a list of all sites and sub sites as well as all lists and libraries within each of those sites – for an entire web application?”

“Of course”, I said…

I had some other scripts and functions that were similarly constructed, so I simply took one that was close and adapted it to make it work.

This function, which I’ve called “Get-SPSiteInventory” will run against either an entire Web Application (using the -WebApplication switch param) or a single Site Collection (using the -SiteCollection switch param).

I’ve tested this both by sending the output straight to a file (using the Out-File cmdlet) as well as just running in the shell – both work pretty nicely.

I’ve excluded comment-based help for better readability, and the syntax is as follows…

To run against a site collection:

Get-SPSiteInventory -Url http://spsite -SiteCollection

To run against a web application:

Get-SPSiteInventory -Url http://spwebapp -WebApplication

The entire function:

function Get-SPSiteInventory {
Param(
[string]$Url,
[switch]$SiteCollection,
[switch]$WebApplication
)
Start-SPAssignment -Global
	if ($SiteCollection) {
		$site = Get-SPSite $Url
		$allWebs = $site.allwebs
		foreach ($spweb in $allWebs) {
			" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "
			$spweb.Url
			$spweb.Lists | select Title, BaseType
			$spweb.dispose()
		}
		$site.dispose()
	} 
	elseif ($WebApplication) {
		$wa = Get-SPWebApplication $Url
		$allSites = $wa | Get-SPSite -Limit all
		foreach ($spsite in $allSites) {
			$allWebs = $spsite.allwebs
			foreach ($spweb in $allWebs) {
			" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "
			$spweb.Url
			$spweb.Lists | select Title, BaseType
			$spweb.dispose()
			}
		}
	}
Stop-SPAssignment -Global
}

DAYSPUG Presentation December 13th 2011


Last night I spoke to a great crowd of PowerShell Enthusiasts at the Dayton SharePoint Users Group (DAYSPUG) in Dayton, Ohio. I knew most of the folks in attendance from other local SharePoint and PowerShell events, so it was nice to see everyone again.

It was a pleasure to speak in front of all of you, and I thoroughly enjoyed the engagement that you all showed.

Since the actual presentation/demo wasn’t much different from my recent engagement at SharePoint Saturday Cincinnati, I’ll simply point to that post for the actual PowerShell and XML code I used in the demos. That content is located here: SPS Cincinnati Post

Here is a post which contains the two Christmas functions I shared last night as well: Two PowerShell Functions for the Christmas Season

Thanks again to Tony Maddin and the rest of the DAYSPUG members for welcoming me to speak. See you all next time!

RD