Continuing the theme of obscure SharePoint issues; this one on a new topic – Word Automation Services. A client has a new requirement to automate some documents and using Word templates and converting them to PDF all from a Nintex workflow would be… nifty. So away we go.
In Word, we created a Word document and then inserted some ‘Content Controls’ – have some options here, but for proof of concept, started with a simple text control. Then in the Developer tab of the ribbon, you can edit the properties of the control and give it a title. Save the document and upload to a document library in SP.
Nintex workflow (for SharePoint 2010 this is…) has a three ready-built actions for Word Automation – the initial one we need is ‘Update a Document.’ Add this to the surface, provide the URL to the Word document and then add a control to edit – referencing it by the Title we assigned in the template.
Save, publish and run the workflow and – yep, crashy crashy no likey. So here are the steps to troubleshoot this.
The initial error is pretty simple ‘Error converting document. There is no word services application proxy for this site collection.’ This is a fancy way of saying Word Automation Services isn’t actually enabled / installed / configured. So it stands to reason that it’s not going to work. This is resolved by heading to Central Admin and then in ‘Service Applications’ under Application Management, create a new Word Automation Services application. Choose to create a new app pool.
Pro Tip: you should really do this via Powershell, because otherwise, despite giving the App Pool a sensible name, it will be created in IIS with a Guid which is a bit of a pain in the tits to debug. So either do it in Powershell; or, at least, have IIS open so that after it’s created you can quickly see which is the new one. With the Service Application in place there’s not much else to configure – it has a settings / properties page if you do want to tweak anything such as file types and so on; but the defaults (for now) should be fine.
With this in place: run the workflow again and new error: ‘No endpoints were found for the service application. Ensure that the service has been enabled on one or more servers within the SharePoint farm.’ This is pretty simple – the Word Automation Services service is probably not running. In Central Admin find ‘Services on Server’ and then start the Word Automation Services service. Obviously tweak this to suit your farm.
Next error: ‘Error converting document. The HTTP service at http://[server]:[port]/GUID/Service.svc is too busy.’ [or similar error.] This is a delightful SharePoint error where it’s actually not in any way too busy – in this case, the first thing to check is that your shiny new IIS app pool is actually running. (It should be ‘started’.) In my case, the app pool was starting and then immediately stopping. I tracked this down to the service account being used and once this was resolved, the app pool would start and stay started.
But – we now know the URL for the service endpoint so this will make debugging easier. Try and hit the service endpoint and: ‘The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing… snip.’
OK, great – let’s enable the faults in the browser so we can actually see what’s happening. But… um, where? By looking at our new IIS App Pool, and then View Applications – you should see one. This will give you a path to the Service Application. By default this is: C:\Program Files\Microsoft Office Servers\version\WebServices\WordServer – where version = 14 for SP2010, 15 for SP2013 and 16 for v++.
In that folder will be a web.config and you can find the serviceBehaviors section. Find the behavior name = “WordServer-ServiceBehavior” and add:
<code><serviceDebug includeExceptionDetailInFaults="true" /></code>
So it should look a bit like this:
<code><serviceBehaviors> <behavior name="WordServer-ServiceBehaviour"> <serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" /> <serviceDebug includeExceptionDetailInFaults="true" /> </behaviour> </serviceBehaviors></code>
Once you save, and reload the service page, you should see it takes a few seconds to spin up (which indicates you’re actually editing the right file!) You should now get a cheeky new error:
An ExceptionDetail, likely created by IncludeExceptionDetailInFaults-true, whose value is: System.InvalidOperationException: An exception was thrown in a call to a policy export extension. Extension: System.SewrviceMode.Channels.TransportSecurityBindingElement. Error: Security policy export failed. The binding contains a TransportSecurityBindingElement but no transport binding element that implements ITransportTokenAssertionProvider. Policy export for such a binding is not supported…. snip.
OK. So back in the web.config for the service in the bindings section you should find a binding called “WordServer-Binding-http” (assuming you’re in a non-HTTPs environment of course) – and in its security element ensure authenticationMode is IssuedTokenOverTransport and allowInsecureTransport = true.
<code><security authenticationMode="IssuedTokenOverTransport" allowInsecureTransport="true" /></code>
Save and reload and BOOM! You should have a normal (ish) looking service page. Buuuttt… you’ll see “Metadata publishing for this service is currently disabled.” OK, easy enough – find the behavior section from earlier, and switch httpGetEnabled to true. Save and reload and yep, there we are.
Joyous and excited, head back to Nintex and run the workflow and heeeyyy presttooo…. maybe. Just remember that in SP2010, Word Automation is not on demand – so you will actually have to allow the Word Automation Timer Job to tick over for it to complete.
If you want to enable some loggin, you can do so by adding this to the same behaviour as before:
<code><serviceSecurityAudit auditLogLocation="Application" serviceAuthorizationAuditLevel="Failure" messageAuthenticationAuditLevel="Failure" suppressAuditFailure="true" /></code>
With this in place, rerunning (to generate the error) creates an event log entry.