Thursday, September 24, 2015

PowerShell to manipulate Skype for Business and Lync converations

A recent project left me playing with the Lync SDK (Skype for Business).

The Skype for Business skin was clad onto the Lync 2013 client.  And thus the Lync 2013 SDK is the one I worked with.
Much of the SDK documentation is centered around writing your own application integration with Lync, event listeners, triggers, etc.  I am just wanting to drive Lync, not write a replacement for it.

Yes, I do know that Office 2016 released (yesterday) but I am not going there just yet.

Lets do some interesting things with Lync:

First - if you want to get started with Lync and PowerShell there are a few blog posts.  I find the automating Lync with PowerShell tutorial from Trevor Sullivan to be the most comprehensive / useful.  I am not going to re-hash what Trevor has already done pretty well.

He walks you though downloading the SDK, installing it, and invoking the 'model' DLL that you need to perform all of the actions.

If you are not a developer, then you still need to install the SDK, for one particular MSI - the redistributable.

In the install folder of the SDK ( C:\Program Files\Microsoft Office\Office15\LyncSDK\Redist ) is the lyncRuntime.msi.  This will give you only the bare minimum DLL that you need to make this all happen.  This would be the thing you package into your application.

Now, allow me to build upon Trevor's foundation.
I am going to launch a 'conversation', then call someone, then start open 'application sharing', then end the conversation.

The big assumption here is that you are logged in to Lync.  I am not going to test for that, MSFT has examples.

# import the Microsoft.Lync.Model.dll
Add-Type -Path 'C:\Program Files\Microsoft Office\Office15\LyncSDK\Assemblies\Desktop\Microsoft.Lync.Model.dll'

# Get the Lync Client SDK automation object
$Automation = [Microsoft.Lync.Model.LyncClient]::GetAutomation()


At this point in time I could go two directions.  I could use the 'Meet Now' feature, or I could build a conversation object the hard way.
What I have learned through experimentation is that the Meet Now sets up quite a few things for you and is thus pretty useful.  I have gone about it the other way and run into sharing problems that I don't run into when using Meet Now.  So I will take advantage of the fact that they have provided this.

# Launch a 'Meet Now' conversation
$meetNow = $Automation.BeginMeetNow({}, 0)
# Get the conversation window to manipulate the conversation.
$conversationWindow = $Automation.EndMeetNow( $meetNow )
# Get the conversation
$conversation = $conversationWindow.Conversation


The first thing that I noticed is that I have something that looks no more than an 'InstantMessage' modality conversation.   That is developer speak for I started a conversation and began a session of the type called instant message.
My experimentation shows that you always have IM in the background, it appears to be the base of the conversation.

Now, I want to create an empty 'Stage' just illustrate the rest.
So I will add the Application Sharing modality.

$Conversation.Modalities['ApplicationSharing'].BeginConnect({}, 0)

The Conversation Window object, there are a few things you can do to it.
Such as, make it full screen.

$conversationWindow.ShowFullScreen('0')

Now, I want to call someone.  And to do that, I add a participant.
To add a participant I do need one piece of information, their SIP URL.  For most organizations this is the email address of the individual, but it does not have to be (that is the caveat).

# add a person only knowing the SIP address (they do not need to be in your contacts list)
$conversation.AddParticipant( $LyncClient.ContactManager.GetContactByUri( "bogusUser@itproctology.com" ) )

# they will receive a Lync Call

You can keep adding folks.  If you forgot who you added, you can look:

$conversation.SelfParticipant # this is you
$conversation.Participants # this is everyone


Now, lets begin to close down this meeting example.

# exit full screen
$conversationWindow.ExitFullScreen() # note the application is left maximized.
# lets resize the window
$conversationWindow.Resize( 300, 300 )

# remove a participant (we only have their SIP address)
$removePeep = $conversation.Participants | where { $_.Contact.Uri -Match "bogusUser@itproctology.com" }


That was a sneaky one liner of me.  Note that I took all of the participants (which is always an array BTW) and I looked for the ContactUri that contained the email of my person to kick off the meeting.

$conversation.RemoveParticipant( $removePeep )
# note, the person you remove receives a notice that they were removed from the conversation.

# End the meeting

$conversation.End()

That is it.  The lifecycle of a Lync meeting from PowerShell

Those objects are powerful.

Say that you already have a meeting running and you want to use PowerShell to find it;
For this we need the Conversation Manager.

$LyncClient = [Microsoft.Lync.Model.LyncClient]::GetClient()
$conversation = $LyncClient.ConversationManager.Conversations


And then find the conversation (as we can have more than one IM conversation at a time).

Now, a few other shortcuts to get you working:

# add IM into IM Window
$Conversation.Modalities['InstantMessage'].BeginSendMessage("Welcome", {}, 0)

# what I can share
$resources = $Conversation.Modalities['ApplicationSharing'].ShareableResources

# share my desktop
$Conversation.Modalities['ApplicationSharing'].BeginShareDesktop( {}, 0)

# share a specific resource
$Conversation.Modalities['ApplicationSharing'].BeginShareResources( $resources[1], {}, 0)

# add voice
$Conversation.Modalities['AudioVideo'].BeginConnect({}, 0)

# remove contect sharing (stop sharing, stage stays present)
$Conversation.Modalities['ApplicationSharing'].BeginDisconnect([Microsoft.Lync.Model.Conversation.ModalityDisconnectReason]::None, {}, 0)

# stop the video call, keep the IM
$conversation.Modalities['AudioVideo'].BeginDisconnect([Microsoft.Lync.Model.Conversation.ModalityDisconnectReason]::None, {}, 0)

# send a thanks for attending IM
$Conversation.Modalities['InstantMessage'].BeginSendMessage("Thanks for attending. Tasks will follow.", {}, 0)