• Assigning Policies

     

    So how do you grant (assign) policies? Well, to begin with, it’s important to keep in mind that the only policies that need to be assigned are the per-user policies. When you create a site, service, or global policy those policies are automatically assigned at the time your create them. If you create a new site policy named site:Redmond you don’t have to assign the policy; instead, the moment you press Enter the policy is created and automatically assigned. And where is it assigned? You got it: it’s assigned to the Redmond site, which explains why you gave it the Identity site:Redmond. Global policies are automatically assigned at the global level and service policies are automatically assigned to the designated service; it’s as easy as that.

     

    Which, of course, leads to an obvious question: how do you assign the policy site:Redmond to a different site? And the answer is a simple one: you don’t. Instead, you have to create a new policy (one that has the same values as site:Redmond) and then give that policy a site-specific Identity, such as site:Dublin. You can’t copy a policy from one site to another.

     

    Note, Well, OK, we’ll show you a simple example of how you could write a script that effectively copies a policy from one site to another. To do this, we’ll make a copy of an existing voice policy (site:Redmond) and then assign that copy to the Dublin site. However, we’ll only copy over a couple of property values; we’ll leave it up to you to figure out how to copy all the property values from one policy to another:

     

    $x = Get-CsVoicePolicy -Identity site:Redmond

     

    New-CsVoicePolicy -Identity site:Dublin –AllowCallForwarding $x.AllowCallForwarding –AllowSimulRing $x.AllowSimulRing

     

    See what we’ve done here? In line 1, we’ve used Get-CsVoicePolicy to retrieve the site:Redmond policy; that policy gets stashed in the variable $x. In line 2, we used New-CsVoicePolicy to create a new voice policy with the Identity site:Dublin; that means this policy will automatically be assigned to the Dublin site. We’ve also added two parameters: -AllowCallForwarding and –AllowSimulRing. But notice the parameter values we’re using. For example, we don’t set –AllowCallForwarding to either $True or $False; instead, we set it to this:

     

    $x.AllowCallForwarding

     

    In other words, we want the Dublin policy’s AllowCallForwarding property to have the same value as the Redmond policy. And it will have the same value, because $x.AllowCallForwarding just happens to be the value of the Redmond policy’s AllowCallForwarding property. (Remember, we stashed the property values for that policy in the variable $x.)

     

    If you continue this pattern (like we did with –AllowSimulRing) for all the parameters you’ll eventually have a command that will copy the settings from site:Redmond to site:Dublin.

     

    It takes a little work, but it’s worth it.

     

    Now, where were we? Oh, yes: per-user policies are different from other types of policies. When you first create a per-user policy that policy doesn’t automatically get assigned anywhere; instead, it simply sits in the database and awaits further instructions. And what happens if you never assign that policy to anyone? Nothing; that just means that the policy never gets assigned to anyone.

     

    Of course, what’s the fun of having a per-user policy that never gets assigned to anyone? Bearing in mind that user policies can be assigned only to users, you can assign a policy using a command similar to this:

     

    Grant-CsVoicePolicy -Identity "Ken Myer" -PolicyName RedmondPolicy

     

    As you can see, all you have to do is call the Grant-CsVoicePolicy cmdlet followed by the Identity of the user you want to assign the policy to; in this case, that’s a user with the display name Ken Myer. You also need to use the –PolicyName parameter followed by the policy Name (which is really the policy Identity minus the policy scope).

     

    Note. So why is this Grant-CsVoicePolicy rather than Assign-CsVoicePolicy? Going with Grant was a decision the Microsoft Lync Server team made to adhere to Windows PowerShell naming standards. As it turns out, “Assign” isn’t an approved verb for PowerShell cmdlets – the closest alternative the Lync Server team could find was Grant. It’s not an exact match, but it does help to keep the Lync Server cmdlets consistent with the cmdlets used in other Microsoft products.

     

    Of course, you’re more likely to want to assign a policy to a group of users than to a single user. Without going into any detail (see Retrieving Active Directory and Microsoft Lync Server User Accounts for more information) here’s a command that assigns a voice policy to all the users in the Sales department:

     

    Get-CsUser -LDAPFilter "Department=Sales" | Grant-CsVoicePolicy -PolicyName RedmondPolicy

     

    Assigned Policies

     

    It’s not always easy to tell which policy is being applied to a given user account. Oh, it’s easy enough to retrieve a list of all the policies that are assigned to a user; for example, this command will take care of that:

     

    Get-CsUser "Pilar Ackerman" | Select-Object DisplayName, ClientPolicy, ClientVersionPolicy, ArchivingPolicy, PinPolicy, LocationPolicy, ConferencingPolicy, ExternalAccessPolicy, HostedVoicemailPolicy, VoicePolicy, DialPlan

     

    Like we said, retrieving the information is no problem. The problem occurs when you try to interpret the information. For example, take a look at the list of policies that have been assigned to Pilar Ackerman:

     

    DisplayName : Pilar Ackerman

    ClientPolicy :

    ClientVersionPolicy :

    ArchivingPolicy :

    PinPolicy :

    LocationPolicy :

    ConferencingPolicy :

    ExternalAccessPolicy :

    HostedVoicemailPolicy :

    VoicePolicy : UserVoicePolicy1

    DialPlan :

    PresencePolicy:

     

    To begin with, you’ll notice that there are a lot of blank entries; for example, nothing is listed for ClientPolicy or for ClientVersionPolicy. That doesn’t mean that no such policy has been applied to Pilar’s user account; instead, it simply means that no per-user policy has been applied to her account. If you don’t see a value listed for a policy that means that either a global policy or a site policy (or, more rarely, a service policy) is being used to manage that account. The following table shows a preliminary list (subject to change) of the scopes where policies can be applied.

     

    Policy

    Global Scope

    Site Scope

    Service Scope

    Per-User Scope

    ClientPolicy

    Yes

    Yes

     

    Yes

    ClientVersionPolicy

    Yes

    Yes

    Yes

    Yes

    ArchivingPolicy

    Yes

    Yes

     

    Yes

    PinPolicy

    Yes

    Yes

     

    Yes

    LocationPolicy

    Yes

     

     

    Yes

    ConferencingPolicy

    Yes

    Yes

     

    Yes

    ExternalAccessPolicy

    Yes

    Yes

     

    Yes

    HostedVoiceMailPolicy

    Yes

    Yes

     

    Yes

    VoicePolicy

    Yes

    Yes

     

    Yes

    DialPlan

    Yes

     

     

    Yes

    PresencePolicy

    Yes

    Yes

    Yes

    Yes

     

    To really determine which policy is governing Pilar’s user account you would need to check to see if Pilar’s account falls under the jurisdiction of a site or service policy. If not, that means the global policy is being used to manage her account. (If there are no other policies available then the global policy will always be used to manage an account.) Is it possible to do this using Windows PowerShell? Yes, but the process is a bit more complicated than we can deal with in this introductory article. (But if you really want to know how to determine the “effective policy” for a user, well, then you should take a look at this article.)

     

    What’s more interesting (at least for our immediate purposes) is the fact that a per-user voice policy has been assigned to Pilar:

     

    VoicePolicy : UserVoicePolicy1

     

    As you can see, Pilar has been assigned a per-user voice policy, a policy with the identity UserVoicePolicy1.

     

    By the way, whatever happened to Group Policy?

     

    Good question. As you have probably figured out, the policies we are discussing here have nothing to do with Group Policy: they aren’t built using Group Policy, they aren’t assigned using Group Policy, and few of the settings correspond to the Group Policy settings used in Microsoft Office Communications Server 2007 R2. So what did happen to Group Policy?

     

    To tell you the truth, it’s gone. Instead of Group Policy, all the client settings for Lync Server will be managed using CsClientPolicy. If you’re wondering why we chose to get rid of Group Policy there are a number of factors that went into that decision. First, there’s no doubt that Group Policy is difficult to target towards specific users; for example, it’s close-to-impossible to apply a Group Policy to all the users in a given department, or all the users who have a certain job title. As we just saw, that’s not the case with Lync Server policies. On top of that, it can also be a challenge to determine which Group Policy object is taking precedence for a given user. That’s also much easier with Lync Server policies:

     

             If there is a per-user policy assigned to the user then the per-user policy is used.

             If there is no per-user policy assigned to the user then the service policy is used.

             If there is no per-user or service policy then the site policy is used.

             If there is no per-user, service, or site policy then the global policy is used.

     

    Another important consideration is the fact that Group Policy is applied only to authenticated users who log on to Active Directory. That means that Group Policy does not get applied to users accessing Office Communications Server over the Internet or via a cell phone. Needless to say, that’s a problem. Or, more correctly, it was a problem. Unlike Group Policy, Lync Server 2010’s in-band policies and configuration settings are applied to everyone who logs on to the system, regardless of who they are, where they are, or what type of device they use to log on.

  • When Do I Have to Put Double Quotes Around Parameter Values?

    Ah, yes: the great double quote conundrum. Compared to, say, VBScript, Windows PowerShell is remarkably good at figuring at what it is you want to do. For example, this command returns the voice policy RedmondVoicePolicy:

     

    Get-CsVoicePolicy "RedmondVoicePolicy"

     

    So does this command:

     

    Get-CsVoicePolicy 'RedmondVoicePolicy'

     

    And so does this one:

     

    Get-CsVoicePolicy RedmondVoicePolicy

     

    In other words, it doesn’t seem to matter: single quotes, double quotes, no quotes – PowerShell always seems to figure out that you want to get back the RedmondVoicePolicy.

     

    And yes, former VBScripters – who, on admittedly-rare occasions, have had to string 5 or 6 double quotes together (i.e., """""") – really should send the PowerShell team a thank-you note.

     

    Now, to be honest, the reason any one of the preceding commands works is due, in part, to the fact that PowerShell’s command parser is pretty clever. However, it’s also due to the fact that we cheated a little; after all, we passed Get-CsVoicePolicy a very simple Identity, one that doesn’t include blank spaces or special characters. The fact of the matter is that, if you start throwing in special characters, well, at that point things get a little dicier. And when things start to get dicey, then it's time to start putting double quotes around your parameter values.

     

    For example, one "special" character that PowerShell has problems with is the blank space. Suppose you have a policy named Redmond Voice Policy. What do you think will happen if you run this command:

     

    Get-CsVoicePolicy Redmond Voice Policy

     

    Hey, good guess; as it turns out, you are going to get this error message:

     

    Get-CsVoicePolicy : A positional parameter cannot be found that accepts argument 'Voice'.

     

    So what does that error message actually mean? Well, with Get-CsVoicePolicy, the Identity parameter is known as a "positional" parameter. That means you can leave off the –Identity portion of a command and, as long as the Identity is the very first parameter in that command, PowerShell will be able to figure things out and everything will work just fine. In other words, these two commands – one that includes the actual parameter name (Identity) and one that doesn't – are functionally identical:

     

    Get-CsVoicePolicy -Identity RedmondVoicePolicy

    Get-CsVoicePolicy RedmondVoicePolicy

     

    Cool, huh? So then what's the problem with this command:

     

    Get-CsVoicePolicy Redmond Voice Policy

     

     

    Well, to begin with, one problem with the command Get-CsVoicePolicy Redmond Voice Policy is that PowerShell doesn’t see a parameter anywhere; remember, PowerShell parameters always have to start with a hyphen. (You know, like –Identity.) Because PowerShell doesn't see anything that starts with a hyphen, it assumes that you must be relying on a positional parameter. (You know, like –Identity.) As a result, Get-CsVoicePolicy assumes that you've left off –Identity (which is perfectly legal) and are using Redmond as the first parameter value; in other words, the cmdlet assumes that you’re looking for a voice policy with the Identity Redmond.

     

    That's great. (Incorrect, but great.) PowerShell then looks for the next parameter; that is, for the next item that starts with a hyphen. Again, however, no such item can be found; all PowerShell sees is the value Voice. As a result, the cmdlet assumes that Voice is a parameter value for the second positional parameter. Unfortunately, though, that doesn’t make sense: Get-CsVoicePolicy doesn’t have a second positional parameter. As a result, the command blows up.

     

    Which is probably what any one of us would do when put in a similar situation.

     

    The fact of the matter is that blank spaces will almost always cause problems when used as parameter values; that’s because PowerShell uses blank spaces as its way of determining where one parameter/parameter value ends and the next one begins. But that’s OK; all you have to do is remember that, if you have a blank space in a parameter value, you just have to enclose that value in double quote marks, like so:

     

    Get-CsVoicePolicy "Redmond Voice Policy"

     

    And there you have it: blank spaces represent one occasion where you absolutely have to surround a parameter value in double quote marks.

     

    Commas represent another occasion. Suppose you have a voice policy with this Identity: Redmond,WA. No blanks spaces means no problem, right? Well, let’s find out. Try running this command:

     

    Get-CsVoicePolicy Redmond,WA

     

    Disaster! This time around you get the following error message:

     

    Get-CsVoicePolicy : Cannot convert 'System.Object[]' to the type 'Microsoft.Rtc.Management.Xds.XdsIdentity' required by the parameter 'Identity'. Specified method is not supported.

     

    Well, that should clear everything up, eh?

     

    OK, on the off-chance that this didn’t clear everything up, here’s what the error message means. Some PowerShell cmdlets allow you to enter multiple values for a single parameter. For example, if you want to use the Get-ChildItem cmdlet to return information from 3 separate folders you can use a command like this:

     

    Get-ChildItem C:\Folder_A, C:\Folder_B, C:\Folder_C

     

    Note. Of course that will work. Give it a try and see for yourself. And, just to make things more confusing, try it without spaces after the commas: it still works.

     

    Notice the syntax of that command: three folder names, all separated by commas. For all intents and purposes, what we’re doing in that command is passing Get-ChildItem an array of folder names. That one command is equivalent to the following two commands:

     

    $x = "C:\Folder_A", "C:\Folder_B", "C:\Folder_C"

    Get-ChildItem $x

     

    Note. Yes, we have to use double quotes here. That’s because we’re using the folder names in a variable assignment rather than a parameter value. If you assign a string value (like a folder name) to a variable you always have to enclose that value in double quotes (or single quotes; that’ll work, too).

     

    That explains why using a parameter value like Redmond,WA fails, at least when using the  Get-CsVoicePolicy cmdlet. In this example, Get-CsVoicePolicy thinks we’re passing it an array of policy Identities: one policy with the Identity Redmond and a second policy with the Identity WA. What's the problem with that? Well, Get-CsVoicePolicy can’t take an array as an Identity parameter value. Remember the error message we got? Silly question; who could forget an error message like this one:

     

    Get-CsVoicePolicy : Cannot convert 'System.Object[]' to the type 'Microsoft.Rtc.Management.Xds.XdsIdentity' required by the parameter 'Identity'. Specified method is not supported.

     

    If you look closely at the text in the error message you'll see the syntax System.Object[]; this syntax indicates an array. (In PowerShell, [] always indicates an array.) The error message is telling us that it can’t convert an array to an Identity. And because Get-CsVoicePolicy can't accept an array of Identities the command blows up.

     

    The workaround to all this? You guessed it: double quote marks. This command will return the Redmond,WA voice policy:

     

    Get-CsVoicePolicy "Redmond,WA"

     

    In other words, if your parameter value includes a comma then you need to enclose that value in double quote marks.

     

    Just like you do if your parameter value includes a blank space.

     

    Ah, good question: why do we keep saying "double quotes" when single quotes work just as well? Well, it's true that, in general, you can use either single quotes or double quotes. For example, either one of these commands returns the voice policy Redmond,WA, no questions asked:

     

    Get-CsVoicePolicy "Redmond,WA"

    Get-CsVoicePolicy 'Redmond,WA'

     

    But consider this: a voice policy that has the Identity Administrator’s Policy. This command is going to work just fine:

     

    Get-CsVoicePolicy "Administrator's Policy"

     

    This command – well, let’s just say that this command isn’t going to work quite as well:

     

    Get-CsVoicePolicy 'Administrator's Policy'

     

    In fact, this command isn’t even going to fail, at least not right away. If you type in the preceding command and then press ENTER PowerShell will respond by showing you this:

     

    >> 

     

    If you’ve never seen the >> prompt before, that’s PowerShell’s way of telling you that it’s waiting for the end of the command. If you press ENTER, PowerShell will respond by showing you this:

     

    >> 

     

    And if you press ENTER again – well, let's put it this way: you're doomed. The truth is, this loop is going continue forever and ever and ever. Or at least until you press Ctrl+C.)

     

    So what’s the deal here: why is PowerShell waiting for you to "finish" your command even though you already finished your command? Well, here’s why. Our command includes this string value:

     

    'Administrator'

     

    We know that the second single quote mark isn't really a single quote mark; it's an apostrophe. However, PowerShell doesn't know that; it has no idea what an apostrophe is. Instead, PowerShell sees a string value enclosed in a pair of single quote marks; consequently, PowerShell thinks this this is a separate parameter value. We then have this:

     

    s Policy'

     

    To the best of our knowledge, PowerShell has no idea what to make of that. However, it does see just one single quote mark and, when it comes to parameter values, single quote marks tend to travel in pairs. Therefore, it’s waiting for you to "finish" the command by entering the second single quote mark. And PowerShell will keep bugging you until you finally do enter that "concluding" quote mark.

     

    Of course, as soon as you do you'll get the following error message:

     

    Get-CsVoicePolicy : A positional parameter cannot be found that accepts argument 's'.

     

    We won’t bother going into any more detail on this; you can probably figure it out on your own. And besides, that's not really the point; the point is, that if you have a parameter value that includes a single quote mark (i.e., an apostrophe) then you must enclose that value in double quotes rather than single quotes. For example:

     

    Get-CsVoicePolicy "Administrator's Policy"

     

    And yes, you do need to flip this around if – heaven forbid – you create a policy that has double quote marks in the Identity; for example, a policy that has the Identity "Test" Policy. In that case, you'll need to enclose the parameter value in single quote marks, like so:

     

    Get-CsVoicePolicy '"Test" Policy'

     

    Of course, the best thing to do is to avoid using special characters when creating Identities. (We’d even go so far as to suggest avoiding blank spaces.) And it's not necessarily a bad idea to get into the habit of enclosing all your string characters in double quotes marks. Do that and – outside of oddball exceptions like "Test" Policy – it should be smooth sailing.

     

    There are one of two exceptions to everything we’ve told you so far. For example, there are times when only single quote marks will do. Here’s an example of creating a new voice normalization rule:

     

    New-CsVoiceNormalizationRule -Parent SeattleUser -Name SeattleFourDigit -Description "Dialing with internal four-digit extension" -Pattern '^(\d{4})$' -Translation '+1206555$1'

     

    Notice the value for the parameter Translastion: '+1206555$1'. This value must be enclosed in single quotes. If you don’t use single quotes, PowerShell will helpfully interpret the $1 at the end of the string as a variable. (Remember that all variables in PowerShell begin with a $.) Assuming you didn’t assign a value to a variable named $1, the value will be empty, so your Translation value will be interpreted as +1206555. We want the $1 to be included with the value, so we must enclose the entire string in single quotes.

     

    Oh, and as long as we’re on the subject, we should mention the fact that special characters can also create some … interesting … issues when you’re working interactively with PowerShell. (That is, when PowerShell is prompting you for information and you’re typing in the requested data and pressing ENTER.) Because of that, it might be worth your while to take a look at Edwin Young’s two-part series %$#@ Special Characters!

    This question is one of many asked during a session on managing Lync Server 2010 with Windows PowerShell at the TechReady 11 conference. For more questions and answers asked during that session, take a look at the Questions and Answers from TechReady 11.

  • How To: Create a GUI Application with PowerShell

     

    If you want to see an example of a GUI application created in Windows PowerShell, take a look at the Lync Server Deleteomatic. If you want to learn how to use PowerShell to create your own Deleteomatic, or any other type of GUI application, read this article. We’ll take you through step-by-step.

     

    This article is for anyone working with Windows PowerShell. If you’re not working with Lync Server, we’ve pointed out where the Lync Server specific code is and you can easily replace it with any other .NET or PowerShell code.

     

    http://blogs.technet.com/b/csps/archive/2011/12/07/guiapp.aspx

     

  • Modify the SIP Address of an Enabled Lync Server User

    Can Set-CsUser construct a SIP address for you?

     

    No, it can't. As you probably know, when you enable a user for Lync Server you can ask the system to create the user's SIP address for you. For example, this command gives Ken Myer a SIP address based on his SamAccountName:

     

    Enable-CsUser –Identity "Ken Myer" –RegistrarPool atl-cs-001.litwareinc.com –SipAddressType SamAccountName –SipDomain litwareinc.com

     

    Do you have to know Ken's SamAccountName in order to run this cmdlet? Nope; Enable-CsUser will figure that out for you.

     

    Of course, you don't have to let Lync Server create a SIP address for you; you can give someone any valid SIP address you want to give them. And, for the sake of argument, let's pretend that, while you were on vacation, another administrator enabled Ken Myer for Lync Server and gave him the SIP address sip:bigkahuna@litwareinc.com.

     

    Yikes.

     

    Note. In case you're wondering "kahuna" is a Hawaiian word that means, among other things, "an expert in any profession." So are the Lync PowerShell blog authors the big kahunas of Lync Server PowerShell?

     

    We'd rather not answer that question.

     

    Fortunately, SIP addresses are not written in stone; after all, you can easily change the SIP address by running the Set-CsUser cmdlet:

     

    Set-CsUser –Identity "Ken Myer" –SipAddress "sip:kenmyer@litwareinc.com"

     

    But suppose you've decided to use a user's email address as his or her SIP address. If you were enabling Ken Myer's user account you could get Lync Server to automatically assign Ken's email address to his SIP address:

     

    Enable-CsUser –Identity "Ken Myer" –RegistrarPool atl-cs-001.litwareinc.com –SipAddressType EmailAddress

     

    But Set-CsUser can't do that; there's no way to auto-assign a SIP address in Lync Server once a user is enabled. That means that you're going to have to look up Ken's email address, then manually type it in when calling Set-CsUser. Which, of course, means you could mistype the address and then have to start all over again:

     

    Set-CsUser –Identity "Ken Myer" –SipAddress "sip:eknmyer@litwareinc.com"

     

    Bummer.

     

    Now, as far as we know, there's no way to somehow coerce Set-CsUser into auto-assigning a SIP address for you. But there is a nifty little trick we stumbled upon while playing around with this. Before we show you that trick, let's recap: Ken Myer has already been enabled for Lync Server; as it turns out, he's also been enabled for Enterprise Voice, been assigned a line URI, and been assigned a bunch of policies. In other words:

     

    Identity               : CN=kenmyer,OU=Redmond,DC=Litwareinc,DC=com

    VoicePolicy            : RedmondVoicePolicy

    ConferencingPolicy     :

    PresencePolicy         :

    DialPlan               : RedmondDialPlan

    LocationPolicy         :

    ClientPolicy           : ITDepartmentPolicy

    ClientVersionPolicy    :

    ArchivingPolicy        :

    PinPolicy              : ITDepartmentPinUser

    ExternalAccessPolicy   : FederationAndPICDefault

    HostedVoiceMail        :

    HostedVoicemailPolicy  :

    HostingProvider        : SRV:

    RegistrarPool          : atl-cs-litwareinc.com.com

    Enabled                : True

    SipAddress             : sip:bigkahuna@litwareinc.com

    LineURI                : TEL:+114255551111;ext=1219

    EnterpriseVoiceEnabled : True

     

    With that in mind, we wondered what would happen if we simply re-enabled Ken's account for Lync Server:

     

    Enable-CsUser –Identity "Ken Myer" –RegistrarPool atl-cs-001.litwareinc.com –SipAddressType EmailAddress

     

    To be honest, we sort of expected that re-enabling his account would wipe out all of his previous policies and settings. But, much to our surprise, that's not what happened:

     

    Identity               : CN=kenmyer,OU=Redmond,DC=Litwareinc,DC=com

    VoicePolicy            : RedmondVoicePolicy

    ConferencingPolicy     :

    PresencePolicy         :

    DialPlan               : RedmondDialPlan

    LocationPolicy         :

    ClientPolicy           : ITDepartmentPolicy

    ClientVersionPolicy    :

    ArchivingPolicy        :

    PinPolicy              : ITDepartmentPinUser

    ExternalAccessPolicy   : FederationAndPICDefault

    HostedVoiceMail        :

    HostedVoicemailPolicy  :

    HostingProvider        : SRV:

    RegistrarPool          : atl-cs-litwareinc.com.com

    Enabled                : True

    SipAddress             : sip:kenmyer@litwareinc.com

    LineURI                : TEL:+114255551111;ext=1219

    EnterpriseVoiceEnabled : True

     

    The only thing that changed was his SIP address, which was automatically set to his email address. Nothing else changed; for example, all the policies that had been assigned to him remained assigned to him.

     

    To be honest, we had no idea that would actually work.

     

    So is that an absolutely perfect solution? No, not necessarily. For one thing, we haven't put this through an exhaustive and comprehensive set of tests; that's something you might want to do before trying on a real user and a real user account. For another, you still have to type in the correct Registrar pool. If you only have one Registrar pool that should be no problem. If you have multiple Registrar pools, you might have to be a bit more careful: you could potentially create some problems by assigning Ken to the wrong pool. Still, we thought it was a cool little trick, and something you might be interested in trying sometime.

     

     

  • Administratively Managing User Contact Lists

    WMI Namespaces and Scripting Examples

     

    By Nick Smith, Microsoft

     

    Introduction

     

    Your company has a requirement to administratively add/remove/modify the membership of Microsoft Office Communications Server 2007 R2 users' contact lists.  This document provides Windows PowerShell scripting examples for managing OCS users' contact lists via the supported Windows Management Instrumentation (WMI) namespaces.  The PowerShell scripting examples will cover managing contact groups, distribution groups, and individual contacts.

     

    Purpose

     

    The LCSAddContacts.wsf script included in the Office Communications Server 2007 R2 Resource Kit Tools provides a full end-to-end script for adding and deleting contacts from user contact lists.  However, the LCSAddContacts.wsf script does not provide the ability to accomplish specific tasks such as deleting a contact group or adding a distribution list to a contact list. This document will detail granular tasks and the WMI namespaces that can be used to manage user contact lists for both individual contacts and groups. PowerShell script examples will be provided to demonstrate how to accomplish each task.

     

    Audience

     

    This document is intended for Office Communications Server administrators that require the ability to administratively add/remove/modify contacts from individual user contact lists.

     

    Disclaimer

     

    The WMI classes used in the scripting examples are supported by Microsoft.  The WMI classes are the only supported methodfor administrative contact list management available in OCS 2007 R2.

     

    The sample code contained in this document is intended for demonstration purposes only.  The scripts are intended to demonstrate functionality; no error checking has been added.  All code is provided as-is and if you choose to implement or enhance the scripts you will be responsible for any subsequent maintenance and support.

     

    Contact Group Management

     

    The scripts in this section demonstrate how to manage contact groups in OCS 2007 R2 by using WMI and Windows PowerShell.

     

    Contact Groups

     

    The MSFT_SIPESUserContactGroupData WMI class exposes the ability to view and modify contact groups for OCS users.  Using this class you can add/delete contact groups, allowing you to define the group name. This WMI class is documented at http://msdn.microsoft.com/en-us/library/dd146647(office.13).aspx.

     

    Below are PowerShell scripting examples demonstrating specific tasks related to user contact list group management.  Each sample script can be run independently.  The highlighted values in the scripts should be changed to the intended values for proper outcomes.

     

    Adding a Contact Group

     

    #Define WMI Put options

    $PutOptions = New-Object System.Management.PutOptions

    $PutOptions.Type = 2 #CreateOnly

     

    #Get User Information

    $User = Get-WmiObject -Query "Select * from MSFT_SIPESUserSetting where PrimaryURI = 'sip:user@domain.com'"

     

    #Create ContactGroup instance

    $ContactGroup = (New-Object System.Management.ManagementClass("MSFT_SIPESUserContactGroupData")).CreateInstance()

    $ContactGroup.UserInstanceID = $User.InstanceID

    $ContactGroup.Name = "NAME OF GROUP HERE"

     

    #Create and commit group

    $ContactGroup.PsBase.Put($PutOptions)

     

    Check if a Contact Group Exists

     

    #Get User Information

    $User = Get-WmiObject -Query "Select * from MSFT_SIPESUserSetting where PrimaryURI = 'sip:user@domain.com'"

    $UserInstanceID = $User.InstanceID

     

    #Check if group is already added to user's contact list

    $ContactGroup = (Get-WmiObject -Query "Select * from MSFT_SIPESUserContactGroupData where UserInstanceID = '$UserInstanceID'" | Where {$_.Name -like "NAME OF GROUP HERE"})

     

    if ($ContactGroup -ne $null) { #The group is on the user's contact list

          Write-Host "The group exists on the user's contact list"

    }

    elseif ($ContactGroup -eq $null) { #The group is not on the user's contact list

          Write-Host "The group does not exist on the user's contact list"

     

    Removing a Contact Group

     

    #Get User Information

    $User = Get-WmiObject -Query "Select * from MSFT_SIPESUserSetting where PrimaryURI = 'sip:user@domain.com'"

     

    #Get the contact group on the user's contact list

    $ContactGroup = (Get-WmiObject -Query "Select * from MSFT_SIPESUserContactGroupData where UserInstanceID = '$UserInstanceID'" | Where {$_.Name -like "NAME OF GROUP HERE"})

     

    #Delete the group

    $ContactGroup.Delete()

     

     

    Contact Distribution Groups

     

    Office Communicator 2007 and Office Communicator 2007 R2 provide the ability to add any mail-enabled group in Active Directory to a user’s contact list. The group and all the OCS-enabled members will be displayed on the user’s contact list.

     

    The MSFT_SIPESUserContactGroupData WMI class exposes the ability to view and modify contact groups for OCS users.  Using this class you can add/delete contact groups, allowing you to define the group name and ExternalURL.  A distribution group is specified by XML content stored in the ExternalURL property. This WMI class is documented at http://msdn.microsoft.com/en-us/library/dd146647(office.13).aspx.

     

    Below are PowerShell scripting examples demonstrating specific tasks related to user contact list distribution group management.  Each sample script can be run independently.  The highlighted values in the scripts should be changed to the intended values for proper outcomes.

     

    Adding a Contact Distribution Group

     

    #Define WMI Put options

    $PutOptions = New-Object System.Management.PutOptions

    $PutOptions.Type = 2 #CreateOnly

     

    #Get User Information

    $User = Get-WmiObject -Query "Select * from MSFT_SIPESUserSetting where PrimaryURI = 'sip:user@domain.com'"

     

    #Create ContactGroup instance

    $ContactGroup = (New-Object System.Management.ManagementClass("MSFT_SIPESUserContactGroupData")).CreateInstance()

    $ContactGroup.UserInstanceID = $User.InstanceID

    $ContactGroup.Name = "NAME OF GROUP HERE"

    $ContactGroup.ExternalURL = "<groupExtension groupType=`"dg`"><email>group@domain.com</email></groupExtension>"

     

    #Create and commit group

    $ContactGroup.PsBase.Put($PutOptions)

     

    Checking if a Contact Distribution Group Exists

     

    #Get User Information

    $User = Get-WmiObject -Query "Select * from MSFT_SIPESUserSetting where PrimaryURI = 'sip:user@domain.com'"

    $UserInstanceID = $User.InstanceID

     

    #Check if group is already added to user's contact list

    $ContactGroup = (Get-WmiObject -Query "Select * from MSFT_SIPESUserContactGroupData where UserInstanceID = '$UserInstanceID'" | Where {$_.ExternalURL -like "*group@domain.com*"})

     

    if ($ContactGroup -ne $null) { #The group is on the user's contact list

          Write-Host "The group exists on the user's contact list"

    }

    elseif ($ContactGroup -eq $null) { #The group is not on the user's contact list

          Write-Host "The group does not exist on the user's contact list"

    }

     

    Removing a Contact Distribution Group

     

    #Get User Information

    $User = Get-WmiObject -Query "Select * from MSFT_SIPESUserSetting where PrimaryURI = 'sip:user@domain.com'"

     

    #Get the contact group on the user's contact list

    $ContactGroup = (Get-WmiObject -Query "Select * from MSFT_SIPESUserContactGroupData where UserInstanceID = '$UserInstanceID'" | Where {$_.ExternalURL -like "*group@domain.com*"})

     

    #Delete the group

    $ContactGroup.Delete()

     

    Individual Contact Management

     

    The MSFT_SIPESUserContactData WMI class exposes the ability to view and modify contacts for OCS users.  Using this class you can add/delete contacts allowing you to define contact group memberships as necessary. This WMI class is documented at http://msdn.microsoft.com/en-us/library/dd185919(office.13).aspx.

     

    Below are PowerShell scripting examples demonstrating specific tasks related to user contact list management.  Each sample script can be run independently.  The highlighted values in the scripts should be changed to the intended values for proper outcomes.

     

    Adding a Contact

     

    #Get User Information

    $User = Get-WmiObject -Query "Select * from MSFT_SIPESUserSetting where PrimaryURI = 'sip:user@domain.com'"

    $UserInstanceID = $User.InstanceID 

     

    #Create Contact instance

    $Contact = (New-Object System.Management.ManagementClass("MSFT_SIPESUserContactData")).CreateInstance()

    $Contact.UserInstanceID = $User.InstanceID

    $Contact.SIPURI = "sip:userToBeAdded@domain.com"

    $Contact.Subscribed = $True

     

    #These next steps are optional unless you want to assign a contact to a contact group

    $ContactGroup = (Get-WmiObject -Query "Select * from MSFT_SIPESUserContactGroupData where UserInstanceID = '$UserInstanceID'" | Where {$_.Name -like "NAME OF GROUP HERE"})

    $Contact.GroupID = $ContactGroup.GroupID

     

    #Create and commit contact

    $Contact.Put()

     

    Checking if a Contact Exists

     

    #Get User Information

    $User = Get-WmiObject -Query "Select * from MSFT_SIPESUserSetting where PrimaryURI = 'sip:user@domain.com'"

    $UserInstanceID = $User.InstanceID

     

    #Check if contact is already added to user's contact list

    $Contact = (Get-WmiObject -Query "Select * from MSFT_SIPESUserContactData where UserInstanceID = '$UserInstanceID'" | Where {$_.SIPURI -like "sip:userToCheck@domain.com"})

     

    if ($Contact -ne $null) { #The contact is on the user's contact list

          Write-Host "The contact exists on the user's contact list"

    }

    elseif ($Contact -eq $null) { #The contact is not on the user's contact list

          Write-Host "The contact does not exist on the user's contact list"

    }

     

    Removing a Contact

     

    #Get User Information

    $User = Get-WmiObject -Query "Select * from MSFT_SIPESUserSetting where PrimaryURI = 'sip:user@domain.com'"

     

    #Get the contact on the user's contact list

    $Contact = (Get-WmiObject -Query "Select * from MSFT_SIPESUserContactData where UserInstanceID = '$UserInstanceID'" | Where {$_.SIPURI -like "sip:userToDelete@domain.com"})

     

    #Delete the contact

    $Contact.Delete()