Archive for June, 2012

I love tips that make my work easier. I got this neat idea from reading Mike F. Robbins’s blog. Mike has a tip on how to learn a new PowerShell cmdlet every day. Its easy – just run the  following line of PowerShell every morning:

Get-Command | Get-Random | Get-Help –Full

I normally keep the Lync, ActiveDirectory and LyncOnline modules loaded in my editor so the code can pull a cmdlet from those or any other loaded module. I know that Lync alone has over 500 cmdlets so I should have a couple years worth of learning here. May have to double up and run the command in the evening also.

Sample output

I ran it several times this morning and got help for several different cmdlets. The output looks like what is shown below for Remove-CsVoiceRoute.

NAME
    Remove-CsVoiceRoute

SYNOPSIS
    Removes a voice route. Voice routes contain instructions that tell Microsoft Lync Server 2010 how to route calls from Enterprise Voice us
    ers to phone numbers on the public switched telephone network (PSTN) or a private branch exchange (PBX).


SYNTAX
    Remove-CsVoiceRoute -Identity <XdsGlobalRelativeIdentity> [-Confirm [<SwitchParameter>]] [-Force <SwitchParameter>] [-WhatIf [<SwitchPara
    meter>]] [<CommonParameters>]


DESCRIPTION
    Use this cmdlet to remove an existing voice route. Voice routes are associated with voice policies through PSTN usages, so removing a voi
    ce route does not change any values relating to a voice policy, it simply changes the routing for the numbers that had matched the patter
    n for the deleted voice route.

    Who can run this cmdlet: By default, members of the following groups are authorized to run the Remove-CsVoiceRoute cmdlet locally: RTCUni
    versalServerAdmins. To return a list of all the role-based access control (RBAC) roles this cmdlet has been assigned to (including any cu
    stom RBAC roles you have created yourself), run the following command from the Windows PowerShell prompt:

    Get-CsAdminRole | Where-Object {$_.Cmdlets -match "Remove-CsVoiceRoute"}



PARAMETERS
    -Identity <XdsGlobalRelativeIdentity>
        A string that uniquely identifies the voice route you want to delete. (If the route name contains a space, such as Test Route, you mu
        st enclose the full string in double quotes.)




        Required?                    true
        Position?                    2
        Default value
        Accept pipeline input?       True
        Accept wildcard characters?  false

    -Force <SwitchParameter>
        Suppresses any confirmation prompts that would otherwise be displayed before making changes.


        Required?                    false
        Position?                    Named
        Default value
        Accept pipeline input?       False
        Accept wildcard characters?  false

    -WhatIf [<SwitchParameter>]
        Describes what would happen if you executed the command without actually executing the command.


        Required?                    false
        Position?                    Named
        Default value
        Accept pipeline input?       False
        Accept wildcard characters?  false

    -Confirm [<SwitchParameter>]
        Prompts you for confirmation before executing the command.


        Required?                    false
        Position?                    Named
        Default value
        Accept pipeline input?       False
        Accept wildcard characters?  false

    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer and OutVariable. For more information, type,
        "get-help about_commonparameters".

INPUTS

        Microsoft.Rtc.Management.WritableConfig.Policy.Voice.Route object. Accepts pipelined input of voice route objects.



OUTPUTS

        Removes an object of type Microsoft.Rtc.Management.WritableConfig.Policy.Voice.Route.



    -------------------------- Example 1 --------------------------

    Remove-CsVoiceRoute -Identity Route1


    Removes the settings for the voice route with the identity Route1.


    -------------------------- Example 2 --------------------------

    Get-CsVoiceRoute | Remove-CsVoiceRoute


    This command removes all voice routes from the organization. First all voice routes are retrieved by the Get-CsVoiceRoute cmdlet. These v
    oice routes are then piped to Remove-CsVoiceRoute, which removes each one.


    -------------------------- Example 3 --------------------------

    Get-CsVoiceRoute -Filter *Redmond* | Remove-CsVoiceRoute


    This command removes all voice routes with an identity that includes the string "Redmond." First the Get-CsVoiceRoute cmdlet is called wi
    th the Filter parameter. The value of the Filter parameter is the string Redmond surrounded by wildcard characters (*), which specifies t
    hat the string can be anywhere within the Identity. After all of the voice routes with identities that include the string Redmond are ret
    rieved, these voice routes are piped to Remove-CsVoiceRoute, which removes each one.



RELATED LINKS
    Online Version http://technet.microsoft.com/EN-US/library/6687e538-e8f6-4bf0-b393-2c7b4a3f2f06(OCS.14).aspx

I hope you found this helpful. Feel free to comment or contact me if you have questions.

Last week I blogged on Setting Up a Tenant’s  Allowed Domains for Federation concerning things I learned (some the hard way) while using PowerShell to setup federation for a Lync tenant using the Microsoft Lync Server 2010 Multitenant Pack for Partner Hosting. Today’s blog post will cover some other things I’ve learned since the last post and will cover some oddities I encountered.

First thing that I noticed was this line in the Deployment Guide: “You should use the Lync Server Control Panel only in read-only mode. You should make all changes to the topology, server configuration, or user configuration by using cmdlets in the Lync Server Management Shell. “  which is all right with me. I love PowerShell and do all of my administration and provisioning from PowerShell so I have no problem with this. But if you are used to using the Control Panel then you are going to have to change the way you operate. There are other differences too but I’ll blog about them in the weeks to come. Now lets get on to the federation goodies.

I wrote last week about how to get the tenant allowed list and when you look at the federation configuration for Tenant1 using Get-CsTenantFederationConfiguration you get something like this:

Identity : Global
AllowedDomains : Microsoft.Rtc.Management.WritableConfig.Settings.Edge.AllowList
BlockedDomains : {}
AllowFederatedUsers : True
AllowPublicUsers : True
SharedSipAddressSpace : False

And I showed how to get the list of allowed domains form the Microsoft.Rtc.Management.WritableConfig.Settings.Edge.AllowList  so I thought that the Blockedlist would work the same way. Surprise! It doesn’t work the same. If you look at the type of the objects you will see that they differ:

PS C:\> $x.AllowedDomains.GetType()
IsPublic     IsSerial       Name                               BaseType
——–      ——–       —-                                  ——–
True           False           AllowList                         System.Object

PS C:\> $x.BlockedDomains.GetType()
IsPublic    IsSerial       Name                                BaseType
——–    ——–       —-                                    ——–
True           False          ListWithEvents`1           System.Object

I’m not exactly sure yet what all of that means (still working it out) but the main thing is that to Add() to the list of Allowed Domains you have call the add method like this:

$x.AllowedDomains.AllowedDomain.Add($d1)

But to add to the Blocked List you call an Add() method directly on the the BlockedDomains object like this:

$x.BlockedDomains.Add($d1)

The Code

Other than what I mentioned above the code is almost identical to last weeks code. As a side note you will notice that I have added some try/catch error handling logic to this version which I also retro fitted into my code for the Set-AllowedDomain. Also, the documentation has the same overwrite bug I mention previously which is why I use the Add method of $x.BlockedDomains.

function Set-BlockedDomain (
[Parameter(Mandatory = $true)][string]$OU,
[Parameter(Mandatory = $false)][array]$domainName
) 
{
    try
    {
        $tenant = Get-CsTenant | Where-Object {$_.Name –eq "$OU"}
        $x = Get-CsTenantFederationConfiguration –Tenant $tenant.TenantId

        # Check to see if the domain is already in blocked list
        $domain = $x.BlockedDomains | ?{$_.Domain -eq $domainName}
        if($domain -eq $null)
        {
            $d1 = New-CsEdgeDomainPattern -Domain "$domainName"
            $x.BlockedDomains.Add($d1)
            Set-CsTenantFederationConfiguration -Tenant $tenant.tenantID -BlockedDomains $x.BlockedDomains
        }
        else
        {
            #Write-Host "ERROR: $domainName already in allowed list for $container"
            Throw (new-object Exception("ERROR: $domainName already in blocked list for $OU"))
        }
    }
    catch [Exception]
    {
        Write-Error "Exception Set-BlockedDomain: $_"
        Throw $_
    }
}

More Confusion Around Allowed Domains List

I was trying out my New-AllowedDomain function when I got this error:

Set-AllowedDomain : Exception Set-AllowedDomain: You cannot call a method on a null-valued expression.
At line:1 char:20  + Set-AllowedDomain <<<<  -OU ‘marshall55′ -domain ‘nextuc.com’    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Set-AllowedDomain

My first thought is what is happening? I didn’t pass in a any null parameters. This doesn’t make any sense. Stupid PowerShell error messages. But then I started digging deeper into the code (seen here) and found that this line was causing the problem.

# Check to see if the domain is in the allowed list
$domain = $x.AllowedDomains.AllowedDomain | ?{$_.Domain -eq $domainName}

Hmm, that hasn’t caused any errors in the past and I had code to handle it if it wasn’t there so why the references to a null valued expression? Time to dig deeper. So I looked at the federation info for the client and this is what I got:

$x = Get-CsTenantFederationConfiguration –Tenant $tenant.TenantId
PS C:\Users\Administrator.S01> $x

Identity              : Global
AllowedDomains        : Microsoft.Rtc.Management.WritableConfig.Settings.Edge.AllowAllKnownDomains
BlockedDomains        : {}
AllowFederatedUsers   : True
AllowPublicUsers      : True
SharedSipAddressSpace : False

It seems that the tenant was set to federate with all known domains (known by who?) and since it was essentially federated with everybody there is no need (and it is impossible) to add a domain. Now I understand the problem and what the weird error message means but I’m not particularly fond of the error.

I’ll continue this discussion in a follow up blog post (this one is getting too long) next week and explain how I got into this mess as well as explain the different levels/types of  federation a tenant can have.  In the meantime I didn’t want you to be confused if you encountered this error.

Let me know if you have questions.

I have been testing out the Microsoft Lync Server 2010 Multitenant Pack for Partner Hosting (that’s a long name) and comparing it to how things are done in  Lync Server 2010. One of the things I need to do is to add domains to the list of domains that a tenant can federate with. That turned out to be a little harder than I thought it would be.

The documentation is somewhat vague on how to do this but you normally see something like this:

$t = Get-CsTenant | Where-Object {$_.DisplayName –eq “Tenant1″}
Get-CsTenantFederationConfiguration –Tenant $t.TenantId
$d1 = New-CsEdgeDomainPattern -Domain “fabrikam.com”
$d2 = New-CsEdgeDomainPattern -Domain “contoso.com”
$a = New-CsEdgeAllowList -AllowedDomain @{replace=$d1,$d2}
Set-CsTenantFederationConfiguration –Tenant $t.TenantId -AllowedDomains $a

I tried that and it worked but it is somewhat limiting and confusing. First off the code above will set things up so that the only domains in Tenant1’s allow list will be “fabrikam.com” and “contoso.com”. That may be okay in some cases but what about the domains that were already in the allowed list for Tenant1? We just replaced them with our two new domains. And to make matters worse if you try to look at the federation configuration for Tenant1 using  Get-CsTenantFederationConfiguration you get something like this:

Identity                                  : Global
AllowedDomains                : Microsoft.Rtc.Management.WritableConfig.Settings.Edge.AllowList
BlockedDomains                : {}
AllowFederatedUsers      : True
AllowPublicUsers             : True
SharedSipAddressSpace : False

Where are our new domains? What the heck is a Microsoft.Rtc.Management.WritableConfig.Settings.Edge.AllowList  and how do I see the allowed domains?

How to use Set-CsTenantFederationConfiguration

It’s not obvious since there is no documentation (that I have found) and the Get-Help cmdlet doesn’t give me anything other than a parameter list. It turns out that it is fairly easy to do; just store the results to a variable then you can look at the AllowedDomains like this:

$tenant = Get-CsTenant | Where-Object {$_.Name –eq “$OU”}
$x = Get-CsTenantFederationConfiguration –Tenant $tenant.TenantId
$x.AllowedDomains

This will give you the following:

AllowedDomain : {Domain=fabrkam.com, Domain=contoso.com}

So we can now see our domains in the allowed list but what about the overwriting of the allowed list contents. Somehow we need to make sure we simply append the new domains and not replace existing domains in the allowed list. Its not hard but also not readily apparent how you go about this.

The Code

I wrote the following function to add a domain to the list and perserve the existing domains in the allowed list.  After getting the configuration we check to see if the domain we are adding is already in the list. If it is not in the list we add otherwise we throw an error. In order to add a domain (i.e. “GotSpeechGuy.com”) we first have to prepare the domain name (which is just a string) by using New-CsEdgeDomainPattern. Then we take advantage of the fact that AllowedDomain has an Add() method. After we do that we have a list that contains all the original domains and the new one we are adding so we simply call Set-CsTenantFederationConfiguration passing in the AllowedDomains. That is all there is to it and when we put it all together it looks like this:

function Set-AllowedDomain (
[Parameter(Mandatory = $true)][string]$OU,
[Parameter(Mandatory = $false)][array]$domainName
) 
{
    $tenant = Get-CsTenant | Where-Object {$_.Name –eq "$OU"}
    $x = Get-CsTenantFederationConfiguration –Tenant $tenant.TenantId
    $domain = $x.AllowedDomains.AllowedDomain | ?{$_.Domain -eq $domainName}
    if($domain -eq $null)
    {
        $d1 = New-CsEdgeDomainPattern -Domain "$domainName"
        $x.AllowedDomains.AllowedDomain.Add($d1)
        Set-CsTenantFederationConfiguration -Tenant $tenant.tenantID  -AllowedDomains $x.AllowedDomains
    }
    else
        {
            #Write-Host "ERROR: $domainName already in allowed list for $container"
            Throw (new-object Exception("ERROR: $domainName already in allowed list for $OU"))
        }
}

I hope this helps you and if you have any questions feel free to ping me.

It is reasonable to think that not all users in your AD are Lync users so there may be times when you will want to know how many Lync  users you have. That happened to me the other day as my boss at NextUC asked me that question. So I thought I would show you an easy way to count your users and even tell what type they are.

To get a count of your enterprise voice users you can use this PowerShell snippet:

$list = Get-CsUser | where {$_.LineUri -ne “”}

$list.Count

If you need to see the users you can just write out the contents of $list but in our case we take advantage of the $list.Count method of the $list object to get the total number of enterprise users.

To get a count of your messenger users we just need to modify the PowerShell snippet slightly:

$list = Get-CsUser | where {$_.LineUri -eq “”}

$list.Count

Why not use EnterpriseVoiceEnabled Property?

That’s all there is to it; we simply take advantage of the fact that enterprise users have their phone number in the LineUri property. You may be wondering why I didn’t just check the EnterpriseVoiceEnabled property. Well, in my case I can’t trust the value of EnterpriseVoiceEnabled. We are a hosting company and sometimes I need to temporarily disable an enterprise voice user so I set EnterpriseVoiceEnabled to false but leave the rest of the Lync properties alone. That makes LineUri  a more reliable property to use for me but if you don’t have that issue then you could just modify the snippets to use EnterpriseVoiceEnabled.

I recently received am email (partly shown below) from the Technology Director of a small school system in Texas. It seems that he wants to include speech programming in the High School curriculum.

 

“We are adding programming to an existing course at our High School next year. This course is going to be different because programming is not the focus of the course. The focus of the course is child development. We have already arranged for two teachers at the elementary level to work with the student groups to define and develop voice based games that reinforce concepts the teachers want the students in the 2nd and 3rd grade to practice. We really want want High School students to develop teaching games for younger students. Basically,  I want to offer voice translation as a “service” to internal game developers within our network. I don’t care if the game is Flash or iOS..for now I just want it to work.”

I see this as a great opportunity for those of us in the field to help the next generation of programmers get involved with speech. I don’t have lots of free time but I have been helping him out some. But I would like to see this as a group effort. If everyone gave just a little time then just think of what these kids could accomplish and just think of the elementary students that would be helped.

If you would like to get involved then email me (marshall at got speech dot net) and I will make the connections for you. Think of how awesome it would be to mentor some High School student as a way of giving back to the community.

“No man stands so tall as when he stoops to help a child” (Abraham Lincoln).

I hope to hear from you and thanks for reading.

Paul Robinson wrote a great article about NextUC. Here is a excerpt:

There’s a new guy on the UCaaS block that’s worth a second look. This new purveyor of cloud-based UC is NextUC – currently a division of CallTower, but with a spin-off in the works with expected completion just around the corner, say June-July timeframe. NextUC comes along with both an intriguing business model and set of solution offering… read more

Lync for Everybody

I am a firm believer that Lync is for everybody and by “everybody” I mean small business that don’t have the expertise or the equipment to do an on site Lync installation. I admit that I work for NextUC so maybe I am somewhat prejudiced but I think we have a great solution for the SMB market. Where else can you go and in 15 minutes be live with Enterprise Voice, external phone numbers and UM integration?

NextUC