Identity Manipulation in UCMA-getting Impersonation to work through a mediation server

There’s a UCMA method on the Microsoft.RTC.Collaboration.Conversation class called Impersonate that lets you place an outbound call as if you were a different user.  You provide the method with a SIP URI, phone URI, and Display Name, and usually it works as expected, but there are some cases where things can fall apart pretty spectacularly if you’re not careful. 

First, let’s look at the case where things work as expected.  Say I want to create a UCMA app that accepts a call from user A (sip:cb@rnddev.computer-talk.com), does something, and then makes an outbound call to user B (sip:cbardon@rnddev.computer-talk.com).  Normally, I’d get something like this when I placed the outbound call:

image

Which is the identity of my application.  When I create my outbound call though, I could do something like this with the conversation:

conversation.Impersonate(“sip:cb@rnddev.computer-talk.com”,null,”cb”);

And then when I establish my call, the inbound call to User B would appear to be from User A, and not your app

image

If all the calls are between Lync endpoints, then everything is fine here.  What if you involve a mediation server or the PSTN though?  First, consider the case where you get an inbound call from the PSTN to your app, and then try to impersonate that caller like this:

conversation.Impersonate(“sip:+19058825000;ext=3154@rnddev.computer-talk.com;user=phone”,null,”Chris Bardon”);

You’d get a result like this:

image

Note that the URI parameter MUST be specified, but I left the phoneUri parameter blank.  This is because the uri is a phone URI already, and trying to specify the phoneUri again in this case would throw an exception.  In any case, an inbound call from the PSTN impersonates correctly.  Now, what about the case where I want to initiate a call from a Lync user out to the PSTN like this:

conversation.Impersonate(“sip:cb@rnddev.computer-talk.com;user=phone”,”tel:+19058140048”,”Chris Bardon”);

This case works just fine too (although it’s more difficult to get a screenshot of-you’ll have to trust me on this one). 

So for the last case-what happens if both the inbound and outbound call are through a mediation server?  As it turns out, this case fails rather spectacularly.  If you try something like this:

conversation.Impersonate(“sip:+19058825000;ext=3154@rnddev.computer-talk.com;user=phone”,null,”Chris Bardon”);

And place a call out to a PSTN endpoint, your app will get a 404 that looks like this:

TL_INFO(TF_PROTOCOL) [0]1F00.1B54::07/03/2012-18:58:29.425.019eda5f (SIPStack,SIPAdminLog::TraceProtocolRecord:SIPAdminLog.cpp(125))$$begin_record
Trace-Correlation-Id: 834215393
Instance-Id: 00024EB3
Direction: incoming
Peer: chrislaptop.corp.computer-talk.com:50061
Message-Type: response
Start-Line: SIP/2.0 404 Not Found
From: <sip:chrisice70_1@rnddev.computer-talk.com;gruu;opaque=app:conf:audio-video:id:1CBIIRTU>;tag=1e2d289a90;epid=72D4E27D44
To: “chrisice70_1″<sip:chrislaptop.corp.computer-talk.com@rnddev.computer-talk.com;gruu;opaque=srvr:chrisice70:_m411PC3cFaS4dn7x0olnAAA>;tag=f4a3e8bc9a;epid=E73F01CD8F
CSeq: 2034 INVITE
Call-ID: 722b999c-9af3-4fca-8e66-c9968d31dcd2
VIA: SIP/2.0/TLS 192.168.201.74:57523;branch=z9hG4bKC9BC6AB4.6E7020D901A5DDAF;branched=FALSE,SIP/2.0/TLS 192.168.201.74:58100;branch=z9hG4bK4f5e3f5;ms-received-port=58100;ms-received-cid=437600
CONTENT-LENGTH: 0
PRIORITY: Normal
SUPPORTED: Replaces
P-ASSERTED-IDENTITY: <sip:15de4e29-d190-491b-920a-f46c79f087ec@rnddev.computer-talk.com>
SERVER: RTCC/4.0.0.0 chrisice70
ms-diagnostics: 1003;reason=”User does not exist”;TargetUri=”+4165752695@rnddev.computer-talk.com”;source=”LYNC2010.rnddev.computer-talk.com”
Ms-Conversation-ID: eb54476dad1e4eada19648d1ba329373
Message-Body: –
$$end_record

The key here is the “User does not exist” flag in the ms-diagnostics, although it should be fairly obvious that the user doesn’t exist on Lync, because it’s not a user, it’s a phone number.  Digging into the logs a little more, you could look at the outbound routing log and see something like this (cleaned up for readability):

Creating a OutboundRoutingTransaction object (57)
Enter
From uri: sip:+19058825000;ext=3154@rnddev.computer-talk.com
From User Uc Enabled: False
Referrer URI: <null>
IsAvMCUDialOut: False
Alternate Tel URI: <null>
IsEmergencyCall = False
Checking for Vacant Number range. Request URI = +4165752695
Checking for Vacant number entries for +4165752695
No matching range found.
No matching Vacant Number Range found.
+4165752695 does not match any Vacant Number range
Checking for CPS range. Request URI = +4165752695
Checking for CPS entry for +4165752695
Input prefix [+] does not match that of range [ ]
No matching range found.
No matching range found
+4165752695 does not match any CPS range
Applying From URI’s outbound policy
Routing request based on caller: sip:+19058825000;ext=3154@rnddev.computer-talk.com
Caller not UC enabled.
Stamping request from non UC enabled user and sending request on its way…
Exit

Basically, this is the outbound routing engine deciding how to route the call to +4165752695.  It determines that the from URI isn’t a UC enabled user, decides that the call doesn’t fall under the unassigned number range, and that it’s not a call park orbit.  It then decides to route the call based on the from user’s policy, which, since the from user isn’t a UC enabled user, is nothing. 

This actually falls under a case that’s documented on NextHop with respect to meeting joins, and the solution that they mention, creating static routes, might very well work.  I wasn’t able to get it working that way, and I managed to find a better alternative.  I did like Mike Stacy’s Post on creating static routes though, but in this case, circumventing all the normalization and routing rules seemed wrong, and like something that’d be difficult to get customers for this app to do.

The key to the solution was the “IsAvMCUDialOut” line-if an MCU dial out worked, then there must be a way to route like this, right?  As it turns out, an AVMCU call not only sets this flag, but also the “ReferrerURI” flag, which translates into the Referred-by header.  Simple enough then, I just added this to my outbound call establish:

outboundCallLegSettings.CallEstablishOptions.Headers.Add(new SignalingHeader(“REFERRED-BY”, “<sip:cb@rnddev.computer-talk.com>”));

Which also proceeded to fail.  As it turns out, Lync expects the referred-by header to be signed, so that it looks something like this:

REFERRED-BY: <sip:cbardon@rnddev.computer-talk.com>;ms-identity=”MIIBxQYJKoZIhvcNAQcCoIIBtjCCAbICAQExCzAJBgUrDgMCGgUAMAs GCSqGSIb3DQEHATGCAZEwggGNAgEBMGowXDETMBEGCgmSJomT8ixkARkW A2NvbTEdMBsGCgmSJomT8ixkARkWDWNvbXB1dGVyLXRhbGsxFjAUBgoJkiaJk/I sZAEZFgZybmRkZXYxDjAMBgNVBAMTBXJuZENBAgodMhniAAAAAAC0MAkGBSs OAwIaBQAwDQYJKoZIhvcNAQEBBQAEggEAKu4exuDavlYfjaJVJqu43mAZ+IK5My XKMkXAZoVm9dT8SS5col2meENQtdVhWRvcb7jGhbLAjpggPpvfQ2CD6CbkScwj5H5 jgcxX9tA4iIT4a3QiMSZA/A7tfVPJ9ipexBro/18eHEur2gpxY82QhNFXgcAzeofFTP+QB REIopLqWqgFlVccZcoUP6sC02L4qbxb1gzouyO+2lEUf+IVCdATMlBleWxQht7l6Dgc9Y 0xEutsWPg8a9ym5Q19rWb14OHUVxmCgFHJ6y56R3/aSUceQ844j/fhCRDctTa4zs6L/ GN5+H7z60vLUuME1utyiJOpXaZ1PB38mkig6KyCIA==:Tue, 03 Jul 2012 18:58:23 GMT”;ms-identity-info=”sip:LYNC2010.rnddev.computer-talk.com:5063;transport=Tls”;ms-identity-alg=rsa-sha1

Instead of this (which is what my message looked like):

REFERRED-BY: <sip:cbardon@rnddev.computer-talk.com>;

Now, there’s no documentation on how this hash is generated that I could find, so I tried some other solutions:

  • Setting P-Asserted-Identity directly?  Nope, Lync won’t let you modify a header after it’s been set.
  • Modifying P-Asserted-Identity in an MSPL script (actually an MSPL managed code app)?  This would work, but took a lot of work to set up.
  • Setting P-Preferred-Identity?  Had no effect…
  • Setting P-Session-On-Behalf-Of (which I stole from a delegate invite)?  Nope, no good.
  • Setting the Remote-Party-ID header?  Still nothing…
  • Mashing in Referred-By manually with a captured signature?  Strangely enough, this worked, but was in no way a manageable solution

Finally, I was looking through the CallEstablishOptions class again, and came across a property called Transferor.  I set it like this:

outboundCallLegSettings.CallEstablishOptions.Transferor = “sip:chrisice70_1@rnddev.computer-talk.com”

And the PSTN call went through with the correct caller ID.  I tried a Lync endpoint again and got a toast window that looked like this:

image

Which had the correct information on it as well.  Now, if only this had been a little more obvious in the documentation (say, when I searched the UCMA docs for Referred-By)…

So, to summarize, if you want to impersonate a mediation server endpoint back to a mediation server, you not only have to impersonate the caller, but you have to set the Transferor property on the CallEstablishOptions to make sure that the call is routed.  I’ve chosen to set this property in all cases, since it simplifies the code, but if you wanted to only set it in certain cases you would certainly have the option.  Keep in mind though, telling how a call is going to be routed from within your UCMA application can be tricky, and is subject to change based on the server configuration.  Also, using the Lync Server Logging Tool and Snooper is a great way to trace what’s going on with the server.  I never would have figured this out had I not compared my failed trace to a successful conference dial out trace and noticed the referrer information. 

Has anyone else run into the same problem, or encountered a case where this doesn’t solve their problem?  Leave a comment or drop me an email if you have.

Advertisements
Posted in Uncategorized | 3 Comments

Lync, UCMA, and DNS load balancing part 1

One of the features that Lync 2010 introduced was DNS load balancing.  In this scheme, requests for an FQDN can return multiple entries, and Lync will send requests to one of the pool endpoints.  There’s some decent information on technet about the basic idea with Lync servers, but there’s actually very little out there on how to use this scheme with UCMA applications.  In theory, it’s supposed to work the same as for servers, but it’s not necessarily evident how to get there.

Provisioning your app

A lot of times, we’ll run New-CSTrustedApplicationPool and pass the machine FQDN in as the pool FQDN, usually because the app is only going to run on one server at a time.  In this case though, we’ll actually have a pool FQDN and a separate ComputerFQDN like this:

PS C:\Users\cbardon> New-CsTrustedApplicationPool -Identity ucmapool.rnddev.comp
uter-talk.com -Registrar lync2010.rnddev.computer-talk.com -Site 1 -ComputerFqdn
ucma1.rnddev.computer-talk.com -RequiresReplication $false

This creates a new pool FQDN, as well as adds the first machine to it.  Next, you’ll need to add the rest of the machines to the pool like this:

PS C:\Users\cbardon> New-CsTrustedApplicationComputer -Pool ucmapool.rnddev.comp
uter-talk.com -Identity ucma2.rnddev.computer-talk.com

Which you repeat for each of the servers that you want in the pool.  Then, configure applications and endpoints the same as with a single computer pool-those still work exactly the same way as before. 

A catch for manual provisioning

Now, if you’re using automatic provisioning, then you should be able to skip on to the next section.  If you noticed the –RequiresReplication $false flag in the pool configuration though, you’d realize that this example uses manual provisioning, which is useful for cases where your app server can’t be joined to the Lync domain.  This means specifying some extra parameters when creating your platform though, including the GRUU.  When you created your application, you may have noticed that the output looked something like this:

image

Note that I have a service GRUU, as well as ComputerGRUUs for each machine in the pool.  For a single computer pool these are the same, but now each individual machine in the pool has it’s own GRUU as well as the one for the service as a whole.  When creating the platform, use the computer GRUU on each app server.  You’ll also want to use the Pool FQDN as the application FQDN.

Certificates

The next deviation in the procedure comes when you request a certificate for your application servers.  Normally, the subject name of the cert needs to be the FQDN of the application server, but in this case, the subject needs to be the pool FQDN.  The certificate you request should be the same on all application servers (so mark the keys as exportable), and should contain the pool FQDN and individual machine FQDNs as Subject Alt Name entries.  This creates a bit of a maintenance headache for adding new capacity, but it’s reasonably easy to request new certs if you control the CA.  What you’ll end up with is something that looks like this in the local machine store:

image

and the SANs:

image

If you’re using the web enrolment tools to request certificates from a windows CA, you can specify the SANs by putting something like this:

SAN:dns=ice7testerpool.rnddev.computer-talk.com.com&dns=ice7tester.rnddev.computer-talk.com&dns=ice7tester2.rnddev.computer-talk.com

in the Attributes field.  I always forget the syntax of this one…

Fun with DNS

At this point, the configuration for Lync and your app is done, so all that remains is the DNS configuration.  Normally, this involves an A record for the app server FQDN, but with load balancing there are a few other things that need to change.  Note-I’m writing this using a windows server 2008 R2 DNS server, so the settings may be different if you have a different DNS.  Basically, we need two things in DNS: an entry for each server, and an entry for the pool that resolves to each server’s IP address.  In my example, I have this in DNS for my pool machines:

image

Now, by the end of this, you want to be able to go to any machine in your network and do this:

image

Or this:

image

Note that the ping command went to different servers each time, and that the nslookup command returned both entries in different orders.  This is important-this means that DNS is working the way it’s supposed to.  Unfortunately, the defaults in DNS might cause it to not work this way, so here’s what you may need to change:

DNS Server properties

Right click on the DNS server and bring up the advanced property page:

image

You’ll want to make sure that Round Robin is enabled, and that netmask ordering is disabled.  Actually, disabling netmask ordering isn’t essential, but it’s a good idea if you want a “real” load balancing scenario.  Basically, netmask ordering is an optimization that says that if you’re in a subnet (say 201.X) and get two entries for a DNS query in different subnets (e.g. 201.1 and 202.1), that you should bias towards the closer result.  The result of this is that an nslookup query will return the entries in the same order every time. 

Time to Live

In most cases, DNS caching isn’t a problem.  The address for a service rarely changes, so as an efficiency, Windows remembers the DNS results for particular lookups.  Most DNS servers also remember results for servers that they forward requests to, which can make it very difficult to actually get a change to propagate out when you want to make one.  For load balancing, it’s even more troubling, since it depends on returning different results for each query.  The test with subsequent PING requests would have each request going to the same server if caching is enabled, which you can verify by running ipconfig/flushdns to clear the local resolver cache, or disabling the cache service completely.  The other way to ensure that your DNS records get re-queried each time though, is to set the Time To Live on the records themselves.  For some reason, this setting is hidden in the Windows DNS server.  Under the view menu check “Advanced”:

image 

And then open your pool entries.  You should see a new field for TTL at the bottom of the page:

image

The TTL is set to an hour by default.  Change this value to 0.  You may need to clear the DNS cache on the DNS server and the Lync server, but at this point you should be able to ping your pool FQDN and get different results back each time.

Finally load balancing?

Now you’re able to start your application instances, which should both register with Lync.  Place a call with both instances running, and one of them will answer.  Shut an instance down, and your call is answered by the other instance.  This makes your app fault tolerant for sure-anytime an instance is down, calls will go to the other instances, but is this actually balancing any load?  If you modify your app so you can identify which instance you’re actually talking to, you’ll notice something odd-calls are usually always going to the same instance of your app.  There are some answers to why this happens, but that’s a blog post in and of itself.  Part 2 will go into how load balancing actually works in Lync, and some techniques to get it to work the way you want it to.

Posted in Uncategorized | 1 Comment

WMAFileSource may have a few more tricks up its sleeve

The Microsoft.Rtc.Collaboration.AudioVideo.WmaFileSource class gets used all over most UCMA apps to play audio files, and from the name it’s pretty clear what it’s intended to do.  Of course, most of the time if you’re playing something like recorded IVR prompts, the odds that those will be recorded directly into WMA are reasonably low, so you’ll need to search for a converter, modify the files, and use them in your project.  The intent appears to be that if you want to play a different format (e.g. wav or mp3), you could create a new MediaSource derived class and go from there.  As it turns out though, that might not be entirely necessary.

I had a tester who wanted to try an mp3 file in a UCMA app, and instead of converting it, just changed the extension to .wma, and it played.  We modified the code to allow you to pass files with the .mp3 and .wav extensions to the application, and that worked too.  Maybe the class isn’t quite so WMA specific after all? 

My suspicion here (and I’m still waiting to find out from the product team that this is the case), is that the WmaFileSource class simply passes the filename to the Windows Media runtime and streams the audio out, so in theory anything that you can open in media player will work like this.  I only tested mp3 and wav in addition to wma though, and both of those seemed to work normally, and would likely cover a wide range of what you might run into that someone would want to drop into your app.  Has anyone else tried passing other formats to this class to see what happens? 

Anyway, something to try if you run into a situation where you just don’t feel like converting a media file from mp3 to wma. 

Posted in Uncategorized | Leave a comment

UCMA apps, load testing, and timeouts

One of the things that often ends up coming up too late in a development cycle is testing your application under load.  Sometimes we think to do this early on, but more often than not, one of the last things developers tend to do is throw traffic at an application until it breaks.  The problem is, finding an issue with load at the end of a dev cycle can be very difficult to fix, and it can call into question some of the fundamental aspects of your architecture.

Recently, I found an issue when testing a UCMA app that looked amazingly like calls to the app were being throttled.  I was even able to reproduce the error using an amazingly trivial UCMA app that simply started an ApplicationEndpoint, registered for AudioVideoCallReceived, accepted a call, and waited (if you want to follow along, the source code for the app and snooper traces are on skydrive here).  I started throwing 10 calls per second at the system up to 100 calls (using SIPP to a mediation server, which is a great SIP traffic generation tool), and after about the first 30 calls, I started noticing failures.  Here’s a sample trace from the front end server:

clip_image002[5]

And here’s the same side of the call from the app server:

clip_image002[7]

As you can see, the original INVITE went to the app, and was received, and the app promptly responded with the 100, 180, and 200.  The front end sent the INVITE at 14:10:21, and if we ignore the time difference between the two servers, you see that the app server’s responses all went out within milliseconds.  If you look at the trace though, it appears that the 100 took 31 seconds to be processed by the front end server?  There’s a lot of signalling going through that system at that point, but did I mention that the front end had 24 CPU cores and 64 GB of memory?  And that it barely registered the traffic?  This was a real puzzle, and I posted in a few forums (both public and private) trying to find an answer. 

As it turns out, the solution involved no code changes, and it was something completely out of my control.  Evidently, a power failure had reset the configuration on the switch that these systems were connected to, which, for some reason, put the ports in half duplex mode, while windows assumed full duplex.  Apparently this can cause some really serious performance problems.  Once I found our network admin, convinced him that this was probably a network issue, and got him to fix the duplex mode, all was well again, and traffic started flowing smoothly. 

I suppose the moral here is two things: One, never assume that the network just works (just like assuming that the power is always going to be on), and Two, sometimes you can blame the network guy and be right about it. 

Posted in Uncategorized | Leave a comment

UCMA Startup errors-when everything else doesn’t work, check the hosts file…

This was a fun round of troubleshooting.  One of our developers needed to debug a UCMA application that we’ve run on dozens of other servers.  He went through the steps to provision the app, just as we had everywhere else, but we got the following exception from starting the platform:

Portal failed establishing the endpoint: Microsoft.Rtc.Signaling.ConnectionFailureException:Operation failed because the network connection was not available. ---> Microsoft.Rtc.Internal.Sip.SipException: Invalid From header: Semantic error:  fTopLabel == true
   at Microsoft.Rtc.Internal.Sip.FromHeader.Parse(SipHeaderLink& headerLink)
   at Microsoft.Rtc.Internal.Sip.FromHeader..ctor(String headerValue)
   at Microsoft.Rtc.Internal.Sip.NegotiateLogic.CreateABlankNegotiate(FunctionType funcType, String negotiateData, SipResponse prevResponse)
   at Microsoft.Rtc.Internal.Sip.NegotiateLogic.StartCompression()
   at Microsoft.Rtc.Internal.Sip.NegotiateLogic.AdvanceOutboundNegotiation()
   at Microsoft.Rtc.Internal.Sip.TlsTransport.DelegateNegotiation(TransportsDataBuffer receivedData)
   at Microsoft.Rtc.Internal.Sip.TlsTransport.OnReceived(Object data)

At first glance, this looks like a network issue, so we made sure that the dev machine could reach the Lync server on all the ports it needed (it could).  Then we rechecked the certificate, and verified that the MTLS connection was forming, but then immediately terminating.  We even tried changing the client machine name to something without hyphens, and re-provisioning the application a couple of times just to make sure that there wasn’t something wrong along the way.  Finally, running OCSLogger.exe on the developer machine and digging through the traces, we saw this:

(000000000270F9F2)local cert SN robinlaptop.corp.computer-talk.com is not same as localfqdn localhost127.0.0.1. Send feature info

Then we checked the hosts file, and noticed a line that looked like this:

127.0.0.1       localhost127.0.0.1       localhost127.0.0.1       localhost127.0.0.1       localhost127.0.0.1       localhost127.0.0.1       localhost

Now, I have no idea how this developer got this in his hosts file, but removing the entry fixed everything.  After discovering this, I tried adding one line for localhost in the hosts file, and everything was fine.  I took the same entry and split it to four lines-everything was fine.  I tried changing ONE CHARACTER in the bad entry-everything was fine.  For some reason, only that exact sequence of characters managed to short-circuit the platform startup.

In any case, I’m putting this out there as a troubleshooting suggestion for anyone else who runs into the same thing.  Make sure that nothing has messed with your hosts file-it’s not necessarily the first thing you’d think to check, but I know I’m adding it to my list of troubleshooting steps from now on.

Also, if anyone knows exactly why this particular entry in the hosts file does what I described here, I’d really like to know why.

Posted in Uncategorized | Leave a comment

Building a standalone Lync server, or, how to write UCMA applications on a plane

One of the difficult things about writing applications using UCMA is the fact that you need to connect to Lync in order to run or debug any of your code.  In fact, since you can’t connect UCMA applications through the edge server, you need direct access to the front end, which probably means VPN connectivity for any remote work.  On top of that, if you want to be able to provision and debug things on the server side, you’ll need administrative access to the Lync server, so it’s likely that there’ll be a separate development lab environment set up apart from your company’s everyday Lync deployment.  In the ideal case, each developer would have access to their own personal Lync sandbox, since then they could write and test whatever they needed to without impacting anyone else. 

Over the past few years, I’ve run into a few people that have built monster laptops that ran Hyper-V and a full Lync stack, but I’d never tried putting one together myself.  Last week though, I finally got the chance, and while it does work, there are a few pitfalls that I found while trying to get everything going. 

First, the hardware.  For this application, I picked up a Samsung 700G7A gamer laptop from Best Buy for about $2200.  I needed something that had a decent processor (Core i7) enough memory (16 GB) and a bunch of hard disk space (1.5 TB).  Having built a few Hyper-V servers before, I probably wouldn’t go with much less than 16GB of memory, especially if you want to run Lync, Exchange, perhaps SharePoint, and then do development on top of that.  The dual hard drives are also probably optional, but you will need a lot of storage for something like this, so 500 GB is probably a good minimum.

Once I got my hands on the hardware and did a quick check to make sure that nothing was broken from the factory, I proceeded to wipe out all of the OEM software and disk partitions and install Windows Server 2008 R2 (Standard) as the new host OS.  Since all of the servers you’ll need to run are 64 bit only, that means running Hyper-V, since Windows Virtual PC can’t run an x64 guest.  We’ll also need a domain controller for Lync, so the host, in addition to being the main development server will also be the DC. 

The initial install of the server OS was fairly straightforward, but once it finished, I ran into the first problem.  Most of the time, after installing a new server, connecting to Windows Update will manage to grab all the relevant signed drivers for a system.  In this case though, there was a whole list of known and unknown devices in the device manager that had no drivers, including the graphics card, Bluetooth module, and wireless adapter.  I suppose we’ve been spoiled by having easily updated drivers from a central location for a few years now, since it wasn’t that long ago that every piece of hardware had its own install disc, so the solution here was to search the web for all the relevant drivers.  For the most part, the Windows 7 drivers did work on Server 2008 R2, but in a few cases there were no drivers available.  Fortunately, enough did end up working that I don’t think it’ll be a major issue-the only major features on the laptop that aren’t working as advertised now are the mute key and the wireless toggle.  The takeaway here though is that before installing a server OS on a consumer laptop, take careful inventory of the hardware, and have as many drivers handy as possible.

As for features on the host OS, another discovery was that even with the driver installed, Windows Server will not connect to a wireless network out of the box.  Under the server manager, you need to add the “Wireless LAN service” feature to enable it.  While you’re at it, add “Desktop Experience”, “.NET 3.5.1” and “Telnet client” to the system (Telnet is still useful for troubleshooting connectivity). 

Once those are installed, it’s time to start with roles on the main OS.  First, install Active Directory Domain Services (you’ll need to do this on its own) and create a brand new domain, which in my case was “icedemo1.local”.  Once this role is set up, you can also go ahead and make sure that Web Server, DNS Server, Active Directory Certificate Services, and HyperV are all installed on the host.  This will probably take a bunch of reboots, so it’s a good thing to do while you have something else to keep busy with.

Eventually, after all the roles are installed, patched, and ready to go, you can start with the virtual machines.  I created three guest servers to start with-a Lync Standard Edition front end, a UCMA application server, and an Exchange server with Unified Messaging.  Creating those is fairly standard HyperV/Lync deployment-install the server OS, join the new domain (icedemo1.local), and then follow the Lync/Exchange install guides-so I won’t go into too much detail, but what is interesting about this isolated scenario is the way that networking is set up.  Since I wanted to have everything on its own isolated subnet (I created IPs in the 10.0.146.X range), I assigned static IPs to all my guests and created entries in the host DNS.  I still wanted the host (and guests) to be able to connect to the internet if it was available though.  Since my host was a DC, it needed a static IP (10.0.146.1), and I could easily assign an extra static IP to connect to the internet, but the catch would be manually reconfiguring this IP for every network that I wanted to get access to.  Ideally, I wanted to have a connection set up with DHCP for the internet, as well as a static IP for the virtual machines, which I managed to do using internet connection sharing.

Set up the main adapter (wired or wireless) for DHCP.  Then, in Hyper-V, create a new virtual network with the “Internal Only” option, assign this new virtual network adapter the static IP address, and connect the VMs to this adapter.  Note that this virtual adapter should also have the preferred DNS server set to the static IP address (e.g. 10.0.146.1) and not the default localhost (192.168.0.1)-if you don’t do this, then DNS queries on the host OS will fail if you’re not connected to a network.  Finally, on the main adapter, allow internet connection sharing with the virtual network-on the Sharing tab, check off both “Allow other network users to connect to the internet though this computer’s Internet Connection” and “Allow other network users to control or disable the shared Internet connection”.  Unfortunately, enabling connection sharing on one connection/adapter disables it on others, so it’s not simple to flip between wired and wireless networks, but other than this one limitation, everything appears to work as expected. 

After setting up Lync and Exchange, as well as some client software on the host OS, I had a small isolated domain that I could finally run UC apps against.  The Lync client on the host server could place a call into a UCMA application, and I could send IMs between two virtual clients.  Multiple voice endpoints, however, were another matter.  If you need to be able to have two users in a voice call at the same time, it may not work as well as you’d expect.  While there are options in remote desktop now to redirect microphone and speakers from a remote machine, I have never been able to get this to work with a virtual machine on Hyper-V.  Windows Virtual PC on Windows 7 has a great feature where USB devices can be redirected to a VM, so I’ve been able to have two USB speakerphones dedicated to different Lync instances that way, but this feature doesn’t exist on Hyper-V yet.  I also tried a couple of different USB sharing utilities, but none of them were able to share my Jabra conference phone to a virtual machine. 

So far, the only way I’ve managed to get two endpoints on an audio call at the same time is to log two users into the host operating system and switch between them.  This mixes the audio from both calls through the same device though, so it won’t sound great, even if you have each session’s Lync using a different audio device.  Still, if you only need to get the users in a particular state to test a scenario, then this will suffice. 

In the end, this is a great way to set up a simple Lync development or demo environment that can run off the grid, and also a good training tool to give to someone to learn Lync configuration in a sandbox environment.  It’s not that much of a stretch to include a connection to an external gateway to make and receive PSTN calls, and you could probably even set up an edge server and federate to other domains (although without a DMZ).  As for improvements, USB redirection would be great to have, as would a better solution for sharing internet connectivity between the host and guests (although this may just be something I haven’t found yet).  I’d also be interested to try again with a high end machine from Dell or Lenovo to see if the driver situation is any better, since the Samsung I’m using was obviously never intended to run a server OS. 

Posted in Uncategorized | 2 Comments

Troubleshooting the Lync Mobile Client

Last night, I noticed that Lync wasn’t able to sign in on my phone for some reason, while it was working fine earlier.  What’s interesting is that I’d seen earlier in the day that under the settings there was a Diagnostic Logging option, similar to what exists on the desktop client.  I’d turned it on already (after all, who doesn’t like diagnostics), but I hadn’t figured out how it actually saved/sent logs yet.  As it turns out, if you go to the about page, there’s a “Send Diagnostic Logs” button.  I’d seen this kind of thing before on the tanjay phones, and that created an etl file that got put on a sharepoint server, so I expected something similarly convoluted here.  Instead, I got instructions about an image. 

As it turns out, the client team takes all the logging information and embeds it into a jpeg.  When you send logs, it asks you to attach an image to an email that looks like this:

image

Once you get the email, save the image, change the extension to .log, open it in a text editor, and you’ll see the logging information right below the binary image data.  As it turns out, the problem was a 500 getting returned from the mobility web service (which an iisreset solved), but the key here was figuring out how to get the logs out of the mobile client.  I’m not sure how end users are going to cope with attaching images to emails or even enabling logging (it’s disabled by default), but it’s great that the feature exists for admins, and it’s actually kind of an ingenious way to get log data off a mobile client. 

Posted in Uncategorized | Leave a comment