Tuesday, December 6, 2011

What to do when AD phone property is formatted wrong.

In my previous post, I just showed you how to search for a security group in AD, and set cs-user properties based on the selection of users.

My script is based on these users having 100% correct formatted phone attribute in Active Directory. But as some of you already know, that might not be the case. Some have formatted their phone numbers with spaces like this: +47 22 22 22 22, some might have used other characters like this +1 (454) 123 4567 and others might not have the proper E.164 international formatting and have left out their international code: (202) 123 4567.

Fear not, I will tell you how to handle this (as long as they all have the same "error" in their syntax).

If you look at the following command:

Get-ADGroup -Identity TestGroup | Get-ADGroupMember -verbose | foreach {get-csaduser -identity $_.Name | foreach {
$sipit = $_.phone
$sipit = "tel:" + $sipit
Set-CsUser -Identity $_.Name -EnterpriseVoiceEnabled $true -LineURI $sipit -PassThru | Grant-CsClientPolicy -PolicyName restrict}}

You can see we grab the "phone" attribute and populate it into a variable I have called $sipit. Then I add the "tel:" to the already existing $sipit variable. After this, the output of $sipit is "tel:-whateverwasinthephoneattrib". If the phone attrib is wrongly formatted, we need to alter this before we add the "tel:". We can do this using the -replace command. And I will sow you two examples of how to do this.

1)

Get-ADGroup -Identity $groupfetch | Get-ADGroupMember -verbose | foreach {get-csaduser -identity $_.Name | foreach {
$sipit = $_.phone
## Next line will remover anythin in the string that is not a number (including +, spaces, () and more)
$sipit = $sipit -rplace "[^0-9]"
## Next line is slightly modified to add the + back into the phone number
$sipit = "tel:+" + $sipit
Set-CsUser -Identity $_.Name -EnterpriseVoiceEnabled $true -LineURI $sipit -PassThru | Grant-CsClientPolicy -PolicyName restrict}}

Given the command above you would take a number like this +1 (234) 567 8900, or this: +1 234 5678900 and turn them into the correct lineURI format tel:+12345678900.

2)
Just as an example, I'll show how to add the national prefix if this is not present in the AD property:

Get-ADGroup -Identity $groupfetch | Get-ADGroupMember -verbose | foreach {get-csaduser -identity $_.Name | foreach {
$sipit = $_.phone
## Next line will remover anythin in the string that is not a number (including +, spaces, () and more)
$sipit = $sipit -rplace "[^0-9]"
## Next line is slightly modified to add the + and the international prefix into the phone number
## I used the North American code +1 in the example. You use whatever you need to in your deployment.
$sipit = "tel:+1" + $sipit
Set-CsUser -Identity $_.Name -EnterpriseVoiceEnabled $true -LineURI $sipit -PassThru | Grant-CsClientPolicy -PolicyName restrict}}

Quite simple, and a very quick way to enable telephony for your users. have fun labbing, and please let me know what you think about these posts.

If you need more power shell commands for Lync, I can really recommend the Lync Powershell Blog  They have a great collection of examples.


Wednesday, November 30, 2011

Enable or edit Lync users based on AdGroupMembership

In my previous blogposts on Lync user management using PowerShell, I have demonstrated how you can edit  by using an OU search string, or import a csv file. I thought maybe it would be nice to show how to add/manage Lync users by searching for a AD Group. It might be a bit tricky, but it is possible.

I have created a tiny script to prompt the admin for the group name, find users within that group, enable them for Lync and finally enabling the user for Enterprise voice (Presuming the phone attribute in AD is populated in the correct E.164 format, or the script might need adjustment) and setting a specific policy (just because I can, not because I have to).

Something worth mentioning, the set-csuser doesn't seem to like being pipelined to, which is why the get-adgroup is run twice.

The first part of the script is not doing anything, it's just a reminder to import the ad module. The first thing you have to do, is to enter the ADGroup Name (the display name of the group).


When the ADGroup is known to the script, we move on to enabling those users for Lync (skip this part if they are already enabled, but you want to edit users). This is quite straight forward.


But I'm not done yet. I also want to enable the user for voice, and grant him a client policy (These are just examples. Your imagination of PS command combinations is your only limitation).

Doing all I wanted to gave me an unexpected problem. As it turns out, the "get-adgroupmember" will not return the phone property of the user, and it became a challenge to figure out a way to get that property. That is why there are two foreach statements with in this last section. After we get hold of the adgroupmemner name, we can run the get-csaduser with that name (A bit dirty? I know, but it works).


As you can see, the script is fetching the $_.phone attrib of the user, and uses it to create the LineURI after adding the "tel:" prefix.

If you want to take a closer look at the actual script, it can be found right here. Have fun playing with it :)

Tuesday, November 15, 2011

Lync script/application to reroute a specific number

There are many Lync installations out there in a need of a 3rd party switchboard. In some of these installations the switchboard number is non-existent for the Lync environment (it is no more than a voice route to a pstn gw through a mediation server). And if we do nothing, the incoming call to the switchboard will be rejected by the registrars as "no user configured with this number" aka user not found.

I found a "dirty way" of fixing this, by creating a unassigned rule for it. Catching the number, sending it (targeturi) to a new destination and then translate it to the correct number on the way to the switchboard application server (through a pool trunk).

The problem with this, is the way it has to be set up, with several translations and rewrites. It became rather difficult to troubleshoot.

My solution to the challenge turned out to be to create a new server application, run before any other app, to catch the call and reroute the sip call to the trunk directly. And this is how I did it:

First of all, I downloaded and installed the "Lync server 2010 sdk" (found here) on the Lync frontend servers. And then I tried to my best to understand the documentation (located in the sdk installation, or online found here).

As it turned out, I was to fresh in the game to understand every little detail I had to think of, and needed some examples to work from. After doing some searches in forums and blogs related to the subject, I finally stumbeled upon a script created for OCS 2007 R2.

I took the script, and modified it to suit my needs, and Lync's way of addressing trunks (not mediation servers) in the reroute statement. It turned out to be quite easy, and I would like to share the script with you.

First we have to create the manifest, and describe what we are looking for. This is done by using standard methods well described in the documentation. One of the important things to notice, is the appURI which has to point to an existing dns entry. The final part is case sensitive, and must match what you later use in Lync Powershell when creating the application. The other thing I want to highlight, is the "requestfilter" which has been set to inspect the "INVITE" element.


Next we look through the sip messages, and try to filter out those messages which do not concern us. Things like "ignor none sip messages, ignore re-invites and ignore calls from other servers.


Then, we have to tell the application what to look for:


And finally we do the lookup and reroute to the static information. A quick note about the new to sip uri. This is set up to route directly to the PSTN GW defined as the destination in the topology, with both port and name of the designated mediation server (to save lookup time).



With the script all finished, save it to disk on the frontend server, and note the path.

Next, it's time to add the application to the frontend server. This is done by running the new-csserverapplication cmdlet (about) (note: the name here is case sensitive and must match the statement in the application manifest. I have also set the priority to 0, so it is run first):

new-csserverapplication -identity "registrar:lyncpool.customer.com/RouteExternalToAttendant -uri "http://www.customer.com/RouteExternalToAttendant -priority 0 -critical $false -enabled $true.

Finally, if the application isn't run, try to stop and start the application server through cscp. Use the SDK apilogger to verify contact and execution. Monitor the eventviewer for application compilation errors.

That should be all. As before, I have a sample script right here for you to read through.

And thanks again to my ex-colleague and friend who helped me fix this in the middle of the night.

Sunday, October 16, 2011

Forcing the Lync client to use MAPI

There are certain scenarios where I would like to provide Lync for users who belong to a different forest than the Lync installation is in. If you can set up a trust and federation between the two forests, then all is well. “All” you have to do, is to follow the following for the users in the separate forest: http://technet.microsoft.com/en-us/library/gg670909.aspx. Or if you also host Exchange services (in the same forest) for the customer, you should not run into any issues.

But, if for some reason you cannot set up the Lync forest as a resource forest for the customer/user, and you do not host the customers Exchange as described, you might run into some challenges with the Exchange integration. I have personally run into these scenarios a couple of times, and have been faced the following client side challenges:

  1. If the customer has not deployed EWS, the Lync client will not find any EWS and default to MAPI. Except for some missing features (I will get back to), “all” is well.
  2. If the customer has deployed EWS but the EWS is unreachable, the client defaults to MAPI
  3. 3. If the customer has deployed EWS and it is reachable by the Lync client, the authentication fails and MAPI is not used. 
The two first scenarios are not too common, but would give most of the functionality for the user (The user might be prompted once for logon with MAPI credentials.

The 3rd scenario is (in my experience) the most common scenario, and renders the Lync client without any integration at all. As it turns out, when the Lync client tries to access EWS it uses the same credentials it has stored for the Lync client logon. It will not prompt you for correct credentials if EWS authentication fails. And it will not fall back to MAPI, as it sees the EWS as up and running. (While if EWS is not available, and MAPI is chosen you will get prompted for credentials if logon fails).

I’ve been looking far and wide for a way to force the client to use MAPI instead EWS in these scenarios. And after a lot of research and quite a few emails with Microsoft support, I have finally been able to “trick” the Lync client to use MAPI.

Functionality 
Well aware of the loss of functionality when forcing MAPI, I have found this a lot better than no functionality at all. The features you will not get when forcing MAPI, include:

  • Read working hours information 
  • Handle Exchange contact sync
  • Read or delete Conversation History items
  • Read or delete voice mail items 
But the clients will still be able to do the following:

  • Read free/busy times 
  • Read Out ofCreate the Conversation History folder 
  • Handle voice mail notifications 
  • Handle missed Conversation notifications 
  • Read Contacts folder 
  • Find related conversations 
  • Open contact card 
  • Create a personal Outlook contact 
  • Open voice mail 
  • Write contacts (on demand) (EWS and Exchange Server 2010 SP1 only) 
  • Write Conversation History items (on demand) Office message 
  • Communicate with Exchange delegates 
  • Send email message 
  • Schedule a meeting 
  • Open voice mail folder 
  • Open the Conversation History folder 
All of this is taken from the following guide: http://technet.microsoft.com/en-us/library/gg398806.aspx (You should read it before thinking of doing what is later described in this post, as the article will be updated as the product might change).

Warning!
If you read the the rest of the blog, and try to implement this in your own system, you do so on your own risk. This solution is no way supported by Microsoft in any way, nor by me. But I have tried this setup in three systems that all work as expected.

Not all of the settings I discuss are necessary, but I have added them to help you make the integration as smooth as possible. These setting should be done before enabling users for Lync (Or you will have to go back and possibly edit users later)

Client tweaking:
First of all, by default, Lync expects the email address of the user to match the user sip URI. To avoid this to be an issue, I disable the email comparison check. Also, the MAPI poll interval is 30 minutes, and this can be a bit long. I wouldn’t set it too low, but 10-15 minutes should be fine. Both of these settings can be done with the set-csclientpolicy command, like this: 

Set-CsClientPolicy -DisableEmailComparisonCheck $true -MAPIPollInterval 00:15:00

Read more about the set-csclientpolicy and its options here: http://technet.microsoft.com/en-us/library/gg398300.aspx

How does Lync know what to look for:
The key to trick the Lync client to use MAPI is to understand how the Lync client tries to access the Exchange EWS. The lync client uses the Active Directory user object’s mail attribute to find the email server. It will use the mail attribute to start the auto-discover process to find the exchange services. If the attribute is empty, it will simply not try to connect to Outlook or Exchange. At least that is what I have experienced.


The easiest part in tricking the Lync client is putting a fake email address/domain in the mail attribute. Something like absolute@nonsence.foo (important: domain must not exist, now or ever). Once this is done, the Lync client will try to autodiscover the “nonsence.foo” domain and fail. It will state: EWS not deployed, and fail over to MAPI.

New problem
But by resolving one problem, we create ourselves another problem; The generated address book will show the fake address, and client interaction (such as sending email) will not work at all.



Next move:
To solve this problem, you need to alter from which field the addressbook service populates its data. It turned out this was just as simple as the previous task.
I downloaded, and used the address book configurator from DrRez: http://blogs.technet.com/b/drrez/archive/2011/01/31/microsoft-lync-server-2010-resource-kit-tool-absconfig.aspx.

First I chose a different user property to populate with the correct email address (I often use the “info” field).


Then, I launch the absconfig and change the email value.

!Warning #2! The absconfig was written for OCS, not for Lync. It works for changing fields, but under no circumstance should you try “restore to default” (or try any of the other features in the tool, it will possibly mess up your system. Read more about it here: http://waveformation.com/2011/02/11/restore-lync-abs-default-configuration/ if you should have done so before reading my warning. I did, and I used this post to repair my system).



After editing your address book behavior, you might want to follow Jeff’s way to update your addressbook: http://blog.schertz.name/2010/09/updating-the-lync-2010-address-book/ Then the user should look alright:



You should also remember not to enable Lync users with the email address as their sip address after doing this, as it will pick the wrong attribute to create a user from:


Friday, September 30, 2011

A quick note to self on adding many locations through PowerShell

I must admit; On most of my Lync installations, bandwidth has not been an issue for me to worry about. Or, I've been adding just a few sites and policies manually through CSCP.

Well, this week all of that changed. A customer I'm working with these days, are implementing a full PBX replacement with Lync Ent Voice and BW is a major issue at several sites (at least until the customer gets to upgrade). They sent me a list of 100 sites with 10Mb or less on their wan links connected to the main site.

I was taken aback on the proposed amount of time I would spend typing, if all of this was to be entered manually into the system. But thanks to the import-csv and foreach functions of powershell, I was able to save a lot of time.

I sat down and discussed a few options for the customer, and decided to narrow it all down to a set of three profiles to keep things simple. (If you're intrested in my way of reasoning on cac, and why it is important, you might want to read my previous post on the matter)

I spent some time tweaking the script, and made a few test-runs using -whatif, but in the end, I was done configuring the system before lunch (An I thought I would spend days typing this manually).

The "trick" is as always, to gather the information in a readable csv-file. Then add a header row on which the import job can sort things on. Here is a sample of my file (total fiction, but real enoug). Notice how I use the "," as the seperator, not the ";" as is the default when saving from Excel:

I've shortened the list to 30 locations, just for the example. And using "Location 1" isn't the most descriptive name, but you'll get the point.

With this file at hand, I created the following script make it all happened. As my script filter on the "BW" column, I had to run this script X-times (where X = the number of XXMB variations in the file), selecting the chosen profile each time. If you are going to use this script yourself, you only run the "New-CsNetworkBandwidthPolicyProfile" once. Either remove it, or remark it out before running it over and over again.


I know this is a really simple implementation of CAC, as I haven't touch upon regions and region links in here. But that might be food for another post down the road.

If you want more reading material on the subject, I can recommend the planning section and deployment section of the Lync Library on Technet

That's how easy your life can get with the power of powershell :) I was not thrilled when first introduced, but now I'm getting quite fond of it. I might even see the need for a course, or at least buy a book on the subject soon.
For your convenience, I have the sample script and file right here to download :)

Labels

Lync (15) Microfoft Lync Server 2010 (15) Microsoft (14) Windows (12) Cisco (11) Voice (11) OCS 2007 (9) CCIE (8) CUCM (8) OCS 2007 R2 (8) Windows Server 2008 (8) Powershell (7) Certifications (5) Internetworkexpert (5) Lab (5) Server (5) Beta (4) CUCM 7 (4) CUCM 8 (4) Freeware (4) MS Exchange (4) Mediation Server (4) Microsoft Office Communicator 2007 R2 (4) Software (4) Unified Messaging (4) Virtual Machine (4) Windows Live (4) Browser (3) CAC (3) Call Manager (3) Chrome (3) Google (3) Graded Labs (3) IPT (3) Iewb-vo-vol1 (3) Install (3) Internet (3) SQL 2008 (3) Upgrading (3) Virtual PC (3) Windows 7 (3) Windows Server 2008 R2 (3) enable-csuser (3) import-csv (3) set-csuser (3) Active Directory (2) Address Book (2) Bandwidth Management (2) Blog tool (2) CUCILYNC (2) Certificate (2) Exchange 2010 (2) Exchange Roles (2) HYPER-V (2) IIS (2) MTP (2) New-aduser (2) R2 (2) Remote control (2) SIP (2) SIP Trunk (2) SQL (2) SSL (2) UC (2) USB (2) User (2) VMWare (2) Virtual Server (2) Voice policies (2) get-adgroup (2) get-adgroupmember (2) 074-924 (1) 0x80070534 (1) 0xC3EC796C (1) 32bit (1) 350-030 (1) 443 (1) 5062 (1) 64bit (1) A/V authentication (1) ADDS (1) Application server (1) Assigned (1) Attendant (1) Brussels (1) CD (1) COMMUNICATIONS MANAGER (1) CUCIMOC (1) CUPS (1) CWA (1) Certificate Authority (1) Certification (1) Charter Member (1) Cisco Presence (1) Communicator Web Access (1) Conference (1) DL360 (1) DNS (1) Deployment (1) Desktop (1) Digit manipulation (1) Domain (1) Dual-boot (1) ESX (1) EWS (1) Edge (1) Encryption (1) Enterprise Voice (1) Error 3221684226 (1) Error 404 (1) Error 503 (1) Event ID 32018 (1) Exchange (1) Exchange 2007 (1) FTP (1) Fail (1) Filemerge (1) Front-end server (1) General network (1) Global Knowledge (1) HCL (1) HD (1) HP (1) HTC Touch (1) INVITE (1) Just for fun (1) Limited External Calling (1) LineURI (1) Linux (1) Livemeeting (1) Lync Server SDK 2010 (1) Lync client (1) MAPI (1) Microsoft Communications Server R2 Attendant (1) Microsoft® Community Contributor Award (1) NTP (1) Netwise CMG (1) Netwise CMGVoice (1) New-CsNetworkBandwidthPolicyProfile (1) New-CsNetworkRegion (1) New-CsNetworkSite (1) New-CsNetworkSubnet (1) Normalization (1) OCS validation (1) Office 2007 (1) Office 2010 (1) PSTN GW (1) Planning Tool (1) RC (1) RDP (1) RSAT (1) Rundtomrundt (1) SDelete (1) SP1 (1) SP3 (1) Sipreroute (1) Switchboard (1) Sysadminday (1) Sysinternals (1) System administrator (1) TeamViewer (1) Topology Builder (1) Translation-rule (1) TrueCrypt (1) Twitter (1) UC520 (1) UM (1) Ubuntu (1) Ubuntu 9 (1) Unassigned (1) Windows Firewall (1) Windows Vista (1) Windows XP (1) aboutme (1) csps (1) cspshell (1) csv (1) get-aduser (1) get-csaduser (1) grant-csdialplan (1) grant-voicepolicy (1) new-csserverapplication (1) notepad (1) passwords (1) set-aduser (1)