Pages Menu
TwitterRssFacebook

Posted by on Nov 17, 2015 in Development, Skype for Business® (Lync®) | 2 comments

Development Project: PowerShIM – run PowerShell on remote servers using Skype for Business Instant Messaging

Development Project: PowerShIM – run PowerShell on remote servers using Skype for Business Instant Messaging

This is a little project I did just to see if it possible (and thanks to Skype for Business MVP Tom Arbuthnot for the idea!). Can I use Skype for Business to enable administrators to execute PowerShell remotely simply by sending Instant Messages, and get response data back.

Is this useful? Well, it could be – you could run this on all FE Servers and have a quick way to interrogate them or run PowerShell commands. And of course, the administrator could be on another network or on the other side of the world – this will work with federation and won’t require complicated holes in firewalls for remote access.

I’m releasing it for free to the community – it’s pretty basic and there are probably some bugs .. but if you’d like to try it out, feel free. I’m also open-sourcing the code on GitHub so you can take it and improve it.

How?

A UCMA application which exposes a Skype for Business endpoint and listens for incoming instant messages. When it receives a message, it validates that the sender is on the approved whitelist of senders (my minimal security checking to opening up a root PowerShell instance to the world!) and then executes the command in a PowerShell run-space. Any output is fed back to the user as another instant message.

TL;DR – Show Me

Get-CsManagementStoreReplicationStatus

Getting the Management Store Replication Status of a remote Skype for Business server by sending it an Instant Message!

Installation Steps

This is a development project. I’m not suggesting it’s a good idea. You do realise what we’re doing here, right? Exposing a SfB contact that is hard-wired to a root PowerShell shell. You see the dangers inherent in that? Good. You should completely understand the risks involved here before doing this on a production system.

OK, so installation is a little bit .. involved 🙂  You can only run this on a Trusted Application Server or Front End server – essentially anywhere you can run UCMA applications. Firstly, download the zipped program files and extract to a location of your choice.

Then, open the Skype for Business Management Shell on that server. Create the Trusted Application entry:

New-CsTrustedApplication -ApplicationId impowershell -Port 6000 -TrustedApplicationPoolFqdn yourPoolFQDN.domain.com

Make sure you use a port number that’s not in use, and substitute the TrustedApplicationPoolFqdn with the actual Trusted Application Pool FQDN. (If you’re not sure about the port number, run get-cstrustedapplication to get a list of existing applications and their port numbers.)

Now, create the endpoint which you will interact with:

New-CsTrustedApplicationEndpoint -TrustedApplicationPoolFqdn yourPoolFQDN.domain.com -ApplicationId impowershell -SipAddress “sip:[email protected]” -DisplayName “PowerShIM App 1”

Again, replace the TrustedApplicationPoolFqdn value. You can also specify your own values for the SIP address and display name to enable you to identify which server you’ll be talking to.


Lync 2013 Users…

If you’re still using Lync 2013 or want to install this on a Lync 2013 application server, you will need to perform an additional step. The code has been compiled against the latest UCMA SDK, which isn’t recognised by Lync 2013. However, a quick tweak to the config file can be used to force the code to use UCMA 4 again. Navigate to the code folder and use Notepad to open IMPowershell.exe.config.  Immediately after the closing </startup> tag, add this section:

&lt;runtime&gt;
 &lt;assemblyBinding xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot;&gt;
      &lt;dependentAssembly&gt;
        &lt;assemblyIdentity name=&quot;Microsoft.Rtc.Collaboration&quot; publicKeyToken=&quot;31bf3856ad364e35&quot; culture=&quot;neutral&quot; /&gt;
        &lt;bindingRedirect oldVersion=&quot;0.0.0.0-6.0.0.0&quot; newVersion=&quot;5.0.0.0&quot; /&gt;
      &lt;/dependentAssembly&gt;
&lt;/assemblyBinding&gt;
&lt;/runtime&gt;

 

Trusted Users

I’ve added a check when accepting incoming messages, so that only white-listed SIP addresses will be processed. That white-list is maintained in the file authorisedusers.txt in the code folder, so go there now and add your SIP address, otherwise you’ll be ignored.

 

When you’re ready, run IMPowershell.exe as an Administrator. A console window should open, and you should see Lync Server Ready. That means everything is set up OK. If not, check the code folder – there will be a log.txt file, errors and other info are logged there.

LyncServerReady

Trying It Out

Find the SIP address you specified earlier and open a new conversation window. Send a PowerShell command as an instant message. If everything has gone well, you’ll see the output:

Get-Date

Good to Know #1

You can only send one line – what you send when you press Enter is what gets executed. However, that doesn’t stop you piping commands, as in this example to find out when the machine was last restarted, and by whom:

Get-EventLog

Good To Know #2

Because lots of the commands might be to do with Skype for Business, I always automatically import the Lync module prior to running commands. So you can do this:

Get-CsManagementStoreReplicationStatus

Run as a Service

The code is structured so that you can also run it as a Windows Service. To do this you need to add it using SC CREATE and point the binPath to the same .exe file you ran above. The only gotcha is that when you do this the code runs in a different folder, so it can’t find the authorised users text file. To fix this, edit the IMPowershell.exe.config file – you’ll notice there’s a setting for the file location. You can change this to be the actual path to the file, not just the relative location. This will fix the service.

Get the Code, Fix the Bugs

This is very much a “try it and see” development project. I’d be interested to hear about things not working, or bugs – but please don’t expect much response from me. If you’re a developer, you’d be much better off grabbing the code yourself and fixing it or changing it to work for you.

Written by Tom Morgan

Tom is a Microsoft Teams Platform developer and Microsoft MVP who has been blogging for over a decade. Find out more.
Tom is currently writing a book: Building and Developing Apps & Bots for Microsoft Teams. Register your interest today and be the first to know when it's ready to read.

2 Comments

  1. Wow…neat idea. I have a few scheduled tasks on our S4B FE that run nightly, but sometimes I kick them off manually. The tasks just execute different ps1 scripts that I created for UM voicemail and disabling S4B users. Could I execute these ps1 files via IM you think using your method?

  2. run IMPowershell.exe as an Administrator. errors :

    ———————————————————————————————-
    2016-09-28 16:22:26,490 INFO [1] IMPowershell.LyncServer – Starting Collaboration Platform
    2016-09-28 16:22:26,582 INFO [1] IMPowershell.Program – Started
    2016-09-28 16:22:27,081 ERROR [5] IMPowershell.LyncServer – Error establishing collaboration platform: Microsoft.Rtc.Collaboration.ProvisioningFailureException:One or more values in the configured settings are invalid or unusable. Check inner exception and logs for more details. —> Microsoft.Rtc.Internal.ServerConfiguration.SettingsInitializationException: 未能初始化设置包装。
    计算机上未安装 ExternalServer 服务。
    在 Microsoft.Rtc.Internal.ServerConfiguration.UCSettings.InitConsumerWithRole(RoleName role)
    在 Microsoft.Rtc.Internal.ServerConfiguration.UCSettings..ctor(String applicationId, SettingsWrapperOptions options)
    在 Microsoft.Rtc.Internal.ServerConfiguration.UCSettings.Get(String applicationId, SettingsWrapperOptions options)
    在 Microsoft.Rtc.Collaboration.ProvisioningSourceImpl.GetInitialPlatformData()

    — End of inner exception stack trace —
    在 Microsoft.Rtc.Signaling.SipAsyncResult`1.ThrowIfFailed()
    在 Microsoft.Rtc.Signaling.Helper.EndAsyncOperation[T](Object owner, IAsyncResult result)
    在 System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
    — 引发异常的上一位置中堆栈跟踪的末尾 —
    在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
    在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    在 IMPowershell.LyncServer.d__11.MoveNext()
    Detected 在 System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
    在 System.Environment.get_StackTrace()
    在 Microsoft.Rtc.Collaboration.ProvisioningFailureException..ctor(String message, Exception innerException, ProvisioningFailureReason failureReason)
    在 Microsoft.Rtc.Collaboration.ProvisioningSourceImpl.GetInitialPlatformData()
    在 Microsoft.Rtc.Collaboration.ProvisioningSourceGetInitialPlatformDataAsyncResult.ProcessCoreHelper()
    在 Microsoft.Rtc.Collaboration.SipCollaborationAsyncResult.ProcessCore()
    在 Microsoft.Rtc.Signaling.AsyncWorkitemQueue.ProcessItems()
    在 Microsoft.Rtc.Signaling.QueueWorkItemState.ExecuteWrappedMethod(WaitCallback method, Object state)
    在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
    在 System.Threading.ThreadPoolWorkQueue.Dispatch()
    FailureReason = 2

Post a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.