Lync PowerShell

  • Find the Number of Users Assigned to Your Per-User Policies

    A koan is a riddle used by Zen Buddhists to help focus the mind during meditation and, with any luck, to help the initiate develop intuitive thinking. Some of the more famous Zen koans include:
     
     

    • "Two hands clap and there is a sound; what is the sound of one hand?"
    • "What is Buddha?" Dongshan said, "Three pounds of flax."
    • "What is the meaning of the ancestral teacher's coming from the west?" Zhaozhou said, "The cypress tree in front of the hall."
    • "Does a dog have Buddha nature or not?" Zhaozhou said, "Wú!"
       

    Note. You know, you’re right: some of those koans do make about as much sense as most technical documentation, don’t they?

     
    Um, not our technical documentation, mind you.

     
    There’s also this somewhat lesser-known koan:

     
    “You create several per-user policies in Microsoft Lync Server 2010. How do you know if all, or even any, of these policies are actually being used?”

     
    Wú!

     
    That’s actually a valid question: what’s the point of creating, and managing, scores of per-user Lync Server policies if you never even use those policies? But how in the world can you tell whether or not any of your per-user policies have actually been assigned to a user (or two)?

     
    As it turns out, it’s pretty easy to determine how many users have been assigned a particular per-user policy. For example, this command returns the number of users who have been assigned the voice policy RedmondVoicePolicy:

     
    (Get-CsUser –Filter {VoicePolicy –eq "RedmondVoicePolicy"}).Count

     
    That’s cool, but what if you have a whole bunch of per user voice policies. Do you have to repeat the preceding command time after time after time, running one command per voice policy? As a matter of fact, you do.

     
    Well, unless you run this script instead:

     
    $identities = @()
     

    $policies = Get-CsVoicePolicy -Filter "tag:*" | Select-Object Identity
     

    foreach ($i in $policies)
        {
            $x = $i.Identity
            $x = $x -replace "Tag:",""
            $identities += $x
        }
     

    foreach ($policy in $identities)
        {
            $policy
            (Get-CsUser -Filter {VoicePolicy -eq $policy}).Count
        }

     
    This is actually a fairly simple little script. (Yes, we know. But in PowerShell, looks can be deceiving.) What we’re doing here is first creating an empty array named $identities; that’s what the command $identities = @() does. We then use Get-CsVoicePolicy and the –Filter parameter to return all the voice policies that have an Identity that begins with the string value tag:; as you well know, any voice policy that has an Identity that begins with tag: is a per-user policy.
     

    Note. You say you didn’t know that? Then you need to read the article All About Identities.

     
    After we return all the per-user voice policies, we set up a foreach loop that strips the Tag: prefix off each Identity; once we’ve done that we can then use Get-CsUser and the
    –Filter parameter to return the number of users who have been assigned each policy. For example, suppose we have three per-user voice policies:
     

    • Tag:RedmondVoicePolicy
    • Tag:DublinVoicePolicy
    • Tag:TokyoVoicePolicy

     

    After stripping off the prefix, we’re left with these policy Identities:
     

    • RedmondVoicePolicy
    • DublinVoicePolicy
    • TokyoVoicePolicy

     
    And then, for all intents and purposes, we end up running these three commands:


    (Get-CsUser -Filter {VoicePolicy -eq "RedmondVoicePolicy").Count
    (Get-CsUser -Filter {VoicePolicy -eq "DublinVoicePolicy").Count
    (Get-CsUser -Filter {VoicePolicy -eq "TokyoVoicePolicy").Count

     
    Which, in turn, gives us back information similar to this:

     
    RedmondVoicePolicy
    9

    DublinVoicePolicy
    1

    TokyoVoicePolicy
    144

     
    As you can see, the Tokyo policy has been assigned to a ton of users; the other two policies are less-widely used.

     
    Of course, this particular script works only for voice policies. What if you’d like to return this same information for, say, client policies? That’s fine: just search for each instance of VoicePolicy and replace it with ClientPolicy. Give it a try and see what happens.

     
    Hey, guess what: we’re pretty sure we hear one hand clapping out there! Thank you, thank you very much.

     

  • Lync Server Admin Guide: Managing Response Groups

     

     

    Managing Agent Groups

     

    Create an Agent Group

     

    • To create an agent group

     

    To create a new Response Group agent group, use the New-CsRgsAgentGroup cmdlet. This command uses the AgentsByUri parameter to add individual users to the agent group:

     

    New-CsRgsAgentGroup -Parent service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk Group" -AgentsByUri "sip:kenmyer@litwareinc.com","sip:pilarackerman@litwareinc.com"

     

    By comparison, this command uses the DistributionGroupAddress parameter to add all the members of the helpdesk@litwareinc.com distribution group to the agent group:

     

    New-CsRgsAgentGroup -Parent service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk Group" -DistributionGroupAddress "helpdesk@litwareinc.com"

     

     

    Change Agent Group Settings or Members

     

    • To change agent group settings or membership

     

    To change a Response Group agent group you must first use the Get-CsRgsAgentGroup cmdlet to retrieve an object reference to the agent group to be changed. After making your changes in memory, use the Set-CsRgsAgentGroup cmdlet to write those changes to Lync Server.

     

    These commands change the routing method for an agent group:

     

    $x = Get-CsRgsAgentGroup -Identity service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk"

    $x.RoutingMethod = "RoundRobin"

    Set-CsRgsAgentGroup -Instance $x

     

    This set of commands changes the distribution address for an agent group:

     

    $x = Get-CsRgsAgentGroup -Identity service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk"

    $x.DistributionGroupAddress = "helpdesk@litwareinc.com"

    Set-CsRgsAgentGroup -Instance $x

     

    The following commands add a new agent to an agent group:

     

    $x = Get-CsRgsAgentGroup -Identity service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk"

    $x.AgentsByUri.Add("sip:kenmyer@litwareinc.com")

    Set-CsRgsAgentGroup -Instance $x

     

    And these commands remove an agent from an agent group:

     

    $x = Get-CsRgsAgentGroup -Identity service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk"

    $x.AgentsByUri.Remove("sip:kenmyer@litwareinc.com")

    Set-CsRgsAgentGroup -Instance $x

     

     

    Delete an Agent Group

     

    • To delete an agent group

     

    To remove a Response Group agent group, use the Remove-CsRgsAgentGroup cmdlet:

     

    Get-CsRgsAgentGroup -Identity service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk" | Remove-CsRgsAgentGroup

     

    This command deletes all the agent groups currently configured for use in your organization:

     

    Get-CsRgsAgentGroup | Remove-CsRgsAgentGroup

     

     

    For more information

     

     

     

     

     

    Managing Response Group Queues

     

    Create a Response Group Queue

     

    • To create a queue

     

    To create a new Response Group queue, use the New-CsRgsQueue cmdlet. The following set of commands creates a new queue, and uses the New-CsRgsCallAction cmdlet to specify what should happen if the queue receives too many calls at the same time:

     

    $x = New-CsRgsCallAction -Action TransferToVoicemailUri -Uri "sip:+14255551298@litwareinc.com"

     

    New-CsRgsQueue -Parent service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk" -OverflowCandidate "OldestCall" -OverflowAction $x -OverflowThreshold 2

     

     

    Change a Response Group Queue

     

    • To change a queue

     

    To modify a Response Group queue, you must first use the Get-CsRgsQueue cmdlet to retrieve an object reference to the queue to be changed. After making your changes in memory, use the Set-CsRgsQueue cmdlet to write those changes to Lync Server:

     

    $x = Get-CsRgsQueue -Identity Service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk"

    $x.OverflowCandidate = "NewestCall"

     

    Set-CsRgsQueue -Instance $x

     

     

    Delete a Response Group Queue

     

    • To delete a queue

     

    To delete a Response Group queue, use the Remove-CsRgsQueue cmdlet:

     

    Get-CsRgsQueue -Identity service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk Queue" | Remove-CsRgsQueue

     

    The following command removes all the Response Group queues configured for use in your organization:

     

    Get-CsRgsQueue | Remove-CsRgsQueue

     

     

    For more information

     

     

     

     

     

    Managing Response Group Workflows

     

    Create a Response Group Workflow

     

    Create a Hunt Group Workflow

     

    • To create a hunt group workflow

     

    To create a hunt group workflow, use the New-CsRgsWorkflow cmdlet:

     

    $prompt = New-CsRgsPrompt -TextToSpeechPrompt "Welcome to the help desk."

     

    $queue = (Get-CsRgsQueue -Identity service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk").Identity

     

    $callAction = New-CsRgsCallAction -Prompt $prompt -Action TransferToQueue -QueueId $queue

     

    New-CsRgsWorkflow -Parent service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk" -PrimaryUri "sip:helpdesk@litwareinc.com" -DefaultAction $callAction

     

    Create an Interactive Workflow

     

    • To create an Interactive workflow

     

    To create an interactive workflow, use the New-CsRgsWorkflow cmdlet:

     

    $ServiceId = "service:ApplicationServer:rtc.contoso.com"

     

    $Group_Sales = New-CsRgsAgentGroup -Parent $ServiceId -Name "Contoso Sales" -AgentAlertTime 20 -ParticipationPolicy Informal -RoutingMethod LongestIdle -AgentsByUri("sip:franz@contoso.com","sip:marco@contoso.com")

     

    $Group_Support = New-CsRgsAgentGroup -Parent $ServiceId -Name "Contoso Support" -AgentAlertTime 20 -ParticipationPolicy Informal -RoutingMethod LongestIdle -AgentsByUri("sip:david@contoso.com","sip:john@contoso.com")

     

    $Queue_Sales = New-CsRgsQueue -Parent $ServiceId -Name "Contoso Sales"

    -AgentGroupIDList($Group_Sales.Identity)

     

    $Queue_Support = New-CsRgsQueue -Parent $ServiceId -Name "Contoso Support" -AgentGroupIDList($Group_Support.Identity)

     

    $PromptA1 = New-CsRgsPrompt -TextToSpeechPrompt "Please wait while we're connecting you with the Contoso Sales department."

     

    $ActionA1 = New-CsRgsCallAction -Prompt $PromptA1 -Action TransferToQueue -QueueID $Queue_Sales.Identity

     

    $Answer1 = New-CsRgsAnswer -Action $ActionA1 -DtmfResponse 1

     

    $PromptA2 = New-CsRgsPrompt -TextToSpeechPrompt "Please wait while we're connecting you with the Contoso Support department."

     

    $ActionA2 = New-CsRgsCallAction -Prompt $PromptA2 -Action TransferToQueue -QueueID $Queue_Support.Identity

     

    $Answer2 = New-CsRgsAnswer -Action $ActionA2 -DtmfResponse 2

     

    $PromptQ = New-CsRgsPrompt -TextToSpeechPrompt "Thank you for calling Contoso. To speak with a Sales representative, press 1. To be connected with our Support line, press 2."

     

    $Question = New-CsRgsQuestion -Prompt $PromptQ -AnswerList ($Answer1, $Answer2)

     

    $ActionWM = New-CsRgsCallAction -Action TransferToQuestion -Question $Question

     

    $Workflow = New-CsRgsWorkflow -Parent $ServiceId -Name "Contoso Helpdesk" -Description "The Contoso Helpdesk line." -PrimaryUri "sip:helpdesk@contoso.com" -LineUri "tel:+14255554321" -DisplayNumber "+1 (425) 555-4321" -Active $true -Anonymous $true -DefaultAction $ActionWM

     

    Change a Response Group Workflow

     

    Change a Hunt Group Workflow

     

    • To change settings for a hunt group workflow

     

    To modify a hunt group workflow, use the Get-CsRgsWorkflow cmdlet to create an object reference to the workflow to be modified, make your changes in memory, and then use the Set-CsRgsWorfklow cmdlet to write those changes to Lync Server:

     

    $businessHours = Get-CsRgsHoursOfBusiness service:ApplicationServer:atl-cs-001.litwareinc.com -Name "US Business Hours"

     

    $y = Get-CsRgsWorkflow service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk"

     

    $y.BusinessHoursId = $businessHours.Identity

     

    Set-CsRgsWorkflow -Instance $y

     

     

    Change an Interactive Workflow

     

    • To change an interactive workflow

     

    To modify an interactive workflow, use the Get-CsRgsWorkflow cmdlet to create an object reference to the workflow to be modified, make your changes in memory, and then use the Set-CsRgsWorfklow cmdlet to write those changes to Lync Server:

     

    $musicFile = Get-Content -ReadCount 0 -Encoding Byte C:\MediaFiles\Hold.wav | Import-CsRgsAudioFile -Identity Service:ApplicationServer:atl-cs-001.litwareinc.com -FileName "HelpDeskHoldMusic.wav"

     

    $y = Get-CsRgsWorkflow -Identity Service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk"

     

    $y.CustomMusicOnHoldFile = $musicFile

     

    Set-CsRgsWorkflow -Instance $y

     

     

    Delete a Response Group Workflow

     

    • To delete a workflow

     

    To delete a Response Group workflow use the Get-CsRgsWorkflow cmdlet to return an instance of the workflow to be deleted, then pipe that instance to the Remove-CsRgsWorkflow cmdlet:

     

    Get-CsRgsWorkflow service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk Workflow" | Remove-CsRgsWorkflow

     

    This command deletes all the workflows configured for use in your organization

     

    Get-CsRgsWorkflow | Remove-CsRgsWorkflow

     

     

    For more information

     

     

     

     

     

  • Active Directory Phone Numbers and Line URIs: Together at Last!

    If you were to tunnel deep enough under the Microsoft campus (note: please don't tunnel under the Microsoft campus, or at least don't tell anyone we suggested it), you would eventually run into the concrete bunker that serves as the Lync PowerShell Fortress of Solitude.

     

    Note. You'll also find thousands of copies of Windows ME buried down there as well. But that's another story.

     

    Inside the Fortress of Solitude, teams of dedicated Lync Server PowerShell blog writers monitor the world of Lync Server PowerShell, 24 hours a day, 7 days a week. Whenever they hear about someone who has a problem or a question involving Lync Server PowerShell, they immediately dispatch a highly-trained Lync PowerShell SWAT team, a SWAT team that can be airlifted and parachuted into your facility within hours. Once your facility has been secured, the SWAT team will fix your problem or answer your question, then disappear into the night, awaiting their next assignment.

     

    Note. OK, it's possible that the preceding description could be a bit of an exaggeration. But you have to consider things from our point of view: it's mid-year review time here at Microsoft, and the things that we really do do – write a daily Lync Server PowerShell haiku and ask you which PowerShell cmdlet is not like the others – don't stack up very well against the things other technical writers do around here.

     

    Oh, and in case you're wondering, no, we haven't been able to talk Microsoft into buying us parachutes. However, the company doesn’t seem to have any problem with us jumping out of airplanes without those parachutes.

     

    But while we may not have a Fortress of Solitude, from time-to-time we do get questions about Lync Server PowerShell, and while we might not be a real SWAT team, whenever we do get a question we sit down and try to come up with an answer. (And, before you ask, yes, we usually try to come up with the right answer.)

     

    For example, just the other day we received this email:

     

    "Do you have or can you post a PowerShell script that will go through AD and find all enabled Lync users and then set their LineURI as a normalized version of their Office Phone number? … This would help us out, otherwise we are going to have to sit and do this by hand, not to mention when the Office Number changes it will have to be manually changed in Lync. I’m sure I’m not the only one who could use something like this."

     

    So can we do that? Can we write a script that will find each user who has been enabled for Lync Server, grab that user's phone number (as currently configured in Active Directory), and then assign that phone number to their LineUri attribute? Beats us. But we'll definitely give it the old college try.

     

    Note. Which sounds pretty good, unless you know how hard we actually tried when we were in college.

     

    Let's start with a very simple scenario. Suppose we have two user accounts that have been enabled for Lync Server. Those two users, and the Active Directory phone numbers that have been assigned to them, are shown below:

     

    Display Name

    Phone Number

    Ken Myer

    1-(206)-555-1219

    Pilar Ackerman

    1-(425)-555-0712

     

    What we want to do is take those existing phone numbers, convert them to the line URI format, and then assign those line URIs to their respective users. For example, Ken Myer has the phone number 1-(206)-555-1219. When our script finishes running, he should also have the LineURI TEL:+12065551219. What kind of script can do that for us? This kind of script:

     

    $enabledUsers = Get-CsAdUser -Filter {Enabled -ne $Null}

     

    foreach ($user in $enabledUsers)

        {

            $phoneNumber = $user.Phone

            $phoneNumber = $phoneNumber -replace "[^0-9]"

            $phonenumber = "TEL:+" + $phoneNumber

            Set-CsUser -Identity $user.Identity -LineUri $phoneNumber

        }

     

    Let's see if we can figure out what's going on here. To begin with, we use the following command to return information about all the users who have been enabled for Lync Server:

     

    $enabledUsers = Get-CsAdUser -Filter {Enabled -ne $Null}

     

    Now, you might be looking at the preceding command and wondering: a) why did they use Get-CsAdUser instead of Get-CsUser; and, b) why did they use the filter {Enabled –ne $Null}? After all, if all we want to get back are the users who have been enabled for Lync Server, shouldn't we use this filter instead: {Enabled –eq $True}?

     

    Believe it or not, we have good reasons for using Get-CsAdUser and for using the filter we chose. (And yes, that might be the first time we ever had a good reason for doing something.) Because we're interested only in users who have been enabled for Lync Server, you might have expected us to use the Get-CsUser cmdlet; after all, those are the only kind of users Get-CsUser can return. However, if we want this script to actually work (which we do), then we need to retrieve each user's current Active Directory phone number. Get-CsAdUser returns that phone number for us; Get-CsUser doesn't. That's why we need to use Get-CsAdUser.

     

    As for the filter we chose, well, the Enabled attribute tells whether or not a user has been enabled for Lync Server. The Enabled attribute can have one of three values:

     

    ·         $True, which means the user has been enabled for Lync Server.

    ·         $False, which means the user has been enabled for Lync Server, but his or her Lync account is temporarily disabled.

    ·         $Null, which means that the user has not been enabled for Lync Server.

     

    As you can see, if we looked for users where the Enabled attribute is equal to $True, we wouldn't get back users whose accounts have been enabled for Lync Server but are temporarily disabled. Therefore, we decided to look for all the users where the Enabled attribute is equal to either $True or $False; in other words, where the attribute is not equal to (-ne) $Null.

     

    Note. Confused? We don't blame you. For more information on how the Enabled attribute works take a look at the article When is a Boolean Not a Boolean?

     

    After we execute the first line in our script we should have information about all the users who have been enabled for Lync Server; that collection of data then gets safely tucked away in the variable $enabledUsers. In the next line of code, we set up a foreach loop that loops through all the users in that collection. Inside that loop, the first thing we do is grab the user's Active Directory phone number (for example, 1-(206)-555-1219) and stash that in a variable named $phoneNumber:

     

    $phoneNumber = $user.Phone

             

    This is where it gets interesting. (Or, should we say, this is where it gets even more interesting.) As you know, line URIs are finicky little things: they have to be formatted just right or Lync Server will reject them. In particularly, line URIs must start with the prefix TEL:+ and then be followed by a phone number consisting of the country code, area code, and the actual phone number. Oh, and that country code, area code, and phone number? Digits only. No blank spaces, no parentheses, no hyphens, nothing but numbers.

     

    Which means we have two problems here. First, Ken Myer's Active Directory phone number – 1-(206)-555-1219 – doesn't start with the TEL:+ prefix. Second, that phone number contains some invalid characters: (, ), and -. Before we can turn this phone number into a line URI we need to fix both of these problems.

     

    Let's start off by tackling the second problem: the presence of invalid characters. What we need to do here is take our Active Directory phone number and remove everything that isn't a number. That sounds like a pretty daunting task; the truth is, it requires just one line of code:

     

    $phoneNumber = $phoneNumber -replace "[^0-9]"

     

    What we're doing here is using the –replace operator and the regular expression [^0-9] to locate everything that isn't a number and replace it with, well, nothing. We know that we're searching for everything that isn't a number because of the regular expression [^0-9]; that value tells PowerShell to use the numeric range 0 through 9. The caret symbol (^) tells PowerShell to look for characters that aren't in the range 0-9. If we wanted to look for characters that are in the range 0-9 (which we don't) we'd simply omit the caret:

     

    "[0-9]"

     

    And to make sure that non-digit characters are deleted (that is, replaced by nothing) we don't include a replacement parameter. Suppose, for some reason, we wanted to replace non-digit characters with the letter X. In that case, we'd use this syntax:

     

    -replace "[^0-9]", "X"

     

    Which is probably more than you really need to know at the moment.

     

    And so what do we end up with when we remove all the non-digit characters? This:

     

    12065551219

     

    As you can see, that's almost a line URI; the only problem is that it doesn't start with the TEL:+ prefix. But that's easy enough to fix:

     

    $phonenumber = "TEL:+" + $phoneNumber

     

    Needless to say, there's nothing too terribly complicated about that command. All we're doing is adding the prefix TEL:+ to the value stored in the variable $phoneNumber. That means $phoneNumber now looks like this:

     

    TEL:+12065551219

     

    And that value does look like a line URI, doesn't it? Which, in turn, means that all we have to do now is assign this value to the LineUri attribute. You know, like this:

     

    Set-CsUser -Identity $user.Identity -LineUri $phoneNumber

     

    If we take a peek at Active Directory right now we should see something that looks like this:

     

    Display Name

    Phone Number

    Line URI

    Ken Myer

    1-(206)-555-1219

    TEL:+12065551219

    Pilar Ackerman

    1-(425)-555-0712

     

     

    As you can see, Ken Myer now has a line URI based on his Active Directory phone number. Granted, Pilar Ackerman doesn't have a line URI yet. But that's because we've only made one trip through our foreach loop. As soon as we loop around we'll repeat the process with Pilar's account and she'll end up with a line URI based on her Active Directory phone number:

     

    Display Name

    Phone Number

    Line URI

    Ken Myer

    1-(206)-555-1219

    TEL:+12065551219

    Pilar Ackerman

    1-(425)-555-0712

    TEL:+14255550712

     

    Etc., etc.

     

    Now, we should note that there is one potential complication here. Our script assumes that you've been storing complete phone numbers in Active Directory; that is, phone numbers that have the country code, area code, and the phone number. As long as you use that format our script will work; for example, it can convert any of these phone numbers to line URIs:

     

    1-(206)-555-1219

    1-206-555-1219

    1.206.555.1219

    1(206)555-1219

     

    And so on.

     

    Now that's great, but what if you don't use the same format we used in our preceding example? Well, in that case, you're going to have to modify the script a little. But don't despair; we'll make a deal with you. We'll show you two additional examples for normalizing your Active Directory phone numbers to the E.164 format used in a line URI. If those examples don't work for you, send an email to cspshell@microsoft.com, making sure to give us an example of how your phone numbers are formatted in Active Directory. We'll hand that email over to one of the SWAT teams and with any luck, within a day or two someone will parachute into your office and hand you the solution.

     

    Or, depending on the budget, someone will email that solution to you.

     

    Note. And we'll also publish any additional scripts here in the Lync Server PowerShell blog, just in case anyone else is interested.

     

    Let's start by looking at an organization that formats phone numbers like this:

     

    Display Name

    Phone Number

    Ken Myer

    51219

    Pilar Ackerman

    60712

     

    As you can see, this organization doesn't store complete phone numbers in Active Directory; instead, they store only extension numbers. And just to make things a little more complicated, it turns out that the area code varies depending on your extension number: if your extension starts with the number 5 then you're in area code 206. If your extension starts with the number 6 then you're in area code 425.

     

    So how are going to go about assigning line URIs in this organization? We're going to go about it like this:

     

    $enabledUsers = Get-CsAdUser -Filter {Enabled -ne $Null}

     

    foreach ($user in $enabledUsers)

        {

            $phoneNumber = $user.Phone

            $phoneNumber = $phoneNumber -replace "[^0-9]"

            $extension = $phoneNumber -match "^5"

            if ($extension)

                {

                    $phoneNumber = "TEL:+120655" + $phoneNumber

                }

            else

                {

                    $phoneNumber = "TEL:+142556" + $phoneNumber

                }

          

            Set-CsUser -Identity $user.Identity -LineUri $phoneNumber

        }

     

    Most of that script should look familiar to you; after all, much of it is copied line-for-line from our previous script. (Of course, we're assuming that you did look at our previous script.) The only real difference comes here:

     

    $extension = $phoneNumber -match "^5"

    if ($extension)

        {

            $phoneNumber = "TEL:+120655" + $phoneNumber

        }

    else

        {

            $phoneNumber = "TEL:+142556" + $phoneNumber

        }

     

    In this block of code we're checking to see if the extension number starts with a 5; that's what the regular expression ^5 is for. Suppose the first number in the extension is a 5. In that case, that means that the user is in the 206 area code and also has the telephone prefix 555. In that case, and in order to turn this phone extension into a line URI, we need to prefix the extension number with TEL:+120655. Like this:

     

    $phoneNumber = "TEL:+120655" + $phoneNumber

     

    If we do that, we'll end up with a phone number that like this one:

     

    TEL:+12065551219

     

    And if the extension doesn’t start with a 5? Well, in this somewhat-simplistic example, that can only mean that the number is in the 425 area code and must have the telephone prefix 556. This line of code takes care of formatting extensions that don't start with a 5:

     

    $phoneNumber = "TEL:+1425556" + $phoneNumber

     

    Got all that? Good. Now let's do one more. Here's a fairly typical way of storing phone numbers in Active Directory; it includes the complete phone number plus an extension number (the number following the x):

     

    Display Name

    Phone Number

    Ken Myer

    1-206-555-1111 x1219

    Pilar Ackerman

    1-206-555-0712

     

    So how do we deal with Ken Myer's phone number, which is a phone number and an extension? Well, for starters, we need to tell you that we might have … misled … you earlier in this article, back when we said line URIs can only contain numbers. That's mostly true, but there are exceptions. For example, this is not only a valid line URI, but it's the way you need to format line URIs that include both a phone number and an extension:

     

    TEL:+12065551111;ext=1219

     

    We need to make Ken Myer's phone number look like that. And here's how we're going to do it:

     

    $enabledUsers = Get-CsAdUser -Filter {Enabled -ne $Null}

     

    foreach ($user in $enabledUsers)

        {

            $phoneNumber = $user.Phone

            $phoneNumber = $phoneNumber -replace "[^0-9|^x]"

            $phoneNumber = $phoneNumber –replace "x", ";ext="

            $phonenumber = "TEL:+" + $phoneNumber

            Set-CsUser -Identity $user.Identity -LineUri $phoneNumber

        }

     

    There are two differences between this script and the script we originally showed you. For starters, there's this line of code:

     

    $phoneNumber = $phoneNumber -replace "[^0-9|^x]"

     

    What's the deal with that line of code? Well, as we know, Ken's phone number starts off looking like this:

     

    1-206-555-1111 x1219

     

    As we also know, we need to remove all the non-digit characters from this number – well, all of them except the letter x. Why can't remove the letter x? We need to keep the letter x in there so we know where the extension part of Ken's phone number begins. That's why we use the regular expression [^0-9|^x]; that pattern tells PowerShell to replace everything that doesn't fall into the numeric range 0-9 or that isn't the letter x (^x). After we execute this line of code Ken's phone number should look like this:

     

    12065551111x1219

     

    That's step 1. Now we need to replace the letter x with the string ;ext=. Which is what we do here:

     

    $phoneNumber = $phoneNumber –replace "x", ";ext="

     

    That gives Ken a phone number that looks like this:

     

    12065551111;ext=1219

     

    That also makes it easy for us to add the TEL:+ prefix and then assign Ken his new line URI.

     

    Oh, and what about Pilar's phone number, a phone number that doesn't use an extension? That's fine. If the script finds an extension it will deal with and, if it doesn't find an extension, it will deal with that.

     

    Which, all in all, really is a heck of a deal, isn't it?

     

    That's all we have time for today; it's time for our shift down in the Lync PowerShell Fortress of Solitude. If you need additional help just send an email to cspshell@microsoft.com. Remember: the SWAT teams are standing by!

     

    Note. Well, to be honest, Greg is probably checking basketball scores and Jean is probably playing Mahjong. But, despite that hectic schedule, we'll try and answer your question as quickly as we can.

     

  • Enabling User Accounts in Lync Server vs. Exchange Server

    Does the Active Directory object for a user have to exist before you can enable that user for Lync Server?

     

    Yes; if you're going to enable a user for Lync Server then that user must already have an Active Directory user account. For example, suppose you try to enable Ken Myer for Lync Server, but Ken doesn't have an Active Directory user account. In that case, you're going to get an error message similar to this:

     

    Enable-CsUser : Management object not found for identity "Ken Myer".

     

    Note. Ken's Active Directory account doesn’t have to be enabled; you can run Enable-CsUser against a disabled account and everything will work just fine. The account just has to exist.

     

    The reason this question came up is because the New-Mailbox cmdlet in Microsoft Exchange will create an Active Directory account for you. For example, this command will not only create a new mailbox for Ken Myer, but it will create a new Active Directory account for him as well:

     

    New-Mailbox -UserPrincipalName "kenmyer@litwareinc.com" -Alias kenmyer -Database "Mailbox Database 1" -Name kenmyer -OrganizationalUnit "Redmond" –Password "ken395HJ2" -FirstName "Ken" -LastName "Myer -DisplayName "Ken Myer" -ResetPasswordOnNextLogon $True

     

    The Lync Server cmdlets won't do this; there's no way to create an Active Directory user account using a Lync Server cmdlet. In fact, and for the most part, the Lync Server cmdlets work only on Lync Server-specific attributes. For example, suppose Ken Myer has gotten a promotion and needs to have his job title changed to Assistant to the Regional Manager. Can you use, say, the Set-CsUser cmdlet to change his job title? No. You're going to have to look elsewhere in order to change a non-Lync Server attribute.

     

    Note. For example, this command mixes a little Lync Server PowerShell (the Get-CsAdUser cmdlet) and some generic Windows PowerShell in order to change Ken Myer's job title:

     

    $userDN = (Get-CsAdUser "Ken Myer").DistinguishedName

    $userAccount = [ADSI] "LDAP://$userDN"

    $userAccount.Title = "Assistant to the Regional Manager"

    $userAccount.SetInfo()

     

    And yes, we know: you're a little disappointed, aren't you? But here's something that will brighten your day: when it comes to user accounts, Exchange PowerShell and Lync Server PowerShell speak the same language. Why do you care about that? Well, for one thing, that means you can use the New-Mailbox cmdlet to create both an Active Directory user account and a new Exchange mailbox and then – in the very same command – pipe that user object to Enable-CsUser and enable that user for Lync Server!

     

    Hey, would we kid you about a thing like that?

     

    New-Mailbox -UserPrincipalName "kenmyer@litwareinc.com" -Alias kenmyer -Database "Mailbox Database 1" -Name kenmyer -OrganizationalUnit "Redmond" –Password "ken395HJ2" -FirstName "Ken" -LastName "Myer -DisplayName "Ken Myer" -ResetPasswordOnNextLogon $True | Enable-CsUser  -RegistrarPool pool0.litwareinc.com  –SipAddressType SamAccountName
    –SipDomain litwareinc.com

     

    Give it a try and see what happens.

     

    Oh, and yes, this also works with the Enable-Mailbox cmdlet. This command enables a user's Exchange mailbox and enables that user for Lync Server, all in one fell swoop:

     

    Enable-Mailbox "Ken Myer" | Enable-CsUser -RegistrarPool pool0.litwareinc.com  –SipAddressType SamAccountName –SipDomain litwareinc.com

     

    Cool, huh?

  • Scopes and Filters

    Scopes and Filters

     

    Suppose you’re a typical English-speaking American and you get the opportunity to live and work in Rome for a year. Could you survive without learning to speak Italian? Maybe. Would you be better off if you took a little time to sit down and learn some Italian? Definitely. When in Rome, do as the Romans do.

     

    Oh: and talk like the Romans talk.

     

    True story. Several years ago, one of the authors accompanied his son to Japan (his son was playing on an all-star baseball team that was touring the Tokyo area). One night the author was talking to a Japanese mother whose family was serving as a “host family” for two nights. (Instead of staying in hotels, the players stayed with host families.) This mother was a trifle nervous about hosting three American children: would they like the food; would they be bored; would they be able to communicate? After chatting in English the author – who had learned a little Japanese prior to the trip – wished the woman “Good luck” in Japanese. The mother just stood and stared at him. “I’m sorry,” said the author. “I was trying to say ‘Good luck,’ but I guess I said it wrong.” “Oh,” said the woman. “I get it: you were speaking Japanese. No, you said it correctly. You’re an American, though, so I never expected you to speak Japanese. I assumed you were speaking English, so I had no idea what you were saying.”

     

    As it turns out, Microsoft Lync Server 2010 introduces a whole new language and a whole new vocabulary all its own. Can you survive without learning this new vocabulary? Maybe. Would you be better off if you took a little time to sit down and learn this new vocabulary? Well, you know what they say: when in Rome ….

     

    With that in mind, let’s take a minute and talk about three important new terms being introduced along with the new Lync Server implementation of Windows PowerShell: identities, scopes, and filters.

     

    Oh, my!

     

    Identities

     

    At the risk of tooting our own horns, we went to great lengths to explain identities in our Identities article, which means that we’re not going to do it again here. Instead, let’s move on to Scopes.

     

    Man, if all the sections turn out like this one this article will be a cinch to write.

     

    Scopes

     

    To explain what a scope let’s start by listing the available scopes:

     

             Global. The global scope is for configuration settings or policies you want enforced throughout your entire Microsoft Lync Server infrastructure. For example, when you configure a policy at the global scope that policy will be applied to every user in the organization. (Well, unless you have configured a policy “downstream.” But more on that in a minute.)

             Site. The site scope is for, well, sites. In Lync Server 2010, sites are typically based on geographic locations: a Redmond site, a Dublin site, etc.

             Service. The service scope represents a service. For example, the Registrar service is one Microsoft Lync Server service; user services is another. In the new Lync Server architecture, a given service might be carried out by a single computer or by all the computers in a pool.

             Tenant. The Tenant scope deals with Lync Server as a service (the so-called “in-the-cloud” scenario). At this point in time the Microsoft Lync Server cmdlets cannot be used “in the cloud.” In turn, that means that it is irrelevant to our needs, and thus won’t be discussed in this article. We mention this only because – as you’ll see in the next section – you might stumble upon a reference to the Tenant scope when trying to determine the allowed scopes for an object.

             Per-User. To be honest, “per-user” isn’t really the name of the scope, it’s more a description of the scope. (We have yet to come up with a good name for this scope.) The basic concept behind the per-user scope is pretty straightforward. The per-user scope (which applies only to policies, and not to configurations) is reserved for policies that can be directly applied to a user or a group of users. This makes the per-user (or, more simply, “user”) scope very different from the global, site, and service scopes, all of which are tied to your Microsoft Lync Server infrastructure. For example, if you don’t have a site named Redmond then you can’t create a policy at the site:Redmond scope; a site-scoped policy must be tied to an actual site. The Identity of a site-scoped policy or a service-scoped policy is simply the Identity of the site or service – period.

    Oh, and each site is limited to one voice policy, one client policy, and so on: you can’t have two site-scoped voice policies at the Redmond site.

    By comparison, you can create a per-user policy and give it pretty much any Identity. That’s because these policies do not have to be anchored to real objects such as sites or services. In turn, that means you can create as many per-user voice policies as you want, even if you don’t ever actually use those policies.

     

    Scopes are primarily used for policy (and configuration) resolution. For example, if a user wants to make a phone call Lync Server needs to check to see such things as which voice policy is used to determine how (or even if) the user makes a call. Microsoft Lync Server applies policies and configurations in this order:

     

    1.    First, the system checks to see if something has been applied at the user scope. If so, then that policy (configurations can’t be applied at the user scope) is used.

    2.    If nothing can be found at the user scope then the system checks to see if something has been applied at the service scope. If so, then that policy or configuration is applied.

    3.    If nothing can found at the service scope then the system checks to see if something has been applied at the site scope. If so, then that policy or configuration is applied

    4.    If nothing can be found at the site scope then the system checks to see if something has been applied at the global scope. If so, then that policy or configuration is applied.

     

    Note that there will always be a global policy or a global configuration setting. Global policies and configuration settings are automatically created for you when you install Microsoft Lync Server 2010, and these global objects can never be removed.

     

    Never.

     

    Got all that? Good, because now we can talk about how you specify an identity and, equally important, when you specify an identity.

     

    Important. This discussion pertains to Microsoft Lync Server policies and configurations; it does not pertain to user accounts. For more information on user account identities see Retrieving Active Directory and Microsoft Lync Server User Accounts.

     

    As we explained in the Identities section of this manual, identities are specified at the time you create a new object; the identity you give that object will depend on both the type of object and on the scope of the object. For example, suppose you want to create a new conferencing policy for use at the site level; what identity will you give that policy? That’s an easy one: a site policy always has an Identity that consists of the string value site: followed by the name of the site. For example, if this conferencing policy is to be assigned to the Redmond site then you would use the following code to create the new policy:

     

    New-CsConferencingPolicy –Identity site:Redmond

     

    OK, then what about a policy that’s supposed to be assigned to a service? That’s another easy one: the Identity is simply the string value service: followed by the service name (i.e., Registrar) followed by the fully qualified domain name of the pool where that service is located. You know, like this:

     

    New-CsClientVersionPolicy –Identity service:Registrar:atl-cs-001.litwareinc.com

     

    As you can see, when you are working at the site scope or the service scope there is no need to assign (or grant) a policy; instead, the policy is automatically assigned based on the Identity you give it. Give a policy the Identity site:Redmond and it will automatically be assigned to the Redmond site.

     

    Note. You might have noticed we skipped the Global policy. Lync Server ships with default policies and configurations at the global scope. However, and because you can’t re-create a policy or configuration that already exists, you won’t be able to call a New cmdlet using the Global identity. You can use verbs like Get and Set when working with a global policy or a global collection of configuration settings, but you’ll receive an error if you try to create a new global object.

     

    That leaves us with one last identity/scope to deal with: per-user. Per-user policies (and, again, only policies can be applied at the per-user scope) represent the one time (for the most part) you get to be a little creative: with per-user policies, you get to pick out the Identity. (Hurray!) That’s because per-user policies aren’t automatically assigned to a portion of the Microsoft Lync Server infrastructure; in fact, per-user policies aren’t automatically assigned to anyone or anything. Upon creation they simply exist; they do nothing more than wait for you to explicitly assign them to a user or group of users.

     

    So what does all that look like? It looks a little something like this:

     

    New-CsConferencingPolicy –Identity RedmondConferencingPolicy

     

    In this case, the Identity consists of a string value that is the policy “name”: RedmondConferencingPolicy. You can give your user policy any name you wish.

     

    Note. Why did we say “name,” with the word name in quotation marks? Well, this value isn’t really a name; it’s – well, we don’t know exactly what to call it. When you grant a policy to a user one of the parameters you have to specify is –PolicyName. The value for the parameter is the policy “name,” the same as the Identity:

     

    Grant-CsConferencingPolicy –Identity "Pilar Ackerman" –PolicyName RedmondConferencingPolicy

     

    So why not just call this the policy name and be done with it? Well, the problem is that some policies (such as voice policies) actually have a Name property; to make matters worse, the true policy Name does not have to be the same as the policy “name.” But the Name property is really more of a description; it is not the value supplied when you assign a policy. Supply a Name (description) if you want. But keep your focus on the “name” that makes up the Identity.

     

    In other words, the Name is not the name. And who is on first?

     

    Is there a way to determine the allowed scopes for an object?

     

    Yes: scope information is included in the Help topic for each cmdlet where this information is relevant. You can determine the allowed scopes simply by perusing the help file for a given cmdlet. That’s the recommended way to determine the allowed scopes for an object.

     

    Note. Before we go any further we should point out a small oddity in the scoping of per-user policies. Early in the development of Lync Server 2010 the per-user scope was called the tag scope. Why “tag?” Well, we never did figure that out. But in spite of the fact that everyone agreed that “tag” wasn’t a particularly great scope name, it didn’t go away completely. For example, “tag” hasn’t been removed from the error messages; in addition, when you call a cmdlet like Get-CsVoicePolicy the Identity property is still displayed like this:

     

    Tag:UserVoicePolicy

     

    So in spite of the fact that we’re not calling this a tag policy, things still appear as tag policies.

     

    Suppose you type the command Get-Help New-CsConferencingPolicy –full | more. Do that and then scroll down until you find the section labeled Parameters. In that section you should find the following information:

     

    -Identity <XdsIdentity>

        Unique identifier for the conferencing policy to be created.

        Conferencing policies can be created at the site or per-use

        r scopes.

     

    Notice that the global scope isn’t mentioned. That’s because a global conferencing policy always exists, you can’t create a new one. If you look at the description of the Identity parameter in the help for Set-CsConferencingPolicy, you’ll see this:

     

    -Identity <XdsIdentity>

        Unique identifier for the conferencing policy to be modified.

        Conferencing policies can be configured at the global, site,

        or per-user scopes.

     

    Filters

     

    Suppose you have a dial plan with the Identity site:Redmond. As it turns out, this dial plan is the only dial plan that has an Identity that starts with the string value site:R. Consequently, you might find yourself thinking, “I bet I could use a wildcard character to identify this dial plan.” With that in mind, you try to retrieve the dial plan using a command like this:

     

    Get-CsDialPlan –Identity site:R*

     

    We hate to be the ones to rain on your parade, but this isn’t going to work; if you try running the preceding command all you’ll get back is the following error message:

     

    Get-CsDialPlan : Cannot get “DialPLan” “Site:R*” because it does not exist.

    At line:1 char:15

    + Get-CsDialPlan <<<< -Identity site:R*

    + CategoryInfo : ResourceUnavailable: (Site:*:XdsIdentity) [Get-C

    sDialPlan], KeyNotFoundException

    + FullyQualifiedErrorId : MissingItem,Microsoft.Rtc.Management.Internal.Ge

    tDialPlanCmdlet

     

    So why didn’t this work? One reason and one reason only: you can’t use wildcards when specifying an object’s Identity. If you gave your dial plan the Identity site:Redmond then you’re going to have to type that entire string each time you need to retrieve that plan. And that’s all there is to it; there’s no way to work around this issue, none whatsoever.

     

    Well, unless you use the –Filter parameter, of course.

     

    The -What parameter? The -Filter parameter. For example, the following command will return the dial plan site:Redmond; in fact, it will return any dial plan that has an Identity that begins with the string value site:R:

     

    Get-CsDialPlan –Filter site:R*

     

    In other words, if you’ve been wondering what the –Filter parameter is for, well, now you know: -Filter enables you to use wildcards when specifying the policies or configurations that you want to retrieve. For example, suppose you have the following dial plans in your organization:

     

             Global

             site:Cairo

             site:Redmond

             site:Dublin

             site:Singapore

             tag:Administrators

             tag:Contractors

             tag:Sales

     

    To return all the dial plans you can just call Get-CsDialPlan without any additional parameters:

     

    Get-CsDialPlan

     

    Now, what if you wanted all the dial plans that were configured at the site scope? This command will do the trick:

     

    Get-CsDialPlan –Filter Site:*

     

    See how that works? We used the wildcard Site:* to find all the dial plans that begin with the string value Site: (which, by definition, makes them site policies). It’s just that easy.

     

    OK, so now what if you want to find all the dial plans configured at the per-user scope? Remember that, when we created an object at the per-user scope, we simply gave the identity a unique name, we didn’t have to include a scope. For example, to create the per-user dial plan NorthAmericaHeadquartersDialPlan, we use a command like this:

     

    New-CsDialPlan –Identity NorthAmericaHeadquartersDialPlan

     

    And we can use -Filter to retrieve that particular dial plan:

     

    Get-CsDialPlan –Filter *North*

     

    Of course, the preceding command will retrieve all the dial plans (regardless of scope) that contain the string North. For example, suppose we also had a dial plan defined for a site named NorthRedmond (Identity site:NorthRedmond). The preceding command would retrieve the dial plan with the identity NorthAmericaHeadquartersDialPlan and the dial plan site:NorthRedmond.

     

    Bummer.

     

    So how do you retrieve only the per-user (tag) dial plans? Well, even though we didn’t include the scope when creating an Identity for our per-user dial plan, we can still use tag: to retrieve these dial plans. In other words, we can run a command like this:

     

    Get-CsDialPlan –Filter tag:*

     

    By including the tag: prefix in our Filter value, we can retrieve all the dial plans that were defined using just a name; in other words, all the dial plans that must be explicitly granted to users in order to take effect (the ones configured at the per-user, or tag scope). The reason we can do this is because even though we defined the identity with just a name, the tag: prefix is added in the background. And it’s added for just this reason – so we can continue to filter on these values.

     

    The moral of the story is this: if you want to retrieve policies or settings by using wildcard values then you will need to use –Filter rather than –Identity.

     

    -- The End –

     

    Note. Well, OK, not quite the end. We should add that while the –Filter parameter applies to most Microsoft Lync Server cmdlets, there are two major exceptions: Get-CsAdUser and Get-CsUser. These cmdlets have a –Filter parameter all their own, one that’s very different from the –Filter parameter we just discussed. (And yes, we probably should have given these parameters different names. But we didn’t.) For more information, see Retrieving Active Directory and Microsoft Lync Server User Accounts.

     

  • Conferencing Policy Settings: EnableP2PVideo

     

    This article is part of the series An In-Depth Guide to Conferencing Policy Settings.

     

     

    Parameter/Property Name

    Allowed Values

    EnableP2PVideo

     

    This is a per-user setting.

    ·         TRUE: Users can use video in a peer-to-peer session. This is the default value.

    ·         FALSE: Users cannot use video in a peer-to-peer session.

     

    Lync Server Control Panel Equivalent Setting: None

     

    Here's another feature of Microsoft Lync that you might not have known you could control: peer-to-peer video. By default, Microsoft Lync allows users to conduct peer-to-peer video sessions. That means at least two things. First, you can start a video call from within the Contact window:

     

     

    Second, you can easily add video to any peer-to-peer session; all you have to do is click Video:

     

     

    But suppose you don't really want your users conducting peer-to-peer video sessions. Based on the fact that there's no setting in the Lync Server Control Panel that allows you to disable peer-to-peer video, you might think you're out of luck.

     

    But hey, as long as you have Windows PowerShell you're never out of luck! (Well, except for those times when you're out of luck.) As it turns out, Lync Server conferencing policies have a setting – EnableP2PVideo – that allows you to disable or enable peer-to-peer video. The one catch is that this setting is available only through Windows PowerShell. That means you need to use a command like this one to disable peer-to-peer video:

     

    Set-CsConferencingPolicy –Identity global –EnableP2PVideo $False

     

    If you run the preceding command, users affected by that policy will lose the ability to make a video call:

     

     

    In addition to that, the Video menu will disappear from their Conversation window:

     

     

    The moral of the story? No more peer-to-peer video.

     

    Note. Too sad of a story for you? Then use this command to re-enable peer-to-peer video:

     

    Set-CsConferencingPolicy –Identity global –EnableP2PVideo $True

     

    Two things to keep in mind here. First, this setting only affects peer-to-peer video. If you want to allow or disallow conference video, you'll need to use the AllowIPVideo setting in the appropriate conferencing policy.

     

    Second, this is a per-user setting, which – in theory anyway – affects only the user assigned a given policy. Why do we say "in theory anyway?" Well, consider the following scenario, with four users and four different conferencing policies:

     

    User

    Conferencing Policy

    EnableP2PVideo

    Pilar Ackerman

    global

    False

    Ken Myer

    RedmondConferencingPolicy

    True

    Aidan Delaney

    DublinConferencingPolicy

    True

    Charles Fitzgerald

    TokyoConferencingPolicy

    False

     

    Let's take a look at some of the different peer-to-peer scenarios and who gets to use peer-to-peer video in each scenario:

     

    User

    EnableP2PVideo

    Who Gets Peer-to-Peer Video?

    Ken Myer

    Aidan Delaney

    True

    True

    Both Ken Myer and Aidan Delaney get peer-to-peer video. That's because both of them have a conferencing policy where EnableP2PVideo has been set to True.

    Pilar Ackerman

    Charles Fitzgerald

    False

    False

    Neither Pilar Ackerman nor Charles Fitzgerald get peer-to-peer video. That's because both of them have a conferencing policy where EnableP2PVideo has been set to False.

    Charles Fitzgerald

    Ken Myer

    False

    True

    This one might surprise you, until you think about: neither Charles Fitzgerald nor Ken Myer gets peer-to-peer video in this session. Charles doesn't get peer-to-peer video because his conferencing policy won't allow it. By contrast, Ken's conferencing policy does allow peer-to-peer video. So then why doesn't he get peer-to-peer video? That's right: because you can't have one-way video; that is, one user can't have video while the other user can have video. That makes no sense. Consequently, neither user gets peer-to-peer video.

     

    And the moral of that story? Peer-to-peer video is possible only if both users taking part in the session are allowed to have peer-to-peer video. It's basically all or nothing.

     

     

     

     

  • Turn logging on in Lync

     

     

    Registry locations

    HKCU\Software\Microsoft\Communicator\EnableTracing

    Allowed registry values

    ·         0 – Tracing is not enabled in Lync

    ·         1 – Tracing is enabled in Lync

    Registry value type

    REG_DWORD

    Default setting

    0: Tracing is not enabled in Lync

     

    As you probably know, Microsoft Lync has the ability to keep an enormous amount of information about everything it does and, perhaps more importantly, anything that might go wrong when it tries to do whatever it does. Needless to say, this information can be incredibly useful when troubleshooting Lync-related problems: there's no real substitute for having multiple log files that pretty much let you recreate a Lync session, SIP message by SIP message. These logs also let you know all the settings and restrictions that were in force when Lync was used. For example, the file Communicator-uccapi-0.uccapilog shows you all the policy settings that were applied when the user connected to Lync Server:

     

    <provisionGroup name="presencePolicyV2" >

        <propertyEntryList >

            <property name="EnablePrivacyMode" >false</property>

            <property name="AutoInitiateContacts" >true</property>

            <property name="PublishLocationDataDefault" >true</property>

            <property name="DisplayPublishedPhotoDefault" >true</property>

            <property name="PersonalNoteHistoryDepth" >3</property>

            <property name="SubscribeToCollapsedDG" >false</property>

        </propertyEntryList>

    </provisionGroup>

     

    Pretty cool, huh?

     

    By default, this type of logging (also known as tracing) is disabled on Microsoft Lync. Considering the vast amount of information that's made available to you, why would you ever want to disable tracing in Microsoft Lync?

     

    Well, believe it or not, there are several reasons. For one thing, a lot of the information is of minimal use (if it's even of any use) to a system administrator. For example, take this small snippet extracted from one of the log files:

     

    05/17/2011|10:27:26.701 1978:12E0 TRACE :: CSIPAsyncSocket::Send this 07FB9E90, sending pbSendBuf 05165070, dwSendBufSize = 154

    05/17/2011|10:27:26.701 1978:12E0 TRACE :: CSIPAsyncSocket::SendHelper - [0x07FB9E90]

    05/17/2011|10:27:26.701 1978:12E0 TRACE :: CSIPAsyncSocket::SendHelper this 07FB9E90, pbSendBuf 05165070, dwSendBufSize = 154, dwBytesSent = 154

    05/17/2011|10:27:26.709 1978:12E0 TRACE :: CSIPTransportLayerNotify::OnRecv - [0x07FB9E98]

     

    Unless you actually wrote the code for Microsoft Lync, thousands of lines like those might not be all that informative.

     

    In addition, log files can quickly consume enormous amounts of disk space. For example, when the author of this article started Microsoft Lync this morning (with tracing enabled), he hit a snag: Lync was unable to connect to the server. Fortunately, after just a few seconds, he was able to get through and make the connection after all. However, that brief 3- or 4-second little snag generated over 72 megabytes worth of log files.

     

    And yes, that is a lot.

     

    Finally, while there is definitely useful troubleshooting information to be found in the log files, much of troubleshooting data of interest/use to system administrators can be gathered by using Monitoring Server. Even better, if you use Monitoring Server and Monitoring Server Reports you can quickly and easily view and analyze this information, much quicker and easier than you could by wading through gigabytes of client log files.

     

    But you know what? It's entirely up to you whether or not you want to enable tracing in Microsoft Lync. Either way, you can manage this option by selecting (or deselecting) the Turn on logging in Lync option:

     

     

    And before you ask, of course you can manage this setting using Windows PowerShell; all you have to do is manipulate the HKCU\SOFTWARE\Microsoft\Communicator\EnableTracing registry value. For example, the following PowerShell script retrieves the current value of EnableTracing 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 for retrieving the value:

     

    $computer = "."

     

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

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

     

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

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

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

    Write-Host "Turn logging on in Lync: $value"

     

    And what if you want to change the value of EnableTracing? Hey, no problem. The following script enables tracing; that's done by setting EnableTracing to 1. To disable tracing, set EnableTracing to 0:

     

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

     

    Like this:

     

    $computer = "."

     

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

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

     

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

     

    Keep in mind that there's also a client policy setting – EnableTracing – that can be used to manage tracing. If EnableTracing is set to True in a user's client policy the tracing will be enabled and the user will not be able to change that setting:

     

    It doesn't matter how the registry has been configured: the client policy setting always takes precedence over the registry setting.

     

    Likewise, if the client policy's EnableTracing setting has been set to False then tracing will be disabled and the user will not be able to enable it.

     

     

     

  • Make This a Priority

    Whether we realize it or not, we prioritize just about everything we do throughout the day. It starts when we wake up in the morning: Do I sleep a little longer or do I get up so I can get to work on time? (That’s probably the toughest one right there.) When we get to work, is it more important to start answering those emails that came in overnight, or to get a cup of coffee so that our email answers are comprehensible? And which of those emails should we answer first?

     

    This continues endlessly all day. Most of the time we don’t think too hard about those decisions and which task we need to do first – sometimes it’s pretty obvious. (Get the coffee first.) But when it comes to Windows PowerShell in Lync Server 2010, there are some decisions about priority we might want to give some serious thought to. For example, if a caller dials an unassigned number that falls within more than one unassigned number range, which announcement do we play? Or if a dialed number matches the pattern for more than one voice route, which route do we want the call to take? And of course the biggest question of all: How do we tell Lync Server how to prioritize these things?

     

    That last question is easy, so we’ll answer that first, then go over some examples so you can see how this all works. The answer to “How do we tell Lync Server how to prioritize these things?” is this: set the Priority parameter of the appropriate cmdlet. Here’s a list of cmdlets that have a Priority parameter:

     

    ·         New-CsClientVersionPolicyRule

    ·         Set-CsClientVersionPolicyRule

    ·         New-CsOutboundTranslationRule

    ·         Set-CsOutboundTranslationRule

    ·         New-CsServerApplication

    ·         Set-CsServerApplication

    ·         New-CsSipResponseCodeTranslationRule

    ·         Set-CsSipResponseCodeTranslationRule

    ·         New-CsUnassignedNumber

    ·         Set-CsUnassignedNumber

    ·         New-CsVoiceNormalizationRule

    ·         Set-CsVoiceNormalizationRule

    ·         New-CsVoiceRoute

    ·         Set-CsVoiceRoute

    ·         Set-CsDialInConferencingAccessNumber

     

    How the Priority Parameter Works

     

    The Priority parameter works the same (okay, mostly the same) no matter which cmdlet you use it with. We’ll start by explaining how to set and change the value, then we’ll talk about what it actually means to do that. We’re going to use the CsVoiceRoute cmdlets to walk you through this general discussion, then get into the specifics of the various other cmdlets.

     

    The first time you call New-CsVoiceRoute to create a new route, that route will be given the Priority 0. For example, run this command to create a new voice route:

     

    New-CsVoiceRoute -Identity Route1 -PstnUsages @{add="Local"} -PstnGatewayList @{add="PstnGateway:redmondpool.litwareinc.com"} -NumberPattern '^(\+1[0-9]\d+)$'

     

    Here’s the new voice route object created by that command:

     

    Identity: Route1

    Priority: 0

    Description:

    NumberPattern: ^(\+1[0-9]\d+)$

    PstnUsages: {Local}

    PstnGatewayList: {PstnGateway:redmondpool.litwareinc.com}

    Name: Route1

    SuppressCalledId:

    AlternateCallerId:

     

    In this example we didn’t use the Priority parameter to explicitly set the priority. Since there were no other voice routes defined, the Priority was automatically set to 0.

     

    Note. Your system may already have a default voice route in place. If that’s the case, the new route you just created will have a Priority value of 1. We’ll explain that next.

     

    Now let’s create another route:

     

    New-CsVoiceRoute -Identity Route2 -PstnUsages @{add="Long Distance"} -PstnGatewayList @{add="PstnGateway:redmondpool.litwareinc.com"} -NumberPattern '^(\+1[0-9]{7})$'

     

    Here’s the object created by this command:

     

    Identity: Route2

    Priority: 1

    Description:

    NumberPattern: ^(\+1[0-9]{7})$

    PstnUsages: {Long Distance}

    PstnGatewayList: {PstnGateway:redmondpool.litwareinc.com}

    Name: Route2

    SuppressCalledId:

    AlternateCallerId:

     

    Notice the Priority value of this route. The first route created received a default Priority value of 0; the second route received a default Priority value of 1.

     

    That seems pretty simple, right? But what happens if we create another voice route, but this time we set the Priority to 0? Let’s try it:

     

    New-CsVoiceRoute -Identity Route3 -PstnUsages @{add="Local","Long Distance"} -PstnGatewayList @{add="PstnGateway:redmondpool.litwareinc.com"} -NumberPattern '^011(\d{7}\d+)$' –Priority 0

     

    Here’s what Route3 looks like. (We’ll show only the Identity and Priority to keep things a little less cluttered. The rest isn’t important right now anyway. It will be in a few minutes, but it isn’t right now.)

     

    Identity: Route3

    Priority: 0

     

    Notice that the Priority is set to 0, just like we specified in the command. But remember, Route1 already had a Priority of 0. Well, guess what? It doesn’t anymore. Here’s what we see when we call Get-CsVoiceRoute:

     

    Identity: Route3

    Priority: 0

     

    Identity: Route1

    Priority: 1

     

    Identity: Route2

    Priority: 2

     

    Take a look at the Priority values of Route1 and Route2. Here’s what the Priority values looked like before and after we created Route3:

     

    Identity

    Priority Before

    Priority After

    Route1

    0

    1

    Route2

    1

    2

    Route3

     

    0

     

     

    When we set Route3 to have a Priority of 0, the Priority values of Route1 and Route2 were automatically incremented to move them down in the priority list.

     

    Don’t you wish it was that easy to set the priorities of your son’s baseball game, your daughter’s soccer game, and your dentist appointment, which all happen to be at the same time?

     

    Let’s look at another example. This time we’re going to try changing the priority on an existing voice route. Suppose we want to make sure that Route3 always has the lowest priority (keeping in mind that the higher the Priority value, the lower the priority). We’re going to do this by setting the Priority value of Route3 to 9:

     

    Set-CsVoiceRoute -Identity Route3 -Priority 9

     

    Again, here are the Priority values before and then after running that command:

     

    Identity

    Priority Before

    Priority After

    Route1

    1

    0

    Route2

    2

    1

    Route3

    0

    2

     

     

    There are a couple of things to notice here. The first is that, because we moved Route3 down in the priority list, Route1 and Route2 automatically moved up: Route1 now has a value of 0 and Route2 a value of 1. The second is that, even though we set the Priority of Route3 to 9, the value is now 2. Setting the Priority to a value beyond the end of the ordered list simply sets it to the last number in the list. That means you can’t have voice routes with priorities of 0, 1, and 9. If there are two routes, with priorities 0 and 1, any number set higher than that will be reset to 2, the next number available.

     

    What Does All This Really Mean?

     

    We’ve seen how priorities are set by default, and how they automatically rearrange themselves when you change the values. But what does it mean to change these values? Well, that depends on the cmdlet. Let’s go through them.

     

    Voice Route Priority

     

    We’ll start with voice routes, since we’ve already been using those as examples. We’ll also start with our same examples. To pick up where our previous examples left off, here’s what we see when we call Get-CsVoiceRoute:

     

    Identity: Route1

    Priority: 0

    Description:

    NumberPattern: ^(\+1[0-9]\d+)$

    PstnUsages: {Local}

    PstnGatewayList: {PstnGateway:redmondpool.litwareinc.com}

    Name: Route1

    SuppressCalledId:

    AlternateCallerId:

     

    Identity: Route2

    Priority: 1

    Description:

    NumberPattern: ^(\+1[0-9]{7})$

    PstnUsages: {Long Distance}

    PstnGatewayList: {PstnGateway:redmondpool.litwareinc.com}

    Name: Route2

    SuppressCalledId:

    AlternateCallerId:

     

    Identity: Route3

    Priority: 2

    Description:

    NumberPattern: ^011(\d{7}\d+)$

    PstnUsages: {Local, Long Distance}

    PstnGatewayList: {PstnGateway:redmondpool.litwareinc.com}

    Name: Route3

    SuppressCalledId:

    AlternateCallerId:

     

    Take a look at the values for the NumberPattern properties. In case you’re not familiar with regular expressions, we’ll explain the values for you.

     

    For Route1, the NumberPattern value of ^(\+1[0-9]\d+)$ will match phone numbers that begin with +1, followed by any number of digits, each digit containing a value of 0 through 9.

     

    For Route2, the NumberPattern value of ^(\+1[0-9]{7})$ will match phone numbers beginning with +1, followed by exactly seven digits, each digit containing a value of 0 through 9.

     

    For Route3, the NumberPattern ^011(\d{7}\d+)$ will match phone numbers that begin with 011, followed by at least seven digits, followed by one or more additional digits.

     

    So, which route will the call take if the number is +15551212?

     

    Let’s see, it starts with +1, so that rules out Route3, but Route1 and Route2 are still possible. For Route1, the +1 must be followed by any number of digits, each digit being 0 through 9. We have a match.

     

    But wait a minute, look at Route2. Our number begins with +1, is then followed by exactly seven digits (5551212), each digit being 0 through 9.

     

    Uh oh, looks like we have two matches. We’ve just broken our voice routing. Or at least we would have if it weren’t for the Priority value. Currently Route1 has the higher priority (the lower Priority value), with a Priority of 0. So this call will be routed through Route1.

     

    If you look closely though, you might have noticed that with this set of rules and priorities, no rule will ever get past Route1 to use Route2: the Route1 rule encompasses any number that will match Route2. However, the opposite isn’t true: rules matching Route1 won’t always match Route2. For example, the number +12065551212 will match Route1 (+1 followed by any number of digits) but not Route2 (+1 followed by exactly seven digits).

     

    So how do we make sure that a number such as +15551212 will be routed through Route2? We simply reset the priority of Route2 to make sure it comes before Route1:

     

    Set-CsVoiceRoute -Identity Route2 -Priority 0

     

    As we’ve seen, setting the Priority of Route2 to 0 will automatically move Route1 down to Priority 1. (Route3 will stay put at Priority 2; where else would it go?)

     

    Voice Normalization Rule Priority

     

    Priorities for voice normalization rules work pretty much the same as for voice routes: the priority determines which normalization rule will be applied if the Pattern value of more than one rule matches the given phone number.

     

    The main difference you’ll notice with voice normalization rules is that there can be more than one rule with a given priority. Take a look at some sample output from a call to Get-CsVoiceNormalizationRule:

     

    Identity            : Global/Prefix All

    Priority            : 0

    Description         :

    Pattern             : ^(\d{11})$

    Translation         : +$1

    Name                : Prefix All

    IsInternalExtension : False

     

    Identity            : Site:Redmond/Redmond Rule 1

    Priority            : 0

    Description         :

    Pattern             : ^(\d{11}\d+)$

    Translation         : +$1

    Name                : Redmond Rule 1

    IsInternalExtension : False

     

    Identity            : Site:Redmond/Redmond Rule 2

    Priority            : 1

    Description         :

    Pattern             : ^(\d{11})$

    Translation         : +$1

    Name                : Redmond Rule 2

    IsInternalExtension : False

     

    As you can see, two of the Priority values for these rules are set to 0. The reason for this is that all voice normalization rules are associated with dial plans that have the same scope as the rule. The scope of the rule is the first part of the Identity value (the part before the /). So the first rule in the list is associated with the Global dial plan, while the next two rules are associated with the Site:Redmond dial plan. The priorities define the priority of the rule within the given dial plan. That means that within the Global scope you have one set of priorities (in this case there’s only one rule, with, of course, a Priority value of 0), and for the Site:Redmond scope you have another set of priorities (0 for the rule named Redmond Rule 1 and 1 for the rule named Redmond Rule 2).

     

    Everything else works the same as we described for voice routes. If you set the Priority of the rule with the Identity Site:Redmond/Redmond Rule 1 to 1, then Site:Redmond/Redmond Rule 2 will be promoted to Priority 0 and Site:Redmond/Redmond Rule 1 will have the Priority of 1 that we gave it. Here’s the command that does that:

     

    Set-CsVoiceNormalizationRule -Identity "Site:Redmond/Redmond Rule 1" -Priority 1

     

    Outbound Translation Rule Priority

     

    In terms of priority, outbound translation rules work exactly the same as voice normalization rules. While voice normalization rules are tied to a dial plan, outbound translation rules are tied to a trunk configuration. As with voice normalization rules, there can be multiple outbound translation rules with a Priority value of 0, but only one within each scope. Here’s some sample rules that come back when we call Get-CsOutboundTranslationRule:

     

    Identity    : Global/Rule1

    Priority    : 0

    Description :

    Pattern     : ^\+(\d*)$

    Translation : $1

    Name        : Rule1

     

    Identity    : Global/Rule2

    Priority    : 1

    Description :

    Pattern     : ^\+1(\d*)$

    Translation : $1

    Name        : Rule2

     

    Identity    : Site:Redmond/SiteRule1

    Priority    : 0

    Description :

    Pattern     : ^\+1206(\d{7})$

    Translation : $1

    Name        : SiteRule1

     

    Once again, notice the Priority values. The first two rules are defined at the Global scope and have priorities of 0 and 1; the last rule is defined at the site scope (Site:Redmond) and has a priority of 0.

     

    Unassigned Number Priority

     

    Unassigned numbers define a range of telephone numbers that belong to an organization but are currently not assigned to a user or phone (thus the “unassigned number” term; clever, huh?). The CsUnassignedNumber cmdlets are used to either define announcement settings for a particular range of numbers or to assign that range to an Exchange UM Auto Attendant.

     

    Priorities for unassigned numbers are used in the case where number ranges overlap. For example, you may have a range +12065551000 through +12065556000 set up to route calls to these numbers to an Exchange UM Auto Attendant, which leads the user through a series of voice prompts to get them to the person or department within the company that they were hoping to reach. However, for the number range +12065552000 through +12065552500, which is within our first range, you decide you don’t want those calls to go through Auto Attendant, you simply want to play a message to the caller. (This could be the case if, for example, that range of numbers used to belong to a division that has been discontinued or moved to a new location with new phone numbers. In such a case you could play a message that informs the caller of the change and give them a new number to dial for assistance.)

     

    In this case, a call to the number +12065552300 would match both unassigned number ranges. To ensure it gets routed as expected, you’d need to set the priority of the smaller range higher (to a lower Priority value) than the larger range. Here are commands to set up those ranges:

     

    New-CsUnassignedNumber -Identity UNSet1 -NumberRangeStart "+12065551000" -NumberRangeEnd "+12065556000" -ExUmAutoAttendantPhoneNumber "+12065550001"

     

    New-CsUnassignedNumber -Identity UNSet2 -NumberRangeStart "+12065552000" -NumberRangeEnd "+12065552500" -AnnouncementService ApplicationServer:redmond.litwareinc.com -AnnouncementName "Moved Announcement"  -Priority 0

     

    Client Version Policy Rule Priority

     

    Client version policy rules are used to determine which client applications are allowed to log on to Lync Server 2010. When a client attempts to connect, the rules will be checked to see whether that client is allowed access, denied access, or some action must be taken to gain access. The Priority value of a client version policy rules simply determines in what order the rules will be checked. Once a client version is found in the set of rules that matches the application requesting access, that rule is applied and no other rules are checked.

     

    What this means is that it will be most efficient to assign the rule you expect to be used most to the highest priority (Priority 0).

     

    Like voice normalization rules and outbound translation rules, client version policy rules apply the priority according to scope. So a rule at the global scope will have Priority 0, and a rule at the site scope will also have Priority 0.

     

    SIP Response Code Translation Rule Priority

     

    A SIP response code translation rule maps a SIP response code with a value anywhere from 400 through 699 to a value used by Lync Server 2010. Here’s an example of creating a new SIP response code translation rule:

     

    New-CsSipResponseCodeTranslationRule -Identity "global/Rule404" -ReceivedResponseCode 434 -TranslatedResponseCode 404

     

    This command sets up a global rule (named Rule404) that will translate the received code 434 to the SIP response code 404. Now take a look at this next command:

     

    New-CsSipResponseCodeTranslationRule -Identity "global/Rule41028" -ReceivedResponseCode 434 -TranslatedResponseCode 410 -ReceivedISUPCauseCode 28 -Priority 0

     

    The rule created by this command is also created at the global scope (this time with the name Rule41028), and translates the received code 434. However, in this command that code is translated to the SIP response code 410. In addition, we’ve added two parameters: ReceivedISUPCauseCode and Priority. By setting the ReceivedISUPCauseCode value, we’ve set up this rule to apply to received response codes of 434 that also must have an ISDN User Part (ISUP) cause code of 28 present in the SIP response message. A Priority value of 0 ensures that this rule is checked before Rule404.

     

    Server Application Priority

     

    Server applications are applications that are hosted on Lync Server 2010. These applications are hosted by Lync Server services. The priority for a server application determines in which order, on a given service, applications start. In other words: 1) there can be multiple applications at the same priority, but each must be on a different service and 2) the priority determines in what order applications are started on each service.

     

    Make sense? Let’s create a couple of server applications just to make sure:

     

    New-CsServerApplication -Identity "EdgeServer:atl-edge-001.litwareinc.com/EdgeMonitor" -Uri http://www.litwareinc.com/edgemonitor -Critical $False

     

    New-CsServerApplication -Identity "EdgeServer:atl-edge-001.litwareinc.com/EdgeApp" -Uri http://www.litwareinc.com/edgeapp -Critical $False -Priority 0

     

    These commands create two new server applications. They both reside on the edge server (EdgeServer:atl-edge-001.litwareinc.com). One is named EdgeMonitor, the other EdgeApp. In the second command, we added a Priority value of 0 to ensure that the EdgeApp application starts before any other application on that edge server.

     

    Dial-in Conferencing Access Number Priority

     

    The priority for dial-in conferencing access number works a little differently than the other Priority values we’ve seen so far. For one thing, you can’t set the priority when you create a new dial-in conferencing access number, the priority will be assigned automatically. Once you’ve created the dial-in conferencing access number you can modify the priority by calling Set-CsDialInConferencingAccessNumber. Here’s an example:

     

    New-CsDialInConferencingAccessNumber -PrimaryUri "sip:RedmondDialIn@litwareinc.com" -DisplayNumber "1-800-555-1234" -LineUri "tel:+18005551234" -Pool atl-cs-001.litwareinc.com -PrimaryLanguage "en-US" -Regions "Redmond"

     

    Set-CsDialInConferencingAccessNumber -Identity ""sip:RedmondDialIn@litwareinc.com"" -Priority 0 -ReorderedRegion Redmond

     

    The first line calls the New-CsDialInConferencingAccessNumber cmdlet to create a new dial-in conferencing access number with the SIP address sip:RedmondDialIn@litwareinc.com. The second line then calls Set-CsDialInConferencingAccessNumber, passing the Identity (the SIP address) of the number we just created, to modify the Priority value of that number.

     

    Another thing that’s a different with priorities for dial-in conferencing access numbers is the fact that the Priority parameter can’t be set by itself; in other words, it can’t be the only parameter being modified in the Set statement. If you set the Priority value, you must also set the ReorderedRegion parameter (as we did in our example). The Priority for a dial-in conferencing access number determines the order in which it’s displayed in the conference invitation. Those numbers are displayed according to region, which is why we also need to specify the ReorderedRegion parameter: we need to specify in which region we’re reordering the number.

     

    That’s All for Now

     

    That’s all there is to priorities in Lync Server 2010 PowerShell cmdlets. It really isn’t complicated, you just need to know what it is you’re prioritizing and the rest is easy.

     

    Now it’s off to lunch. Or should we answer those emails from our manager first? Or maybe start on that next article…

     

    Lunch it is.

     

  • Return the Effective Policy Assignments for a User

    You say you want to know which Microsoft Lync Server 2010 policies have been assigned to the user Ken Myer? Well, that’s easy enough; all you have to do is run the following command:

     
    Get-CsUser "Ken Myer"

     
    In return, Windows PowerShell should show you information similar to this:

     
    VoicePolicy                       : RedmondVoicePolicy
    ConferencingPolicy                :
    PresencePolicy                    :
    DialPlan                          :
    LocationPolicy                    :
    ClientPolicy                      : RedmondClientPolicy
    ClientVersionPolicy               :
    ArchivingPolicy                   :
    PinPolicy                         : RedmondPinPolicy
    ExternalAccessPolicy              :
    HostedVoiceMail                   :
    HostedVoicemailPolicy             :

     
    As you can see, Ken has been assigned three different per-user policies: RedmondVoicePolicy; RedmondClientPolicy; and RedmondPinPolicy. Any other questions?

     
    Oh, right: what about all those other policies? Take conferencing policy, for example. There’s nothing listed next to conferencing policy; does that mean Ken is under no restrictions whatsoever when it comes to conferencing?

     
    In a word: no. With Lync Server 2010 you are always under the jurisdiction of a policy of some kind; if you haven’t been assigned a per-user conferencing policy that means that you’re either under the sway of a conferencing policy configured for your site or, if there is no such site policy, the global conferencing policy. No user account ever goes unmanaged.

     
    So then why doesn’t anything show up next to ConferencingPolicy? Well, as it turns out, Get-CsUser only shows you per-user policies; that’s because that information is stored in Active Directory and is readily available. What the cmdlet doesn’t do is try to resolve the so-called “effective policy;” in other words, it doesn’t try to determine whether Ken’s conferencing behaviors are managed by a site policy or by a global policy. All we know is that those behaviors aren’t managed by a per-user policy.

     
    Which simply means this: if you want to determine the effective policy assignments for a user, well, you’re going to have to do that yourself. In the case of a conferencing policy, that means determining the site where Ken Myer’s user account is homed and checking to see if there’s a conferencing policy assigned to that site. If there is, you must then report back the Identity of the site policy; if not, you need to report back the Identity of the global policy. And then you have to repeat this same process for voice policies, dial plans, client version  policies, archiving policies, etc., etc.

     
    You know, that does sound like an awful lot of work, doesn’t it? Which is why you might want to run this script instead:

     
    $y = @()
     

    $user = Get-CsUser $args[0]
    $site = (Get-CsSite | Where-Object {$_.Pools -contains $user.RegistrarPool}).Identity
     

    if ($user.ClientPolicy -eq $Null)
        {
            $x = Get-CsClientPolicy  
     

            foreach ($i in $x)
                {$y += ($i.Identity)}         

                    if ($y -contains "site:$site")
                        {"Client Policy            :  site:$site"}
                    else
                       {"Client Policy            :  Global policy"}  
        }
    else
       {"Client Policy            :  " + $user.ClientPolicy}

     
    $y = @()
    if ($user.LocationPolicy -eq $Null)

        {
            $x = Get-CsLocationPolicy  
     

            foreach ($i in $x)
                {$y += ($i.Identity)}
             

                    if ($y -contains "site:$site")
                        {"Location Policy          :  site:$site"}
                    else
                       {"Location Policy          :  Global policy"}   
        }
    else
       {"Location Policy          :  " + $user.LocationPolicy}

     
    $y = @()
    if ($user.VoicePolicy -eq $Null)
        {
            $x = Get-CsVoicePolicy  
     

            foreach ($i in $x)
                {$y += ($i.Identity)}
             

                    if ($y -contains "site:$site")
                        {"Voice Policy             :  site:$site"}
                    else
                       {"Voice Policy             :  Global policy"}   
        }
    else
       {"Voice Policy             :  " + $user.VoicePolicy}

     
    $y = @()
    if ($user.ConferencingPolicy -eq $Null)
        {
            $x = Get-CsConferencingPolicy  
     

            foreach ($i in $x)
                {$y += ($i.Identity)}
              
                    if ($y -contains "site:$site")
                        {"Conferencing Policy      :  site:$site"}
                    else
                       {"Conferencing Policy      :  Global policy"}   
        }
    else
       {"Conferencing Policy      :  " + $user.ConferencingPolicy}

     
    $y = @()
    if ($user.PresencePolicy -eq $Null)
        {
            $x = Get-CsPresencePolicy  
     

            foreach ($i in $x)
                {$y += ($i.Identity)}
             

                    if ($y -contains "site:$site")
                        {"Presence Policy          :  site:$site"}
                    else
                       {"Presence Policy          :  Global policy"}
        }
    else
       {"Presence Policy          :  " + $user.PresencePolicy}
     

     
    $y = @()
    if ($user.ArchivingPolicy -eq $Null)
        {
            $x = Get-CsArchivingPolicy  
     

            foreach ($i in $x)
                {$y += ($i.Identity)}
             

                    if ($y -contains "site:$site")
                        {"Archiving Policy         :  site:$site"}
                    else
                       {"Archiving Policy         :  Global policy"}   
        }
    else
       {"Archiving Policy         :  " + $user.ArchivingPolicy}

     
    $y = @()
    if ($user.PinPolicy -eq $Null)
        {
            $x = Get-CsPinPolicy  
     

            foreach ($i in $x)
                {$y += ($i.Identity)}
             

                    if ($y -contains "site:$site")
                        {"Pin Policy               :  site:$site"}
                    else
                       {"Pin Policy               :  Global policy"}   
        }
    else
       {"Pin Policy               :  " + $user.PinPolicy}

     
    $y = @()
    if ($user.ExternalAccessPolicy -eq $Null)
        {
            $x = Get-CsExternalAccessPolicy  
     

            foreach ($i in $x)
                {$y += ($i.Identity)}
             

                    if ($y -contains "site:$site")
                        {"External Access Policy   :  site:$site"}
                    else
                       {"External Access Policy   :  Global policy"}   
        }
    else

       {"External Access Policy   :  " + $user.ExternalAccessPolicy}

     
    $y = @()
    if ($user.HostedVoiceMailPolicy -eq $Null)
        {
            $x = Get-CsHostedVoiceMailPolicy  
     

            foreach ($i in $x)
                {$y += ($i.Identity)}
             

                    if ($y -contains "site:$site")
                        {"Hosted Voice Mail Policy :  site:$site"}
                    else
                       {"Hosted Voice Mail Policy :  Global policy"}   

         }
    else
       {"Hosted Voice Mail Policy :  " + $user.HostedVoiceMailPolicy}

     
    $y = @()
    if ($user.ClientVersionPolicy -eq $Null)
        {
            $x = Get-CsClientVersionPolicy  
     

            foreach ($i in $x)
                {$y += ($i.Identity)}
                    if ($y -contains "Service:Registrar:" + $user.RegistrarPool)

                       {"Client Version Policy    :  Service:Registrar:" + $user.RegistrarPool}
                    elseif ($y -contains "site:$site")
                        {"Client Version Policy    :  site:$site"}
                    else
                       {"Client Version Policy    :  Global policy"}  
        }
    else
       {"Client Version Policy    :  " + $user.ClientVersionPolicy}

     
    $y = @()
    if ($user.DialPlan -eq $Null)
        {
            $x = Get-CsDialPlan  
     

            foreach ($i in $x)
                {$y += ($i.Identity)}
                    if ($y -contains "Service:Registrar:" + $user.RegistrarPool)
                       {"Dial Plan                :  Service:Registrar:" + $user.RegistrarPool}
                    elseif ($y -contains "site:$site")
                        {"Dial Plan                :  site:$site"}
                    else
                       {"Dial Plan                :  Global dial plan"}   
        }
    else
       {"Dial Plan                :  " + $user.DialPlan}

     
    We aren’t going to bother trying to explain how this script works; suffice to see if methodically takes each policy type, checks to see if the user has been assigned a per-user policy and, if not, checks to see if that user is under the jurisdiction of a site policy or the global policy. (And, in the case of dial plans and client version policies, whether the user is managed by a service-scoped policy instead.) For now, all you need to know is that the script just needs the Identity of the user. For example, if you’ve saved this script as C:\Scripts\Get-EffectivePolicy.ps1 you simply need to run a command like this one:

     
    C:\Scripts\Get-EffectivePolicy.ps1 "Ken Myer"

     
    Do that, and PowerShell will reward you with output similar to this:

     
    Client Policy            :  RedmondClientPolicy
    Location Policy          :  site:Redmond
    Voice Policy             :  RedmondVoicePolicy
    Conferencing Policy      :  Global policy
    Presence Policy          :  Global policy
    Archiving Policy         :  site:co1
    Pin Policy               :  RedmondPinPolicy
    External Access Policy   :  site:Redmond
    Hosted Voice Mail Policy :  Global policy
    Client Version Policy    :  Service:Registrar:red-cs-001.vdomain.com
    Dial Plan                :  site:co1

     
    Pretty cool, huh?

  • When is a Boolean Not a Boolean?

    If you've ever read Alice in Wonderland, you might recall the famous riddle posed by the Mad Hatter: "Why is a raven like a writing desk?" And that -- hmmm, that is an interesting question, isn't it? Why is a raven like a writing desk?

     

    Of course, one reason why the riddle has become so famous is that in the book, the Mad Hatter never bothers to answer the riddle. Many years later Carroll himself explained why the Mad Hatter never bothered the answer the riddle: there was no answer to the riddle. According to Carroll "Enquiries have been so often addressed to me, as to whether any answer to the Hatter's Riddle can be imagined, that I may as well put on record here what seems to me to be a fairly appropriate answer, viz: 'Because it can produce a few notes, tho they are very flat; and it is never put with the wrong end in front!' This, however, is merely an afterthought; the Riddle, as originally invented, had no answer at all."

     

    Well, here at the Lync Server PowerShell blog we like to consider ourselves the Lewis Carroll of the Microsoft Lync Server 2010 world. (Or at least the Windows PowerShell part of that world.) And that means that if Lewis Carroll can pose a riddle, well, by golly, we can pose a riddle, too. And here goes: When is a Boolean value not a Boolean value?

     

    OK, maybe that's not quite as catchy as "Why is a raven like a writing desk?" On top of that, it's nowhere near as mysterious and challenging. After all, our riddle seems to have a pretty straightforward answer: there is no answer, because a Boolean value is always a Boolean value. In fact, if you do a quick search for the word Boolean you'll invariably find definitions similar to this:

     

    "In computer science, the Boolean or logical data type is the most primitive data type, having one of two values (true or false), intended to represent the truth values of logic and Boolean algebra."

     

    There you have it: a Boolean value is a data type that has two possible values: a Boolean value is either True or a Boolean value is False. As the immortal Ken Kesey used to say, you're either on the bus or you're off the bus.

     

    Note. We should probably point out that Ken Kesey isn't truly immortal; he died back in the year 2001. Before he died, however, Kesey did write the novel One Flew Over the Cuckoo's Nest. The protagonist of that novel was a red-headed Irishman named Randle Patrick McMurphy. When the novel was made into an Academy Award-winning movie, McMurphy was played by, uh, Jack Nicholson.

     

    Incidentally, as good as One Flew Over the Cuckoo's Nest was, the novel Sometimes a Great Notion was even better. Don't listen to what anyone else has to say, take it from us: Sometimes a Great Notion is better. Hey, if you can't trust the Lewis Carroll of the Microsoft Lync Server 2010 world who can you trust?

     

    In other words, Boolean values are pretty straightforward: they're either True or they're False. So then when is a Boolean value not a Boolean value? Here's the answer to the riddle: When that Boolean value represents the Enabled attribute that gets added to Active Directory when you install Microsoft Lync Server.

     

    Note. Or when it can produce a few notes, tho they are very flat; and it is never put with the wrong end in front! We'll accept either answer.

     

    Let's see if we can explain what we mean when we say that the Boolean attribute Enabled is not a Boolean attribute. Up till now, we've been under the impression that the Enabled attribute, like all good Boolean attributes, can be either True or False: we've been assuming that the value is True if a user has been enabled for Lync Server and False if a user has not been enabled for Lync Server. And, the fact is, for most of the things you do with Lync Server PowerShell, you can act as though that really is the case. However, there's an exception to that general rule, an exception you should know about it.

     

    As it turns out, the Enabled attribute actually has three possible values:

     

    ·         If the user has been enabled for Microsoft Lync Server 2010 then his or her Enabled attribute is equal to True (or, in Windows PowerShell-speak, $True).

    ·         If the user has had their Lync Server account temporarily disabled, then his or her Enabled attribute is equal to False ($False). What do we mean by "temporarily disabled?" Well, let's suppose we've enabled Ken Myer for Lync Server; that means his Enabled attribute is equal to True. We now run this command to temporarily disabled Ken's Lync Server account:

    Set-CsUser -Identity "Ken Myer" -Enabled $False

    At that point, Ken's Enabled attribute will be set to False. To restore his account, and set the Enabled attribute back to True, we need to run this command:

    Set-CsUser  -Identity "Ken Myer" -Enabled $True

    ·         If the user is not enabled for Lync Server then his Enabled attribute is set to a null value ($Null). That's an important distinction. A null value means "no value at all." A False value is an actual value; it's just False rather than True. But because it is a real value, it's not a null value. And there's a difference.

     

    That should clear everything up, eh?

     

    Let's take a look at three users and then show you why this distinction is important:

     

    User

    Account Status

    Enabled Attribute

    Ken Myer

    Enabled for Lync Server

    $True

    Pilar Ackerman

    Lync Server account temporarily disabled

    $False

    April Reagan

    Not enabled for Lync Server

    $Null

     

    Suppose we run the following command, which returns the value of the DisplayName and the Enabled attributes for all our Active Directory users:

     

    Get-CsAdUser | Select-Object DisplayName, Enabled

     

    In return we get back data that looks like this:

     

    DisplayName     Enabled

    -----------     -------

    Ken Myer        True

    Pilar Ackerman  False

    April Reagan

     

    OK, so far so good; all our users are there. Now we want to get back data just for users who have been enabled for Lync Server. That means we're going to run this command:

     

    Get-CsAdUser –Filter {Enabled –eq $True} | Select-Object DisplayName, Enabled

     

    In other words, this time we only want to get back information for users who have an Enabled attribute equal to $True: {Enabled –eq $True}. And if we run that command we should get back this:

     

    DisplayName     Enabled

    -----------     -------

    Ken Myer        True

     

    The only information we get back this time is information about Ken Myer. Which is exactly what we should have gotten back. After all, Ken is the only user we have who's been enabled for Lync Server.

     

    Now here's the part that could cause some confusion. Suppose we now want information about the users who have not been enabled for Lync Server. We know that the Enabled attribute is a Boolean value, and that Boolean values can either be True or False. Just a few seconds ago, to find all the users who have been enabled for Lync Server we used this command:

     

    Get-CsAdUser –Filter {Enabled –eq $True} | Select-Object DisplayName, Enabled

     

    Logically enough, we might then expect that the following command, which looks for user accounts where the Enabled attribute is equal to $False, will return all the users who have not been enabled for Lync Server:

     

    Get-CsAdUser –Filter {Enabled –eq $False} | Select-Object DisplayName, Enabled

     

    But guess what? It doesn't do that. Instead, this is what we get back:

     

    DisplayName     Enabled

    -----------     -------

    Pilar Ackerman  False

     

    In this case, we get back just what we asked for: all the users where the Enabled attribute is False. What we didn't get back, however, is what we really wanted to get back: all the users who are not enabled for Lync Server. In our case, that would be April Reagan, the only user we have who hasn't been enabled for Lync Server. (Remember, Pilar Ackerman is enabled for Lync Server, but we've temporarily shut down her account.) We don't get back April Reagan because her Enabled attribute isn’t set to $False; instead, it's not set to anything at all (that is, it's equal to a null value). The returned data is a little deceiving, because we looked for users with an Enabled attribute equal to $False and we got back a collection of users who all have an Enabled attribute equal to $False. But we never should have looked for those users in the first place.

     

    Instead, if you want to return all the Active Directory users who are not enabled for Lync Server, you need to use this command, which searches for accounts where the Enabled attribute is null:

     

    Get-CsAdUser –Filter {Enabled –eq $Null} | Select-Object DisplayName, Enabled

     

    That command returns April Reagan, and anyone else who hasn't been enabled for Lync Server:

     

    DisplayName     Enabled

    -----------     -------

    April Reagan

     

    See how that works and, equally important, why that works? If you've never been enabled for Lync Server, then your Enabled attribute is equal to a null value. That's also true if you were at one point enabled for Lync Server, but then had your account disabled by using the Disable-CsUser cmdlet:

     

    Disable-CsUser "Pilar Ackerman"

     

    When you use Disable-CsUser to disable an account, all the user account attributes related to Lync Server are wiped clean, including the Enabled attribute. That means our user accounts now look like this:

     

    User

    Account Status

    Enabled Attribute

    Ken Myer

    Enabled for Lync Server

    $True

    Pilar Ackerman

    No longer enabled for Lync Server

    $Null

    April Reagan

    Not enabled for Lync Server

    $Null

     

    So then what happens if we run this command against the preceding set of user accounts:

     

    Get-CsAdUser –Filter {Enabled –eq $Null} | Select-Object DisplayName, Enabled

     

    You got it. This time we get back our two users who are not enabled for Lync Server:

     

    DisplayName     Enabled

    -----------     -------

    Pilar Ackerman 

    April Reagan

     

    The moral of the story? This:

     

    ·         If you are enabled for Lync Server then the Enabled attribute is equal to $True.

    ·         If your Lync Server account is temporarily disabled then the Enabled attribute is equal to $False.

    ·         If your account is not enabled for Lync Server at all, then the Enabled attribute is equal to $Null.

     

    Note. In case you're wondering, no, you can't actually set the Enabled attribute to null. This command will fail and give you the error message "Cannot bind argument to parameter 'Enabled' because it is null.":

     

    Set-CsUser -Identity "Ken Myer" –Enabled $Null

     

    So with Set-CsUser the Enabled property is a true Boolean: it can only be True or False. But with Get-CsAdUser, the Enabled property can be True or False or it can be null.

     

    And people thought Wonderland was a crazy place.

     

    Of course, in some cases you might be interested in returning information for users who can't currently log on to Lync Server; that includes users who are not enabled for Lync Server (Enabled attribute equals $Null) and users who have had their accounts temporarily disabled (Enabled attribute equals $False). How can you find both sets of users? That's easy; just look for users who do not have an Enabled attribute equal to $True:

     

    Get-CsAdUser –Filter {Enabled –ne $True} | Select-Object DisplayName, Enabled

     

    Finally, and just to complicate matters a little, we should add one last note. Suppose you have a user who has a SIP address but is not enabled for Lync Server. (Let's assume that April Reagan is a user like that.) Let's run this command:

     

    Get-CsAdUser –Identity "April Reagan" | Select-Object DisplayName, Enabled

     

    That's going to return data that looks like this:

     

    DisplayName     Enabled

    -----------     -------

    April Reagan

     

    OK, no problem there. Now let's try returning information about April Reagan by using the Get-CsUser cmdlet:

     

    Get-CsUser –Identity "April Reagan" | Select-Object DisplayName, Enabled

     

    Here's what we get back when we run that command:

     

    DisplayName     Enabled

    -----------     -------

    April Reagan    False

     

    Oops. In this case, Get-CsUser tells us that April's Enabled attribute is equal to False. Unfortunately, that's not really true; her Enabled attribute actually is equal to a null value. These results lead us to believe that April's account is just temporarily disabled, and that's not really the case.

     

    Which simply means that Get-CsUser can sometimes return misleading data. Therefore, the other moral of the story is to use Get-CsAdUser if you really want to know the status of all your users.

     

    That's all we have for now; we're just going to sit back and wait for someone to make a movie version of this article. We can hardly wait.

     

  • Lync Server Admin Guide: Managing External Connectivity

     

     

    Enable or Disable External User Access for Your Organization

     

    Enable or Disable Remote User Access for Your Organization

     

    • To enable or disable remote user access for your organization

     

    To enable remote user access, use the Set-CsAccessEdgeConfiguration cmdlet to set the AllowOutsideUsers property to True:

     

    Set-CsAccessEdgeConfiguration –Identity global –AllowOutsideUsers $True

     

    To disable remote user access, set the AllowOutsideUsers property to False:

     

    Set-CsAccessEdgeConfiguration –Identity global –AllowOutsideUsers $False

     

     

    Enable or Disable Federation for Your Organization

     

    • To enable or disable federated user access for your organization

     

    To enable federated user access, use the Set-CsAccessEdgeConfiguration cmdlet to set the AllowFederatedUsers property to True:

     

    Set-CsAccessEdgeConfiguration –Identity global –AllowFederatedUsers $True

     

    To disable federated user access, set the AllowFederatedUsers property to False:

     

    Set-CsAccessEdgeConfiguration –Identity global –AllowFederatedUsers $False

     

     

    Enable or Disable Anonymous User Access for Your Organization

     

    • To enable or disable anonymous user access for your organization

     

    To enable anonymous user access, use the Set-CsAccessEdgeConfiguration cmdlet to set the AllowAnonymousUsers property to True:

     

    Set-CsAccessEdgeConfiguration –Identity global –AllowAnonymousUsers $True

     

    To disable anonymous user access, set the AllowAnonymousUsers property to False:

     

    Set-CsAccessEdgeConfiguration –Identity global –AllowAnonymousUsers $False

     

     

    For more information

     

     

     

     

     

    Manage Communications with External Users

     

    Manage Remote User Access

     

    • To configure an external access policy to support remote user access

     

    To create an external user access policy that allows access by remote users, use the New-CsExternalAccessPolicy cmdlet and set the EnableOutsideAccess property to True:

     

    New-CsExternalAccessPolicy –Identity "RedmondExternalAccess" –EnableOutsideAccess $True

     

     

    Manage Federated Partner Access

     

    Configure Policies to Control Federated User Access

     

    • To configure a policy to support access by users of federated domains

     

    To create an external user access policy that allows access by federated users, use the New-CsExternalAccessPolicy cmdlet and set the EnableFederationAccess property to True:

     

    New-CsExternalAccessPolicy –Identity "RedmondExternalAccess" –EnableFederationAccess $True

     

     

    Enable or Disable Discovery of Federation Partners

     

    • To enable or disable automatic discovery of federated domains for your organization

     

    To enable automatic discovery of federated domains, use the Set-CsAccessEdgeConfiguration cmdlet and the UseDnsSrvRouting parameter, and set EnablePartnerDiscovery to True:

     

    Set-CsAccessEdgeConfiguration -UseDnsSrvRouting -EnablePartnerDiscovery $True

     

    To disable automatic routing, use the UseDefaultRouting parameter, and set DefaultRouteFqdn to the fully qualified domain name of the Edge Server used for federation requests:

     

    Set-CsAccessEdgeConfiguration -UseDefaultRouting -DefaultRouteFqdn "atl-edge-001.litwareinc.com"

     

     

     

    Control Access by Individual Federated Domains

     

    • To add an external domain to the list of allowed domains

     

    To add a domain to the list of domains your users are allowed to communicate with, use the New-CsAllowedDomain cmdlet followed by the fully qualified domain name of the allowed domain:

     

    New-CsAllowedDomain -Identity "fabrikam.com"

     

     

    • To add an external domain to the list of blocked domains

     

    To add a domain to the list of domains your users are not allowed to communicate with, use the New-CsBlockedDomain cmdlet followed by the fully qualified domain name of the "outlawed" domain:

     

    New-CsBlockedDomain -Identity "contoso.com"

     

     

    Enable or Disable Sending an Archiving Disclaimer to Federated Partners

     

    • To enable or disable sending an archiving disclaimer to federated partners

     

    To display an archiving disclaimer to federated partners at the beginning of an instant messaging session, use the Set-CsAccessEdgeConfiguration cmdlet and set the EnableArchivingDisclaimer property to True:

     

    Set-CsAccessEdgeConfiguration –Identity global -EnableArchivingDisclaimer $True

     

    To prevent the display of the archiving disclaimer, set the EnableArchivingDisclaimer property to False:

     

    Set-CsAccessEdgeConfiguration –Identity global -EnableArchivingDisclaimer $False

     

     

    For more information

     

     

     

     

     

     

    Manage IM Provider Support

     

    Configure Policies to Control Access by Users of IM Service Providers

     

    • To configure an external access policy to support public user access

     

    To enable users to communicate with a public provider such as MSN or AOL, use the Set-CsExternalAccessPolicy cmdlet to set the EnablePublicCloudAccess property of the appropriate external access policy to True:

     

    New-CsExternalAccessPolicy -Identity site:Redmond -EnablePublicCloudAccess $True

     

     

    Specify Supported IM Service Providers

     

    • To configure support for an IM service provider

     

    To enable users to communicate with a new public provider, use the New-CsPublicProvider cmdlet to add the new organization to the list of approved providers:

     

    New-CsPublicProvider -Identity "Fabrikam" -ProxyFqdn "proxyserver.fabrikam.com" -Enabled $True -VerificationLevel "AlwaysUnverifiable"

     

     

    Configure Conferencing Policies to Support Anonymous Users

     

    • To configure policies to allow anonymous participation in meetings

     

    To allow users to host meetings that include anonymous users, use the Set-CsConferencingPolicy cmdlet to set the AllowAnonymousParticipantsInMeetings property of the appropriate conferencing policy to True:

     

    Set-CsConferencingPolicy –Identity global -AllowAnonymousParticipantsInMeetings $True

     

     

     

    For more information

     

     

     

     

     

    Apply Policies for External User Access to Users

     

    Apply External User Access Policies to Users

     

    • To apply an external user policy to a user account

     

    To assign a per-user external access policy to a user, use the Grant-CsExternalAccessPolicy cmdlet.

     

    Grant-CsExternalAccessPolicy –Identity "Ken Myer" –PolicyName "RedmondExternalAccess"

     

    To unassign a per-user policy, use Grant-CsExternalUserAccess and set the PolicyName to a null value:

     

    Grant-CsExternalAccessPolicy –Identity "Ken Myer" –PolicyName $null

     

     

    Apply Conferencing Policies to Support Anonymous Users

     

    • To configure a user policy for anonymous participation in meetings

     

    To allow users to host meetings that include anonymous participants, use the Set-CsConferencingPolicy cmdlet and set AllowAnonymousParticipantsInMeetings to True:

     

    Set-CsConferencingPolicy –Identity site:Redmond -AllowAnonymousParticipantsInMeetings $True

     

    To prevent users from hosting meetings that include anonymous participants, set AllowAnonymousParticipantsInMeetings to False:

     

    Set-CsConferencingPolicy –Identity site:Redmond -AllowAnonymousParticipantsInMeetings $False

     

     

    Reset or Delete External User Access Policies

     

    Delete a Site or User Policy for External User Access

     

    • To delete a site or user policy for external user access

     

    To delete a per-site external access policy, use the Remove-CsExternalAccessPolicy cmdlet followed by the policy Identity:

     

    Remove-CsExternalAccessPolicy –Identity site:Redmond

     

    You can also use this command to remove all the per-site policies:

     

    Get-CsExternalAccessPolicy –Filter "site:*" | Remove-CsExternalAccessPolicy

     

    To delete a per-user policy, use the Remove-CsExternalAccessPolicy cmdlet followed by the policy Identity:

     

    Remove-CsExternalAccessPolicy –Identity "RedmondExternalAccessPolicy"

     

    This command removes all the per-user policies:

     

    Get-CsExternalAccessPolicy –Filter "tag:*" | Remove-CsExternalAccessPolicy

     

     

    Reset the Global Policy for External User Access

     

    • To reset the global policy to the default settings

     

    To reset all the properties in the global external user access policy to their default values, use the Remove-CsExternalAccessPolicy cmdlet:

     

    Remove-CsExternalAccessPolicy –Identity global

     

    Note that this command will not remove the global policy. However, all the properties in that policy will be reset to their default values.

     

     

    For more information

     

     

     

     

     

     

  • Conferencing Policy Settings: AllowUserToScheduleMeetingsWithAppSharing

     

    This article is part of the series An In-Depth Guide to Conferencing Policy Settings.

     

     

    Parameter/Property Name

    Allowed Values

    AllowUserToScheduleMeetingsWithAppSharing

     

    This is a per-organizer setting.

    ·         TRUE: Application sharing is allowed in meetings. This is the default value.

    ·         FALSE: Application sharing is not allowed in meetings.

     

    Lync Server Control Panel Equivalent Setting: Application sharing

     

     

     

    As a general rule, the conferencing policy settings aren't too difficult to understand. Some of them have odd names – like EnableDataCollaboration – but once you know what the EnableDataCollaboration setting is for, well, then it's all pretty simple and straightforward.

     

    So is the AllowUserToScheduleMeetingsWithAppSharing setting somehow different from the other policy settings? Well, no, not really: once you know what the AllowUserToScheduleMeetingsWithAppSharing setting is for it turns out to be pretty simple and straightforward, too. But this one is a wee bit more complicated than most settings, if only because it seemingly-conflicts with the EnableAppDesktopSharing setting. How do those two settings conflict, and who wins if and when they do conflict? We'll get to that in just a minute.

     

    But first things first. As you probably know, Microsoft Lync gives the chance to share things during a meeting or a peer-to-peer session: by default, you can share either you entire desktop (which essentially means sharing your entire computer) or a specific application. It's up to you.

     

    And what exactly does it mean to share your desktop or an application? It means two things. First, it means that everything you do on your computer (or in that single application) is visible to everyone else in the meeting; for example, if you're typing away in Microsoft Word, everyone will be able to see what you're typing, how you're formatting the text, etc., etc.

     

    Note. Yes, it is sort of like being on TV.

     

    Sort of.

     

    Second (and, again, by default) anyone in the meeting can ask to take control of your desktop or your shared application. Again, suppose you're sharing Microsoft Word. Anyone else in the meeting could request to take control of the application and, if you say OK, they'll be able to start typing in Word. It's your copy of Word and it's running on your computer, but someone else can sit at their desk and type away as if the application belonged to them.

     

    That's the easy part. Now here's where things get a little complicated. (But just a little.) As we noted a minute ago, there are two different conferencing policy settings that control application sharing. The EnableAppDesktopSharing setting is a per-user setting that allows a given user to share:

     

    ·         His or her desktop

    ·         A specific application

    ·         Nothing

     

    Because this is a per-user setting that means it applies to each individual user each time they join a meeting. In turn, that means that, in a given meeting, some users might be able to share applications and some users might not be able to share applications. (Everyone can always see the shared applications, regardless.) That means you could potentially have a meeting in which the meeting organizer can't share applications but everyone else in the meeting can share applications. That isn't necessarily good or bad; it's just a scenario that could crop up.

     

    And what if you'd just as soon that this scenario doesn't crop up? That's where the AllowUserToScheduleMeetingsWithAppSharing setting comes in. This setting applies to the organizer of the meeting and lets you determine whether or not anyone in a meeting will be allowed to use application sharing. If this value is set to True (and if the meeting organizer is allowed to share his or her desktop) then that organizer will see something like this when he or she clicks on the Share menu in the Conversation window:

     

     

     

    Meanwhile, everyone else in the meeting will see whatever options have been granted to them using the EnableAppDesktopSharing setting in their conferencing policy.

     

    Got that? OK. Now suppose our meeting organizer has a conferencing policy in which AllowUserToScheduleMeetingsWithAppSharing has been set to False. In that case, the meeting organizer, and everyone else in the conference, will see something like this when they click the Share menu:

     

     

    Why are the Desktop and Program options grayed-out? That's easy: because the meeting organizer is not allowed to schedule a meeting that includes application sharing. That means that the organizer can't share applications; equally important, it means that no one else in the meeting can share applications.

     

    No one.

     

    In other words, suppose we have two users and two conferencing policies. Like, say, these two:

     

    User

    Conferencing Policy

    AllowUserToScheduleMeetingsWithAppSharing

    Pilar Ackerman

    global

    False

    Ken Myer

    RedmondConferencingPolicy

    True

     

    Pilar Ackerman schedules a meeting. Will Pilar be able to share applications in that meeting? No. Will anyone be allowed to share applications in that meeting? In a word; no. Pilar's conferencing policy doesn't allow for shared applications.

     

    Now, Ken Myer schedules a meeting. Will Ken be allowed to share applications in that meeting? Maybe; it depends on the value of the EnableAppDesktopSharing setting in his conferencing policy. Will Pilar be allowed to share applications in that meeting? Maybe; it depends on the value of the EnableAppDesktopSharing setting in her conferencing policy. But as long as your EnableAppDesktopSharing setting allows you to share applications then yes, you (whoever you are) can share applications in one of Ken's meetings.

     

    Incidentally, here's some sample code for disabling application sharing in a meeting organized by anyone who uses the global conferencing policy:

     

    Set-CsConferencingPolicy –Identity global –AllowUserToScheduleMeetingsWithAppSharing $False

     

    And here's code that enables application sharing in a meeting organized by anyone who uses the global conferencing policy:

     

    Set-CsConferencingPolicy –Identity global –AllowUserToScheduleMeetingsWithAppSharing $True

     

     

     

     

  • New Articles: Working with Microsoft Lync Registry Settings

     

    When the registry was first introduced in its current form in the Microsoft Windows 95 operating system, everyone at Microsoft was told “DO NOT tell users to touch anything in the registry. Ever.” We then proceeded to tell people that in some cases the only way to change something was to modify the registry.

     

    Flash forward 16 years to 2011. The message from Microsoft today is “DO NOT tell users to touch anything in the registry. Ever.” Hey, at least we’re consistent.

     

    But, again, there are exceptions. One of those exceptions happens to be if you’re writing Windows PowerShell scripts. In the set of articles Working with Microsoft Lync Registry Settings, we show you how to change Microsoft Lync 2010 settings by using scripts to modify the registry. Keep in mind that you can also modify these settings directly from the client, but if you’d like to use a script instead, this is how you do it.

     

    http://blogs.technet.com/b/csps/archive/2011/06/15/regintroduction.aspx

     

     

  • Conferencing Policy Settings: AllowConferenceRecording

     

     

     

    This article is part of the series An In-Depth Guide to Conferencing Policy Settings.

     

     

    Parameter/Property Name

    Allowed Values

    AllowConferenceRecording

     

    This is a per-organizer setting.

    ·         TRUE: Users can record conferences.

    ·         FALSE: Users cannot record conferences. This is the default value.

     

    Lync Server Control Panel Equivalent Setting: Recording

     

     

     

    Do you know how many times the authors of the Lync Server PowerShell blog have sat in a meeting and come up with a brilliant idea, only to see that brilliant idea wither and die because no one in the meeting happened to be taking notes? Well, OK, you're right: that's never happened, at least not to us. On the other hand, it could happen to someone else, someone who might actually have a brilliant idea. Which, needless to say, is at least one reason why Microsoft included a feature in Microsoft Lync that allows attendees to record meetings and conferences.

     

    If you didn't know this feature exists, then do the following: the next time you're in a meeting, click the More Options button, the little button that looks like this:

     

    >> 

     

    In the popup menu that appears, you should see the Start Recording option:

     

     

    Click that, and you'll be able to record the meeting. (And not just the audio: you'll be able to record all the application sharing, polls, whiteboards, instant messages, and anything else that takes place during the meeting.)

     

    Actually, we need to clarify that a bit: you'll be able to record the meeting if the AllowConferenceRecording setting in your conferencing policy has been set to True. If this setting is False (the default value) then the Start Recording option will not be available when you click More Options:

     

     

     

    In other words, your ability to record a conference hinges on the settings in the appropriate conferencing policy. What do we mean when we say the "appropriate" conferencing policy? Well, AllowConferenceRecording turns out to be a per-organizer setting: that means it applies to the person who organized the meeting (and, by extension, to everyone who participates in that particular meeting). Consider two users and two conferencing policies:

     

    User

    Conferencing Policy

    AllowConferenceRecording

    Pilar Ackerman

    global

    False

    Ken Myer

    RedmondConferencingPolicy

    True

     

    Pilar Ackerman's conferencing policy does not allow conference recording; Ken Myer's conferencing policy does allow conference recording. What does that mean? That means that conference recording will not be allowed in any meeting organized by Pilar: the Start Recording option will not be available in anyone's copy of Microsoft Lync. And yes, that includes Ken Myer's copy of Lync. When it comes to a per-organizer setting like AllowConferenceRecording, the only conferencing policy that matters is the conferencing policy assigned to the meeting organizer.

     

    Of course, that also means that everyone who participates in a meeting organized by Ken Myer will be able to record the conference. That's because Ken Myer's conferencing policy allows recording, and that setting is thus applied to every single person in the meeting, including Pilar Ackerman. In other words, either everyone in the meeting can record the proceedings or no one in the meeting can record the proceedings.

     

    Note. Well, OK: every authenticated user logged on to the internal network can (or cannot) records meetings. As it turns out, there's a separate setting – AllowExternalUsersToRecordMeeting – that applies to external users.

     

    So how do you enable the conference recording feature? Funny you should ask:

     

    Set-CsConferencingPolicy –Identity global –AllowConferenceRecording $True

     

    To disable recording, just set AllowConferenceRecording to False. You know, like this:

     

    Set-CsConferencingPolicy –Identity global –AllowConferenceRecording $False

     

     

     

     

     

     

  • Haiku #78

    I now pronounce you

    Voice policy and voice route.

    Thank you, phone usage.

     

    If you glance at the sports page these days, you'll see that former baseball superstar Barry Bonds is currently on trial for allegedly lying to the government as part of a steroids investigation; another ex-baseball star, Roger Clemmons, will go on trial this summer, and Tour de France legend Lance Armstrong might not be far behind. The NFL has locked out its players and there's a 50-50 chance that the 2011 season will be cancelled. The University of Tennessee recently fired its men's basketball coach for lying to NCAA investigators, and the CEO of the Fiesta Bowl has been fired for collecting $2 million for … expenses ….

     

    But as impressive as all those news stories are, guess what the absolute biggest scandal in the sports world is. You got it: former NBA player Jalen Rose was recently quoted as saying that, when he was being recruited by colleges back in 1991, he didn't want to go to Duke University because – gasp! – he didn't like Duke University! Needless to say, Duke supporters were shocked – shocked – to hear that, 20 years ago, someone didn’t like them.

     

    Imagine that.

     

    Rose, of course, has been forced to at least partially backpedal from his statements, even though he never said that he didn't like Duke University now, just that he didn't like Duke University 20 years. Based on the fallout caused by Rose's statements, the author of today's haiku has decided to be proactive and apologize for the following statements that he made about Duke University 20 years ago:

     

    ·         In a 1990 article in the New England Journal of Medicine, he wrote that the death toll from the Black Death that swept across Europe in the 14th century was due, in part, to people saying that they would rather die from the plague than go to Duke University. He has since learned that Duke University was not even founded until 1838 (how was he supposed to know that?), and apologizes for the mistake.

    ·         In 1991, while accepting the Academy Award for Best Supporting Actor, he mentioned offhandedly that Attila the Hun, Ivan the Terrible, and Jack the Ripper all graduated from Duke University. As it turns out, he was incorrect: Jack the Ripper did not graduate from Duke University. (However, former President Richard M. Nixon did graduate from the Law School in 1937.)

    ·         In 1992, after losing the election for the President of Sierra Leone, he stated that, if Duke University offered him a basketball scholarship, he would decline that offer. That was not true. (After all, enrolling at Duke University on a basketball scholarship would probably be the fastest and most effective way for him to ruin both the academic and the athletic reputation of the school.) If Duke is still prepared to offer him a basketball scholarship he is prepared to accept.

     

    To make a long story short, the author of today's haiku regrets anything and everything he has ever said about Duke University. (For that matter, he pretty much regrets anything and everything he has ever said, period.) After visiting the Duke University Web site, he now realizes that the school has world-class faculty helping to expand the frontiers of knowledge, and that the university has a strong commitment to applying knowledge in service to society, both near its North Carolina campus and around the world.

     

    He also learned that the motto of Duke University is Eruditio et Religio, which means "Knowledge and Religion."

     

    As you might expect of any place that has Latin motto, Duke University has earned world renown for its academic program and for the achievements of its graduates; for example, William Styron won a Pulitzer Prize for the novel The Confessions of Nat Turner, and Robert Richardson won the Nobel Prize in Physics for his work in superfluidity. As impressive as all those achievements are, however, the author of today's haiku did manage to find one breakthrough discovery that did not take place at Duke University: the creation of the CsPstnUsage cmdlets (Get-CsPstnUsage and Set-CsPstnUsage).

     

    Note. Yes, it is hard to believe. But those cmdlets were actually created here at Microsoft, and not at Duke University.

     

    So what are the CsPstnUsage cmdlets and, for that matter, what the heck are PSTN usages? Wow; you would have to ask that question, wouldn't you? Well, let's see if we can explain this.

     

    Note. True story: PSTN usages used to be called phone usages. However, here at Microsoft we were concerned that no one in the real world knew what a phone usage was, so we changed the name to PSTN usage.

     

    You're welcome.

     

    A PSTN usage is really nothing more than a string value that links a voice policy and a voice route. If a voice route and a voice policy share at least one PSTN usage, then that route and that policy are linked. What happens if a voice policy doesn't share a PSTN usage with a voice route? That's easy: in that case, phone calls made by any user who has been assigned that voice policy will not be able to traverse that route.

     

    It actually kind of makes sense, although we've never fully understood why you need a separate object – a PSTN usage – to link a route and a policy as opposed to, say, simply listing the policy as a route property and vice-versa. However, we don't have anywhere near enough time today to discuss all the things that we don't fully understand, so we'll simply accept PSTN usages for what they are and get on with our lives.

     

    In Lync Server, all your PSTN usages are stored in a single global collection. How do you know what usages are available to you? That's another easy one; all you have to do is call the Get-CsPstnUsage cmdlet:

     

    Get-CsPstnUsage | Select-Object –ExpandProperty Usage

     

    That was easy. Now here's a tougher one: what do you do if you need to create a brand-new usage?

     

    Note. Why would you ever need to create a new usage? Well, here's one thought: maybe the current set of usages is shared among a number of policies and routes, and you want to create a unique usage that will link this one particular route with this one particular policy.

     

    New usages are created (or, to look at it another way, new usages are added to the global collection) by using the Set-CsPstnUsage cmdlet. For example, this command adds the usage International (remember, a usage is just a string value) to the global collection:

     

    Set-CsPstnUsage -Identity global -Usage @{Add="International"}

     

    And this command removes the usage International from the global collection:

     

    Set-CsPstnUsage -Identity global -Usage @{Remove="International"}

     

    That's really all there is to it.

     

    Note. Oh, what the heck, there's at least one more thing we can show you. Here's a way to add multiple usages using a single command:

     

    Set-CsPstnUsage -Identity global -Usage @{Add="International", "Europe", "Asia"}

     

    As you can see, all you have to do is separate the individual usages using commas. You can also remove multiple usages with a single command, using the same basic approach:

     

    Set-CsPstnUsage -Identity global -Usage @{Remove="International", "Europe", "Asia"}

     

    After a usage has been added to the global collection, that value can then be used in a voice policy and/or a voice route. We won't bother to go into a detailed explanation of voice policies and voice routes today; that's a bit beyond the scope of the daily haiku. However, this command adds the PSTN usage International to a voice policy:

     

    Set-CsVoicePolicy –Identity "RedmondVoicePolicy" -PstnUsages @{Add="International"}

     

    If it turns out that there isn't a PSTN usage named International then the preceding command will generate the following error message:

     

    Set-CsVoicePolicy : Cannot find PSTN usage "International" in the Central Management Store.

     

    Which, of course, explains why we needed to add this usage to the global collection in the first place: if you don't do that, the usage can't be added to a voice policy or voice route.

     

    Note that it's possible to create a voice policy that doesn't have any PSTN usages associated with it. We should emphasize, however, that while this is possible, it's not very practical: if there aren't any PSTN usages associated with a voice policy then users assigned that policy won't be able to make outbound telephone calls to the PSTN network.

     

    And there you have it: the truth behind PSTN usages. We hope you enjoyed today's haiku and, again, our apologies if we have ever offended anyone from Duke University.

     

    Oh, and if you happen to work with someone who graduated from Duke, could you please read that last sentence to them, slowly? Thanks!

     

    Note to graduates from Duke University. Hey, we're just kidding. Surely the people responsible for the Cameron Crazies can take a joke, right?

     

     

     

  • Haiku #18

    What big eyes you have.

    And what big … ears … you have, too.

    Cs Media.

     

    The first time the author of today's haiku ever saw Internet video, he wasn't exactly impressed. What he saw, way back then, was a postage stamp-sized window that showed what looked to be a still photo of a person, along with the caption Buffering. All of a sudden, the person in the photo would engage in a few spasmodic, insect-like movements, then resume buffering again. Watching this "video," the author's first instinct was to roll up a newspaper (back in those days people actually had newspapers) and swat

    that hideous, insect-like thing on the monitor.

     

    And then turn around and swat the person who wasted his time by calling him over and making him watch that hideous, insect-like thing on the monitor.

     

    Historical note. In 1964, Bell Telephone installed "picture phones" in the Prudential Building in Chicago, the National Geographic Society Building in Washington, and Grand Central Station in New York. People could make an appointment to use the picture phone to call someone who, needless to say, had to be in the Prudential Building in Chicago, the National Geographic Society Building in Washington, or Grand Central Station in New York. Assuming you could coordinate schedules, you could place a three-minute picture phone call from Chicago to New York for just $27. Even Bell Telephone admitted that "the attractiveness of this system is limited."

     

    Needless to say, video communication has come a long way since the debut of the PicturePhone, and from the first halting attempts to transmit video over the Internet. For example, with Microsoft Lync Server 2010, administrators have the option of allowing Microsoft Lync users to conduct video conversations using high definition video.

     

    Oh, you noticed that, did you? Well, you're right, we did say that administrators have the option of allowing users to conduct video conversations using high definition video.  HD video is obviously nicer (bigger, brighter, clearer – you name it) than other forms of video; at the same time, however, HD video requires a huge amount of network bandwidth.  If bandwidth is a concern, then you might not want people conversing in high definition video.

     

    Note. In case you're wondering, no one ever wants to converse with the author in high definition video. But we don't think that's entirely due to bandwidth concerns.

     

    In Lync Server 2010, you can set the maximum allowed video resolution to any of the following:

     

    ·         Hd720p15M - High definition, with a resolution of 1280 x 720 and aspect ratio 16:9.

    ·         VGA600K - VGA, with a resolution of 640 x 480, 25 fps with the aspect ratio 4:3. (This, by the way, is the default value.)

    ·         CIF250K - Common Intermediate Format (CIF) video format, 15 fps with a resolution of 352 x 288.

     

    And how exactly do you specify the maximum allowed video resolution? Why, by using one of the CsMediaConfiguration cmdlets (Get-CsMediaConfiguration, New-CsMediaConfiguration, Remove-CsMediaConfiguration, and Set-CsMediaConfiguration), of course:

     

    Set-CsMediaConfiguration –Identity global – MaxVideoRateAllowed Hd720p15M

     

    That's all there is to it. Incidentally, these media configuration settings can be assigned to the service scope (for the Mediation Server service) and the site scope as well as the global scope. Why do you care about that? Well, suppose you have a site that doesn't have particularly robust network connections. (Or maybe the people in that site just aren't very attractive.) In that case, you could enable HD video at the global scope, but create a separate collection of media configuration settings for that one site, liming those users to a maximum video rate of VGA or CIF.

     

    Oh, and you can manage a few other items of interest by using the CsMediaConfiguration cmdlets; for example, you can enable of disable QoS (Quality of Service), and you can configure the encryption settings for client applications. A pretty handy little family of cmdlets, if you ask us.

     

    As you can see, Internet video has come a long way. For example, now the author no longer has to content himself watching hideous, insect-like creatures onscreen. Instead, he can watch classic Internet videos like Tootin' Bathtub Baby Cousins, Fred Goes Swimming, Keyboard Cat, Sneezing Baby Panda, and many, many more.

     

    Like we said, Internet video has, uh, come a long way ….

     

  • Show photos of contacts

     

     

    Registry locations

    HKCU\Software\Microsoft\Communicator\ShowPhoto

    Allowed registry values

    ·         0: Photos of yourself and all your contacts are not displayed in Microsoft Lync

    ·         1: Photos of yourself and all your contacts are displayed in Microsoft Lync

    Registry value type

    REG_DWORD

    Default setting

    1: Photos of yourself and all your contacts are displayed in Lync

     

    One of the cool new features found in Microsoft Lync 2010 is the ability to show not only your picture, but pictures of all your contacts as well. Assuming you and your contacts have all decided to allow your pictures to be shown, an instance of Microsoft Lync will look something like this:

     

    Like we said: pretty cool.

     

    But one of the other cool new features in Microsoft Lync is this: Microsoft Lync gives you a considerable amount of control over the things you see (or don't see) in the user interface. For example, suppose you don't want to see everyone's picture (including your own) in the Contact List. That's fine; one of the settings available in the Options dialog box is the Show photos of contacts checkbox:

     

     

     

     

    If you clear this checkbox then neither your photo nor the photos of any of your contacts will be shown in Lync:

     

    It's entirely up to you.

     

    We should probably point out that this does not prevent other people from seeing your picture; if you want to do that, you need to go back into the Options dialog box and select the option Do not show my picture

     

     

     

    You can even configure things so that you can see everyone's picture except yours. To do that, select Do not show my picture and leave Show photos of contacts selected. After you've done all that, no one (including you) will see your photo. But you'll still be able to see everyone else's photo (unless they, too have selected Do not show my picture):

     

     

     

     

    Another way to enable/disable the showing of photos within Microsoft Lync is to modify the HKCU\SOFTWARE\Microsoft\Communicator\ShowPhoto value in the registry. Before we show you how to do that, however, let's first show you a script that retrieves that value 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 that lets you retrieve the value:

     

    $computer = "."

     

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

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

     

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

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

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

    Write-Host "Show photos of contacts: $value"

     

    And here's a script that sets the value of ShowPhoto. In this case, the script enables showing pictures of yourself and your contacts; that's done by setting ShowPhoto to 1. To suppress pictures of both yourself and your contacts, set ShowPhoto to 0:

     

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

     

    In other words:

     

    $computer = "."

     

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

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

     

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

     

     

     

  • Lync Server Admin Guide: Configuring Voice Routing

     

     

    Configuring Dial Plans and Normalization Rules

     

    Create a Dial Plan

     

    • To create a dial plan

     

    To create a new dial plan, use the New-CsDialPlan cmdlet:

     

    New-CsDialPlan -Identity site:Redmond -SimpleName RedmondSiteDialPlan

     

    The following command creates a new dial plan, then uses the New-CsVoiceNormalizationRule cmdlet to immediately add a new normalization rule to that dial plan:

     

    New-CsDialPlan -Identity site:Redmond -SimpleName RedmondSiteDialPlan

     

    New-CsVoiceNormalizationRule -Identity "site:Redmond/SeattlePrefix" -Pattern "^9(\d*){1,5}$" -Translation "+1206$1"

     

     

    Modify a Dial Plan

     

    • To modify a dial plan

     

    To modify a dial plan, use the Set-CsDialPlan cmdlet:

     

    Set-CsDialPlan -Identity RedmondDialPlan –ExternalAccessPrefix 8

     

     

    For more information

     

     

     

     

     

    Create or Modify a Normalization Rule Manually

     

    • To define a normalization rule manually

     

    To create a new voice normalization rule, use the New-CsVoiceNormalizationRule cmdlet. The following rule has the name SeattleFourDigit and will be assigned to the dial plan SeattleUser:

     

    New-CsVoiceNormalizationRule -Parent SeattleUser -Name SeattleFourDigit -Description "Dialing with internal four-digit extension" -Pattern '^(\d{4})$' -Translation '+1206555$1'

     

    For more information

     

     

     

     

     

    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

     

    • To create a voice policy

     

    To create a new voice policy, use the New-CsVoicePolicy cmdlet:

     

    New-CsVoicePolicy –Identity UserVoicePolicy2 -AllowSimulRing $False -PstnUsages @{Add = "Local"}

     

     

    Modify a Voice Policy and Configure PSTN Usage Records

     

    • To modify a voice policy

     

    To modify an existing voice policy, use the Set-CsVoicePolicy cmdlet:

     

    Set-CsVoicePolicy UserVoicePolicy2 -AllowSimulRing $False -PstnUsages @{add = "Long Distance"}

     

     

    View PSTN Usage Records

     

    • To view a PSTN usage record

     

    To view all your PSTN usage records, use the Get-CsPstnUsage cmdlet:

     

    Get-CsPstnUsage | Select-Object –ExpandProperty Usage

     

     

    Configuring Voice Routes for Outbound Calls

     

    Create a Voice Route

     

    • To create a voice route

     

    To create a new voice route, use the New-CsVoiceRoute cmdlet:

     

    New-CsVoiceRoute -Identity Route1 -PstnUsages @{add="Long Distance"} -PstnGatewayList @{add="PstnGateway:redmondpool.litwareinc.com"}

     

     

    Modify a Voice Route

     

    • To modify a voice route

     

    To modify a voice route, use the Set-CsVoiceRoute cmdlet:

     

    Set-CsVoiceRoute -Identity Route1 -Description "Test Route"

     

    The following commands use both the Get-CsVoiceRoute and Set-CsVoiceRoute cmdlets to add a new PSTN gateway to a voice route:

     

    $y = Get-CsVoiceRoute -Identity Route1

    $y.PstnGatewayList.Add("PstnGateway:192.168.0.100")

    Set-CsVoiceRoute -Instance $y

     

     

    For more information

     

     

     

     

     

    Configuring Trunks and Translation Rules

     

    Configure Media Bypass on a Trunk

     

    • To configure media bypass on a trunk

     

    To enable media bypass for a new SIP trunk, use the New-CsTrunkConfiguration cmdlet and set the EnableBypass property to True:

     

    New-CsTrunkConfiguration -Identity site:Redmond -EnableBypass $True –MaxEarlyDialogs 40 –SRTPMode Required

     

     

    Configure a Trunk Without Media Bypass

     

    • To configure a trunk without media bypass

     

    To disable media bypass for a new SIP trunk, use the New-CsTrunkConfiguration cmdlet and set the EnableBypass property to False:

     

    New-CsTrunkConfiguration -Identity site:Redmond -EnableBypass $False –MaxEarlyDialogs 40 –SRTPMode Required

     

     

    Defining Translation Rules

     

    Create or Modify a Translation Rule Manually

     

    • To define a translation rule manually

     

    To create a new translation rule use the New-CsSipResponseCodeTranslationRule cmdlet:

     

    New-CsSipResponseCodeTranslationRule -Identity "PstnGateway:192.168.0.240/Rule404" -ReceivedResponseCode 434 -TranslatedResponseCode 404

     

     

    For more information

     

     

     

     

     

    Exporting and Importing Voice Routing Configuration

     

    Export a Voice Route Configuration File

     

    • To export a voice routing configuration

     

    With Windows PowerShell you cannot directly export a voice route in the VCFG file format used by the Lync Server Control Panel. However, it is possible to export a voice route in an XML format that can later be imported using Windows PowerShell:

     

    Get-CsVoiceRoute –Identity "RedmondRoute" | Export-Clixml –Path "C:\Routes\RedmondRoute.xml"

     

     

    Import a Voice Route Configuration File

     

    • To import a voice routing configuration

     

    To import a voice route that was previously exported using the Export-Clixml cmdlet, use the following command:

     

    Import-Clixml –Path "C:\Routes\RedmondRoute.xml" | Set-CsVoiceRoute

     

     

    For more information

     

     

     

     

     

    Test Voice Routing

     

    Create a Voice Routing Test Case

     

    • To create a test case

     

    To create a new test case for voice routing, use the New-CsVoiceTestConfiguration cmdlet:

     

    New-CsVoiceTestConfiguration -Identity TestConfig1 -DialedNumber 5551212 -ExpectedTranslatedNumber +5551212

     

     

    Run Voice Routing Test Cases

     

    • To run all voice routing test cases

     

    Use the following command to run all your voice routing test cases, one after another:

     

    Get-CsVoiceTestConfiguration | Test-CsVoiceTestConfiguration

     

     

     

    • To run one or more selected voice routing test cases

     

    Use the following command to run a specific voice routing test case:

     

    Get-CsVoiceTestConfiguration -Identity TestConfig1 | Test-CsVoiceTestConfiguration

     

     

    For more information

     

     

     

     

  • Check if a Contact Distribution Group Exists

    For more information on using this script, see the article Adminstratively Managing User Contact Lists by Nick Smith.

     

    #Contributed by Nick Smith, Microsoft

    #Supported Versions: Office Communications Server 2007 R2

    #Get User Information

    $User = Get-WmiObject -Query "Select * from MSFT_SIPESUserSetting where PrimaryURI = 'sip:user@domain.com'"

    $UserInstanceID = $User.InstanceID

     

    #Check if group is already added to user's contact list

    $ContactGroup = (Get-WmiObject -Query "Select * from MSFT_SIPESUserContactGroupData where UserInstanceID = '$UserInstanceID'" | Where {$_.ExternalURL -like "*group@domain.com*"})

     

    if ($ContactGroup -ne $null) { #The group is on the user's contact list

          Write-Host "The group exists on the user's contact list"

    }

    elseif ($ContactGroup -eq $null) { #The group is not on the user's contact list

          Write-Host "The group does not exist on the user's contact list"

    }

  • Haiku #174

    It's like magic but

    Better. That can only be

    Simple URLs.

     

    Hello everyone, and welcome to the Lync Server PowerShell haiku of the day. Today's topic: the Test-CsCertificateConfiguration cmdlet!

     

    Whoa, hey, take it easy: we were just kidding. Trust us, we haven't forgotten that, in yesterday's haiku, we promised that today's haiku would be about the New-CsSimpleUrl cmdlet. We were just having a little fun with you, sort of like – after the first Harry Potter book was published – J. K. Rowling announced that her next book would be a scientific treatise on the digestive system of the common North American earthworm.

     

    Note. No, J. K. Rowling did not announce that her next book would be a scientific treatise on the digestive system of the common North American earthworm; she's way too smart for that. As it turns out, she might also be smarter than the authors of the Lync Server PowerShell blog. Although few people know this, the authors of the Lync Server PowerShell blog were the original choices to write the Harry Potter books. They declined the offer, however, believing that a daily haiku about Lync Server PowerShell would appeal to a much wider audience, would be easier to translate into movies, action figures, and theme park rides, and would end up being far more popular and far more profitable. Regrets? None. After all, the Harry Potter series is finished, but the daily haiku goes. We'll see who has the last laugh.

     

    To briefly recap yesterday's haiku (and set the stage for today's haiku), 24 hours ago we noted that the process of creating simple URLs was really a three-step process. (And what exactly is a simple URL? See yesterday's haiku for details.) To create a simple URL you need to:

     

    1.    Specify the actual URL (e.g., https://meet.litwareinc.com) that will be used for online meetings. That's done by using the New-CsSimpleUrlEntry cmdlet.

    2.    Create a simple URL that, among other things, references the URL you just created. The simple URL is created using the New-CsSimpleUrl cmdlet, which we'll talk about in a few minutes.

    3.    Add the newly-created simple URL to a collection of simple URLs (something which we discussed a long, long time ago).

     

    Or, if you prefer code over blather (hint: if you do, you've come to the wrong place):

     

    $urlEntry = New-CsSimpleUrlEntry -Url "https://meet.fabrikam.com"

     

    $simpleUrl = New-CsSimpleUrl -Component "meet" -Domain "fabrikam.com" -SimpleUrl $urlEntry -ActiveUrl "https://meet.fabrikam.com"

     

    Set-CsSimpleUrlConfiguration -Identity "site:Redmond" -SimpleUrl @{Add=$simpleUrl}

     

    In today's haiku we're going to focus on this line of code (which, in our opinion, is better PowerShell code than you'll find in any of the Harry Potter books, including Harry Potter and the Half-Blood Prince!):

     

    $simpleUrl = New-CsSimpleUrl -Component "meet" -Domain "fabrikam.com" -SimpleUrl $urlEntry -ActiveUrl "https://meet.fabrikam.com"

     

    Before we discuss this command in any detail, let's talk about the three kinds of simple URLs that Lync Server allows you to create:

     

    ·         Meet – Provides simple URLs for online meetings. You must have at least one Meet URL for each of your SIP domains.

    ·         Admin – Provides a simple URL for the Lync Server 2010 Control Panel.

    ·         Dialin – Provides a simple URL for the Dial-in Conferencing Web page.

     

    The Admin and Dialin simple URLs are optional; if you don't bother to create a simple URL for Dialin, users will still be able to access the Dial-in Conferencing Web page; they just won't be able to do so using a URL like https://dialin.fabrikam.com. However, the Meet URL is a different story. When you install Lync Server, no simple URLs are pre-created for you; you have to create all these URLs yourself. Suppose you decide to skip the Meet URL and create just an Admin URL instead. Here's what will happen when you try to add that URL to a simple URL collection:

     

    Set-CsSimpleUrlConfiguration : There must be an Active URL for Meet for each SIP Domain.

     

    And before you ask, no, there's absolutely no way around that. You must create a Meet simple URL before you can create any other simple URLs. And, as we noted yesterday, you must have a simple URL for each of your SIP domains. Otherwise you're going to run into problems.

     

    Oh, and as long as we're on the subject, every simple URL you create must point to a real, live SIP domain. Suppose you decide to create a few simple URLs now, and then figure you'll create the corresponding SIP domains later. Is that going to work? Nope:

     

    Set-CsSimpleUrlConfiguration : Simple URL cannot be specified for a nonexistent SIP Domain.  Please add the SIP Domain first.

     

    OK, let's go back to our New-CsSimpleUrl command:

     

    $simpleUrl = New-CsSimpleUrl -Component "meet" -Domain "fabrikam.com" -SimpleUrl $urlEntry -ActiveUrl "https://meet.fabrikam.com"

     

    As you can see, when we call New-CsSimpleUrl we need to first specify the type of URL we are creating (the Component) as well as the URL domain (in this example, fabrikam.com). We also need to include the SimpleUrl parameter followed by the simple URL entry (Web address) that we created using the New-CsSimpleUrlEntry cmdlet. If you've forgotten about that command, it looks a lot like this:

     

    $urlEntry = New-CsSimpleUrlEntry -Url "https://meet.fabrikam.com"

     

    Finally, we need to indicate which simple URL is the active URL, the URL that will actually be employed by your users. A simple URL can contain multiple URLs, but only one of those URLs can be active at any time.

     

    If all goes well, we'll end up with a new Meet URL in (in this case) the Redmond site:

     

    Identity  : site:Redmond

    SimpleUrl : {Component=meet;Domain=fabrikam.com;ActiveUrl=https://meet.fabrikam.com}

     

    And what if we wanted to create all three simple URL types, and all three at the same time? No problem:

     

    $urlEntry = New-CsSimpleUrlEntry -Url "https://meet.fabrikam.com"

    $simpleUrl = New-CsSimpleUrl -Component "meet" -Domain "fabrikam.com" -SimpleUrl $urlEntry -ActiveUrl "https://meet.fabrikam.com"

     

    $urlEntry2 = New-CsSimpleUrlEntry -Url "https://admin.fabrikam.com"

    $simpleUrl2 = New-CsSimpleUrl -Component "admin" -Domain "fabrikam.com" -SimpleUrl $urlEntry2 -ActiveUrl "https://admin.fabrikam.com"

     

    $urlEntry3 = New-CsSimpleUrlEntry -Url "https://dialin.fabrikam.com"

    $simpleUrl3 = New-CsSimpleUrl -Component "dialin" -Domain "*" -SimpleUrl $urlEntry3 -ActiveUrl "https://dialin.fabrikam.com"

     

    Set-CsSimpleUrlConfiguration -Identity "site:Redmond" -SimpleUrl @{Add=$simpleUrl,$simpleUrl2,$simpleUrl3}

     

    Just a couple things to note about these commands. As you can see, we had to create separate URL entries and simple URLs for each component: Meet, Admin, and Dialin. And then when we called the Set-CsSimpleConfiguration cmdlet, we simply had to specify all three of the variables containing the simple URLs. In other words:

     

    Set-CsSimpleUrlConfiguration -Identity "site:Redmond" -SimpleUrl @{Add=$simpleUrl,$simpleUrl2,$simpleUrl3}

     

    It's not hard, it's just something you need to watch out for.

     

    Oh, and here's something else to watch out for: always use the Add method when adding a new simple URL:

     

    Set-CsSimpleUrlConfiguration -Identity "site:Redmond" -SimpleUrl @{Add=$simpleUrl}

     

    Why does that matter? Well, that will add a new URL to your collection. This syntax is perfectly valid:

     

    Set-CsSimpleUrlConfiguration -Identity "site:Redmond" –SimpleUrl $simpleUrl

     

    However, that syntax will replace all your existing simple URLs with the one URL contained in the variable $simpleUrl. If that's what you want to do, well, then go for it. But if you just wanted to add $simpleUrl to the collection, make sure you do it like this:

     

    Set-CsSimpleUrlConfiguration -Identity "site:Redmond" -SimpleUrl @{Add=$simpleUrl}

     

    Needless to say, that's a lesson some of us learned the hard way.

     

    That's pretty much all for today. We've got to go change clothes and head off to a meeting with a representative from Disney. You just wait, J. K. Rowling: Lync Server PowerShell World isn't dead yet. Not by a long shot.

     

     

     

     

     

     

  • Check if a Contact Exists

    For more information on using this script, see the article Adminstratively Managing User Contact Lists by Nick Smith.

     

    #Contributed by Nick Smith, Microsoft

    #Supported Versions: Office Communications Server 2007 R2

    #Get User Information

    $User = Get-WmiObject -Query "Select * from MSFT_SIPESUserSetting where PrimaryURI = 'sip:user@domain.com'"

    $UserInstanceID = $User.InstanceID

     

    #Check if contact is already added to user's contact list

    $Contact = (Get-WmiObject -Query "Select * from MSFT_SIPESUserContactData where UserInstanceID = '$UserInstanceID'" | Where {$_.SIPURI -like "sip:userToCheck@domain.com"})

     

    if ($Contact -ne $null) { #The contact is on the user's contact list

          Write-Host "The contact exists on the user's contact list"

    }

    elseif ($Contact -eq $null) { #The contact is not on the user's contact list

          Write-Host "The contact does not exist on the user's contact list"

    }

  • Turn on Windows Event logging for Lync

     

     

    Registry locations

    HKCU\Software\Microsoft\Communicator\EnableEventLogging

    Allowed registry values

    ·         0 – Lync will not log events to the Application event log

    ·         1 – Lync will log events to the Application event log

    Registry value type

    REG_DWORD

    Default setting

    0: Lync events are not recorded to the Application event log

     

    Can Microsoft Lync record events (in particular, information about failed calls) to the Application event log? You bet it can: all you have to do is ask.

     

    Wait, we take that back: you’ll probably have more success if you specifically enable event logging in Lync as opposed to merely asking Lync if it could start logging events for you. (We're still working on that "Ask Lync to do something and it'll just do it for you" feature.) Fortunately, it’s very easy to enable (or disable) event logging; all you have to do is select the Turn on Windows Event logging for Lync checkbox, a checkbox found under the General options of the Options dialog box:

     

     

    It’s also very easy to enable (or disable) event logging by modifying the registry: in that case you just need to change the HKCU\SOFTWARE\Microsoft\Communicator\EnableEventLogging registry value. Set this value to 1 and Lync will start recording events to the Application event log. Set this value to 0 and Lync will not record events to the Application event log.

     

    See? All you had to do was ask.

     

    Well, that and modify the EnableEventLogging registry value.

     

    Before we go much further we should point out that certain events – in particular, events having to do with connection failures – are recorded in the event log regardless of whether event logging has been enabled or disabled. For example, suppose you start Lync and are unable to connect to Lync Server. In that case, messages similar to these will be recorded to the event log:

     

    • Lync failed to connect to server atl-sip-001.fabrikam.com (192.168.1.1) on port 5061 due to error 10065.  The server is not listening on the port in question, the service is not running on this machine, the service is not responsive, or network connectivity doesn't exist.

    • Lync was unable to resolve the DNS hostname of the login server atl-sip-001.fabrikam.com.

     

    Etc. However, what isn't logged in the event log (unless you enable this feature) is detailed diagnostic information about failed calls. If you disable event logging then nothing gets logged if you're involved in a failed call. But if you do enable event logging, then each failed call will result in event log entries similar to this:

     

     

    That's pretty useful information if you're trying to figure out why a call failed.

     

    Note. Actually we should qualify that statement a little: that's useful information if you haven't deployed Monitoring Server and Monitoring Server Reports. If you're using Monitoring Server you don't need to record failed call events in the event log: instead, all that information will automatically be forwarded to Monitoring Server itself. That's nice, because all the information about all your failed calls will be in one place, without having to retrieve this data from the event logs on all your client machines.

     

    We should also point out that client policies have a property (EnableEventLogging) that deals with event logs. If you set the value of this property to False, then failed calls will not be logged to the event log. In addition to that, the option for enabling/disabling event logging will no longer be available in Lync:

     

    Note. And how do you set that client policy property to False? Like this:

     

    Set-CsClientPolicy –Identity global –EnableEventLogging $False

     

    So what happens if you set EnableEventLogging to True (or, in PowerShell-speak, $True)? You got it: event logging will be enabled, and users will not be able to change that setting:

     

     

    As you can see, the client policy always trumps whatever value might be set in the registry. Because of that, if you're wondering whether event logging has been enabled for a given user, the first place to check is that user's client policy. If no value has been configured for the EnableEventLogging property you can then use the following PowerShell script to retrieve the current value of EnableEventLogging on 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 how you do that:

     

    $computer = "."

     

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

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

     

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

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

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

    Write-Host "Turn on Windows event logging for Lync: $value"

     

    And, of course, there's also a way to set the value of EnableEventLogging. The following script enables event logging; that's done by setting EnableEventLogging to 1. To disable event logging, set EnableEventLogging to 0:

     

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

     

    Like this:

     

    $computer = "."

     

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

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

     

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

     

     

     

     

  • Connect to Microsoft Lync Server 2010 Remote PowerShell from the Desktop

    By Nick Smith, Microsoft

     

    Before running this script, ensure that the client meets all the requirements for a remote connection to Windows PowerShell. Here are the requirements:

     

    ·         Windows PowerShell v2.0

    ·         .NET Framework 2.0 Service Pack 1

    ·         Windows Remote Management (WinRM) 2.0

     

    The requirements are included in the Windows Management Framework, available for download. (For a detailed description of system requirements for Windows PowerShell 2.0, check out this support article: http://support.microsoft.com/kb/968929.)

     

    To create a desktop shortcut to start a Windows PowerShell remote session connected to Microsoft Lync Server 2010, follow these instructions:

     

    1.    Open Notepad (or your favorite text or script editor).

    2.    Copy the following script and paste it into Notepad.

     

     

    #*******************************************************************************

    #* Author: Nick Smith (karsmith@microsoft.com)

    #* Date: 6/20/2010

    #* Purpose: This is a script that can be used when creating desktop icons that

    #*    will allow the user an easy way of connecting to remote powershell into a

    #*    Lync Server environment.

    #* NOTE: THIS WAS DEVELOPED AGAINST LYNC SERVER BETA AND DOES NOT INCLUDE MUCH

    #*    ERROR CHECKING.  USE AT YOUR OWN RISK.

    #*******************************************************************************

    Param (

        [switch] $AuthenticationPrompt = $false,

        [string] $CSPoolFQDN = (read-host "Enter the FQDN of the Lync Server Pool"),

        [string] $AdminUsername = "domain\username"

    )

     

    $Error.Clear()

     

    If ($AuthenticationPrompt) {

        Write-Host -ForegroundColor Green "Requesting Credentials for Lync Server Administrative Session"

        $CSSession = New-PSSession -ConnectionUri https://$CSPoolFQDN/ocspowershell -Credential $AdminUsername -ErrorAction SilentlyContinue

    }

    else {

        $CSSession = New-PSSession -ConnectionUri https://$CSPoolFQDN/ocspowershell -Authentication NegotiateWithImplicitCredential -ErrorAction SilentlyContinue

     

    }

     

    If ($Error.count -gt 0){

        Write-Host -ForegroundColor Red "Unable to Connect to Lync Server Administrative Session`n Error:" $Error

    }

    else {

        $ImportResults = Import-PSSession -Session $CSSession

        Write-Host -ForegroundColor Green "Connected to Lync Server Administrative Session`nImported" $ImportResults.ExportedFunctions.count "CS Functions"

    }

     

    cd $env:UserProfile

     

     

    3.    Save the file with a .ps1 extension, for example, C:\Scripts\Start-CsRemotePowerShell.ps1

     

    4.    Create an shortcut on the client desktop with the following target. Be sure the change the highlighted values to match your environment.

     

    o    To use integrated authentication

    §  powershell.exe -noexit -executionpolicy bypass -file c:\scripts\start- csremotepowershell.ps1 –CSPoolFQDN CSPool01.contoso.local

    o    To use an authentication prompt

    §  powershell.exe -noexit -executionpolicy bypass -file c:\scripts\start- csremotepowershell.ps1 -AuthenticationPrompt -CSPoolFQDN CSpool01.contoso.local -AdminUsername contoso\administrator

     

  • Haiku #165

    Break glass in case of

    A haiku emergency.

    Import announcements.

     

    OK, so, originally we planned to have a really exciting and interesting haiku for you today. (Hey, who said, "Well, that would be a nice change"?) Like we said, we planned to have a really exciting and interesting haiku for you today but then … stuff … began to happen, most of it happening between the hours of 3:00 AM and 6:00 AM, and most of it involving an issue that turned out not to be an issue after all, and a long car trip that turned out not to be a long car trip after all.

     

    Note. Explain it? To tell you the truth, if we could explain it we probably never would have gotten involved in the situation in the first place.

     

    At any rate, all's well that ends well and in this case, at least, everything ended perfectly well. Except for one thing: somewhere along the way, we must have misplaced our really exciting and interesting haiku, because the only haiku that we could find was this one.

     

    Bummer.

     

    Note. When he was growing up, his mother always told the author of today's haiku that he should keep a few spare daily Lync Server PowerShell haikus on hand in case of emergency. To be honest, the author of today's haiku thought that was ridiculous: how lame would you have to be to suddenly find yourself in desperate need of a Lync Server PowerShell haiku? Well, now he knows just exactly how lame you would have to be.

     

    The moral of the story? Kids, always listen to your parents.

     

    And especially to your dad.

     

    Fortunately, though, there's a bright side here: after all, there's no such thing as a truly bad Lync Server PowerShell haiku. (OK, good point. But we didn't write that one.) And today's haiku is no exception; after all, today's topic is the Import-CsAnnouncementFile cmdlet. Which means that, now that we think about it, we just might have a really exciting and interesting haiku for you after all.

     

    Note. Yes, yes: which would be a first for us. We get it.

     

    So what's an announcement file? Well, with Lync Server you will likely have valid phone numbers that are currently not assigned to a user. If you want, you can configure an audio file to be played any time someone dials one of those unassigned numbers. For example, you might play a recording that says something like "We're sorry, but the number you have dialed is not currently in service. Your call will now be transferred to our receptionist." Alternatively, you might play In-A-Gadda-Da-Vida by Iron Butterfly. It's entirely up to you.

     

    Note. Don't play In-A-Gadda-Da-Vida by Iron Butterfly. In addition to copyright issues, the song is over 17 minutes long.

     

    Incidentally, and according to legend, In-A-Gadda-Da-Vida was originally titled In the Garden of Eden. (The Simpsons were right!) However, someone in Iron Butterfly (the stories vary) was either drunk or high and couldn't say "In the Garden of Eden" without slurring their words. The rest is rock-and-roll history.

     

    The important thing is that you can play an audio file (a .WAV or .WMA file) of some kind whenever someone dials an unassigned number (and right before you would then typically transfer that call to another user, to voicemail, etc). And how do you import one of these announcement files to Lync Server 2010? Why, like this, of course:

     

    Get-Content "C:\AudioFiles\NoOneIsHere.wav" -ReadCount 0 -Encoding Byte | Import-CsAnnouncementFile -Parent ApplicationServer:atl-cs-001.litwareinc.com -FileName "NoOneIsHere.wav"

     

    What exactly have we done here? Well, we've first used the Get-Content cmdlet to retrieve the contents of the file C:\AudioFiles\NoOneIsHere.wav. Note that we've also included the ReadCount and the Encoding parameters. Setting the ReadCount to 0 ensures that the file is read in all at the same time (as opposed to being read in piece-by-piece), while setting the Encoding property to Byte tells Get-Content to read the file as a series of bytes rather than, say, a series of plain-text characters.

     

    After we've retrieved the file using Get-Content, we then save that file to Lync Server by using the Import-CsAnnouncementFile cmdlet. To do that we need to specify the service location of the Application Server where the file will be stored (in this case, that's ApplicationServer:atl-cs-001.litwareinc.com) and we need to give the file a file name. For this file, that file name is NoOneIsHere.wav.

     

    As soon as you've imported the file, it can then be used by the Lync Server announcement service. We won't discuss the details in today's haiku (after all, we already discussed the details in haiku #31), but here's a sample command that turns our imported file into a real, live announcement:

     

    New-CsAnnouncement -Identity ApplicationServer:atl-cs-001.litwareinc.com -Name "No One is Here" -AudioFilePrompt "NoOneIsHere.wav"

     

    That's all you have to do.

     

    Coincidentally enough, that's all that we have to do, at least for now. But don't worry: we're going to spend the rest of the day writing Lync Server PowerShell haikus and storing them away in case of emergency. Next time, we're going to be better prepared.

     

    See you tomorrow.

     

     

     

     

     

     

     

     

  • Conferencing Policy Settings: MaxMeetingSize

     

    This article is part of the series An In-Depth Guide to Conferencing Policy Settings.

     

     

    Parameter/Property Name

    Allowed Values

    MaxMeetingSize

     

    This is a per-organizer setting.

    Any integer value between 2 and 250, inclusive.

     

    Lync Server Control Panel Equivalent Setting: Maximum meeting size

     

     

     

    So just how many people can you have in any one meeting? That's a good question. And the answer is: it depends on the conference policy that the meeting organizer has been assigned.

     

    By default, you can have a maximum of 250 people in a meeting. Note that this doesn't mean you can only invite a maximum of 250 people to a meeting. Lync Server doesn't place any limit on the maximum number of people you can invite to a meeting; if you want to, you can invite everyone in the world to your meeting.

     

    Note. Don't invite everyone in the world to your meeting.

     

    Like we said, you can invite as many people as you want to a meeting. However, the people who will actually be able to get into that meeting will be determined on a first-come, first-served basis. In other words, by default the first 250 people to show up will be allowed to join the meeting. And what happens to the 251st person? This:

     

    As you can see, he or she will receive a message stating that the meeting is full, and will be given the option to try again. Of course, that next try will also fail unless someone has dropped out of the meeting and opened up a slot.

     

    The good news is that the user who is unable to join the meeting will know why they aren't able to join the meeting: the meeting is full. However, the meeting organizer won't get a similar notice. During the meeting the organizer will be able to see that Pilar is trying to join the meeting:

     

     

    However, when Pilar is denied entrance (because the meeting is full) the organizer will only see the following somewhat-misleading message:

     

     

    As far as we know, there's nothing that can be done about that. That's just the way the system works.

     

    Here's something else you should know about the system and how it works. You can modify the maximum number of people allowed in a meeting by using the CsConferencingPolicy cmdlets and the MaxMeetingSize parameter. For example, this command sets the maximum meeting size in the global conferencing policy to 100 people:

     

    Set-CsConferencingPolicy –Identity global –MaxMeetingSize 100

     

    That's the good news. The bad news is that you can seemingly allow an all-but infinite number of people to participate in a single meeting. Do you want to allow 999,999,999 people to join a meeting? Well, believe it or not, this command will actually work:

     

    Set-CsConferencingPolicy –Identity global –MaxMeetingSize 999999999

     

    So do we recommend that you set the maximum meeting size to 999,999,999. Uh, no. Instead, meeting sizes should be limited to any integer value between 2 and 250, inclusive. You can't go less than 2 (which means that everyone is at least allowed to initiate a peer-to-peer session), and you should never go higher than 250. Yes, you can set MaxMeetingSize to a higher value, but, then again, there are lots of things in life than you can do but shouldn't do.

     

    And this is one of them.

     

    Incidentally, the maximum size of a meeting is a setting that applies to the organizer of a conference. For example, suppose we have two users with two different conferencing policies:

     

    User

    Conferencing Policy

    MaxMeetingSize

    Pilar Ackerman

    global

    50

    Ken Myer

    RedmondConferencingPolicy

    250

     

    If Pilar Ackerman sets up a meeting, that meeting can have no more than 50 people in it, period. By comparison, if Ken Myer sets up a meeting that meeting can have as many as 250 participants. Suppose Ken Myer is the 51st person who tries to join one of Pilar's meetings. Will he be allowed in? Nope; any meeting organized by Pilar can have only 50 people. And what if Pilar is the 51st person who tries to join a meeting organized by Ken; will she be allowed in? You bet she will. Because she's not the meeting organizer, her conferencing policy doesn't apply.