Pages

Monday, June 6, 2016

PowerShell Script to Add Normalization Rule to Multiple Dial Plans


During some recent Lync and SfB deployments, there has been a need to add a normalization rule to all existing dial plans or only user dial plans. Of course, the easiest way to add the same normalization rule to multiple dial plans is to script it. So here is the info for the script as well as the script itself:


First create the normalization rule in one of the dial plans. For example, you can create a "Catch All" rule in the Global dial plan that will normalize any number to prepend it with a +. The rule will look like this:

Normalization rule name: Catch All
Pattern to match: ^(\d*)$
Translation pattern: +$1

So the idea is to copy the normalization rule that you just created in the Global dial plan to all the User dial plans that already exist (I will explain later how to copy to All dial plans including Site dial plans later in this post). What the script is doing is getting all dial plans that begin with Tag:. This is how User dial plans are identified. Then it will get the normalization rule settings and copy them into all the User dial plans. Here is the script:

$DialPlans=Get-CSDialplan -Filter Tag:*
$CatchAll=Get-CsVoiceNormalizationRule -Filter "Global/Catch All"

foreach ($D in $DialPlans) 
{
Write-Host ("Adding Catch All Normalization Rules to Dial Plan: " + $D.Identity)    
ForEach ($Rule in $CatchAll) {New-CsVoiceNormalizationRule -Parent $D.Identity -Name $Rule.Name -Description $Rule.Description -Pattern $Rule.Pattern -Translation $Rule.Translation -IsInternalExtension $Rule.IsInternalExtension | Out-Null} 
}

If you didn't create the rule in the Global dial plan but in a User dial plan, you would change the -Filter parameter in the second line of the script to be the name of the dial plan/normalization rule name. If you are not sure how to find that out, run the Get-CsDialplan cmdlet and get the name of the dial plan. For example, a User dial plan will look like this: Tag:NA-HoustonDP. User dial plans start with Tag: while site dial plans start with Site:. Then put a forward slash and the name of the normalization rule in that dial plan. Make sure to put quotes around the whole dialplan/normalizationrule name if you have spaces in the names.

So by default, this script will add this "Catch All" normalization rule to the User dial plans and it will be the last normalization rule in the list of normalization rules. The order can be changed by setting the -Priority parameter. Be careful with this though because if you have a lot of global dial plans, you will need to make sure that it doesn't take precedence over some of the normalization rules it shouldn't. Otherwise, you will have to go into each dial plan and move the rule to the appropriate order.

Also, you can narrow the dial plan selection down to just site dial plans by changing the -Filter parameter on the first line of the script from Tag:* to Site:*. This will choose just the site dial plans to add the number normalization rule. And if you want to add the normalization rule to all dial plans, just remove the -Filter parameter all together.

Hope this helps. Let me know if you have any other questions, comments or issues.

Tuesday, February 16, 2016

Do not install .NET 4.6.1 on Lync or Skype for Business servers


.NET 4.6.1 is available and is a recommended update on Windows Update. Both the Exchange team and SfB team have both recommended you do not install this update on Exchange or Lync/SfB servers.

Guidance on how to block the installation of the recommended update: https://support.microsoft.com/en-us/kb/3133990

Link to the NextHop blog with this info.

Link to the Exchange team blog with Exchange info and information on how to roll back to .NET Framework 4.5.2 if 4.6.1 was already automatically installed.

Thursday, August 22, 2013

Microsoft Enables Remote Powershell for Lync Online


As announced by the Office 365 Team here, Microsoft finally enabled remote Powershell for Lync Online. Here is the link to Technet's Introduction to Lync Online Powershell Documentation which will give you information about how to connect, list available Powershell cmdlets, and other general information about the new feature. You can download the Powershell Module for Lync Online here and see the list of all Lync Online cmdlets here.

Thursday, August 1, 2013

Script to mass enable users for Exchange UM

During Lync deployments, I have often run into the need to mass enable multiple users for Unified Messaging. To do this, I altered an existing script (I cannot remember where I found it) to do this using a CSV file. Here is the script:

if ($args[0] -eq $null) 
{
$userNameFile = read-host "Please enter the full path of the .csv file with user aliases."
$usernamefile  = $usernamefile -replace '"',"" 
} else {
$usernamefile = $args[0]
}
if($userNameFile -ne "") 
{    
$csv=import-csv $userNameFile 
} else {
"Unable to find a valid CSV with user Aliases, extensions and UM policy name. Please try again later."
exit
}
foreach($c in $csv )
{
"Enabling  User " + $c.Alias + " using UM mailbox policy = " + $c.umpolicy
Enable-ummailbox -id $c.Alias -PinExpired $True -ummailboxpolicy $c.umpolicy -extensions $c.extension   
}

The CSV file for this script will look like this:
Alias extensions umpolicy
User1 1234 Policy Name
User2 1235 Policy Name
User3 1236 Policy Name

The Alias column can contain any of the following values for the Identity parameter:
  • ADObjectID
  • GUID
  • Distinguished name (DN)
  • Domain\Account 
  • user principal name (UPN)
  • LegacyExchangeDN
  • SmtpAddress
  • Alias
If you want the PIN to not prompt the user to change it on first log on, change the -PinExpired value to $False. You can also manually set the PIN if the need arises by adding '-Pin $c.pin' to the Enable-ummailbox line and add a column to the CSV file with the header of 'pin'.

Let me know if you have any questions or comments about this script. Hope this helps.

Friday, July 12, 2013

Lync 2013 Server Prerequisites on Server 2008 R2 SP1

The following Powershell cmdlets will install the system prerequisites for Server 2008 R2 SP1:

Import-Module ServerManager

Add-WindowsFeature RSAT-ADDS,desktop-experience,Web-Server,Web-Static-Content,Web-Default-Doc,Web-Scripting-Tools,Web-Windows-Auth,Web-Asp-Net,Web-Log-Libraries,Web-Http-Tracing,Web-Stat-Compression,Web-Dyn-Compression,Web-ISAPI-Ext,Web-ISAPI-Filter,Web-Http-Errors,Web-Http-Logging,Web-Net-Ext,Web-Client-Auth,Web-Filtering,Web-Mgmt-Console,msmq-server,msmq-directory,Telnet-Client

Then download and install the following:
.NET 4.5 Framework (install this before Powershell 3.0) - http://www.microsoft.com/en-us/download/details.aspx?id=30653
Powershell 3.0 - http://www.microsoft.com/en-us/download/details.aspx?id=34595
Windows Identity Foundation - http://www.microsoft.com/en-us/download/details.aspx?id=17331
Windows Update (heap corruption in IIS 7.5) - http://support.microsoft.com/?kbid=2646886

I have not had a chance yet to test this yet as I only installed Lync 2013 on Server 2008 R2 SP1 last year. If there is anything missing or it errors out, please let me know. I will remove this statement once I get a chance to test this. *Update - I have run this script during a recent Lync 2013 deployment for a client and it is valid.

Wednesday, May 29, 2013

Lync 2013 replication issue with the Edge server installed on Windows Server 2012

On a recent Lync 2013 deployment to migrate from Lync 2010, I ran into an issue with the Lync 2013 Management Store Replication not working on the Edge servers that were installed on Windows 2012 OS (all the servers in the Lync 2013 deployment were on 2012). I notice the red X's on replication on the Lync Control Panel and I ran the PowerShell cmdlet Get-CsManagementStoreReplicationStatus showing UpToDate as FalseLastStatusReport blank and ProductVersion blank for both Lync 2013 Edge servers.

The event logs on the Lync 2010 Front Ends, Lync 2013 Front Ends and Lync 2013 Edge servers didn't show any errors or warnings stating that replication was failing. I checked all the usual suspects:

  • Certificate issues
    • I looked and verified the internal certificate had the proper Subject Name
    • Verified the internal root CA certificate was in the proper location
    • Verified that certificate checks based on the Microsoft KB article Lync Server 2013 Front-End service cannot start in Windows Server 2012 http://support.microsoft.com/kb/2795828 was not the case
  • Verified I could telnet to the Edge servers from the Front Ends over port 4443
  • Verified DNS was correct
  • Verified the computer name on the Edge servers were correct with the proper primary DNS suffix entered
  • Performed a WireShark trace on the Edge server and verified that there were connections to the server over port 4443
...and finally performed an comprehensive web search about this issue with no luck.

I spent enough time on this issue and decided to enroll the help of a very knowledgeable coworker named James Denavit (who happens to be a Lync 2010 MCM) before I throw in the towel and call Microsoft Support.

Luckily for me, James had the great idea of running the Lync Logging Tool from one of the Lync 2010 Front End servers. We ran it on the 2010 Front End because I did not move the CMS to the Lync 2013 yet. So we ran logging with the following options:
 
After running the Invoke-CsManagementStoreReplication PowerShell cmdlet from the Lync 2010 Front End server a couple times. I stopped the Logging tool and analyzed them. What we found were the following warnings:
 
One of the red flags we saw was:

TL_WARN(TF_COMPONENT) [2]0560.2E58::05/24/2013-20:25:24.920.4d11ccab (XDS_File_Transfer_Agent,FileTransferTask.CopyFilesFromReplicaUsingWcf:filetransfertask.cs(644))
(0000000002D2AF4D)[FileTransferTask(7, 5/24/2013 1:21:30 PM): {TASK_NOT_STARTED, fromReplica, [L13EdgeServerFQDN, HttpsWebService, 4443], 0}] Failed to copy files from replica. Exception: [System.ServiceModel.EndpointNotFoundException: Could not connect to https://L13EdgeServerFQDN:4443/ReplicationWebService. TCP error code 10061: No connection could be made because the target machine actively refused it 10.61.1.204:4443.  ---> System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it 10.61.1.204:4443
   at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)

James did a search based on this and found the following post from The Lync Guy Blog:

I did find this blog post during my web search but lucky for me, James read the responses (which obviously I didn't) and pointed out the response from Jonatan talking about adding the registry entry: DWord value SendTrustedIssuerList to the HKey_Local_Machine\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL key and assigning it a value of 0.

After modifying the registry, I rebooted the Edge server and ran the Invoke-CsManagementStoreReplication PowerShell cmdlet from the Lync 2010 Front End server a couple times again and still no luck. I then proceeded to read more of the responses and the last response from Chris Duva stated that he added another registry entry. 

I added the registry entry DWord value ClientAuthTrustMode to the HKey_Local_Machine\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL key and assigning it a value of 2. I rebooted the Edge server again and ran the Invoke cmdlet again and voila!!! It worked. 

Since I had 2 Lync 2013 Edge servers, I just wanted to verify that only the ClientAuthTrustMode registry entry was needed so I added only that entry to the second Edge server and rebooted it and it worked! So I removed the registry entry for SendTrustedIssuerList from the first Edge server, rebooted it and verified that the replication still worked. 

Thanks to James for helping me with this issue as well as answering questions whenever I have them.

Hope this helps.


Thursday, March 28, 2013

Lync PowerShell Script to Mass Enable Users

* Updated 1/22/15 - I added the option to add dial plan and voice policy as well to the script
* Updated 6/20/13 - I forgot to add the dash in set-csuser on the bottom script so the enable-csuser cmdlet would fail.

Of course there is always a need to mass enable users for Lync once you have Lync deployed in your environment. The easiest way to mass enable users for Lync is via a PowerShell script and a CSV file with the user information. 

To enable PC-to-PC users only
Here is a script I created to mass enable users:

if ($args[0] -eq $null)
    {
    $userNameFile = read-host "Enter the full path of the .csv file with the user information."
    $userNameFile  = $usernamefile -replace '"',""} 
else 
    {$usernamefile = $args[0]}
if ($userNameFile -ne "") 
    {$csv=import-csv $userNameFile} 
else 
    {"Could not find a valid .csv with the user information."
    exit}
foreach($c in $csv)
# enable for lync
{
"Enabling " + $c.Identity + " for Lync 2010"
Enable-csuser -identity $c.Identity -registrarpool $c.RegistrarPool –sipaddresstype $c.SipAddressType -sipdomain $c.SipDomain
}

This script will enable the users with basic PC-to-PC configuration. This script allows for organizations with multiple SIP domains and multiple pools with the columns in the .csv file for RegistrarPool and SipDomain. The .csv file will look like this:
Identity RegistrarPool SipAddressType SipDomain
John Doe PoolFQDN.Domain.com EmailAddress SIPDomain.com


To enable Enterprise Voice users
To enable the users for Enterprise Voice, we will make the following addition (highlighted in yellow and green) to the above script. The green highlighted section is optional if you have extensions set up in your LineURI’s like tel+12815551234;ext=1234. You can omit this section and remove the Extensions column from the .csv file if you are not using extensions in your LineURI’s:

if ($args[0] -eq $null)
    {
    $userNameFile = read-host "Enter the full path of the .csv file with the user information."
    $userNameFile  = $usernamefile -replace '"',""} 
else 
    {$usernamefile = $args[0]}
if ($userNameFile -ne "") 
    {$csv=import-csv $userNameFile} 
else 
    {"Could not find a valid .csv with the user information."
    exit}
foreach($c in $csv)
# enable for lync
{
"Enabling " + $c.Identity + " for Lync"
$lineuri = "tel:+1" + $c.PhoneNumber + ";ext=" + $c.Extension
Enable-csuser -identity $c.Identity -registrarpool $c.RegistrarPool -sipaddresstype $c.SipAddressType -sipdomain $c.SipDomain 

# Pause for 30 seconds for AD Replication
write-host -foregroundcolor Green "Pausing for 30 seconds for AD Replication"

Start-Sleep -s 30

Set-CsUser -Identity $c.Identity -enterprisevoiceenabled $True -lineuri $lineuri
Grant-CsDialPlan -Identity $c.Identity -PolicyName $c.DialPlan
Grant-CsVoicePolicy -Identity $c.Identity -PolicyName $c.VoicePolicy
}

As you can see, the script turns the 10 digit phone number into E.164 format and adds the extension (if needed) and then sets it as the LineURI for the user. The .csv file will have the following columns added for this script:


PhoneNumber Extension DialPlan VoicePolicy
2815551234 1234 HoustonDialPlanHoustonVoicePolicy