• Not Like the Others: Challenge 10 Hint

    Once again we’re not sure we really need to give you a hint to this week’s Challenge: the early returns suggest that most people are coming up with pretty good answers all by themselves. Still, we promised to give you a hint every Thursday and, except for the promises that we break, we never break a promise. Therefore, here’s this week’s hint: Does anybody really know what time it is?

     

    Challenge Home

  • Haiku #73

    Every day can be a

    Holiday, as long as you

    Use the right command.

     

    Well, today is Wednesday, often referred to as "Hump Day" here in the U.S., referring to the fact that Wednesday falls smack dab in the middle of the workweek. In theory, if you can get over the hump (that is, if you can get through Wednesday) then the rest of the week should be a piece of cake; after all, at that point the week's practically over.

     

    That's the theory anyway.

     

    At any rate, this has been an especially … interesting … Wednesday; right now it's 9:25 AM Pacific Daylight Time and we're only about three paragraphs into an article that typically gets published around 9:00 AM Pacific time. What does that mean? That means we need to quit fooling around and get to work.

     

    Note. But first we really must point out that Wednesday is named after the Norse god Odin, also known as Woden. In other words, Woden's Day equals Wednesday. Similarly, Thursday is named after the Norse god Thor (Thor's Day); Friday after the Norse goddess Freya (Freya's Day); and Tuesday after the American actress Tuesday Weld.

     

    Well, we assume that Tuesday is named after Tuesday Weld. We didn't really have time to do a lot of research for today's article.

     

    Although Wednesday is often referred to as Hump Day, it turns out that Hump Day is not an official holiday, at least not in the U.S. (Of course, the U.S. is somewhat unique in that we don't have any official holidays. Even General Pulaski Memorial Day is just a day of recognition, not an actual national holiday.) But that's one of the great things about the Response Group application: it lets you turn any day into a holiday. And to do that, all you have to do is know how to use the New-CsRgsHoliday cmdlet.

     

    We should probably note that, as far as the Response Group application is concerned, a holiday is simply a day when you would expect people to be working but, for whatever reason, they aren't. For example, suppose your building is going to be closed next Wednesday while they upgrade the plumbing. Not really a day for joyous celebration (well, depending on just how badly the plumbing needs upgrading) but a day when you would typically be at work. Because you won't be at work, you need to configure the Response Group application to act accordingly if someone calls next Wednesday; for example, instead of just letting the phone ring and ring and ring (even though no is there to answer it) you might want to have any calls automatically transferred to voice mail.

     

    In order to do that, you need to configure next Wednesday (March 30, 2011) as a holiday. And how do you do that? Here's one way:

     

    $holiday = New-CsRgsHoliday –Name "Plumbing Replacement Day" –StartDate "3/30/2011" –EndDate "3/31/2011"

     

    What have we done here? Well, we simply called the New-CsRgsHoliday cmdlet, passing it three parameters:

     

    ·         Name. As you might have guessed, this is simply the name of the holiday. You can name this holiday anything you want; just make sure that the name is unique among your holidays. For example, if you already have a holiday named Plumbing Replacement Day then you'll need to call this something like Plumbing Replacement Day II.

    ·         StartDate. The date and time that the holiday begins. You might have noticed that we didn't actually include a time here; in that case, that means the holiday officially starts at midnight (12:00:00 AM). What if we didn't want the holiday to start until 3:00 PM? Well, using the date-time syntax for US English, we could configure the start date like this:

    -StartDate "03/30/2011 3:00 PM"

    Alternatively, we can use the 24-hour clock format, in which 3:00 PM is equivalent to 15:00.

    -StartDate "03/30/2011 15:00"

    ·         EndDate. The date and time that the holiday ends. The end date uses the same syntax as the start date. That means that, because we didn't specify a time, the holiday will end at 12:00 AM on March 31, 2011. Which, in turn means that the holiday will start at midnight on March 30, run through 11:59:59 PM on that same day and then, as the clock strikes midnight on March 31st, come to an end.

    Yes, pretty much like
    Cinderella.

     

    You might have noticed that we also configured our command so that the resulting holiday is stored in a variable named $holiday. Why did we do that? Well, the Response Group application doesn't allow you to create "free range" holidays; instead, every holiday we create must be tied to a holiday set (a collection of holidays). If we didn't store Plumbing Replacement Day in a variable, then the holiday would be created and then instantly vanish the moment we finished running our command.

     

    Note. Which, now that we think about it, is the way most holidays seem to work: no sooner do they start then they come to an end and you have to go back to work again.

     

    So how do you add a holiday to a holiday set? Well, here's one example:

     

    $holiday = New-CsRgsHoliday –Name "Plumbing Replacement Day" –StartDate "3/30/2011" –EndDate "3/31/2011"

     

    $y = Get-CsRgsHolidaySet -Identity "service:ApplicationServer:atl-cs-001.litwareinc.com"  -Name "2011 Redmond Holidays"

     

    $y.HolidayList.Add($holiday)

    Set-CsRgsHolidaySet -Instance $y

     

    As you can see, what we've done here is first create our new holiday and stash it in the variable $holiday. We then use the Get-CsRgsHolidaySet cmdlet to retrieve an instance of the holiday set (a set we created previously) named 2011 Redmond Holidays. We then use this command to add Plumbing Replacement Day to the holiday set:

     

    $y.HolidayList.Add($holiday)

     

    So is that all we have to do? Well, not quite. Right now our modified holiday set exists only in memory; if we were to terminate our Windows PowerShell session right now Plumbing Replacement Day would never get added to the real holiday set. In order to actually save the change we just made, we need to use this line of code:

     

    Set-CsRgsHolidaySet -Instance $y

     

    At that point, you can break out the party hats and noisemakers, because Plumbing Replacement Day is now an official Response Group holiday.

     

    That's pretty much all there is to it. Happy Hump Day everyone and we'll see you tomorrow.

     

    Note. Which, despite being the birthdate of Samuel Ashe, the ninth governor of North Carolina, and of Steve Ballmer, Microsoft CEO, is not an official holiday either. We don't understand it, either. And before you ask, no, Steve doesn't give Microsoft employees his birthday off.

     

    He does, however, make us all bring him expensive presents and bake him a cake. And if he doesn't like your cake, then you're fired on the spot.

     

    Note. No, not really. However, all 50,000 of us here on the Redmond campus are required to gather in his office and sing Happy Birthday.

     

    Note. No, not really. Even Steve's office isn't that big.

     

    Well, not that we've ever actually been invited to Steve's office. But we assume it's not that big.

  • New Script: Retrieve Lync Connection Information

    Quite a while ago we published a script we received from Nick Smith and Scott Stubberfield that lists connections to registrar pools. (And more recently we published updates to that script, one for direct connections and one for remote access.) One of the nicest things about Windows PowerShell is that you can take bits and pieces from one script to help you create another. And that’s exactly what Tracy Cerise did. Tracy used the database connection information from Nick and Scott’s script to create a new script that retrieves Microsoft Lync connection information. Here’s Tracy’s description of what this script does:

     

    “Program to pull Lync connection information.  This program will pull complete information across all frontend servers in a pool.  It can also be used to find specific connection information on an individual user by supplying the user's sip address.  The parameter for the pool to access for connection information can be pre-populated so it doesn't have to be passed on the command line.”

     

    Seems pretty useful to us, we’re sure some of you will find it pretty useful too.

     

    http://blogs.technet.com/b/csps/archive/2011/03/22/scriptconnections.aspx

     

  • Retrieve Lync Connection Information

    Submitted by Tracy A. Cerise, University of Kentucky

     

    This script pulls Microsoft Lync 2010 connection information, such as the number of connections per Front End Server and number of unique users, and either displays it to the console or writes it to a file, depending on the parameters you supply with the call.

     

    To use this script, copy the code shown below, paste it into a text editor (like Notepad) and then save the file with a .PS1 extension (for example, C:\Scripts\Connections.ps1). After that, you can run the script from within the Lync Server Management Shell simply by typing the full path to the .PS1 file and then pressing ENTER:

     

    C:\Scripts\Connections.ps1

     

    For a full description as well as requirements for connecting to the Front End Server database, see the introductory comments in the script below. For additional help on using this script, type the following at the Lync Server Management Shell prompt and press ENTER:

     

    C:\Scripts\Connections.ps1 -help

     

     

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

    # Connections.ps1

    #

    # Program to pull Lync connection information. 

    # This program will pull complete information across all

    # frontend servers in a pool.  It can also be used to find

    # specific connection information on an individual user by

    # supplying the user's sip address.  The parameter for

    # the pool to access for connection information can be

    # pre-populated so it doesn't have to be passed on the command line.

    #

    # ACKNOWLEDGEMENT: This program's database connection information

    #                  was originally taken from the "List Connections

    #                  to Registrar Pools" submitted by Scott Stubberfield

    #                  and Nick Smith from Microsoft to the Lync 2010

    #                  PowerShell blog

    #                  (http://blogs.technet.com/b/csps/) on June 10, 2010.

    #

    # NOTE: In order to gain remote access to each frontend server's

    #       RTCLOCAL database where connection information is found,

    #       you need to open the local firewall for port 1434.

    #       Also, need to go into the SQL Server Configuration Manager

    #       and for RTCLOCAL, enable named pipes and restart the SQL

    #       service for the named pipes to take effect.

    #

    #       Port 1434 is required in order to make the connection to

    #       the named instance RTCLOCAL on the remote machines.

    #

    #

    # Written by: Tracy A. Cerise (tracy@uky.edu)

    #       Date: March 2011

    #

    # Modification History

    # --------------------

    #

    # Could add # users with enterprise voice enabled and other

    # individual stats to this page as well

    #

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

     

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

    # Commandline Parameters to use for running this program #

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

    param($Pool = "your.pool.here", $SIPAddr, $FilePath, [switch] $Help)

     

     

     

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

    #################################       Functions       #########################################

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

     

     

    function GetData {

     

       param ($sipAddr = $null, $server)

     

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

       # Went to using a named parameter for this function due to the

       # way Powershell does its thing with parameter passing, which

       # is NOT GOOD!  At any rate, need to call this function

       # as you would from a command line: GetData -sipAddr "value"

       # -server "value"

       #

       # Also, assuming a value of NULL for the SIP address of an

       # individual user, mostly to use this for finding overall

       # values, only occasionally to seek specific users.

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

     

     

       if ($sipAddr) {

          $whereClause = "where R.UserAtHost = '$sipAddr' "

       }

       else {

          $whereClause = $null

       }

     

       #Define SQL Connection String

       $connstring = "server=$server\rtclocal;database=rtcdyn;trusted_connection=true;"

     

       #Define SQL Command

       $command = New-Object System.Data.SqlClient.SqlCommand

     

       $command.CommandText = "Select (cast (RE.ClientApp as varchar (100))) as ClientVersion, R.UserAtHost as UserName, Reg.Fqdn `

                               From rtcdyn.dbo.RegistrarEndpoint RE `

                               Inner Join `

                               rtc.dbo.Resource R on R.ResourceId = RE.OwnerId `

                               Inner Join `

                               rtcdyn.dbo.Registrar Reg on Reg.RegistrarId = RE.PrimaryRegistrarClusterId `

                               $whereClause `

                               Order By ClientVersion, UserName "

     

       $connection = New-Object System.Data.SqlClient.SqlConnection

       $connection.ConnectionString = $connstring

       $connection.Open()

     

       $command.Connection = $connection

     

       $sqladapter = New-Object System.Data.SqlClient.SqlDataAdapter

       $sqladapter.SelectCommand = $command

     

       $results = New-Object System.Data.Dataset

     

       $recordcount=$sqladapter.Fill($results)

     

       $connection.Close()

     

       return $Results.Tables[0]

    }

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

     

     

    function Help {

     

    "

    NAME

        Connections.ps1

     

     

    SYNOPSIS

        Returns current connection count for all frontend servers in a given pool

        including a breakdown of connection by client, frontend server and users.

     

        It can also be used to return connection information on an individual user.

       

     

    SYNTAX

        Connections.ps1 [-Pool <PoolFQDN>] [-SIPAddr] [-FilePath] [-help]

     

     

    DESCRIPTION

        This program will return a connection count for a given pool.  The program

        can be edited to set a default pool.  You will also be able to get

        information on an individual user by providing the users SamAccountName.

     

     

    EXAMPLES

     

        -------------------------- EXAMPLE 1 --------------------------

     

        C:\PS>connections.ps1

     

        Description

        -----------

        Returns information on all connections on all frontend servers in the

        default pool that has been hard-coded into the program.

     

     

        -------------------------- EXAMPLE 2 --------------------------

     

        C:\PS>connections.ps1 -Pool alternate.pool.fqdn

     

        Description

        -----------

        Returns information on all connections on all frontend servers in the

        pool given with the pool parameter.

     

     

        -------------------------- EXAMPLE 3 --------------------------

     

        C:\PS>connections.ps1 -SIPAddr userid@sip.domain

     

        Description

        -----------

        Returns all connection information for the given user including which

        frontend server connected to, how many connections and which clients

        connected with.

     

        -------------------------- EXAMPLE 4 --------------------------

     

        C:\PS>connections.ps1 -filepath c:\path\to\file\filename.csv

     

        Description

        -----------

        Returns information on all connections on all frontend servers in the

        default pool that has been hard-coded into the program and in addition

        writes out all the raw connection information into the filename specified.

     

     

    "

       exit

     

    }

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

     

     

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

    #################################     Main Program      #########################################

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

     

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

    ### Main program

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

     

    # If the help switch is toggled

    if ($help) {

       Help

    }

     

    #

    # Here is where we pull all the frontend server(s) from our topology for the designated

    # pool and iterate through them to get current connections from all the servers.

    #

    # There are three possibilities here:

    #    1. Have collection of frontend servers

    #    2. Have a single frontend server or

    #    3. Have no servers

    #

    # Only need to check for the first two cases, the third is implied otherwise...

     

    $feServers = Get-CsComputer -Pool $Pool

     

     

    if ($feServers.count) {

       # Frontend pool collection, iterate through them

       for ($i=0; $i -lt $feServers.count; $i++) {

     

          if ($SIPAddr) {

             $data = GetData  -sipAddr $SIPAddr -server $feServers[$i].identity

          }

          else {

             $data = GetData -server $feServers[$i].identity

          }

     

          # Since an individual user's connections are all homed on one server, won't have

          # data coming back from all frontend servers in the case of searching for a

          # single user

          if ($data) {

             $overallrecords = $overallrecords + $data

          }

       }

    }

    elseif ($feServers) {

       # Have a standalone server or a FE pool of only one server

       if ($SIPAddr) {

          $data = GetData  -sipAddr $SIPAddr -server $feServers.identity

       }

       else {

          $data = GetData -server $feServers.identity

       }

     

       # Make sure we have data to work with...

       if ($data) {

          $overallrecords = $data

       }

    }

     

    # Check to see if we have any data to act on

    if (! $overallrecords) {

       write-host -ForegroundColor Yellow "`r`nNothing returned from query!`r`n"

     

       # Nothing else to do

       exit

    }

    else {

       $count=0

     

       $userHash = @{}

       $clientHash = @{}

       $serverHash = @{}

     

       $overallrecords | foreach-object {

          # Each record has three components: Connected Client Version, User's SIP

          # address and the frontend server's FQDN.  Here, we'll build a hash

          # for each of these components for each record.

     

          # Build hash of users

          if (! $userHash.ContainsKey($_.UserName)) {

             $userHash.add($_.UserName, 1)

          }

          else {

             $userHash.set_item($_.UserName, ($userHash.get_item($_.UserName) + 1))

          }

     

          # Build hash of servers

          if (! $serverHash.ContainsKey($_.fqdn)) {

             $serverHash.add($_.fqdn, 1)

          }

          else {

             $serverHash.set_item($_.fqdn, ($serverHash.get_item($_.fqdn) + 1))

          }

     

          # Build hash of clients

          # Lets get rid of the extraneous verbage from the client version names, if applicable

          if ($_.ClientVersion.contains('(')) {

             # Get rid of extraneous verbage

             $clientName = $_.ClientVersion.substring(0, $_.ClientVersion.IndexOf('('))

          }

          else {

             # Have a client name with no extraneous verbage

             $clientName = $_.ClientVersion

          }

     

          if (! $clientHash.ContainsKey($clientName)) {

             $clientHash.add($clientName, 1)

          }

          else {

             $clientHash.set_item($ClientName, ($clientHash.get_item($ClientName) + 1))

          }

     

          $count++

       }

    }

     

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

    ####  Output Query Results  ####

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

     

    # If output to file is chosen, then write out the results and a note to that effect

    # then exit

     

    if ($FilePath) {

       $overallrecords | Export-Csv $FilePath

     

       write-host -foregroundcolor green "`r`nQuery Results written to $FilePath`r`n"

     

       exit

    }

     

     

    write-host -foregroundcolor cyan "`r`nClient Version/Agent                   Connections"

    write-host -foregroundcolor cyan "--------------------------------------------------"

     

    foreach ($key in $clientHash.keys) {

       # Break down client version into its two component parts and print

       # them out along with their respective counts in a nice format

       $index = $key.indexof(" ")

     

       if ($index -eq "-1") {

          # No second part

          $first = $key

          $second = " "

       }

       else {

          # Client version/agent has to main parts

          $first = $key.substring(0, $index)

          $second = $key.substring($index + 1)

       }

     

       $value = $clientHash.$key

     

       "{0, -20} {1, -20} {2, 5}" -f $first, $second, $value

    }

     

    write-host -foregroundcolor cyan "--------------------------------------------------"

     

    write-host -foregroundcolor cyan "`r`n`r`nFrontend Server     Connections"

    write-host -foregroundcolor cyan "-------------------------------"

     

    foreach ($key in $serverHash.keys) {

       $value = $serverHash.$key

     

       "{0, -22} {1, 5}" -f $key, $value

    }

     

    write-host -foregroundcolor cyan "-------------------------------"

     

     

    "{0, -22} {1, 5}" -f "Total connections...", $count

    "`r`n"

     

    write-host -foregroundcolor cyan "Total Unique Users/Clients"

    write-host -foregroundcolor cyan "-------------------------------"

    "{0, -22} {1, 5}" -f "Users...............", $userHash.count

    "{0, -22} {1, 5}" -f "Client Versions.....", $clientHash.count

    write-host -foregroundcolor cyan "-------------------------------"

    "`r`n"

     

    Write-Host -ForegroundColor Green "Query complete`r`n"

     

  • Haiku #72

    1 2 3 4 5?

    That's not an acceptable

    Pin. Please try again.

     

    We don't wish to alarm anyone, but we felt a duty to call your attention to this. As you may have heard, the Mayan calendar runs out on December 21, 2012. And the minute that calendar runs out, the world is destined to come to an end!

     

    That's what we said: Eep.

     

    Note. Why will the world come to an end just because the Mayan calendar doesn't go beyond December 21, 2012? Well, we're not really sure, although we have read that this might have something to do with the fact that 12/21/12 is the day that the mysterious Planet Nibiru is supposed to crash into the Earth. Now, admittedly, Nibiru was originally supposed to crash into the Earth back in 2003 and maybe it did: we don't always keep up with current events as well as we should. But when Nibiru crashes into the Earth in 2012 it's going to be bad, really bad.

     

    Although, considering the rate at which our 401Ks continue to lose money, maybe it won't be all that bad after all.

     

    If you know what we mean.

     

    Of course, right now you're probably thinking, "Gosh, how can the authors of the Lync Server PowerShell blog save the Earth?" Well, to be perfectly honest, we can't: we're all doomed. What we can do, however, is help to make everyone's last few months on Earth as pleasant as possible. Suppose we had only a year or so left to live, we thought to ourselves. (Which, if the prophecy is correct, is all we do have left.) How would we like to spend that time? The answer, of course, was easy: we wanted to be with our families, relaxing on a tropical beach, and using the CsPinPolicy cmdlets (Get-CsPinPolicy, Grant-CsPinPolicy, New-CsPinPolicy, Remove-CsPinPolicy, and Set-CsPinPolicy) to manage dial-in PIN settings for Lync Server users. Needless to say, we figured you'd want to do the same.

     

    Thus the reason for today's haiku. If you aren't sure what dial-in PIN policies are, or why you'd want to manage them, let's take one of our few remaining moments on Earth to explain. As you probably know, Lync Server provides a way for users to connect to the system and/or dial into audio conferences using any old telephone. As you also know, when you connect to Lync Server you typically need to provide a user name and password. Is that a problem? Well, not if you have a name like 546 82341. But suppose you have a name like Ken Myer (that is, a name that contains letters as opposed to numbers). In that case, that is a problem: entering alphabetic characters using a standard telephone keypad isn't all that easy. (You should see the author of today's haiku trying to send text messages to his son. It's not a pretty sight.) Therefore, Lync Server gives dial-in users the option of authenticating by using a PIN number; instead of entering a user name and password, dial-in users can type in a PIN number like this: 121989. That's a tad bit easier, to say the least.

     

    So what do you need PIN policies for? Well, PIN policies enable you to place some restrictions on the PIN numbers that users can and cannot use. You know how you use password policies to prevent all your users from using a password like this one: password. Well, that's one reason to use PIN policies: it prevents all your users from using 12345 as their PIN number.

     

    So what kind of restrictions can you impose using PIN policies? Well, for one thing, you can specify the minimum number of digits that must be used in a PIN.  By default, the global PIN policy requires you to have at least 5 digits in a PIN; a user who tries to use the PIN 9827 will have that PIN rejected. (Why? Because it only contains 4 digits.) The minimum number of digits can be set to any integer value between 5 and 24, inclusive. For example:

     

    Set-CsPinPolicy –Identity global –MinPasswordLength 8

     

    And then there's the AllowCommonPatterns parameter. When set to False, this setting prevents users from creating PINs that consist solely of repeating digits or consecutive digits. For example, a PIN like this will be rejected:

     

    123456

     

    So will a PIN like this one:

     

    654321

     

    However, you can use consecutive digits as long as you break the pattern in some way. In other words, this PIN will be allowed:

     

    1234568

     

    Why? Because while there are consecutive digits, the entire PIN does not consist of consecutive digits. The same thing  is true for repeating digits. This PIN will be rejected:

     

    333333

     

    This one will not:

     

    333336

     

    It's not super-duper password protection by any means, but it does help.

     

    Oh, and here's how you allow/disallow common patterns:

     

    Set-CsPinPolicy –Identity global –AllowCommonPatterns $False

     

    You can also set an expiration date on PINs, meaning that users will have to periodically get a new PIN number (just like they periodically have to get a new password). To do that, you need to do two things:

     

    ·         Set the PinLifetime parameter to the number of days a PIN is valid before it expires. For example, setting PinLifetime to 90 means that the PIN will expire 90 days after it's issued.

    ·         Set the PinHistoryCount parameter to a value greater than 0. PinHistoryCount specifies how often a user can use the same PIN. For example, suppose PinHistoryCount is set to 2 and the user has the PIN 071267. When the user needs to refresh his or her PIN, they cannot reuse PIN 071267; they have to pick a different PIN. What happens the next time they need to refresh their PIN? Because this is the second time they've had to pick a new PIN they, again, cannot use 071267. However, the third time they have to select a new PIN they can reuse 071267. That's because the PinHistoryCount is set to 2. If that value was set to 10 they would have to wait until the 11th try before they could reuse a PIN.

    If you set a value for the PinLifetime parameter then you must set PinHistoryCount to a value greater than 0.

     

    Here's how all that might look:

     

    Set-CsPinPolicy –Identity global –PinLifetime 30 –PinHistoryCount 5

     

    Interesting Note. The PIN history applies to a PIN that a user chooses for himself (or herself) by going to the Dial-In Conferencing Web page. However, the PIN history count is not enforced if an administrator assigns the user a PIN. Suppose a user desperately wants to reuse PIN 071267. The Dial-In Conferencing Web page won't allow them to reuse the same PIN. (Assuming that PinHistoryCount is greater than 0.) However, an administrator can assign them that PIN, even if the PIN history count would seem to prohibit that:

     

    Set-CsClientPin "Ken Myer" –Pin 071267

     

    Note that administrators do have to abide by the minimum PIN length and the allow common patterns settings.

     

    In case you're wondering, PIN policies can be assigned to the global, site, or the per-user scope. Use New-CsPinPolicy to create new policies and use Set-CsPinPolicy to modify existing policies. You can probably figure out for yourself what Get-CsPinPolicy, Grant-CsPinPolicy, and Remove-CsPinPolicy do.

     

    Note. Just in case, Get-CsPinPolicy retrieves information about all your existing PIN policies; Grant-CsPinPolicy assigns a per-user PIN policy to one or more users; and Remove-CsPinPolicy deletes an existing PIN policy.

     

    By the way, administrators cannot retrieve the actual PIN that has been assigned to a user; all you can do is determine whether or not a user currently has a PIN. Here's a command that returns the Identity of all the users currently assigned a PIN number:

     

    Get-CsUser | Get-CsClientPinInfo | Where-Object {$_.IsPinSet –eq $True}

     

    That's all we have for today. Again, we didn't mean to alarm anyone by revealing that the world is going to come to an end on December 21, 2012; we just felt that most people would prefer to hear that kind of news from the authors of the Lync PowerShell blog. And for those of you who remain skeptical, consider this: today's haiku is haiku number 72. What do you get if add the digits in 72 together? You get this:

     

    7 + 2 = 9

     

    What do you get if you add the digits in 12/21/12 together? See for yourself:

     

    1 + 2 + 2 +1 + 1 + 2 = 9

     

    Now, guess how many letters there are in PinPolicy? Coincidence? If it makes you feel better then, sure, it's all just a coincidence ….

     

    But if you have a dentist appointment for December 22, 2012, well, you might consider rescheduling, if you know what we mean.