Getting to grips with the Tridion core service in Powershell
As regular readers of this blog will know, I've been a long-standing fan of the Windows Powershell as a tool for interacting with Tridion. On more than one project, the flexibility of the Powershell has allowed me to process Tridion data in ad-hoc ways that would be unthinkable if you had to bring with you all the overhead of, say, C# and Visual Studio. All of that is, of course, positive, but the downside of it has been that I don't seem to be making the jump over to the core service, which, after all, I should expect to be one of my primary APIs for some time to come. So time to make a change.
A while ago, I had tinkered with using the TOM.NET API from the Powershell, but I stopped putting effort into that once I got the basics working. The advice from SDL is clearly to use the core service for the kind of scenarios that the Powershell covers. Just for the record, though - getting a TOM.NET session in the Powershell is considerably more difficult than the equivalent activity using the core service. To be fair, neither technique even remotely approaches the simplicity of "$tdse = new-object -com TDS.TDSE", but like I said, it's time to move on.
So once I started looking at this, I had a quick look at Frank van Puffelen's GetCoreServiceClientWithoutConfigFile recipe in the Tridion Cookbook, and then I spent some time snuffling around in Peter Kjaer's Tridion Powershell Modules. Both of these are great resources, but I suffer quite badly from Not Invented Here syndrome, so at the very least, I had to poke around a bit and see what's going on. After some blatant stealing: mostly from Peter's code, I ended up with this:
Add-Type -assemblyName System.ServiceModel $binding = new-object System.ServiceModel.WsHttpBinding $binding.MaxBufferPoolSize = [int]::MaxValue $binding.MaxReceivedMessageSize = [int]::MaxValue $binding.ReaderQuotas.MaxArrayLength = [int]::MaxValue $binding.ReaderQuotas.MaxBytesPerRead = [int]::MaxValue $binding.ReaderQuotas.MaxNameTableCharCount = [int]::MaxValue $binding.ReaderQuotas.MaxStringContentLength = [int]::MaxValue $endpoint = new-object System.ServiceModel.EndpointAddress http://localhost/webservices/CoreService2011.svc/wsHttp Add-Type -Path 'C:\Program Files (x86)\Tridion\bin\client\Tridion.ContentManager.CoreService.Client.dll' $core = new-object Tridion.ContentManager.CoreService.Client.SessionAwareCoreServiceClient $binding,$endpoint
So what's going on here? What I've extracted is pretty close to the barest minimum implementation I could get to. Maybe you could get it smaller if you weren't bothered by running up against the fairly low default quota values offered by the Windows Communication Framework. In fact, I'm quite unsure about my approach to the quotas. What I've done is effectively to say that quotas aren't helpful for my scenario, and set them all to the maximum possible. Does this make sense? Let me know what you think. (Edit: I asked a question about this on stackoverflow, and got some good answers.)
So - to use the service you need three things:
- The core service client assembly. (It's great that SDL are now shipping this with the product. This means I can mail you a script, and say "use the 'official' client", and expect it to work.)
- A System.ServiceModel.WsHttpBinding object
- A System.ServiceModel.EndpointAddress object
So we load the System.ServiceModel assembly using Powershell's Add-Type cmdlet. This assembly is part of the .NET framework so we can just ask for it by name. Later in the script we use the same cmdlet to load the client dll, but then we have to specify its location. Once we have System.ServiceModel loaded, we can instantiate a binding and an endpoint, and pass those to the constructor of the client. Even though we end up with a few lines of code, it's not really hard, eh?
From here on we can just use the $core object to talk to the service. To be honest, having had a bit of a dig into how it works, you're probably better off just using Peter's module, which takes care of more than my hard-coded version does, and also offers some utility methods, for example, to create a new user. In fact, assuming you have installed the module, getting started is even easier than instantiating a TDSE: just "$core = Get-TridionCoreServiceClient". Nice job, Peter. Thanks.
Edit: If you prefer a NetTcp binding, this is pretty simple too: Just instantiate the correct binding type:
$binding = new-object System.ServiceModel.NetTcpBinding
And use a different endpoint
$endpoint = new-object System.ServiceModel.EndpointAddress net.tcp://localhost:2660/CoreService/2011/netTcp