• Haiku #191

    Group Policy gone?

    How can we go on? Oh, right:

    Client policies.

     

    In case you've been wondering whether or not today's going to be a good day, well, here's your answer: no, today is not going to be a good day.

     

    At least not here in the Seattle area. For one thing, it's cloudy and overcast yet again. For another, it took the author of today's haiku – who only lives about 10 miles from Microsoft – over 40 minutes to get to work this morning, mainly because of one .4 mile stretch that took nearly 15 minutes to get through.

     

    Note. Why did it take 15 minutes to go less than half a mile? Because there are too many people here, and the vast majority of them don't have the slightest idea how to drive a car.

     

    At any rate, if you do the math – and don't worry, you don't have to do the math because we did it for you – if the entire journey had proceeded at the same pace (15 minutes to go .4 miles) it would have taken the author of today's haiku over 6 hours to make the 10-mile drive to work. Figuring that he'd probably have to stop for gas along the way, and stop to eat at least once during that time, the trip would probably take closer to 7 hours, meaning that he would have arrived at work just in time to turn around and go home.

     

    So, OK, maybe there would be an upside to that after all.

     

    And before you ask, no, taking the bus is not an option. The author of today's haiku would love to take the bus to work, but for him to take the bus to work means:

     

    ·         A 10-15 minute walk to the bus stop.

    ·         A half hour bus ride to downtown Kirkland.

    ·         A half hour wait for the next bus. (Sadly, there aren't any busses that go directly from his house to Microsoft.)

    ·         A 10-15 minute walk from the bus stop near Microsoft to his office.

     

    Add that all up and you get about 7 hours once again. And that's assuming that the busses run exactly on schedule, which is no guarantee.

     

    And no, riding his bike to work is out of the question, too. So far this summer there have been 9 or 10 fatal cases where a bike rider has been hit by a car. The author of today's haiku doesn't like riding in a car on the streets around here; there's no way he's riding a bike on those streets.

     

    To be perfectly honest, the only solution is just to quit his job and stop working altogether. That would wreak havoc on the mortgage, but would definitely cut down his commuting time.

     

    And, for once, his coworkers would support him 100%!

     

    On the bright side, however – well, never mind, we can't actually think of a bright side to anything this morning. So let's talk about the CsClientPolicy cmdlets instead (Get-CsClientPolicy, Grant-CsClientPolicy, New-CsClientPolicy, Remove-CsClientPolicy, and Set-CsClientPolicy).

     

    Now, admittedly, you might think that the mere mention of the CsClientPolicy cmdlets would be enough to put a smile on the face of the author of today's haiku. For better or worse, however, the client policy cmdlets present administrators (and haiku writers) with a mixed bag of goodies: there are some really, really good things about the client policy cmdlets, and then there are some not-so-good things about the client policy cmdlets. But let's talk about the good things first.

     

    The client policy cmdlets were primarily designed to replace the Group Policy settings used in Office Communications Server 2007; all you have to do is look at many of the client policy parameter names to see that. Need an example of that? OK, how about this one: the parameter BlockConversationFromFederatedContacts is equivalent to the old Group Policy setting Block conversation from federated contacts. And, needless to say, there are plenty more examples where that came from.

     

    So why didn't we just keep Group Policy? Why did we create these new client policies? Well, Group Policy is very powerful and very useful, but it definitely has its limitations. For one thing, Group Policy only applies to people who actually log on to the network: if you access Lync Server remotely (which many people do these days) Group Policy doesn't get applied to you. (Which means you might have a very different experience depending on how you access the system.) In addition to that, Group Policy is designed to apply to Active Directory: you assign policies to OUs or to domains. With Lync Server, however, there are often settings that you want to target much more finitely: you want salespeople in the Sales department to have different capabilities than accountants in the Finance department. Can you do that sort of targeting with Group Policy? Yes, sort of. But, take it from someone who's tried this, it's not easy.

     

    At any rate, client policies make user/client administration much, much easier; if nothing else, policy resolution is much easier in Lync Server than it is in Group Policy. In Lync Server, per-user policies trump per-service policies which trump per-site per site policies which trump global policies. That's way easier than Group Policy, in which you have to worry about domain-level policies, OU-level policies, machine policies vs. user policies, inheritance vs. non-inheritance, etc., etc. Oh, and with Lync Server policies, the entire policy gets applied in-toto. You don't have to worry about settings from some other policy "trickling" down and affecting a user.

     

    In short: client policies are better. Way better.

     

    And client policies are much easier to create as well. For example, suppose you want to create a new per-user client policy that limits users to no more than 50 contacts. How do you do that? Why, like this, of course:

     

    New-CsClientPolicy -Identity RedmondClientPolicy –MaximumNumberOfContacts 50

     

    And then what if you want to assign that policy to Ken Myer? That's just as easy:

     

    Grant-CsClientPolicy -Identity "Ken Myer" –PolicyName RedmondClientPolicy

     

    Or, if you prefer, you can assign that policy to all the salespeople in the Sales department:

     

    Get-CsUser -LdapFilter "(&(Title=Salesperson)(Department=Sales))" | Grant-CsClientPolicy -PolicyName RedmondClientPolicy

     

    In case you're wondering, what we've done here is use the Get-CsUser cmdlet to return all the salespeople in the Sales department. And how exactly do we get all the salespeople in the Sales department? By using the LdapFilter parameter and this filter value:

     

    "(&(Title=Salesperson)(Department=Sales))"

     

    That's a bit complicated-looking, but it simply tells Get-CsUser to return all the users whose Active Directory Title attribute is equal to Salesperson and (the & character) whose Department attribute is equal to Sales. We then pipe all those user accounts to the Grant-CsClientPolicy cmdlet which, in turn, assigns each user the policy RedmondClientPolicy.

     

    Note. You know, you're right: it would be cool to know how to write filters for the LdapFilter parameter, wouldn't it? If that's something you're interested in, take a peek at the article Retrieving Active Directory and Microsoft Lync Server 2010 User Accounts.

     

    Of course, by now you're probably thinking, "These client policies and client policy cmdlets look fantastic! What could possibly be wrong with them?" Well, for the most part nothing. However, it is true that some of the New-CsClientPolicy and Set-CsClientPolicy parameters (and thus some of the settings you think you can set) don't actually work. For example, the TabURL parameter, which ostensibly helps you add custom tabs to the Microsoft Lync window? Well, that parameter doesn't work: it won't let add custom tabs to the Microsoft Lync window. The same is true for the ShowRecentContacts parameter: ShowRecentContacts doesn't actually work, either. The vast majority of parameters do exactly what they purport to do. Unfortunately, however, there are a couple of exceptions.

     

    Note. So how did we end up with parameters that don't work? Well, it's a long story, and it mainly has to do with the fact that Lync Server development and testing were going on right up to the moment that the product shipped. The fact of the matter is that, with a big, complicated, largely-rewritten-from-scratch piece of software like Lync Server there were bound to be a few minor issues that slipped through the cracks. We'd still like to do an extensive look at all the client policy settings and what they actually do or don't do (similar to what we did with conferencing policies) but up until now we just haven't had the time to undertake such a big project. But we'll see. We'll see.

     

    But don't let the fact that the TabURL parameter doesn't work keep you from using client policies; like we said, most of the parameters – like limiting the number of contacts a user can have (MaximumNumberOfContacts), adding a disclaimer to instant messaging sessions (IMWarning), including Microsoft Outlook calendar data in your presence information (DisableCalendarPresence) – work just fine, and can be extremely useful as well.

     

    That's pretty much all we have to say about client policies; as you might expect, you can use the Get-CsClientPolicy cmdlet to return information about your existing policies, and you can use the Remove-CsClientPolicy cmdlet to delete any of those existing policies. One thing that might not be readily apparent, however, is the fact that the client policies include a pair of properties – EnableHotdesking and HotdeskingTimeout – which actually apply to common area phones rather than to users or to Microsoft Lync. "Hot desking" refers to the ability of an individual user to log on to a common area phone using his or her own credentials, and thus have access to their own contacts. If you'd like to enable hot desking you'll need to create a client policy for your common area phones; for example:

     

    New-CsClientPolicy –Identity CommonAreaPhonePolicy –EnableHotdesking $True –HotdeskingTimeout 00:15:00

     

    You can then use a command like this one to assign that policy to all your common area phones:

     

    Get-CsCommonAreaPhone | Grant-CsClientPolicy –PolicyName CommonAreaPhonePolicy

     

    Just something we thought you should be aware of.

     

    At any rate, that's all we have for now. Enjoy the rest of the day (yeah, right), and we'll see you again on Monday.

     

     

     

     

     

     

     

  • Haiku #190

    Hit 600 home

    Runs? Create new workflows? But

    What else have you done?

     

    This is bound to stir up a hornet's nest of controversy, but here it is: the author of today's haiku does not believe that Jim Thome should be in the Baseball Hall of Fame.

     

    There, he said it.

     

    Now, many of you who read that first paragraph are probably thinking, "Who the heck is Jim Thome and what does he have to do with Lync Server PowerShell?" (And many more of you who have read that first paragraph are probably no longer around to read this third paragraph.) Jim Thome is a professional baseball player (now playing for the Cleveland Indians) who, by all accounts, is a wonderful human being. He's also been a pretty good baseball player: he recently hit his 600th home run, putting him in very rarefied company indeed.

     

    Note. The only other players with 600 career home runs? Barry Bonds, Hank Aaron, Babe Ruth, Willie Mays, Ken Griffey, Jr., Sammy Sosa, and Alex Rodriguez. During his baseball career, spanning Little League through high school, the author of today's haiku missed joining the 600 home runs club by a mere 600 home runs. So near and yet so far!

     

    At any rate, you obviously have to have a considerable amount of talent in order to hit 600 home runs. But does that mean you have Hall of Fame talent? The author of today's haiku would argue that no, you don't. Jim Thome has always been a very good ball player, but even in his prime no one considered him to be the best ball player: he never struck fear into the opposition the way a Barry Bonds or a Tony Gwynn or a Ken Griffey, Jr. did. Good ball player? Yes. Hall of Famer? No.

     

    As good as Jim Thome might have been at hitting home runs (and he was, and is, less adept at running, fielding, throwing, or hitting for average) what he is really good at is playing for a long time: Thome is now in his 21st season in the big leagues. Unfortunately, baseball is now in the habit of rewarding people not so much for how good they were, but for how old they were when they retired. Craig Biggio was another nice guy who had a long and fruitful career. But a Hall of Famer? Everyone says yes, because he played for 20 seasons and got over 3,000 hits. But here's a look at Craig Biggio's batting averages for the last 6 years of his career:

     

    ·         .253

    ·         .264

    ·         .281

    ·         .264

    ·         .246

    ·         .251

     

    That doesn't sound like Hall of Fame material. The author of today's haiku does not believe you should be applauded just because you performed at a mediocre level for a long time.

     

    Note. Which reminds us: today's in the 190th Lync Server PowerShell haiku.

     

    So what does any of that have to do with Lync Server PowerShell? As far as we know, absolutely nothing. It's just something that had to be said.

     

    And here's something else that has to be said: we really should talk about Lync Server PowerShell today. And, believe it or not, we were just about to do that: we were just about to talk about the CsRgsWorkflow cmdlets (Get-CsRgsWorkflow, New-CsRgsWorkflow, Remove-CsRgsWorkflow, and Set-CsRgsWorkflow).

     

    Before we talk about the CsRgsWorkflow cmdlets (and whether or not these cmdlets should be in the PowerShell Cmdlet Hall of Fame), let's take a second to talk about the Response Group service and about Response Group workflows. As you probably know, the Response Group service enables you to automatically respond to incoming phone calls. For example, if someone calls your help desk, you can configure the Response Group to simultaneously transfer that call to a group of "agents," any one of whom can answer that call. If you want to get fancier, you program the service so that it prompts the user for additional information: "Press 1 if you are experiencing a hardware-related problem. Press 2 if you are experiencing a software-related problem." By gleaning a little information up front, the service can then transfer the call to a support person who might be best-suited to deal with the problem.

     

    And yes, now that you mention it, that is a really nice little feature, isn't it?

     

    So then what's a Response Group workflow? Well, basically a workflow is a Response Group. If you want to use the Response Group service to handle incoming calls to your help desk, you need to set up a workflow for that help desk, a workflow uniquely associated with the SIP address and the telephone number for the help desk. And one of the best ways to set up and then manage a Response Group workflow? You got it: by using the CsRgsWorkflow cmdlets.

     

    Note. See? Every now and then we do talk about something related to Lync Server PowerShell.

     

    So does that mean that we do think that the CsRgsWorkflow cmdlets should be in the PowerShell Cmdlet Hall of Fame? To be honest, we have mixed emotions about that. There's no doubt that the CsRgsWorkflow cmdlets are incredibly powerful and incredibly useful; at the same time, however, these cmdlets can be a bit … quirky …. And that's something you need to be aware of before you start working with them.

     

    So what do we mean when we say that these cmdlets can be "quirky?" Well, let's start with this fact: it's basically impossible to create a Response Group workflow (or at least a workflow that actually does something) without using a bunch of other cmdlets as well. Why? Because many of the properties of a Response Group workflow only accept objects created by other Response Group cmdlets. (This, by the way, is just the way the Response Group object model works: you tend to create objects which are assigned as properties to other objects.)

     

    Good idea: maybe we should show you what we're talking about here. Let's take business hours, for example. Business hours define the days and times when agents are available to answer the phone; the idea is that, during business hours, calls to the help desk will be transferred to the help desk agents, while calls outside of business hours might be sent directly to voice mail. So how do you assign business hours to a workflow? Well, to be honest, you have to do something a little crazy, like this:

     

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

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

    $x.BusinessHoursId = $businessHours.Identity

    Set-CsRgsWorkflow -Instance $x

     

    As it turns out, business hours are a totally separate object, created and maintained by the CsRgsHoursOfBusiness cmdlets. To assign business hours to a workflow, you either have to create those business hours using New-CsRgsHoursOfBusiness or, as we've done here, retrieve a collection of existing business hours using Get-CsRgsHoursOfBusiness. After retrieving the business hours, you next need to retrieve an object reference to your Response Group workflow; that's what we do here:

     

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

     

    After we run the previous command, we'll have a "virtual" instance of the Help Desk workflow that exists only in memory (and is stored in the variable $x). We update the value of the BusinessHoursId property for $x, then use this command to write the changes back to the real Help Desk workflow:

     

    Set-CsRgsWorkflow -Instance $x

     

    We see you're confused by all this; why didn't we just use a command like this one to set the business hours:

     

    Set-CsRgsWorkflow –Identity "Help Desk" –BusinessHoursId $businessHours

     

    Well, the main reason we didn't use a command like that is because it wouldn't work. The Response Group cmdlets are somewhat unique in the fact that (in most cases) you can't directly modify the property values of an object. Instead, you need to create a virtual copy of the object, change the property values of that virtual copy, then use the appropriate Set-CsRgs cmdlet and the Instance parameter to change the real object. You can make a case that this isn't the most intuitive way of doing things, but that's just the way the Response Group cmdlets work.

     

    History lesson. When Lync Server was first being developed, pretty much all the cmdlets worked that same way; it was only a bit later in the development cycle when the Set-Cs cmdlets were changed so that they could directly modify property values. That was fine for most cmdlets: the majority of them didn't even exist at the time. However, the Response Group team was different: most of their cmdlets were done at the time the change was made. Because the cmdlets were done, and because they all worked, the decision was made to leave them as-is. If it ain't broke, don't fix it.

     

    If you’re been wondering why the Response Group cmdlets seem a little different from the other Lync Server cmdlets, well, now you know.

     

    Incidentally, this same approach is going to be true for a bunch of other workflow properties: CustomMusicOnHoldFile; DefaultAction; HolidayAction; HolidaySetListId; NonBusinessHoursAction; and so on. We can't go through all the possibilities and permutations in today's haiku; that would take us 20 years or more, and we just don't have the time for that. (Although it probably would get us into the Baseball Hall of Fall.) For more information, see the help topic for New-CsRgsWorkflow. Better yet, take a peek at the Response Group scripts written by Frédéric Dubut:

     

    ·         Create a Response Group with a Hunt Group

    ·         Create a Response Group with an IVR

     

    These scripts show how to use the CsRgs cmdlets to create different types of Response Group workflows and are definitely worth looking into.

     

    One other quirky thing about the CsRgsWorkflow cmdlets. In Lync Server PowerShell, you're used to working with the Identity of an object. You can do that with workflow objects, too, except that workflow objects tend to have Identities that look like this:

     

    service:ApplicationServer:atl-cs-001.litwareinc.com/1987d3c2-4544-489d-bbe3-59f79f530a83

     

    So what does that mean? Well, it means that you can delete a workflow by using a command similar to this:

     

    Remove-CsRgsWorkflow –Identity "service:ApplicationServer:atl-cs-001.litwareinc.com/1987d3c2-4544-489d-bbe3-59f79f530a83"

     

    That's what we said. Therefore, you might want to retrieve the workflow using Get-CsRgsWorkflow (and the Name parameter), then pipe that retrieved object to Remove-CsRgsWorkflow:

     

    Get-CsRgsWorkflow –Name "Help Desk" | Remove-CsRgsWorkflow

     

    Just something to keep in mind.

     

    Oh, and here's something else to keep in mind: Ray Schalk is in the Baseball Hall of Fame, the same Ray Schalk who had a lifetime batting average of .253 and 11 career home runs. (That's only 11 more than the author of today's haiku!) George Kelly never received more than 5 votes on any Hall of Fame ballot (for comparison purposes, neither has the author of today's haiku), yet 40 years after he was retired the Veteran's Committee (chaired by two of his former teammates) put him into the Hall of Fame anyway. Wilbert Robinson, former manager of the Brooklyn Dodgers, is in the Hall of Fame, even though he never won a World Series and had 10 losing seasons in his 18 years as the Dodger manager.

     

    Which simply means this: Jim Thome, we owe you an apology. Enjoy the Hall of Fame!

     

    See you tomorrow.

     

     

     

     

     

     

     

     

     

  • Haiku #189

    Eye of newt and toe

    Of frog. Or just use CS

    Trusted computer.

     

    Yesterday afternoon, while waiting for a meeting to start, the author of today's haiku found himself browsing through an online copy of the Malleus Maleficarum (The Hammer of Witches). If you aren't familiar with the Malleus Maleficarum, it's a treatise written in 1486 that served as a manual for the witch trials that swept over Europe in the 15th and 16th century (and, just for good measure, was heavily relied upon by the folks responsible for the Spanish Inquisition).

     

    Note. In case you're wondering, it's just a coincidence that the meeting the author of today's haiku was waiting on was his annual performance review.

     

    At any rate, the Malleus Maleficarum makes for some interesting reading, even if it doesn't include any haikus or references to Lync Server PowerShell. (Go figure.) For example, how do you even know whether or not someone is a witch? Well, one way to determine that is to toss them in prison for a few weeks and then bring them to trial. If they don't cry during the trial, well, then they must be a witch, because witches (and other people possessed by the Devil) are unable to cry.

     

    Well, duh.

     

    And so if the prisoner does cry then she's off the hook, right? Well, not exactly. After all, the Devil – regardless of what else you might think of him – is no dummy: he'll often instruct his minions to cry in an attempt to fool their jailers. Which simply means this: if you don't cry then you're probably a witch and if you do cry, well, then you're probably a witch. Damned if you do, and damned if you don't.

     

    Like we said, just a coincidence that he was reading this right before his annual performance review.

     

    Note. For the record, the author of today's haiku would like to state that he did not cry during his performance review. That only came later, after he actually saw his review score.

     

    We mention all this simply because many people believe that witchcraft and black magic are required in order to manage trusted application computers in Microsoft Lync Server 2010. Granted, that might have been true in an early beta version of the product; we can't remember for sure. But witchcraft and black magic are definitely not required in order to manage trusted application computers in the released version of Lync Server 2010. Instead, all you need are the CsTrustedApplicationComputer cmdlets: Get-CsTrustedApplicationComputer, New-CsTrustedApplicationComputer, and Remove-CsTrustedApplicationComputer.

     

    Those of you who are witches (or otherwise have magical powers) might have guessed that a trusted application computer is a computer that runs trusted applications. In turn, a trusted application is an application that isn't actually part of Microsoft Lync Server but, thanks to its trusted status, is allowed to run as if it is a built-in part of Lync Server. (For example, Outlook Web Access can be configured to run as a trusted application.)

     

    Admittedly, the trusted application landscape (which includes everything from trusted application pools to trusted application endpoints) can get a little complicated. We'll try to keep things as simple as possible today by saying that trusted applications must run on trusted computers, and trusted computers must belong to a trusted application pool.

     

    That's easy enough, right? Now, as it turns out, there are two different types of trusted application pools: there are pools that can contain multiple computers, and there are pools that can contain just one computer. If you want to house multiple computers in a single trusted application pool then, when you create that pool, all you need to do is use the computer's fully qualified domain name as the pool Identity (and specify the site where the pool will be located). For example:

     

    New-CsTrustedApplicationPool -Identity atl-trust-00l.litwareinc.com -Site Redmond

     

    Do that, and you'll have a pool that consists solely of the computer atl-trust-001.litwareinc.com. If you try to add an additional computer to that pool the command will fail with a somewhat-vague error message:

     

    New-CsTrustedApplicationComputer : A pool consisting of Standard Edition servers can contain only one computer.

     

    Alternatively, you can give your trusted application pool a different Identity than the first computer to be added to the pool. (You can't have empty pools by the way: all trusted application pools must contain at least one computer). For example:

     

    New-CsTrustedApplicationPool -Identity trusted-computers.litwareinc.com -Site Redmond –ComputerFqdn atl-trust-00l.litwareinc.com

     

    What we have now is a trusted application pool (trusted-computers.litwareinc.com) that contains the computer atl-trust-001.litwareinc.com. And this is a pool we can add additional computers to.

     

    And how exactly do we do that? That, at long last, is where the New-CsTrustedApplicationComputer cmdlet comes in. To add another computer to a trusted application pool, we need to use a command similar to this:

     

    New-CsTrustedApplicationComputer Identity atl-trust-002.litwareinc.com -Pool trusted-computers.litwareinc.com

     

    See how that works? All we did was specify the Identity (FQDN) of our new trusted computer, as well as the fully qualified domain name of the pool the computer should be added to.  If we now run the Get-CsTrustedApplicationComputer cmdlet we should see two different trusted application computers, both in the same pool:

     

    Identity : atl-trust-001.litwareinc.com

    Pool     : trusted-computers.litwareinc.com

    Fqdn     : atl-trust-001.litwareinc.com

     

    Identity : atl-trust-002.litwareinc.com

    Pool     : trusted-computers.litwareinc.com

    Fqdn     : atl-trust-002.litwareinc.com

     

    What do you know about that: the system works!

     

    Note. OK, admittedly, it's not always quite that easy: like we said, for the purposes of today's haiku we're simplifying things a bit. You might want to take a peek at the article General Application Activation for a little more detail on the trusted application infrastructure.

     

    The moral of the story is that New-CsTrustedApplicationComputer is used to add additional computers to a trusted application pool. However, this cmdlet is only required if you create a pool that can house multiple computers in the first place. If you create a single-computer pool you don't need to use this cmdlet; instead, New-CsTrustedApplicationPool will automatically create a new trusted application computer for you. For example, suppose we run this command:

     

    New-CsTrustedApplicationPool -Identity atl-trust-999.litwareinc.com -Site Redmond

     

    That command creates a new trusted application pool with the Identity atl-trust-999.litwareinc.com. Let's now see what happens when we run Get-CsTrustedApplicationComputer:

     

    Identity : atl-trust-999.litwareinc.com

    Pool     : atl-trust-999.litwareinc.com

    Fqdn     : atl-trust-999.litwareinc.com

     

    Black magic? Witchcraft? Sorcery? Maybe; to be honest, we've never actually seen the source code. However, we think it's just due to the fact that New-CsTrustedApplicationPool automatically creates a new trusted application computer for you.

     

    So what happens if you change your mind and want to decommission a trusted application computer? Well, if that computer is one of several computers in a trusted application pool then all you have to do is run the Remove-CsTrustedApplicationComputer cmdlet. You know, like this:

     

    Remove-CsTrustedApplicationComputer -Identity atl-trust-001.litwareinc.com

     

    As long as atl-trust-001.litwareinc.com is in a pool that contains multiple computers then the preceding command will get rid of atl-trust-001.litwareinc.com. Period.

     

    Ah, but what happens if atl-trust-001.litwareinc.com is the only computer in a trusted application pool? In that case, the preceding command will not work. Instead, you'll get an error message like this one:

     

    Remove-CsTrustedApplicationComputer : A pool must have at least one computer. You cannot convert a pool consisting of Standard Edition servers to an Enterprise pool, or vice versa. Instead, you must re-create the pool.

     

    OK, again, maybe not the best error message ever written. What we probably should have said is this: if there's only one computer in a pool then you can't get rid of that computer by running Remove-CsTrustedApplicationComputer. (Why not? Because then you'd have an empty pool, and we've already learned that Lync Server won't allow you to have an empty pool.) Instead, you need to delete the pool itself:

     

    Remove-CsTrustedApplicationPool -Identity trusted-computers.litwareinc.com

     

    That command will work: it will remove the trusted application pool and, by extension, any trusted application computers in that pool.

     

    Note. If you remove a trusted application computer that computer, as you might expect, will no longer be able to run trusted applications, and will no longer show up when you run Get-CsTrustedApplicationComputer. In addition to that, the computer will no longer show up when you run the Get-CsComputer cmdlet: as far as Lync Server is concerned, that machine no longer exists. Just something to be aware of if you start playing around with trusted applications and the CsTrustedApplicationComputer cmdlets.

     

    And there you have it: the CsTrustedApplicationComputer cmdlets. As for the Malleus Maleficarum, well, the author of today's haiku was definitely interested in the following paragraph:

     

    If he confesses and is impenitent, he is to be handed over to the secular courts to suffer the extreme penalty … or he is to be imprisoned for life …. But if he does not confess, and stoutly maintains his denial, he is to be delivered as an impenitent to the power of the Civil Court to be punished in a fitting manner …

     

    Which boils down to this: if you admit to being a witch, you will be punished. And if you don't admit to being a witch, you will be punished.

     

    Just a coincidence that yesterday the author of today's haiku had his annual performance review ….

     

    See you tomorrow!

     

    Note. All kidding aside, don't worry about the author of today's haiku. He got just exactly the kind of performance review you'd expect someone who spends his days writing haikus about Lync Server PowerShell to get.

     

     

     

     

     

     

     

     

  • Haiku #188

    Test certificate:

    Do not try this at home. Try

    Something else instead.

     

    The author of today's haiku has always had an affinity for product disclaimers, those little warning labels that tell you why you should never, ever use whatever it is you just bought. Car makers – and car company commercials – are notorious for their product disclaimers, and often for good reason: a car commercial might show someone racing a car over a drawbridge that's beginning to open, or maybe spinning out over a set of train tracks just before the train whizzes by. Admittedly, the author of today's haiku isn't sure how many people watch a commercial and think, "Wow, I want to take my truck to the Mojave desert and have it tow a trailer up a steep, winding path and through a tunnel of fire!" But better safe than sorry, right?

     

    Last night, however, the author of today's haiku saw what had to be the ultimate product disclaimer. What he saw was a car commercial and, to be honest, a pretty boring car commercial at that: it just showed the car driving along the road. However, as the car puttered along the following disclaimer appeared at the bottom of the screen:

     

    Professional driver on closed course. Do not attempt.

     

    Now, again, there was no drawbridge and no tunnel of fire, not even a rushing train to deal with: this was just a car driving along the road. Which, of course, can only mean one thing: although the car company obviously wants you to buy the car, they don't want you to ever actually drive the car. Oh, sure, if you're a trained professional and you have access to a closed course, well, maybe then you can drive the car. But otherwise, do not attempt to drive this car.

     

    Ever.

     

    Note. Which, considering the fact that the price of gas has gone up a good 20 cents or so per gallon in the past week, might actually be pretty good advice.

     

    As it turns out, at least one of the Lync Server 2010 PowerShell cmdlets (Test-CsCertificateConfiguration) should also be accompanied by a product disclaimer, one that reads something like this:

     

    Not designed for use by administrators. Do not attempt.

     

    We should hasten to add that there's nothing wrong with Test-CsCertificateConfiguration: it works just fine, even in a tunnel of fire. (Well, we're not totally sure about the tunnel of fire. But we assume it works in a tunnel of fire.) However, there's really no reason for administrators to use Test-CsCertificateConfiguration; in fact, the cmdlet was never designed to be used by administrators in the first place. (It's actually intended for use by the Lync Server Certificate Wizard.) Instead, administrators should use the Get-CsCertificate cmdlet, a cmdlet we'll discuss in a little more detail in the next day or two.

     

    You know, that's a good idea: maybe we should explain what the heck we're talking about. (We can do that, because we're trained professionals working on a closed course.) If you just look at the cmdlet name you might think that the Test-CsCertificateConfiguration cmdlet tests the certificate configuration on a computer. However, that's not really what it does. Instead of testing the certificate configuration, the cmdlet really just returns some information about your Lync Server certificates, and minimal information at that. For example:

     

    ‎Thumbprint                                  Use

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

    D00F5752C2363EBBCFE1B2F44247EE055109A6A2    Default

    ‎FC4AC602AA418B934CD5BB90A39CC366441AC913    WebServicesInternal

    ‎A72971C2355B8803D635BF9B353754F40AA1BBAB    WebServicesExternal

     

    Is that useful information? Well, it does tell you that you have three different certificates installed on this computer, and it does give you a Thumbprint for each of those certificates. But it doesn't give you any other information about those certificates, information like, say, who issued the certificate or when the certificate expires.

     

    If you want that kind of information, then you need to use the Get-CsCertificate cmdlet. Get-CsCertificate will return information like this for each of your Lync Server certificates:

     

    Issuer           : CN=MyCa

    NotAfter         : 4/19/2013 8:55:09 AM

    NotBefore        : 4/19/2011 8:55:09 AM

    SerialNumber     : 6138B0000000091363

    Subject          : CN=atl-cs-001.litwareinc.com

    AlternativeNames : {meet.litwareinc.com,dialin.litwareinc.com}

    Thumbprint       : D00F5752C2363EBBCFE1B2F44247EE055109A6A2

    Use              : Default

     

    Interestingly enough, when you run Test-CsCertificateConfiguration one of the first things the cmdlet does is call the Get-CsCertificate cmdlet in order to retrieve certificate information. If you just use Get-CsCertificate in the first place you cut out the middleman.

     

    Oh, and we should also mention this: Test-CsCertificateConfiguration can only be run on the local computer; the cmdlet is not available when you're using remote PowerShell. If you want to get certificate information from a remote computer you have no choice but to use Get-CsCertificate.

     

    Note. But isn't it true that Test-CsCertificateConfiguration automatically generates a report (in HTML format) for you each time it runs, a report that will list any errors or misconfigurations that it finds? Yes, that is true. However, you can also ask Get-CsCertificate to generate a similar report for you simply by tacking on the Report parameter:

     

    Get-CsCertificate –Report C:\Logs\Certificates.html

     

    Like we always say, however: to each his own. If Test-CsCertificateConfiguration does everything you need it to do, well, then by all means use Test-CsCertificateConfiguration. All you have to do is run the following command:

     

    Test-CsCertificateConfiguration

     

    Or, if you want to specify a file path for the report that the cmdlet generates, include the Report parameter as well:

     

    Test-CsCertificateConfiguration –Report C:\Logs\Certificates.html

     

    That is – literally – everything that you can do with Test-CsCertificateConfiguration.

     

    Well, everything that we trained professionals on a closed course can do. Everyone else should just use Get-CsCertificate instead.

     

    See you tomorrow.

     

     

     

     

     

     

     

     

     

  • Haiku #187

    You can come in and

    So can you. But no, not you.

    Client version rules.

     

    Well, it's Monday, and the author of today's haiku is home at last. OK, actually he got home late Saturday afternoon, but he never truly felt at home: after all, both Saturday and Sunday the sun was shining and the weather was warm. When he woke up this morning, however, he looked out the bedroom window and saw that the sky was a familiar, and comforting, shade of gray, with not a ray of sunshine to be seen. That was the moment when he realized that he was really home.

     

    Hurray.

     

    At any rate, the author of today's haiku definitely enjoyed his time in Park City, UT, even if he had to put up with blue skies and sunshine every day that he was there. On the other hand, it definitely is possible to have too much of a good thing, and the author of today's haiku realized that it just isn't right to have fun and enjoy yourself day after day after day.

     

    Which, in case you're wondering, is his answer to the question, "If it was so much fun in Park City, then why did you come back?"

    No, hey, just kidding. Granted, he won't be riding bikes or climbing mountains every day now that he's back in Seattle. But that doesn't mean he won't still have all the fun he can handle. After all, the topic of today's haiku is the CsClientVersionPolicyRule cmdlets (Get-CsClientVersionPolicyRule, New-CsClientVersionPolicyRule, Remove-CsClientVersionPolicyRule, and Set-CsClientVersionPolicyRule).

     

    Note. Talk about having too much of a good thing, eh?

     

    As devoted readers of the Lync Server PowerShell blog know, client version policies are used to determine the client applications that users can (or cannot) use to log on to Lync Server. Can your users log on to Lync Server using Office Communicator 2007? Well, that depends on your client version policies. If you have a client version policy that expressly allows users to log on using Communicator 2007 then the answer is yes. And what if you have a client version policy that expressly forbids users from logging on using Communicator 2007? In that case, the answer is "Not yes."

     

    Note. OK, technically the whole thing can get a bit more complicated than that. For example, what if you don't have a client policy that doesn’t mention Communicator 2007 at all? Well, in that case, you can do things like configure Lync Server to allow (or prohibit) any client application not covered by a client version policy. But we aren't going to talk about that today; for that discussion, see Haiku #125

     

    What we are going to talk about today are policy settings that do explicitly deal with a specific client application. If you want to expressly allow (or not allow) users to log on with a specific client application then you need to create a client version policy rule for that application and then assign that rule to the appropriate client version policy.

     

    OK, that isn't 100 percent true: when you install Lync Server, by default you get a global client version policy that includes rules for 19 of the more common client applications (different versions of Office Communicator, different flavors of Office Communicator/Lync Phone Edition, and so on). That means you probably won't have to create a new rule: more often than not, you'll only have to modify an existing rule.

     

    But let's pretend your company has created its own client application, and you need to create a new client version policy rule that will allow users to log on using that application. How are you going to do that? Like this:

     

    $x = [guid]::NewGuid()

     

    New-CsClientVersionPolicyRule -Parent global -RuleId $x -MajorVersion 1 -UserAgent LitwareincClient –CompareOp GEQ –Action Allow

     

    So what have we done here? Well, in the first command, we've created a new GUID and stored that GUID in a variable named $x. Why did we do that? That's easy: because the Identity for a client version policy rule is composed of the scope where the rule is to be assigned plus a unique GUID. In other words, the Identity of a client version policy rule looks something like this:

     

    global/4730866f-8594-431b-ad57-6f1c798ce0d8

     

    The best way to create a unique GUID for use with a client version policy rule is to let Windows PowerShell create that GUID for you.

     

    Note. In case you're wondering, the scope part of the Identity simply refers to the policy that the new rule should be added to (that is, the global part of global/4730866f-8594-431b-ad57-6f1c798ce0d8).

     

    In our second command, we then use New-CsClientVersionPolicyRule to create the new rule and add that rule to the global policy (use the Parent parameter to indicate the policy the rule should be added to). As you can see, we use the variable $x (the GUID we just created) as the parameter value for the RuleID parameter; we also set the MajorVersion of the rule to 1 and the UserAgent to LitwareincClient. Why? Well, we want to allow anyone who has version 1 of the application LitwareincClient to log on to Lync Server; to do that, we need to specify the version number and the user agent (which is simply the designation used by the client application).

     

    So is that all we have to do? Almost. We also need to do two things. First, we want people to be able to log on using version 1.01 of the application, as well as version 1.02, 1.4, 2.0, 3.9, and so on. (Granted, none of those versions exist yet, but it never hurts to plan ahead.) Therefore, we set the CompareOp parameter to GEQ, which simply means "greater than or equal to." (For a complete list of comparison operators, see the help topic for New-CsClientVersionPolicyRule.) Suppose a user tries to log on using version 1.2 of LitwareincClient. Will they be able to log on to Lync Server? You bet; after all, version 1.2 is greater than (or equal to) version 1. And what if they try to log on using version 0.9? That's not going to work: version 0.9 is not greater than (or equal to) version 1.

     

    Note. As we mentioned earlier, it can get a little more complicated than either yes you can log on or no you can't log on. But for now we're going to ignore all those complications.

     

    But wait, we're not done just yet. We want people who are running LitwareincClient to be able to log on to Lync Server; therefore, we need to set the Action property to Allow. And what if we didn't want them to be able to log on to Lync Server? In that case, we would set the Action property to Block, like so:

     

     

    New-CsClientVersionPolicyRule -Parent global -RuleId $x -MajorVersion 1 -UserAgent LitwareincClient –CompareOp GEQ –Action Block

     

    Etc., etc.

     

    And here's a cool little trick. Suppose you have a bunch of different client version policies. (Which is possible: policies can be created at the site scope as well as the service scope.) What if you want to add this new rule to all your client version policies? Is there a way to do that? Of course there is:

     

    $x = [guid]::NewGuid()

     

    Get-CsClientVersionPolicy | ForEach-Object {New-CsClientVersionPolicyRule -Parent $_.Identity -RuleId $x -MajorVersion 1 -UserAgent LitwareincClient –CompareOp GEQ –Action Allow}

     

    All we've done here is use the Get-CsClientVersionPolicy cmdlet to return a collection of all our existing client version policies. We've then piped that collection to the ForEach-Object cmdlet, and asked ForEach-Object to add the new rule to each policy in the collection. The trick to making this work? We set the value of the Parent parameter to $_.Identity; that tells ForEach-Object to use the Identity of the client version policy (whichever policy ForEach-Object happens to be working with at the moment) as the new rule's parent.

     

    It's a tiny bit clunky, but it works around the fact that you can't directly pipe data to New-CsClientVersionPolicyRule.

     

    Got all that? OK. Now suppose you later change your mind and need to modify this rule; for example, suppose you now want to block the use of LitwareincClient. In that case, it's Set-CsClientVersionPolicyRule to the rescue:

     

    Get-CsClientVersionPolicyRule | Where-Object {$_.UserAgent -eq "LitwareincClient"} | Set-CsClientVersionPolicyRule -Action "Block"

     

    Note that we cheated a little bit here. If we wanted to, we could have used a command like this one to modify the rule:

     

    Set-CsClientVersionPolicyRule –Identity "global/4730866f-8594-431b-ad57-6f1c798ce0d8" -Action "Block"

     

    Oddly enough, however, we had no desire to type in global/4730866f-8594-431b-ad57-6f1c798ce0d8 as the Identity of the rule we need to change. Therefore, we simply used Get-CsClientVersionPolicyRule to return all the rules, then piped that collection of rules to the Where-Object cmdlet. We then used Where-Object to pick out only those rules that have a UserAgent equal to LitwareincClient, then piped that filtered collection to Set-CsClientVersionPolicyRule.

     

    And sure, we could get a little more specific if we wanted to, or needed to. For example, this command will only modify LitwareincClient applications found in the global policy:

     

    Get-CsClientVersionPolicyRule | Where-Object {$_.UserAgent -eq "LitwareincClient" –and $_.Identity –match "^global"} | Set-CsClientVersionPolicyRule -Action "Block"

     

    To do that, we simply added another stipulation to Where-Object:

     

    Where-Object {$_.UserAgent -eq "LitwareincClient" –and $_.Identity –match "^global"}

     

    Now we want only those rules where the UserAgent is equal to LitwareincClient and the Identity matches ^global. That's a regular expression that tells PowerShell to match only rules where the Identity begins with (^) the string value global.

     

    Note.  Why didn't we just match the value of the Parent property? That's easy: rules don't actually have a Parent property. The Parent parameter is a little shortcut way to specify the Identity for a new rule, but that Identity – as we've already seen – will end up being something like global/4730866f-8594-431b-ad57-6f1c798ce0d8. And the only way to get at the global portion of that Identity is to use a method similar to the one we just showed you.

     

    And if you change your mind and decide you don't need this rule after all, well, you can also get rid of it using Remove-CsClientVersionPolicyRule:

     

    Get-CsClientVersionPolicyRule | Where-Object {$_.UserAgent -eq "LitwareincClient"} | Remove-CsClientVersionPolicyRule

     

    So is there even more to the CsClientVersionPolicyRule cmdlets than what we just showed you? Maybe. But, like we said, you can have too much of a good thing. With that in mind, we'll see everyone tomorrow.