• Assign a Policy to All the Users in a Security Group

    When it comes time to assign per-user policies, those of us here at Microsoft thought of everything. For example, if you take a look at the article Assigning Policies you’ll see how per-user policies can be assigned to a single user; to all the users with accounts in a specified OU; to all the users in a particular department; to all the users with a given job title; etc., etc., etc. Like we said, we thought of everything.

     

    What’s that? How can you assign a policy to all the users in a particular security group? Hmmm, we never thought of that ….

     

    OK, we admit it: we didn’t add a straightforward way to assign a policy to all the users in a security group. So does that mean that there’s no way to assign a policy to all the users in a security group? Let’s put it this way:

     

    $strFilter = "(&(objectCategory=Group)(SamAccountName=" + $args[0] +"))"

     

    $objDomain = New-Object System.DirectoryServices.DirectoryEntry

     

    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher

    $objSearcher.SearchRoot = $objDomain

    $objSearcher.Filter = $strFilter

    $objSearcher.SearchScope = "Subtree"

     

    $colProplist = "member"

    foreach ($i in $colPropList)

        {[void] $objSearcher.PropertiesToLoad.Add($i)}

     

    $colResults = $objSearcher.FindAll()

     

    foreach ($objResult in $colResults)

        {$objItem = $objResult.Properties; $group = $objItem.member}

     

    foreach ($x in $group)

        {

            Grant-CsClientPolicy $x -PolicyName $args[1]

        }

     

    Before we explain what this script does (although, by now, you can probably guess what it does) let’s explain how it works. Assuming you’ve copied this code and saved it as a .ps1 file (e.g., C:\Scripts\Assign-ToGroup.ps1) you run the thing by using a command similar to this:

     

    C:\Scripts\Assign-ToGroup.ps1 "FinanceUsers" "FinanceClientPolicy"

     

    In this command, FinanceUsers is the name of the security group we want to assign a policy to (in this example, we’re assigning a client policy). And which policy are we assigning? That’s the second parameter passed to the script; in this example, we’re assigning the client policy FinanceClientPolicy.

     

    As for the script itself, the first thing it does is search Active Directory in order to find the specified security group. Once that’s done the script then uses this snippet of code to retrieve all the group members and store those users in a variable named $group:

     

    $group = $objItem.member

     

    From there the script takes the group members and, one-by-one, connects to the appropriate user account in Active Directory. The script retrieves the user’s display name (stored in the variable $z), then uses this line of code to assign FinanceClientPolicy to the user in question:

     

    Grant-CsClientPolicy $z -PolicyName $args[1]

     

    Like we said, $z is the user’s display name; meanwhile, $args[1] is a Windows PowerShell variable that references the second command-line argument passed to the script.

     

    And that, as they say, is that.

     

    Keep in mind that this script assigns a client policy to all the users in a security group, and a client policy is the only kind of policy it can assign. What if you want to assign, say, a voice policy to all the users in a security group? That’s fine; just search the script for the cmdlet name Grant-CsClientPolicy and replace it with Grant-CsVoicePolicy. That’s all you have to do.

     

    Gee, maybe we did think of everything after all ….

  • Lync Server PowerShell Challenge: Rules

    One of these things is not like the others,

    One of these things just doesn't belong.

    Can you tell which thing is not like the others

    By the time I finish my song?

     

      Sesame Street

     

     

    One of These Things is Not Like the Others

     

    If you fancy yourself an educator (as those of us here at the Lync Server PowerShell blog are wont to do) then you have one mortal enemy in life: Sesame Street. Not that there's anything wrong with Sesame Street; as a matter of fact, Sesame Street is about as close to the perfect TV show (and educational vehicle) as you can find. And that's the problem: how can any educator hope to stand up to the juggernaut that is Sesame Street? No matter what you try to do, Sesame Street has not only done it, but has done it better. Case in point? Our all-muppet version of the help file for Get-CsAddressBookConfiguration turned out to be a complete disaster.

     

    But you know what they say: when the going gets tough, the tough get going. With that in mind, we made a vow to do something that was better than anything Sesame Street had ever done. And, after several long, hard days and sleepless nights, we came up with something: we realize that there's no way we'll ever do anything better than Sesame Street. At that point, and being true Americans, we made a new vow: if you can't beat 'em, steal one of their ideas and pass it off as your own. Ladies and gentlemen, our new weekly feature: One of These Things is Not Like the Others.

     

    So what is this new weekly feature? We're glad you asked that. If you've ever watched Sesame Street, then you've no doubt seen their version of One of These Things is Not Like the Others. On the TV show, they'll typically show you three things; for example:

     


    As a Sesame Street viewer, your job is to figure out which of these things is not like the others. Which of these things is not like the others? You got it: the blue circle, which is not like the two blue rectangles.

     

    Note. Um, you did get it, right? Just checking ….

     

    So how does this work with Lync Server PowerShell? The exact same way: we'll show you four things (four cmdlets, four PowerShell nouns, four PowerShell parameters, etc.), and then it's your job to figure out which of the four is not like the others. For example:

     

    Get-CsClientPolicy

    Set-CsConferencingPolicy

    New-CsMeetingConfiguration

    Remove-CsVoicePolicy

     

    Which of those four things is not like the others? That's right: New-CsMeetingConfiguration. And why isn’t New-CsMeetingConfiguration like the others? Right again: because that cmdlet deals with configuration settings, and the other three cmdlets deal with policies. See how easy that is? Kind of makes you wonder why Sesame Street plays their game using blue rectangles and circles instead of Lync Server PowerShell cmdlets, doesn't it?

     

    And before you ask, yes, each time we post a new challenge it's going to be as easy as the New-CsMeetingConfiguration challenge. Or at least it will be for us; after all, we have the answers right in front of us. For the rest of you, however, some of these challenges might prove to be a bit more, uh, challenging. But that's OK; here's what we're going to do about that, and here's how the weekly challenges will work:

     

    ·         Every Monday morning, we'll post a new One of These Things is Not Like the Others challenge. If you think you know the answer, then send an email to cspshell@microsoft.com.

    Oh, and be sure to include the answer in that email. Otherwise it's kind of pointless, if you know what we mean.  Also include your name or an alias you’d like to use for the challenge. We’ll keep a running total of points as we go along.

    If you come up with the correct answer (or an answer that's seems equally-plausible, at least in our opinion) you'll be awarded 3 points. And what do those points get you? We'll talk about points in a minute.

    ·         Of course, there's always the chance that you won't know the correct answer: some challenges are bound to be a little more difficult than others. So suppose a given challenge is kind of hard? Are you just out of luck? You bet you're out of luck. Maybe next time you'll try a little harder, you big crybaby.


    No, hey, just joking. If a challenge is hard, you're definitely not out of luck; far from it. After all, we're not doing these weekly challenges just to be mean; we're just trying to find a different way to help educate people on Lync Server PowerShell. With that in mind, every Thursday we'll publish a hint that should help you solve the challenge. Suppose the hint does help you solve the challenge; what then? Well, again, send your answer (along with your name or an alias you’d like to use) to cspshell@microsoft.com. If we agree with you, we'll give you 1 point for your effort.

    And yes, we know, if you solve the challenge without a hint you get 3 points. But fair is fair, right? What do you want, 5 points if you solve the challenge after being given a hint?

    ·         Every Monday morning we'll post the solution to the previous challenge. And when we do so, we'll post not only the answer we came up with, but any interesting/unique answers that you came up with.

     

    Admittedly, it's not quite as exciting as watching grass grow or paint dry. But we like to think it's pretty close.

     

    We should also note that – unless you really know Lync Server PowerShell – you probably won't be able to solve all of the challenges simply by looking at the four choices. So how can you solve the challenges? Let's put it this way. We know that many of you spend your days thinking, "Why does Microsoft still write help for their applications? Don't they know that nobody reads help these days?" Well, now you have the answer to your question. We write help for Lync Server PowerShell for one reason: because that help can assist you in solving the One of These Things is Not Like the Others challenges. If it's not obvious which of the items is not like the others, well, you could do worse than take a peek at the Lync Server PowerShell help. (And how do you do that? See this article for some useful hints.) And here's another hint: when you look at the help, take a look at things like scopes, datatypes, available verbs, etc., etc.

     

    And yes, we could give you an even more-specific list of things to look for. But if we take out the challenging aspect of this that would sort of defeat the whole purpose of doing a weekly challenge in the first place, wouldn't it?

     

    Anything else? Oh, right, we almost forgot: what about those points; what do you get for accumulating a bunch of points in the One of These Things is Not Like the Others challenge?

     

    Well, obviously, there is the enormous sense of pride and the worldwide prestige that comes with being one of the true giants in the field of One of These Things is Not Like the Others. We’ll be posting your scores (that’s why you should include your name or an alias you’d like to use to identify your score). Besides that – well, to tell you the truth, we're not really sure what there is besides that. No doubt you're thinking to yourself, "Man, these guys are obscure technical writers at Microsoft: they must have a huge budget for contests, giveaways, and other promotional items!" We're not allowed to talk about money in this blog, so all we can say is this: Microsoft gives us the exact budget that we deserve.

     

    Yes, it's that bad.

     

    But don't despair; we're looking into ways to con somebody out of something that can be awarded to people who earn X number of points. Don't worry about prizes and awards; let us worry about prizes and awards. You worry about acquiring as many points as possible.

     

    In fact, don't even worry about that. Instead, the only thing you should worry about is this: how can you solve the first-ever One of These Things is Not Like the Others challenge?

    Go to the Challenge Home Page.

  • How To Export Lync Contacts to Excel

    Operator, oh could you help me place this call?

    See, the number on the matchbook is old and faded…

    - Jim Croce, “Operator (That’s Not the Way it Feels)”

     

     

    When was the last time you called the telephone operator? Do operators even exist anymore? With your lists of contacts in your cell phone, Outlook, and Lync, how could you ever be without the number you need? And with everything all nicely synched up, you have all the numbers you need regardless of the device or the application you’re using. Right?

     

    Okay, maybe not.

     

    It’s possible that maybe things aren’t always synched. Because of that you might want to export your contacts from an application so you can either import them somewhere else, save them as a backup, or just have them available in another format. However, you’ll soon discover that Microsoft Lync 2010 doesn’t actually have an Export feature. But that’s okay, we’ve come up with a script to remedy that situation. This script will export your Lync 2010 contacts to a Microsoft Excel spreadsheet.

     

    Requirements. Before you get too excited about this, there are a few things you need to know about. First of all, this script runs only on the client machine and applies to the currently logged-in Lync user. Lync must be running and you must be logged in to it for this script to work.

     

    Second, you must have the Microsoft Lync 2010 SDK installed on the client machine.

    Download the Lync 2010 SDK.

     

    And, of course, third, you need Windows PowerShell 2.0. You don’t need the Lync Server Management Shell; this script runs against Lync, not Lync Server.

     

    Oh yeah, you also need Microsoft Excel.

     

    I’ve overcome the blow, I’ve learned to take it well

    I only wish my words could just convince myself…

     

    Hey, come on, the requirements aren’t that bad!

     

    We’re going to start by showing you a stripped-down version of the script. In this version we retrieve all the contact information and display it to the screen. After walking you through how to retrieve all the information we’ll show you the full script that exports everything to Excel.

     

    Ready? Okay, here we go:

     

    $assemblyPath = “C:\Program Files (x86)\Microsoft Lync\SDK\Assemblies\Desktop\Microsoft.Lync.Model.DLL”

    Import-Module $assemblyPath

     

    $DisplayName = 10

    $PrimaryEmailAddress = 12

    $Title = 14

    $Company = 15

    $Phones = 27

    $FirstName = 37

    $LastName = 38

     

    $cl = [Microsoft.Lync.Model.LyncClient]::GetClient()

     

    $gs = $cl.ContactManager.Groups

     

    foreach ($g in $gs)

    {

        Write-Host

        $g.Name

       

        foreach ($contact in $g)

        {

            $contact.GetContactInformation($LastName)

            $contact.GetContactInformation($FirstName)

            $contact.GetContactInformation($Title)

            $contact.GetContactInformation($Company)

            $contact.GetContactInformation($PrimaryEmailAddress)

            $eps = $contact.GetContactInformation($Phones)

               

            foreach ($ep in $eps)

            {

                switch ($ep.Type)

                {

                    "WorkPhone" {"work: " + $ep.DisplayName}

                    "MobilePhone" {"mobile: " + $ep.DisplayName}

                    "HomePhone" {"$home: " + $ep.DisplayName}

                 }

            }

               

        }

    }

     

    Yes, we did say this is the simplified version. But don’t worry, we’ll walk through it and you’ll see it’s not nearly as complicated as it might seem at first.

     

    The first two lines are required anytime you’re going to be working with the Lync SDK:

     

    $assemblyPath = “C:\Program Files (x86)\Microsoft Lync\SDK\Assemblies\Desktop\Microsoft.Lync.Model.DLL”

    Import-module $assemblyPath

     

    The first line sets a variable ($assemblyPath) to the full path of the DLL that contains the objects for the Microsoft.Lync.Model namespace. A namespace is simply a container for a bunch of objects, methods, and properties. (Stay with us here just a minute longer, it gets easier.) When we create objects (such as Lync Contacts) that are part of an SDK, Windows PowerShell doesn’t know where to get those objects from until you tell it which SDK, and which namespace, the object is in. The path we’ve provided here is the default installation path of the Lync SDK, and the Microsoft.Lync.Model.DLL file contains all the objects for the Microsoft.Lync.Model namespace. If you didn’t install the SDK to the default path you’ll need to modify this line.

     

    That was probably more explanation than you needed, but it’s there in case you wanted to know. All you really need to know is that you have to include the full path to the DLL for this script to work, and you need to pass that DLL path to the Import-Module cmdlet:

    Import-Module $assemblyPath

     

    Import-Module is a Windows PowerShell cmdlet that reads the DLL and enables you to use all the objects, methods, and so on that are in that DLL.

     

    Okay, we now have everything from the Lync SDK that we need to actually get started. The next thing we do is define some variables:

     

    $DisplayName = 10

    $PrimaryEmailAddress = 12

    $Title = 14

    $Company = 15

    $Phones = 27

    $FirstName = 37

    $LastName = 38

     

    We’ll get back to these in a bit. For now we’re going to move on:

     

    $cl = [Microsoft.Lync.Model.LyncClient]::GetClient()

     

    This line grabs the instance of Lync 2010 currently running on your machine and puts it in the variable $cl. It does this by calling the GetClient method of the Microsoft.Lync.Model.LyncClient class.  (This class is part of the Lync SDK, so the script would crash at this line if we hadn’t imported the DLL with the Import-Module cmdlet.) Now that we have a reference to your Lync client stored in $cl, we need to access all the contact groups (such as Frequent Contacts and Current Contacts). We do that with this line:

     

    $gs = $cl.ContactManager.Groups

     

    In this line we retrieve the Groups collection, which is available through the client’s ContactManager object. The Groups collection contains just what it sounds like: all the group you have in your instance of Lync. We store the collection of groups in the variable $gs.

     

    Now all we need to do is go through all the groups and pull out the information for each contact. To do that we set up a foreach loop to loop through the groups collection and look at one group at a time:

     

    foreach ($g in $gs)

    {

        Write-Host

        $g.Name

     

    We start the loop by saying that for every group ($g) in the groups collection ($gs) we’re going to display a blank line, then the name of the group ($g.Name). (The Write-Host cmdlet, with no parameters or values, will write a blank line.) We don’t really need to do this to display contact information, but we thought it would be helpful to know which group each contact is in.

     

    For each group, we want to get information for every contact within that group. What do you think we need to do next? That’s right, we need another foreach loop. (The For each at the beginning of the sentence gave it away, didn’t it?)

     

    foreach ($contact in $g)

     

    Each group is really a collection of contacts. So for each group ($g) we go through and retrieve information about one contact ($contact) at a time. In order to retrieve information about a contact, you call the GetContactInformation method:

     

            $contact.GetContactInformation($LastName)

            $contact.GetContactInformation($FirstName)

            $contact.GetContactInformation($Title)

            $contact.GetContactInformation($Company)

            $contact.GetContactInformation($PrimaryEmailAddress)

     

    Remember all those variables we defined at the beginning of the script? Well, here they are. The GetContactInformation method takes a number as a parameter. The number you pass to GetContactInformation determines the information the method will return. For example, if you pass GetContactInformation the value 38, it will return the last name of the contact. We could have written the preceding lines like this:

     

            $contact.GetContactInformation(38)

            $contact.GetContactInformation(37)

            $contact.GetContactInformation(14)

            $contact.GetContactInformation(15)

            $contact.GetContactInformation(12)

     

    This works just fine, but it’s not really considered good scripting. We put the numbers into variables with names that describe what they’re for, which makes the script much easier to read (and debug).  This line doesn’t tell you what data to expect in return:

     

    $contact.GetContactInformation(38)

     

    Whereas this line makes it clear you’re expecting a last name:

     

    $contact.GetContactInformation($LastName)

     

    Note. Yes, you could just add comments to the script. (We haven’t done that here simply to keep things cleaner as we walk you through.) But it’s still best to use descriptive variables, too. Trust us.

     

    Oh, and if you’re wondering where all these numbers are coming from, take a look at the Lync SDK documentation. It tells you which numbers will retrieve which piece of contact information.

     

    Operator, oh could you help me place this call?

    ‘Cause I can’t read the number that you just gave me…

     

    So as you can see, we’ve displayed the last name, first name, title, company, and primary email address of the contact. But what about this next line?

     

    $eps = $contact.GetContactInformation($Phones)

     

    Notice here that we put the output from the phone information into a variable. Why didn’t we simply display the phone numbers like we did everything else? Because it’s possible for a contact to have more than one phone number. You know what that means: that’s right, another foreach loop:

     

    foreach ($ep in $eps)

     

    One thing to note here: The variable $eps doesn’t actually contain a collection of phone numbers. What it contains is a collection of contact endpoints. An endpoint can be one of several things, including a telephone number or a SIP address. You determine what type of endpoint you’re working with by checking the Type property of the endpoint. Since we’re interested only in phone numbers, we put in a switch statement to take an action based on the endpoint type:

     

                switch ($ep.Type)

                {

                    "WorkPhone" {"work: " + $ep.DisplayName}

                    "MobilePhone" {"mobile: " + $ep.DisplayName}

                    "HomePhone" {"$home: " + $ep.DisplayName}

                 }

     

    If you haven’t seen a switch statement before, don’t worry, it’s actually pretty simple. For this statement, we’re looking at the Type property of the endpoint, $ep.Type. Within the curly braces ({}) we include each of the values we’re interested in. We want to retrieve the contact’s work phone number, mobile phone number, and home phone number (assuming you have the level of access required to see those values, and that the contact has provided them). Take a look at the first line in the switch statement:

     

    "WorkPhone" {"work: " + $ep.DisplayName}

     

    All we’re doing here is checking to see if the value of $ep.Type is the string WorkPhone. If it is, we perform the actions inside the {}, which is to display the string work: followed by the DisplayName property of the endpoint ($ep.DisplayName). The DisplayName for an endpoint of type WorkPhone is the work phone number.

     

    If the type matches WorkPhone, we display the work phone number, exit the switch statement, then move on to the next endpoint for the current contact. If the type isn’t WorkPhone, we check to see if the type is MobilePhone, and so on.

     

    Note. For a list of all the endpoint types you can check for, take a look at the ContactEndpointType enumeration in the Lync SDK.

     

     

    After we loop through all the endpoints, we go back and do this all over again with the next contact in the group. When we’re done with the current group, we go back to the first foreach loop we started with and move on to the next group, and once again do this all over again with all the contacts in that group. This continues until we run out of groups and contacts, at which point the script ends.

     

    That’s all well and good that we can display this information to the Windows PowerShell window, but that really isn’t much more useful than simply looking at the information in Lync.

     

    Isn’t that the way they say it goes?

    Well let’s forget all that.

     

    No, no, don’t forget all that; we still need everything we just did. But now we’re going to add to it. This script collects all the information from your Lync contacts, and it exports that information to an Excel spreadsheet. (You’ll probably need to do a little formatting of the spreadsheet to make it look nice.)

     

    $assemblyPath = “C:\Program Files (x86)\Microsoft Lync\SDK\Assemblies\Desktop\Microsoft.Lync.Model.DLL”

    Import-module $assemblyPath

     

    $DisplayName = 10

    $PrimaryEmailAddress = 12

    $Title = 14

    $Company = 15

    $Phones = 27

    $FirstName = 37

    $LastName = 38

     

    $objExcel = New-Object -ComObject Excel.Application

     

    $wb = $objExcel.Workbooks.Add()

    $item = $wb.Worksheets.Item(1)

     

    $item.Cells.Item(1,1) = "Contact Group"

    $item.Cells.Item(1,2) = "Last Name"

    $item.Cells.Item(1,3) = "First Name"

    $item.Cells.Item(1,4) = "Title"

    $item.Cells.Item(1,5) = "Company"

    $item.Cells.Item(1,6) = "Primary Email Address"

    $item.Cells.Item(1,7) = "Work Phone"

    $item.Cells.Item(1,8) = "Mobile Phone"

    $item.Cells.Item(1,9) = "Home Phone"

     

     

    $cl = [Microsoft.Lync.Model.LyncClient]::GetClient()

     

    $gs = $cl.ContactManager.Groups

     

    $i = 2

     

    foreach ($g in $gs)

    {

        $gn = $g.Name

       

        foreach ($contact in $g)

        {

            $ln = $contact.GetContactInformation($LastName)

            $fn = $contact.GetContactInformation($FirstName)

            $t =  $contact.GetContactInformation($Title)

            $c =  $contact.GetContactInformation($Company)

            $email = $contact.GetContactInformation($PrimaryEmailAddress)

            $eps = $contact.GetContactInformation($Phones)

           

            foreach ($ep in $eps)

            {

                switch ($ep.type)

                {

                    "WorkPhone" {$work = $ep.DisplayName}

                    "MobilePhone" {$mobile = $ep.DisplayName}

                    "HomePhone" {$homep = $ep.DisplayName}

                 }

            }

               

            $item.Cells.Item($i,1) = $gn

            $item.Cells.Item($i,2) = $ln

            $item.Cells.Item($i,3) = $fn

            $item.Cells.Item($i,4) = $t

            $item.Cells.Item($i,5) = $c

            $item.Cells.Item($i,6) = $email

            $item.Cells.Item($i,7) = $work

            $item.Cells.Item($i,8) = $mobile

            $item.Cells.Item($i,9) = $homep

              

            $ln     = ""

            $fn     = ""

            $t      = ""

            $c      = ""

            $email  = ""

            $work   = ""

            $mobile = ""

            $homep  = ""

             

            $i++

              

        }

    }

     

    $objExcel.Visible = $True

     

    # Remove the comment marks from the following lines to save

    # the worksheet, close Excel, and clean up.

     

    # $wb.SaveAs("C:\Scripts\LyncContacts.xlsx")

    # $objExcel.Quit()

     

    # [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb) | Out-Null

    # [System.Runtime.Interopservices.Marshal]::ReleaseComObject($item) | Out-Null

    # [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objExcel) | Out-Null

    # [System.GC]::Collect()

    # [System.GC]::WaitForPendingFinalizers()

     

    Remove-Variable objExcel

    Remove-Variable wb

    Remove-Variable item

     

    We’re not going to explain how this all works, that’s a bit too much for this article. To learn about using Windows PowerShell to export data to Excel, take a look at this article. And if you’d like to see a version of this script fully commented, look here.

     

    Thank you for your time

    Oh you’ve been so much more than kind

    You can keep the dime.

     

     

  • The Microsoft Lync Server 2010 Administration Guide: PowerShell Supplement

    The Untold Story!

     

    A few weeks ago the Microsoft Lync Server 2010 Administration Guide suddenly appeared in the Microsoft Download Center. So what's wrong with the Administration Guide? Well, to tell you the truth, there's absolutely nothing wrong with the Administration Guide; as it turns out, the Guide is chock-full of really good, very practical information used for managing Microsoft Lync Server 2010. Need to know how to configure a file transfer filter? It's in the Guide. Need to know how to configure a voice route for outbound calls? It's in the Guide. Need to know how to delete a Call Park Orbit? It's – well, yes: it's in the Guide. The truth is, we think the Administration Guide is so useful that everyone should stop whatever they're doing and download a copy right now.

     

    And yes, we do mean everyone. Go ahead; we'll wait.

     

    Um, we're still waiting on that one guy in Iowa. You know who you are.

     

    OK, that's better. Like we said, the Microsoft Lync Server 2010 Administration Guide is chock-full of useful information about Lync Server management. However (and you knew there had to be a “however” sooner or later, didn't you?) there is one thing that's missing from the Administration Guide: Lync Server PowerShell commands. Granted, there are a handful of commands scattered throughout the Guide, but those are primarily commands used for tasks (such as configuring an Address Book server) that can't be done using the Lync Server Control Panel. For most management activities (creating an archiving policy, removing a dial-in conferencing access number, putting a domain on the list of blocked domains) the Guide provides step-by-step instructions for performing the task using the Control Panel, but doesn't let you know how (or even if) you can  do the same thing using Lync Server PowerShell.

     

    Which, now that you mention it, is exactly why we've put together this supplement to the Administration Guide. What we've done is gone through the Administration Guide (all 287 pages!), identified all the tasks that don't include a PowerShell option, and, well, provided a PowerShell option. For example, the Administration Guide offers these steps for locking a Lync Phone Edition phone:

     

    1.  Open a browser window, and then enter the Admin URL to open the Lync Server Control Panel. For details about the different methods you can use to start Lync Server Control Panel, see Open Lync Server Administration Tools.

    2.  Click Clients, and then click Device Configuration.

    3.  On the Device Configuration tab, in the list of device configurations, double-click the configuration for which you want to change the phone lock settings.

    4.  In the Edit Device Configuration dialog box, verify that the Enforce device locking check box is selected.

    5.  In Minimum PIN length, accept the default value or specify a new value.

    6.  In Phone lock time-out, accept the default value or specify a new value.

    7.  Click Commit.

     

    To supplement those instructions, we've offered the a Lync Server PowerShell equivalent:

     

    Set-CsUCPhoneConfiguration -Identity global –EnforcePhoneLock $True –PhoneLockTimeout 00:30:00

     

    Thanks; we thought that was kind of useful, too.

     

    Like we said, we consider this to be a supplement to the Administration Guide; it's definitely not a replacement for the Guide. The Administration Guide includes a lot of useful information that explains what phone locking is and why you might want to use it. We don't offer any of that kind of information; we just provide you with a one-sentence introduction and then a PowerShell command that shows you how to do something along the lines of enabling phone locking.

     

    To tell you the truth, the ideal way to do things here would be to download the Administration Guide, then copy our PowerShell commands and paste them into the appropriate sections in that Guide. That way you'd have the best of both worlds: all the explanatory information and Control Panel steps found in the Guide, and all the PowerShell commands found here. What could be better than that?

     

    Well, OK, sure: having us combine the two probably would be better, wouldn't it? For better or worse, however, that's not a decision that we’re authorized to make. Which means that, for now, you're kind of on your own.

     

    Note. What's that? Can we give you an example of a decision that we are authorized to make? No. But if ever do get authorized to make one, we'll let you know.

     

    In the meantime, here's a very long list of the management tasks discussed in the Administration Guide, along with links to their PowerShell equivalents:

     

    Edit or Configure Simple URLs     

     

    Managing Users      

    Search for Lync Server 2010 Users     

    Add a New User to Lync Server 2010  

    Enable or Disable Users for Lync Server 2010 

    Set, View, and Send a User's Dial-in Conferencing PIN         

    Move Users to Another Pool     

    Assign Policies to Users  

    Assign a Conferencing Policy to Modify a User's Default Meeting Experience    

    Specify Client Versions Supported for Sign-in by a User  

    Assign Specific Dial-in Conferencing PIN Security Settings to a User      

    Apply External User Access Policies to Users        

    Configure Archiving of a User's Communications  

    Assign a Location Policy to a User   

    Presence Policy Settings     

    Enable Users for Enterprise Voice       

    Configure Telephony for Users  

     

    Managing Computers in Your Topology

    View a List of Computers Running Lync Server 2010  

    View the Status of Services Running on a Computer  

    View Details About a Service    

    Start or Stop Lync Server 2010 Services       

    Prevent Sessions for Services  

    View Microsoft SIP Processing Language (MSPL) Server Applications

    Enable or Disable a Microsoft SIP Processing Language (MSPL) Server Application 

    Mark a Microsoft SIP Processing Language (MSPL) Application as Critical or Not Critical

    View a List of Trusted Applications      

    View the Simple URL Details

     

    Filtering Instant Messages and Client Versions           

    Configuring Filtering for Instant Messaging (IM)        

    Modify the Default File Transfer Filter  

    Create a New File Transfer Filter for a Specific Site   

    Modify the Default URL Filter    

    Create a New URL Filter to Handle Hyperlinks in IM Conversations   

    Specify Client Versions Supported for Sign-in by a User       

     

    Configuring Voice Routing

    Configuring Dial Plans and Normalization Rules

    Create a Dial Plan    

    Modify a Dial Plan    

    Defining Normalization Rules

    Create or Modify a Normalization Rule by Using Build a Normalization Rule      

    Create or Modify a Normalization Rule Manually   

    Configuring Voice Policies, PSTN Usage Records, and Voice Routes  

    Configuring Voice Policies and PSTN Usage Records to Authorize Calling Features and Privileges       

    Create a Voice Policy and Configure PSTN Usage Records

    Modify a Voice Policy and Configure PSTN Usage Records

    View PSTN Usage Records   

    Configuring Voice Routes for Outbound Calls        

    Create a Voice Route

    Modify a Voice Route

    Configuring Trunks and Translation Rules       

    Configure Media Bypass on a Trunk

    Configure a Trunk Without Media Bypass   

    Defining Translation Rules   

    Create or Modify a Translation Rule by Using the Build a Translation Rule Tool  

    Create or Modify a Translation Rule Manually       

    Exporting and Importing Voice Routing Configuration  

    Export a Voice Route Configuration File     

    Import a Voice Route Configuration File    

    Test Voice Routing        

    Create a Voice Routing Test Case   

    Export Voice Routing Test Cases    

    Import Voice Routing Test Cases    

    Running Voice Routing Tests 105

    Run Informal Voice Routing Tests   

    Run Voice Routing Test Cases        

    Publish Pending Changes to the Voice Routing Configuration  

     

    Configuring Incoming Call Handling Features  

    Configure Phone Number Extensions for Parking Calls

    Create a Call Park Orbit Range      

    Change a Call Park Orbit Range     

    Delete a Call Park Orbit Range       

    Configure Routing of Unassigned Phone Numbers      

    Create an Unassigned Number Range       

    Change an Unassigned Number Range      

    Delete an Unassigned Number Range       

     

    Managing Response Groups         

    Managing Agent Groups 

    Create an Agent Group       

    Change Agent Group Settings or Members 

    Delete an Agent Group       

    Managing Response Group Queues      

    Create a Response Group Queue    

    Change a Response Group Queue   

    Delete a Response Group Queue    

    Managing Response Group Workflows  

    Create a Response Group Workflow

    Create a Hunt Group Workflow      

    Create an Interactive Workflow     

    Change a Response Group Workflow        

    Change a Hunt Group Workflow     

    Change an Interactive Workflow    

    Delete a Response Group Workflow

     

    Managing On-Premises Meetings           

    Configuring Conferencing Settings       

    Modify the Default Conferencing User Experience 

    Create or Modify Conferencing User Experience for a Site or Group of Users   

    Delete a Conferencing Policy for a Site or Group of Users

    Configuring the Meeting Join Experience    

    Modify the Default Meeting Join Experience    

    Create or Modify Meeting Join Settings for a Site or Pool 

    Delete Meeting Join Settings for a Site or Pool     

    Configure Settings for a Dial-in Conferencing Access Number

    Create or Modify a Dial-in Conferencing Access Number  

    Delete a Dial-in Conferencing Access Number      

    Configure Dial-in Conferencing Personal Identification Number (PIN) Rules  

    Modify the Default Dial-in Conferencing PIN Settings      

    Create or Modify Dial-in Conferencing PIN Settings for a Site or Group of Users        

    Delete Dial-in Conferencing PIN Settings for a Site or Group of Users   

     

    Configuring Support for Clients and Devices    

    Specify the Client Versions Supported in Your Organization   

    View the Status of Services Running on a Computer  

    Modify the Default Action for Clients Not Explicitly Supported or Restricted 

    View Software Updates for Devices in Your Organization      

    Add a Device to Test Update Functionality      

    Modify Settings for Log Files of Device Update Activity        

    Configure Security Settings for Lync 2010 Phone Edition      

    Configure Voice Quality of Service for Lync 2010 Phone Edition  

    Configure Phone Lock for Lync 2010 Phone Edition

     

    Managing External Connectivity  

    Enable or Disable External User Access for Your Organization

    Enable or Disable Remote User Access for Your Organization     

    Enable or Disable Federation for Your Organization         

    Enable or Disable Anonymous User Access for Your Organization

    Manage Communications with External Users  

    Manage Remote User Access         

    Manage Federated Partner Access  

    Configure Policies to Control Federated User Access       

    Enable or Disable Discovery of Federation Partners        

    Control Access by Individual Federated Domains  

    Enable or Disable Sending an Archiving Disclaimer to Federated Partners        

    Manage IM Provider Support

    Configure Policies to Control Access by Users of IM Service Providers   

    Specify Supported IM Service Providers    

    Configure Conferencing Policies to Support Anonymous Users    

    Apply Policies for External User Access to Users   

    Apply External User Access Policies to Users        

    Apply Conferencing Policies to Support Anonymous Users

    Reset or Delete External User Access Policies      

    Delete a Site or User Policy for External User Access      

    Reset the Global Policy for External User Access  

     

    Managing Monitoring         

    Create a Site Policy for Call Detail Recording   

    Create a Site Policy for Quality of Experience 

    Enable Call Detail Recording      

    Enable Quality of Experience    

    Configure Call Detail Recording 

    Configure Quality of Experience

    Delete a Site Policy for Call Detail Recording   

    Delete a Site Policy for Quality of Experience  

     

    Managing Archiving

    Configuring Support for Archiving of Internal and External Communications

    Change the Global Policy for Archiving of Internal and External Communications         

    Create a Site Policy for Archiving   

    Enable or Disable Archiving for a Site       

    Create a User Policy for Archiving  

    Enable or Disable Archiving for Users       

    Delete an Archiving Policy   

    Apply an Archiving Policy to a User or User Group

    Enable or Disable Archiving      

    Specify the Types of Communications To Be Archived

    Enable or Disable Purging for Archiving

    Block or Allow IM and Web Conferencing Sessions If Archiving Fails 

    Enable or Disable Sending an Archiving Disclaimer to Federated Partners    

     

    Configuring Security          

    Create a New Registrar 

    Modify an Existing Registrar     

    Delete a Registrar         

    Create a New Web Service      

    Modify an Existing Web Service 

    Delete a Web Service    

    Create a New PIN Policy

    Modify an Existing PIN Policy    

    Delete a PIN Policy        

     

    Configuring Your Network           

    Enabling Call Admission Control 

    Enabling Media Bypass   

    Configuring Location Policy       

    Configuring Bandwidth Policy Profile    

    Configuring Network Regions    

    Configuring Network Sites        

    Configuring Network Subnets   

    Configuring Network Region Links       

    Configuring Network Region Routes     

    Configuring Network Site Links 

     

    Change the Web Services URL     

     

    Prevent New Connections to Lync Server 2010 for Server Maintenance   

     

    Delegating Control of Microsoft Lync Server 2010     

     

    Configure a New Trusted Application Server   

     

     

     

     

     

     

     

     

  • Show emoticons in instant messages

     

     

    Registry locations

    HKCU\Software\Microsoft\Communicator\ShowEmoticons

    Allowed registry values

    ·         0 – Emoticons are sent as text

    ·         1 – Emoticons are sent as graphics

    Registry value type

    REG_DWORD

    Default setting

    1: Emoticons are shown in instant messages

     

    Where would the world be without emoticons? Back in the dark ages of computing, the only way to add a "smiley face" to an instant message would be to use the typewritten equivalent, like so:

     

    :)

     

    Thank goodness that’s no longer the case, at least not with Microsoft Lync. Instead, with Microsoft Lync you can add a real smiley face to your instant messages:

     

     

    Progress is truly a wonderful thing, isn’t it?

     

    Historical note. The first known use of the classic smiley face emoticon: :-)? That dates all the way back to 1982 and the computer science department at Carnegie Mellon University. (As if anyone other than a computer science major at Carnegie Mellon University could have come up with the smiley face emoticon.)

     

    In Microsoft Lync you can enable the use of emoticons by checking the Show emoticons in instant messages checkbox:

     

     

    Once you do that, you can then insert any Lync emoticon into your instant messages and have that emoticon be sent as a graphic. Likewise, you can type an emoticon character equivalent and Lync will automatically convert that typewritten value to a graphic. For example, suppose you type the following into an instant message:

     

    8-|

     

    Do that, and Lync will convert that text to the following emoticon when the message is sent:

     

     

    Note. In case you’re wondering, the preceding emoticon is officially known as the “nerd smiley.”

     

    As if that wasn’t cool enough, this conversion also takes place for any emoticons (or their text equivalents) that get sent to you. For example, suppose someone types 8-| in an instant message sent to you. When you receive that message, Lync will automatically show you – that’s right, the beloved nerd smiley:

     

     

    This really is a wonderful age in which to be alive, isn't it?

     

    Of course, it’s always possible that you’d rather not use emoticons in your instant messages. (Killjoy.) In that case, all you have to do is clear the Show emoticons in instant messages checkbox in the Options dialog box. Do that, and Lync will no longer convert typed characters to their emoticon equivalents.

     

    And yes, in some ways that does take all the fun out of instant messaging. But in some organizations, fun might not be the overriding concern when it comes to sending and receiving instant messages.

     

    Go figure.

     

    And before you ask, yes, you can use Windows PowerShell to manage the use of emoticons; that's done by manipulating the registry value HKCU\SOFTWARE\Microsoft\Communicator\ShowEmoticons. For example, the following PowerShell script retrieves the current value of ShowEmoticons from the local computer. If you'd prefer to retrieve this value from a remote computer, simply set the value of the variable $computer to the name of that remote computer. For example:

     

    $computer = "atl-ws-001.litwareinc.com"

     

    Here's the script we were just talking about:

     

    $computer = "."

     

    $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("CurrentUser", $computer)

    $key = $registry.OpenSubKey("SOFTWARE\Microsoft\Communicator", $True)

     

    $value =$key.GetValue("ShowEmoticons",$null)

    if ($value -eq 1) {$value = "Yes"}

    if ($value -eq 0) {$value = "No"}

    Write-Host "Show emoticons: $value"

     

    You can also use a script to set the value of ShowEmoticons. In the code we're about to show you, the script enables the use of emoticons in instant messages; that's done by setting ShowEmoticons to 1. To disable the use of emoticons, set ShowEmoticons to 0:

     

    $key.SetValue("ShowEmoticons",0,"DWORD")

     

    You know, like this:

     

    $computer = "."

     

    $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("CurrentUser", $computer)

    $key = $registry.OpenSubKey("SOFTWARE\Microsoft\Communicator", $True)

     

    $key.SetValue("ShowEmoticons",1,"DWORD")

     

    One thing to keep in mind here is that there is also a client policy setting (DisableEmoticons) that can be used to regulate the use of emoticons in instant messages. If DisableEmoticons is set to anything other than a null value ($null) then users will not be able to use emoticons in their instant messages, and they won't be able to change that setting:

     

     

    And yes, oddly enough, it doesn't matter whether you set DisableEmoticons to True or to False; anything other than a null value will disable the use of emoticons in instant messages.

     

    That's what we said: go figure.