Automating the Lync Client with PowerShell

Automating the Lync Client with PowerShell

You love PowerShell, right? And you love the Microsoft .NET Framework? Are you setting out to automate the Microsoft Office 2013 Lync Client with PowerShell? If you answered “yes” to the last three questions, then you’ve come to the right place! We’re going to take a look at how to get started automating the Lync 2013 client using PowerShell! Thanks to PowerShell’s direct support for Microsoft .NET Framework types, we can easily manage Lync Client functions from PowerShell, much in the way that C# developers can!

Download and Install the SDK

The first thing you need to do is go out to Microsoft’s download site and grab the Lync 2013 SDK. The installation process is fairly painless, so just click “Next” through it. By default, the installation path is: %ProgramFiles(x86)%\Microsoft Office\Office15\LyncSDK.

Figure: The root folder of the Lync 2013 SDK.
The root folder of the Lync 2013 SDK.

When you install the Microsoft Lync 2013 SDK, what you get is basically a series of Microsoft .NET assemblies (aka. .NET libraries) that allow you to perform automation functions on the Lync 2013 Client! Additionally, there is a CHM (compiled HTML help) file that contains some detailed documentation on how to utilize the SDK. If you’re interested in developing, get used to reading documentation!

Import the .NET Assemblies

Lync SDK Assmblies
Lync SDK Assemblies

Once you’ve installed the Microsoft Lync 2013 SDK, you’ll need to import the appropriate Microsoft .NET assembly(ies) into the PowerShell session. There are a few different ways to do this, but in this case, I’ll use the built-in Add-Type PowerShell command. If you drill into the .\Assemblies\Desktop folder, you’ll notice that there are a few different .NET assemblies available, but in my testing, I only needed to directly import the Microsoft.Lync.Model.dll.

Add-Type -Path "C:Program Files (x86)Microsoft OfficeOffice15LyncSDKAssembliesDesktopMicrosoft.Lync.Model.dll";

Get a Reference to the Lync Client

First, you’ll want to make sure that the Lync 2013 client is up and running on your system, ideally signed in, but not necessary either. To get a reference to the main LyncClient object, and a special Automation object, you can use the following lines of code:

$LyncClient = [Microsoft.Lync.Model.LyncClient]::GetClient();
$Automation = [Microsoft.Lync.Model.LyncClient]::GetAutomation();

Explore the Lync Client

Once you’ve got the references to the LyncClient and Automation objects, you can start exploring their members using the Get-Member command in PowerShell. Let’s take a look and see what we’ve got to work with:

Automation Object

Here are the members of the Automation .NET object:

   TypeName: Microsoft.Lync.Model.Extensibility.Automation

Name                      MemberType Definition                                                        
----                      ---------- ----------                                                        
BeginMeetNow              Method     System.IAsyncResult BeginMeetNow(System.IntPtr parentHWND, Syst...
BeginStartConversation    Method     System.IAsyncResult BeginStartConversation(string conferenceUrl...
CreateObjRef              Method     System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)   
EndMeetNow                Method     Microsoft.Lync.Model.Extensibility.ConversationWindow EndMeetNo...
EndStartConversation      Method     Microsoft.Lync.Model.Extensibility.ConversationWindow EndStartC...
Equals                    Method     bool Equals(System.Object obj)                                    
GetConversationWindow     Method     Microsoft.Lync.Model.Extensibility.ConversationWindow GetConver...
GetHashCode               Method     int GetHashCode()                                                 
GetLifetimeService        Method     System.Object GetLifetimeService()                                
GetType                   Method     type GetType()                                                    
InitializeLifetimeService Method     System.Object InitializeLifetimeService()                         
LaunchAddContactWizard    Method     void LaunchAddContactWizard(string contactEmail)                  
ToString                  Method     string ToString()                                                 
InnerObject               Property   System.Object InnerObject {get;}

LyncClient Object

Here are the members of the LyncClient .NET object:

   TypeName: Microsoft.Lync.Model.LyncClient

Name                          MemberType Definition                                                    
----                          ---------- ----------                                                    
CapabilitiesChanged           Event      System.EventHandler`1[Microsoft.Lync.Model.PreferredCapabil...
ClientDisconnected            Event      System.EventHandler ClientDisconnected(System.Object, Syste...
CredentialRequested           Event      System.EventHandler`1[Microsoft.Lync.Model.CredentialReques...
DelegatorClientAdded          Event      System.EventHandler`1[Microsoft.Lync.Model.DelegatorClientC...
DelegatorClientRemoved        Event      System.EventHandler`1[Microsoft.Lync.Model.DelegatorClientC...
SignInDelayed                 Event      System.EventHandler`1[Microsoft.Lync.Model.SignInDelayedEve...
StateChanged                  Event      System.EventHandler`1[Microsoft.Lync.Model.ClientStateChang...
BeginInitialize               Method     System.IAsyncResult BeginInitialize(System.AsyncCallback ca...
BeginShutdown                 Method     System.IAsyncResult BeginShutdown(System.AsyncCallback comm...
BeginSignIn                   Method     System.IAsyncResult BeginSignIn(string userUri, string doma...
BeginSignOut                  Method     System.IAsyncResult BeginSignOut(System.AsyncCallback commu...
CreateApplicationRegistration Method     Microsoft.Lync.Model.Extensibility.ApplicationRegistration ...
CreateObjRef                  Method     System.Runtime.Remoting.ObjRef CreateObjRef(type requestedT...
EndInitialize                 Method     void EndInitialize(System.IAsyncResult asyncResult)           
EndShutdown                   Method     void EndShutdown(System.IAsyncResult asyncResult)             
EndSignIn                     Method     void EndSignIn(System.IAsyncResult asyncResult)               
EndSignOut                    Method     void EndSignOut(System.IAsyncResult asyncResult)              
Equals                        Method     bool Equals(System.Object obj)                                
GetHashCode                   Method     int GetHashCode()                                             
GetLifetimeService            Method     System.Object GetLifetimeService()                            
GetType                       Method     type GetType()                                                
InitializeLifetimeService     Method     System.Object InitializeLifetimeService()                     
ToString                      Method     string ToString()                                             
Capabilities                  Property   Microsoft.Lync.Model.LyncClientCapabilityTypes Capabilities...
ContactManager                Property   Microsoft.Lync.Model.ContactManager ContactManager {get;}     
ConversationManager           Property   Microsoft.Lync.Model.Conversation.ConversationManager Conve...
DelegatorClients              Property   System.Collections.Generic.IList[Microsoft.Lync.Model.Deleg...
DeviceManager                 Property   Microsoft.Lync.Model.Device.DeviceManager DeviceManager {get;}
InnerObject                   Property   System.Object InnerObject {get;}                              
InSuppressedMode              Property   bool InSuppressedMode {get;}                                  
RoomManager                   Property   Microsoft.Lync.Model.Room.RoomManager RoomManager {get;}      
Self                          Property   Microsoft.Lync.Model.Self Self {get;}                         
Settings                      Property   Microsoft.Lync.Model.ClientSettings Settings {get;}           
SignInConfiguration           Property   Microsoft.Lync.Model.SignInConfiguration SignInConfiguratio...
State                         Property   Microsoft.Lync.Model.ClientState State {get;}                 
Uri                           Property   string Uri {get;}                                             
Utilities                     Property   Microsoft.Lync.Model.Utilities Utilities {get;}               

As you can see, there are quite a few different properties, methods, and events available to us. I would first point out that I ran into some issues when trying to subscribe for events. For some reason, the events don’t always seem to fire when you expect them to, however it depends on specifically which event you’re talking about.

On the LyncClient object, a few properties that are of special interest are:

  • ContactManager – allows limited querying of your contact list, and searching for new contacts
  • DeviceManager – allows you to manage audio and video devices (eg. get/set the active A/V device)
  • ConversationManager – CRUD operations for Lync client conversations. You can view/create chat, voice, and video conversations.

Conversations

I’ll talk briefly about the ConversationManager object here. The ConversationManager allows you to view a list of currently open conversations, add new conversations, and remove conversations. This object has a method called AddConversation(), which returns a new Conversation object. Once you have a reference to a Conversation object, you can add a “participant” to it by using the AddParticipant() method. You can pass in either a Contact or ContactEndpoint object into this method, while it outputs a Participant object. I haven’t found any particular uses for the Participant object at this point, so I typically just discard it by casting to [void].

Let’s take a look at how to achieve what we just described:

# Get the conversation manager object from the Lync Client
$ConversationManager = $LyncClient.ConversationManager;
# Add a new conversation, and assign it to a variable
$Conversation = $ConversationManager.AddConversation();
# Add yourself as a participant, but cast the output to [void].
# We don't care about the Participant object for now.
[void] $Conversation.AddParticipant($LyncClient.Self.Contact);
# Send yourself a message
$Conversation.Modalities['InstantMessage'].BeginSendMessage("Testing", {}, 0);

Wait, what was that last line? Modalities? Let’s talk about those for a minute.

Modalities

After adding the appropriate participants to the conversation, you can utilize the Modalities on the conversation. Modalities are basically defined as functions exposed by the conversation, such as: instant message, audio/video, and application/content sharing. Each modality returns an object, with the methods appropriate to that Lync end user functionality. For example, you can initiate a Lync call by using the BeginConnect() on the Audio/Video modality. As you saw in the previous section, the BeginSendMessage() method on the InstantMessage modality allows us to send an instant message.

A Practical Lync Automation Example

A fairly simple, yet practical use case that I came across, while exploring the Lync Client API, was to extend the core LyncClient object to include a StartTestCall() method. It just so happens that the Contact object returned by $LyncClient.Self has a property called TestCallEndpoint, which is a ContactEndpoint object. We can add this object to a Conversation, and then invoke a Lync call, using the Audio/Video modality.

Here is the code to add this custom ScriptMethod, using the Add-Member command:

$StartTestCallMethod = {
    $Conversation = $this.ConversationManager.AddConversation();
    [void]$Conversation.AddParticipant($this.Self.TestCallEndpoint);
    [void]$Conversation.Modalities['AudioVideo'].BeginConnect({}, 0);

    Add-Member -InputObject $Conversation -MemberType ScriptMethod -Name HangUp -Value {
        [void]$this.Modalities['AudioVideo'].BeginDisconnect([Microsoft.Lync.Model.Conversation.ModalityDisconnectReason]::None, {}, 0);
        } -PassThru;
    };
Add-Member -InputObject $LyncClient -MemberType ScriptMethod -Name StartTestCall -Value $StartTestCallMethod -Force;

As an added bonus, we also added a custom HangUp() method, which simplifies the call to the Audio/Video modality on the Conversation object. This saves typing each time we want to hang up our test call! So, now that we’ve declared these custom methods, how do we actually put them to use?

# Initiate the test call
$Conversation = $LyncClient.StartTestCall();
# Sleep for a few seconds
Start-Sleep -Seconds 10;
# Hang up the conversation (using our custom ScriptMethod)
$Conversation.HangUp();

Extending Types with Types.ps1xml

The downside to the earlier implementation, which uses Add-Member, is that we have to manually adapt each object whenever we create a new one. This can be cumbersome, so why don’t we just tell PowerShell to automatically adapt all future Conversation objects with a HangUp() method, and every LyncClient object with a StartTestCall() method? That way, when we destroy our PowerShell session and create a new one, these objects will always have these custom methods define, without any extra typing! Sounds great! How do we do that?

PowerShell offers a feature called “types.ps1xml”, where you declare custom properties and methods once, and then all future objects of the specified type automatically inherit these capabilities. These files are declared in a XML format, and are imported into your PowerShell session by using the Update-TypeData command.

Here is the “types” file I came up with:

<?xml version="1.0" encoding="utf-8"?>
<Types>
  <Type>
    <Name>Microsoft.Lync.Model.Conversation.Conversation</Name>
    <Members>
      <ScriptMethod>
        <Name>SendMessage</Name>
        <Script>$AsyncResult = $this.InstantMessage.BeginSendMessage($args[0], { }, 0); [void]$AsyncResult.AsyncWaitHandle.WaitOne();</Script>
      </ScriptMethod>
      <ScriptMethod>
        <Name>HangUp</Name>
        <Script>[void]$this.Modalities['AudioVideo'].BeginDisconnect('None', { }, 0);</Script>
      </ScriptMethod>
    </Members>
  </Type>
  <Type>
    <Name>Microsoft.Lync.Model.LyncClient</Name>
    <Members>
      <ScriptMethod>
        <Name>StartTestCall</Name>
        <Script>$Conversation = $this.ConversationManager.AddConversation();
$Participant = $Conversation.AddParticipant($this.Self.TestCallEndpoint);
[void]$Conversation.Modalities[[Microsoft.Lync.Model.Conversation.ModalityTypes]::AudioVideo].AudioChannel.BeginStart({},0); $Conversation</Script>
      </ScriptMethod>
    </Members>
  </Type>
</Types>

Save this file as something like c:\test\Lync.ps1xml. To use this, from a completely fresh PowerShell session, simply run the following:

# Load the custom type data
Update-TypeData -PrependPath "C:testLync.ps1xml";
# Add reference to the Lync Client DLL
Add-Type -Path "C:Program Files (x86)Microsoft OfficeOffice15LyncSDKAssembliesDesktopMicrosoft.Lync.Model.dll";
# Get a reference to the LyncClient object
$LyncClient = [Microsoft.Lync.Model.LyncClient]::GetClient();

##################################

# Initiate the test call
$Conversation = $LyncClient.StartTestCall();
# Sleep for a few seconds
Start-Sleep -Seconds 10;
# Hang up the conversation (using our custom ScriptMethod)
$Conversation.HangUp();

If you want to learn more about how to use the “types.ps1xml” file to augment objects in PowerShell, then check out this article on MSDN: http://technet.microsoft.com/en-us/library/hh847881.aspx.

Conclusion

This article has reviewed the introductory use of the raw Lync 2013 Client SDK inside of PowerShell, to automate functions within the Microsoft Office 2013 Lync Client. We also reviewed how to use PowerShell’s adaptive type system to augment the core Lync SDK .NET types provided out of the box. I hope that this has helped increase your understanding of PowerShell and the Lync 2013 SDK.