• Assign a Policy to All the Users in a Security Group

    When it comes time to assign per-user policies, those of us here at Microsoft thought of everything. For example, if you take a look at the article Assigning Policies you’ll see how per-user policies can be assigned to a single user; to all the users with accounts in a specified OU; to all the users in a particular department; to all the users with a given job title; etc., etc., etc. Like we said, we thought of everything.

     

    What’s that? How can you assign a policy to all the users in a particular security group? Hmmm, we never thought of that ….

     

    OK, we admit it: we didn’t add a straightforward way to assign a policy to all the users in a security group. So does that mean that there’s no way to assign a policy to all the users in a security group? Let’s put it this way:

     

    $strFilter = "(&(objectCategory=Group)(SamAccountName=" + $args[0] +"))"

     

    $objDomain = New-Object System.DirectoryServices.DirectoryEntry

     

    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher

    $objSearcher.SearchRoot = $objDomain

    $objSearcher.Filter = $strFilter

    $objSearcher.SearchScope = "Subtree"

     

    $colProplist = "member"

    foreach ($i in $colPropList)

        {[void] $objSearcher.PropertiesToLoad.Add($i)}

     

    $colResults = $objSearcher.FindAll()

     

    foreach ($objResult in $colResults)

        {$objItem = $objResult.Properties; $group = $objItem.member}

     

    foreach ($x in $group)

        {

            Grant-CsClientPolicy $x -PolicyName $args[1]

        }

     

    Before we explain what this script does (although, by now, you can probably guess what it does) let’s explain how it works. Assuming you’ve copied this code and saved it as a .ps1 file (e.g., C:\Scripts\Assign-ToGroup.ps1) you run the thing by using a command similar to this:

     

    C:\Scripts\Assign-ToGroup.ps1 "FinanceUsers" "FinanceClientPolicy"

     

    In this command, FinanceUsers is the name of the security group we want to assign a policy to (in this example, we’re assigning a client policy). And which policy are we assigning? That’s the second parameter passed to the script; in this example, we’re assigning the client policy FinanceClientPolicy.

     

    As for the script itself, the first thing it does is search Active Directory in order to find the specified security group. Once that’s done the script then uses this snippet of code to retrieve all the group members and store those users in a variable named $group:

     

    $group = $objItem.member

     

    From there the script takes the group members and, one-by-one, connects to the appropriate user account in Active Directory. The script retrieves the user’s display name (stored in the variable $z), then uses this line of code to assign FinanceClientPolicy to the user in question:

     

    Grant-CsClientPolicy $z -PolicyName $args[1]

     

    Like we said, $z is the user’s display name; meanwhile, $args[1] is a Windows PowerShell variable that references the second command-line argument passed to the script.

     

    And that, as they say, is that.

     

    Keep in mind that this script assigns a client policy to all the users in a security group, and a client policy is the only kind of policy it can assign. What if you want to assign, say, a voice policy to all the users in a security group? That’s fine; just search the script for the cmdlet name Grant-CsClientPolicy and replace it with Grant-CsVoicePolicy. That’s all you have to do.

     

    Gee, maybe we did think of everything after all ….

  • Introduction to Windows PowerShell Scripting

    Need to perform the same Windows PowerShell task over and over and over again? Ever thought about put­ting the commands for carrying out this task into a script?

    In this world there’s always something to be glad about. (Hey, it’s true – just ask Pollyanna.) For example, system administration can occasionally be a complicated job that requires a lot of time and effort. But we can all be glad that we have Windows PowerShell to make the job easier. Sometimes PowerShell requires that you type a lot of information from the command line. But we can be glad we have shortcuts (see Windows PowerShell Shortcuts) and aliases (see Windows PowerShell Aliases) to make this quicker and simpler. And we can be especially glad that we have Windows PowerShell scripts.

    Suppose you find yourself typing the same commands over and over practically every time you start up Windows PowerShell. Wouldn’t it be much easier to simply type a file name at the command prompt than to retype this potentially long and complicated command over and over? Of course it would. Or suppose you want to schedule a command to run at a particular time. You can’t always be there at 2:00 AM to type in the command, but with a script you can schedule the command. Or, as another example, maybe you have a long series of commands that need to be run in sequence in order to perform a somewhat complicated set of actions. A script will make this a very simple task to accomplish.

     In this section we’re going to explain everything you need to know to get started writing scripts in Windows PowerShell. But before we start writing scripts, you need to know how some of the pieces work.

     Objects, Properties, and Variables

     If you read the Getting Started with Windows PowerShell article you know (or at least you should know) that cmdlets are the heart and soul of Windows PowerShell. What we didn’t tell you – because you were just getting started and we didn’t want to scare you away – was that there’s a basic principal of software development that lies behind the workings of every cmdlet: the object.

     Hang on, don’t go yet. Yes, we said “software development,” and you’re not a software developer, you’re a system administrator. Big difference, whole different skill set, we know. But don’t worry, you don’t need to be a developer to work with PowerShell (that would kind of defeat the whole purpose, wouldn’t it?), and you don’t need to be a developer to understand objects. But as a system administrator who’s going to be working with Windows PowerShell, objects are a very useful thing to know about.

     So your first question is probably, ”Why do I need to know about objects?” (Either that or “What is an object?”, but we’re sure you’ll quickly get around to the “Why do I care?” question.) If we do our jobs right (which is never a guarantee, but, in this case, we’ll try), the reason you need to know will become clear as you read through this. As to what an object is, well, that’s easy: an object is a thing.

    See, that was painless, wasn’t it?

     What’s that? What kind of thing? Well…anything. A boat is an object. A chair is an object. A donut is an object. (Mmmm, donuts.) A process is an object. A shoe is an object. A –

     Huh? Yes, as a matter of fact we did say that a process is an object. Okay, let’s start over. We’ll start with the chair. (We’d start with the donut, but some of us tend to lose our focus when donuts are mentioned.) A chair is an object – it’s a thing that exists and has various qualities. Take a look at our chair:

    As we mentioned, an object has various qualities. Let’s look at some of the qualities of this chair: it has a color; it has arms; it has feet; it has a cushion; it has a back. These are all qualities that you can use to identify this particular chair, like this:

     

    Color

    Purple

    Arms

    True

    Legs

    4

    Cushion

    True

    Back

    True

     In the world of computers, these qualities are known as properties. For this particular chair, the Color property has a value of Purple. You might prefer to choose a chair with a Color property of Green. A chair either has arms or it doesn’t: this chair does, so the Arms property is True. Some stools have only three legs, while some rolling chairs don’t really have legs, they have wheels. Our chair has four legs, so our Legs property has a value of 4.

     Computers work the same way. A process is a thing: it has qualities, or properties, that identify that particular process. Try running the Get-Process cmdlet in Windows PowerShell. Run this from the command prompt and press Enter:

     Get-Process

     Depending on the processes running on your computer, you’ll get output similar to the following:

     Handles NPM(K)  PM(K) WS(K) VM(M) CPU(s) Id ProcessName

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

         71      3  1640   5544    52   0.08 2276 AcroBroker

         27      1   376   1616    11        1136 AEADISRV

         38      2   732   2348    21        1332 agrsmsvc

         57      3  1480   4944    53   0.06 3472 apdproxy

        105      3  1100   4108    37        1172 Ati2evxx

        137      4  1820   6216    42        1636 Ati2evxx

        111      4 11812  14520    43        1352 audiodg

        314     12 22684   3536   158   0.48 2824 CCC

        517     19 33476  11236   198   2.37 4400 CCC

       1066     16 38848  50692   148        2956 CcmExec

       1219     27 34580  49268   250  24.94 3936 communicator

         723    6  1752   5528     86         560 csrss

     The output shows you some of the properties of a process. For example, you can see that each process has a Name. The first process in the list has a Name of AcroBroker. A process also has an Id; here the first process in the list has an Id of 2276. And so on.

     As a general rule, you can find objects based on specific properties by using cmdlet parameters. For example, if you want to find the process with the Id 2824 you’d type this at the command prompt:

     Get-Process –Id 2340

     And, at least on our test machine, this is what we got back:

     Handles NPM(K)  PM(K) WS(K) VM(M) CPU(s)    Id ProcessName

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

        125      5         5928  9744     60  2340 svchost

     Another way to work with properties is by assigning the output of your commands to a variable. A variable is simply a place to save data in memory so you can later manipulate that data. In Windows PowerShell, all variables begin with a dollar sign ($). Let’s assign the results of our Get-Process call to a variable we’ll call $p:

     $p = Get-Process –Id 2340

     The first thing you’ll notice when you run this command is that you don’t get any output. (Keep in mind you might not have a process with Id 2340 on your computer. Run Get-Process and choose an Id that will work on your computer.) That’s because, instead of going to the display, all the information about this process object has been stored in the variable $p. How do we know this for sure? Just type this at the command prompt:

     $p

     Surprise! There’s the output. Now let’s find out what the name of this process is. To do that, we simply add a dot (.) plus the name of the property to the end of our variable name, like this:

    $p.Name

    Type this at the command prompt and you’ll see the name of the process with the Id 2340.

    You can also use variables for values other than objects. For example, type this at the command prompt:

    $a = 2 + 3

    Now type $a at the command prompt and press Enter. Here’s what you’ll see:

    5

    You can continue to use this variable for as long as this Windows PowerShell session is open. Try this:

    PS C:\scripts> $b = 8

    PS C:\scripts> $b - $a

    3

    Aren’t you glad we showed you that?

    Creating a Script

    Now that we have a general idea of how objects, properties and variables work, let’s talk a little bit about actually creating a script. A script in Windows PowerShell is simply a text file with a .PS1 file extension. Let’s create our first script.

    Start by opening Notepad. (Simply type notepad.exe at the PowerShell command prompt; or, from the Start menu, select All Programs, Accessories, then select Notepad.) Do that, and you’ll have an empty Notepad window:

    All you need to do here is type in the commands you would normally type at the command prompt. For example, type this into Notepad:

    $a = Get-Process –Name svchost

    Write-Host "Here are all the processes with the name svchost:"

    $a

    Before you run this script you need to save it. Select Save from the File menu (or press Ctrl+S). In the Save dialog box, enter the name you’d like to save the script to. Before you select Save, however (you didn’t just go ahead and try to save did you?), make sure you save the file with a .PS1 file extension. If you simply type test.ps1 in the File Name box of the Save dialog box you’ll end up with a file with the name test.ps1.txt. That’s because Notepad will helpfully assume that you’re saving a text file and will append the .txt extension for you. To keep this from happening, either enclose the file name in double quotes (“test.ps1”) or type test.ps1 in the File Name box and select All Files (*.*) from the Save As Type drop-down list box.

    You’ve just created your very first Windows PowerShell script. Congratulations! Aren’t you glad you know how to write a script now?

    Note: Keep in mind that what we showed you here isn’t the only thing you can do in a script. Anything you can do from the command prompt you can put in a script. A script can be anywhere from one line to hundreds of lines long. It all depends on what you want it to do and how complicated you want to get.

     Running a Script

     Now that you’ve created your script you probably want to run it and see it in action. So you open up Windows Explorer to the folder where you saved your script file, you double-click the .PS1 file, sit back, and wait for the magic to happen.

     As it turns out, however, your script opened up in Notepad.

    Hmmm, instead of running, your script opened up in Notepad. Interesting, but not exactly what you had in mind. “Oh wait,” you think. “I get it: you probably have to run Windows PowerShell before you can run a Windows PowerShell script. OK, that makes sense.” And so, with that in mind, you open up Windows PowerShell and type the path to the .PS1 file at the command prompt. You press ENTER and wait for the magic to happen.

     As it turned out, however, this is what happens:

     File C:\scripts\test.ps1 cannot be loaded because the execution of scripts is disabled on this system. Please see "get-help about_signing” for more details.

     Wow, how … nice. A new command shell and scripting environment that doesn’t even let you run scripts. What will those guys at Microsoft think of next?

     Listen, don’t panic; believe it or not, everything is fine. You just need to learn a few little tricks for running Windows PowerShell scripts.

     Running Scripts From Within Windows PowerShell

     Let’s start with running scripts from within Windows PowerShell itself. (Which, truth be told, is probably the most common way to run Windows PowerShell scripts.) Why do you get weird error messages when you try to run a script? That’s easy. The security settings built into Windows PowerShell include something called the “execution policy;” the execution policy determines how (or if) PowerShell runs scripts. By default, PowerShell’s execution policy is set to Restricted; that means that scripts – including those you write yourself – won’t run. Period.

     

    Note. You can verify the settings for your execution policy by typing the following at the PowerShell command prompt and then pressing ENTER:

    Get-ExecutionPolicy

     

    Now, admittedly, this might seem a bit severe. After all, what’s the point of having a scripting environment if you can’t even run scripts with it? But that’s OK. If you don’t like the default execution policy (and you probably won’t) then just go ahead and change it. For example, suppose you want to configure PowerShell to run – without question – any scripts that you write yourself, but to run scripts downloaded from the Internet only if those scripts have been signed by a trusted publisher. In that case, use this command to set your execution policy to RemoteSigned:

     Set-ExecutionPolicy RemoteSigned

     Alternatively, you can set the execution policy to AllSigned (all scripts, including those you write yourself, must be signed by a trusted publisher) or Bypass (all scripts will run, regardless of where they come from and whether or not they’ve been signed).

     See? No need to panic at all, is there?

     

    Note. Not sure what we mean by “signing scripts?” Then open up PowerShell, type the following, and press ENTER:

    Get-Help About_Signing

     After you change your execution policy settings it becomes possible to run scripts. However, you still might run into problems. For example, suppose you change directories from your Windows PowerShell home directory to C:\Scripts (something you can do by typing cd C:\Scripts). As it turns out, the C:\Scripts folder contains a script named Test.ps1. With that in mind you type the following and then press ENTER:

     Test.ps1

     And here’s the response you get:

     The term 'test.ps1' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.

     We know what you’re thinking: didn’t we just change the execution policy? Yes, we did. However, this has nothing to do with the execution policy. Instead, it has to do with the way that PowerShell handles file paths. In general, you need to type the complete file path in order to run a script. That’s true regardless of your location within the file system. It doesn’t matter if you’re in C:\Scripts; you still need to type the following:

     C:\Scripts\Test.ps1

     Now, we said “in general” because there are a couple of exceptions to this rule. For example, if the script happens to live in the current directory you can start it up using the .\ notation, like so:

     .\Test.ps1

     

    Note. There’s no space between the .\ and the script name.

     And while PowerShell won’t search the current directory for scripts it will search all of the folders found in your Windows PATH environment variable. What does that mean? That means that, if the folder C:\Scripts is in your path, then you can run the script using this command:

     Test.ps1

     But be careful here. Suppose C:\Scripts is not in your Windows path. However, suppose the folder D:\Archive is in the path, and that folder also contains a script named Test.ps1. If you’re in the C:\Scripts directory and you simply type Test.ps1 and press ENTER, guess which script will run? You got it: PowerShell won’t run the script in C:\Scripts, but it will run the script found in D:\Archive. That’s because D:\Archive is in your path.

     Just something to keep in mind.

     

    Note. Just for the heck of it, here’s a command that retrieves your Windows PATH environment variable and displays it in a readable fashion:

    $a = $env:path; $a.Split(";")

     Even More About File Paths

     Now we know that all we have to do is type in the full path to the script file and we’ll never have to worry about getting our scripts to run, right? Right.

     Well, almost right. There’s still the matter of scripts whose path name includes a blank space. For example, suppose you have a script stored in the folder C:\My Scripts. Try typing this command and see what happens:

     C:\My Scripts\Test.ps1

     Of course, by now you’ve come to expect the unexpected, haven’t you? Here’s what you get back:

     The term 'C:\My' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.

     This one you were able to figure out on your own, weren’t you? Yes, just like good old Cmd.exe, PowerShell has problems parsing file paths that include blank spaces. (In part because blank spaces are how you separate command-line arguments used when you started the script.) In Cmd.exe you can work around this problem by enclosing the path in double quotes. Logically enough, you try the same thing in PowerShell:

     "C:\My Scripts\Test.ps1"

     And here’s what you get back:

     C:\My Scripts\Test.ps1

     Um, OK …. You try it again. And here’s what you get back:

     C:\My Scripts\Test.ps1

     You try it – well, look, there’s no point in trying it again: no matter how many times you try this command, PowerShell will simply display the exact same string value you typed in. If you actually want to execute that string value (that is, if you want to run the script whose path is enclosed in double quotes) you need to preface the path with the Call operator (the ampersand). You know, like this:

     & "C:\My Scripts\Test.ps1"

     

    Note. With this particular command you can either leave a space between the ampersand and the path name or not leave a space between the ampersand and the path name; it doesn’t matter.

     To summarize, here’s how you run scripts from within Windows PowerShell:

     ·         Make sure you’ve changed your execution policy. By default, PowerShell won’t run scripts at all, no matter how you specify the path.

    ·         To run a script, specify the entire file path, or either: 1) use the .\ notation to run a script in the current directory or 2) put the folder where the script resides in your Windows path.

    ·         If your file path includes blank spaces, enclose the path in double quote marks and preface the path with an ampersand.

     And, yes, that all takes some getting used to. However, you will get used to it. (To make life easier for you, we recommend that you keep all your scripts in one folder, such as C:\Scripts, and add that folder to your Windows path.)

     

    Note. So can you use PowerShell to add a folder to your Windows Path? Sure; here’s a command (that we won’t bother to explain in this introductory article) that tacks the folder C:\Scripts onto the end of your Windows path:

    $env:path = $env:path + ";c:\scripts"

     Bonus: “Dot Sourcing” a Script

     Admittedly, up to this point the news hasn’t been all that good: you can’t run a PowerShell script by double-clicking the script icon; PowerShell doesn’t automatically look for scripts in the current working directory; spaces in path names can cause all sorts of problems; etc. etc. Because of that, let’s take a moment to talk about one very cool feature of Windows PowerShell scripting: dot sourcing.

    Suppose we have a very simple PowerShell script like this one:

     $a = 5

    $b = 10

    $c = $a + $b

     Suppose we run this script, then type $c at the command prompt. What do you think we’ll get back? If you guessed nothing, then you guessed correctly.

    In other words, we don’t get back anything at all. Which, again, should come as no great surprise. And we know what you’re thinking, you’re thinking: “Come on, shouldn’t this be leading us somewhere?”

    Yes, it should. And believe it or not, it is. Let’s run our PowerShell script again, only this time let’s “dot source” it; that is, let’s type a period and a blank space and then type the path to the script file. For example:

     . c:\scripts\test.ps1

     When we run the script nothing will seem to happen; that’s because we didn’t include any code for displaying the value of $C. But now try typing $C at the command prompt . Here’s what you’ll get back:

     15

     Good heavens! Was this a lucky guess on the part of the PowerShell console, or is this some sort of magic?

     Surprisingly enough, it’s neither. Instead, this is dot sourcing. When you dot source a script (that is, when you start the script by prefacing the path to the script file with a dot and a blank space) any variables used in the script become global variables that are available in multiple scopes. What does that mean? Well, a script happens to represent one scope; the console window happens to represent another scope. We started the script Test.ps1 by dot sourcing it; that means that the variable $C remains “alive” after the script ends. In turn, that means that this variable can be accessed via the command window. In addition, these variables can be accessed from other scripts. (Or at least from other scripts started from this same instance of Windows PowerShell.)

     Suppose we have a second script (Test2.ps1) that does nothing more than display the value of the variable $c:

     $c

     Look what happens when we run Test2.ps1 (even if we don’t use dot sourcing when starting the script):

     15

     Cool. Because $c is a global variable everyone has access to it.

     And, trust us here: this is pretty cool. For example, suppose you have a database that you periodically like to muck around with. If you wanted to, you could write an elaborate script that includes each and every analysis you might ever want to run on that data. Alternatively, you could write a very simple little script that merely connects to the database and returns the data (stored in a variable). If you dot source that script on startup you can then sit at the command prompt and muck around with the data all you want. That’s because you have full access to the script variables and their values.

     

    Note. OK, sure, this could cause you a few problems as well, especially if you tend to use the same variable names in all your scripts. But that’s OK; if you ever need to wipe out the variable $C just run the following command (note that, with the Remove-Variable cmdlet, we need to leave off the $ when indicating the variable to be removed):

    Remove-Variable C

     Play around with this a little bit and you’ll start to see how useful dot sourcing can be.

     Running Scripts Without Starting Windows PowerShell

     We realize that it’s been awhile, but way back at the start of this article we tried running a Windows PowerShell script by double-clicking a .PS1 file. That didn’t go quite the way we had hoped: instead of running the script all we managed to do was open the script file in Notepad. Interestingly enough, that’s the way it’s supposed to work: as a security measure you can’t start a PowerShell script by double-clicking a .PS1 file. So apparently that means that you do have to start PowerShell before you can run a PowerShell script.

     In a somewhat roundabout way, that’s technically true. However, that doesn’t mean that you can’t start a PowerShell script from a shortcut or from the Run dialog box; likewise you can run a PowerShell script as a scheduled task. The secret? Instead of calling the script you need to call the PowerShell executable file, and then pass the script path as an argument to PowerShell.exe. For example, in the Run dialog box you might type a command like this:

     powershell.exe -noexit &’c:\scripts\test.ps1’

     There are actually three parts to this command:

             Powershell.exe, the Windows PowerShell executable.

            -noexit, an optional parameter that tells the PowerShell console to remain open after the script finishes. Like we said, this is optional: if we leave it out the script will still run. However, the console window will close the moment the script finishes, meaning we won’t have the chance to view any data that gets displayed to the screen. Incidentally, the –noexit parameter must immediately follow the call to the PowerShell executable. Otherwise the parameter will be ignored and the window will close anyway.

            C:\Scripts\Test.ps1, the path to the script file.

     What if the path to the script file contains blank spaces? In that case you need to do the ampersand trick we showed you earlier; in addition, you need to enclose the script path in single quote marks, like so:

     powershell.exe -noexit &'c:\my scripts\test.ps1'

     Strange, but true!

     

    Note. Here’s an interesting variation on this same theme: instead of starting PowerShell and asking it to run a particular script you can start PowerShell and ask it to run a particular command. For example, typing the following in the Run dialog box not only starts PowerShell but also causes it to run the Get-ChildItem cmdlet against the folder C:\Scripts:

    powershell.exe -noexit get-childitem c:\scripts

     It’s possible to get even more elaborate when starting Windows PowerShell, but this will do for now. If you’d like more information on PowerShell startup options just type powershell.exe /? from either the Windows PowerShell or the Cmd.exe command prompt.

     So is there a catch here? Unfortunately, there is. If you are trying to carry out a task that requires administrator privileges then you can’t start Windows PowerShell from the Run dialog box on either Vista, Windows Server 2008, or Windows 7. OK, check that: you can start PowerShell, but the command you are trying to run will fail. For example, if this is your script, it will fail; that’s because changing the execution policy requires administrator privileges:

     Set-ExecutionPolicy Unrestricted

     Fortunately, there’s a workaround. If you’d like to always be able to start PowerShell from the Run dialog box on one of these operating systems then you should check out the Script Elevation PowerToys for Windows Vista (http://technet.microsoft.com/en-us/magazine/2008.06.elevation.aspx).

     See? That Wasn’t So Bad

     Admittedly, running Windows PowerShell scripts might not be as straightforward and clearcut as it could be. On the other hand, it won’t take you long to catch on, and you’ll soon be running PowerShell scripts with the best of them. Most important, you’ll also be able to say things like, “You know, you really ought to dot source that script when you run it.” If that doesn’t impress your colleagues then nothing will.

     Aren’t you glad you know how to do all this now?

  • Limiting the Number of Contacts a User Can Have

    Having friends – lots of friends – is great. For example, on Facebook actor Ashton Kutcher has 3,367,035 friends; his wife, actress Demi Moore, has 1,292,835 friends of her own. (Sending out Christmas cards must be a full-time job at their house.) By comparison, PowerShell writer Jean Ross has 21 friends. As for PowerShell writer Greg Stemp, well, here’s the message that the good folks at Facebook left for him:

     
    “We'd like to help you find your friends. Your friends on Facebook are the same friends, acquaintances and family members that you communicate with in the real world. You can use any of the tools on this page to find more friends.”

     
    That should give you an idea how many friends he has.

     
    Note. Sadly, our new Microsoft Lync Server 2010 PowerShell group doesn’t have very many friends, either. To join our new group, log onto your Facebook account and search for Communications Server PowerShell. We can’t promise to be as entertaining as Ashton Kutcher (sample post on Ashton’s wall: “dont forget to dance today!”) but we can promise to provide more information on the Microsoft Communications Lync 2010 implementation of Windows PowerShell than Ashton will.

     
    Well, OK, maybe not more PowerShell information than Ashton Kutcher. But definitely more than Demi Moore.

     
    The point of all this (and trust us, there is a point hidden in here – somewhere) is that you can never have too many friends. However, it is possible to have too many Microsoft Lync contacts. After all, Microsoft Lync has to continually retrieve and update presence and status information for each of your contacts (and each of your colleagues’ contacts). If you have a couple hundred contacts, that’s probably no big deal. But if you have 3,367,035 contacts, well ….

     
    In other words, in order to save on network bandwidth, and to reduce wear-and-tear on your servers, you might want to limit the number of contacts your users can have. And in this article, we’ll tell you exactly how to do that.

     
    Before we begin, however, we should note that there already is a limit on the number of contacts your users can have: it’s 250. How do we know that? Well, we know that because we closely follow the postings of people like Ashton Kutcher and Demi Moore (who no doubt just as closely follow our postings). In turn, we’re pretty sure one of them told us that we can determine the maximum number of contacts a user can have by running this command:

     
    Get-CsUserServicesConfiguration

     
    If we look at the value of the MaxContacts property we should see something like this:

     
    MaxContacts:         250

     
    There you go: by default users can have, at most, 250 contacts. If you’re fine with limiting all your users to a maximum of 250 contacts then your work is done. Take the rest of the day off, with our compliments.

     
    But what if you’re not fine with limiting all your users to a maximum of 250 contacts? What if that number is too high, or too low? Or what if certain users (e.g., sales people or human resource counselors) need more contacts than other users? If that’s the case, then you’ll have to keep reading.

     
    Yes, there always is a catch, isn’t there?

     
    To begin with, you might have noticed that we used the cmdlet Get-CsUserServicesConfiguration to retrieve the maximum number of contacts a user can have. When you install Microsoft Lync Server 2010 you get a single collection of user services configuration settings, a collection configured at the global scope. However, you can also create custom user services configuration settings at either the site scope or the service scope. (For more information, see the article Scopes and Filters.) For example, suppose you have additional bandwidth in the Redmond site, and would like to allow those users to have as many as 400 contacts. In that case, you can create a custom set of user services configuration settings for the Redmond site:

     
    New-CsUserServicesConfiguration –Identity site:Redmond –MaxContacts 400

     
    See how easy that is? You can also separately configure each User Server. For example, suppose the users who log on to the pool paris-cs-001.litwareinc.com don’t need very many contacts. In that case, you can create a new user services configuration collection and assign it just to the User Server on that pool:

     
    New-CsUserServicesConfiguration –Identity service:UserServer:paris-cs-001.litwareinc.com –MaxContacts 100

     
    See how that works? In that case, only users who log on to the pool paris-cs-001.litwareinc.com will be limited to 100 contacts. Users who log on to other pools in that same site will have as many contacts as the site or global settings allow them to have.

     
    Of course, you can also use the Set-CsUserServicesConfiguration cmdlet to modify an existing collection of user service settings. For example, suppose bandwidth isn’t a major concern, and you figure that you can allow all your contacts to have at least 750 contacts (the absolute maximum number allowed by Lync Server is 1000 contacts). In that case, a command like this will change the maximum number of contacts allowed by the global user services configuration settings:

     
    Set-CsUserServicesConfiguration –Identity global –MaxContacts 750

     
    Now, that’s pretty good – up to a point. If it’s OK that all the users in a given site have the same limitation in regards to the number of contacts they have, well, then by all means create a custom set of user configuration settings for that site and call it good. But what happens if you want different strokes for different folks: in other words, what if some people in the site should be able to have more contacts than others?

     

    Note. Yes, it does kind of sound like the book Animal Farm, doesn’t it: “All animals are created equal. But some animals are more equal than others.” But, trust us: in reality it’s nothing at all like Animal Farm.

     
    Here’s where it gets a little complicated. (Not hard, mind you, just a little complicated.) The user services configuration settings determine the maximum number of contacts you can have per User Server, per site, or per installation of Microsoft Lync Server. (The per-installation setting, typically referred to as the global setting, is used if you haven’t configured any per-service or per-site settings.) Let’s say you use only the global settings, and you configure MaxContacts to 500. That means that no one in the organization – no one – can have more than 500 contacts.

     
    However, thanks to client policies, it’s possible for someone to have a maximum number of contacts less than 500. (Remember? Some users are more equal than others.) For example, let’s say you want to limit Ken Myer – and just Ken Myer – to 100 contacts. (OK, not a very likely scenario, but we want to keep things simple for now.) In order to do that, the first thing we need to do is create a new per-user policy that sets the maximum number of contacts a user can have to 100. Here’s a command that creates a new per-user policy named MinimalContacts and sets the maximum number of contacts to 100:

     
    New-CsClientPolicy –Identity "MinimalContacts" –MaximumNumberOfContacts 100

     
    And here’s how you assign that policy to Ken Myer:

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

     
    And what’s the net effect of all this? The net effect is that Ken Myer will be allowed a maximum of 100 contacts; that’s the value set in the per-user client policy Ken’s been assigned, and the per-user policy will take precedence over the user services configuration settings.

     

    Note. The per-user client policy takes precedence as long as the maximum number of contacts configured in the policy is less than the maximum number configured in the user services settings. You can never exceed the value set in the user services settings.

     
    As for everyone else in the organization, well, they’ll be allowed to have up to 500 contacts. That’s because, with no per-user policy dictating otherwise, they can have as many contacts as the user services settings allow them to have.

     
    See how that works? You can never have more contacts than the user services settings allow you to have. But you can be restricted to fewer contacts than that, provided that you’ve been assigned a client policy that limits the maximum number of contacts you can have.

     
    In other words, if you want to know the number of contacts any user can have you need to check in two places. First, you need to check the user services configuration settings to determine the absolute maximum number of contacts that user can have. For illustration purposes, let’s assume that’s 350 contacts. Second, you need to check to see if the user has been assigned a client policy (client policies can be assigned at the global, site, or per-user scope) that further limits the number of contacts. If a user’s client policy limits him or her to 200 contacts then he or she can have, at most, 200 contacts. And what if the client policy allows the user 900 contacts? In that case, the user can have, at most, 350 contacts. That’s because the user service configuration settings have imposed an absolute maximum, of 350.

     
    Or to put it a little more graphically:

     

    User service settings

    Client policy settings

    Actual number of contacts allowed

    350

    200

    200

    350

    900

    350

    350

    No policy configured

    350

     
    Like we said, it’s a little complicated. But definitely not hard.

     
    By the way, here’s a bonus command that might be of interest to you. Suppose you wanted to limit all your users to 250 contacts, with one exception: people in the Sales department (who should be allowed up to 500 contacts). How would you do that? Well, here’s one suggestion. First, use this command to set the global user services configurations settings to allow users 500 contacts:

     
    Set-CsUserServicesConfiguration –Identity global –MaxContacts 500

     
    Next, create a new per-user client policy that limits users to 250 contacts:

     
    New-CsClientPolicy –Identity "BasicClientPolicy" –MaximumNumberOfContacts 250

     
    Finally, assign that new policy to everyone except people who work in the Sales department:

     
    Get-CsUser –LDAPFilter "!(Department=Sales)" | Grant-CsClientPolicy "BasicClientPolicy"

     
    We won’t explain how this particular command works; for more information, see the articles on creating filters and assigning policies. But trust us: it works.

     

    Note. Don’t forget to check out our new Facebook group devoted to Lync Server PowerShell. Needless to say, some of us need all the friends we can get. At the rate we’re going right now, just 3,367,034 more friends and we’ll pass Ashton Kutcher. Be afraid, Ashton; be very afraid.

     

  • A Brief Introduction to Role-Based Access Control – Part 1

    “If you want something done right, do it yourself.”
     

    That, by the way, is excellent advice … unless, of course, you happen to be a Microsoft Lync Server 2010 administrator charged with managing thousands of users and multiple sites. After all, in a case like that, doing it yourself (right or wrong) might prove to be a bit … challenging ….
     

    To say the least.
     

    Of course, in previous versions of the software (e.g., Office Communications Server 2007 R2) you might have found it easier to just go ahead and do it yourself rather than trying to delegate administrative control of the product. With Office Communications Server you could easily make someone an all-powerful, full-fledged OCS administrator; unfortunately, it wasn’t quite as easy to give someone the rights to manage just Enterprise Voice, or just the users with accounts in a given OU, or just the – well, you get the idea. Delegating control wasn’t very easy, it wasn’t very granular, and – to be honest – it was probably more trouble than it was worth.
     

    Which is why many administrators just ended up doing it themselves.
     

    But now we have a brand-new version of Lync Server.  Is delegation of control still a major hassle in Lync Server 2010? Apparently you haven’t heard of RBAC, have you?
     

    Welcome to RBAC
     

    Actually, you probably have heard of RBAC: Role-Based Access Control. Role-Based Access Control (which is used in other Microsoft products, including Active Directory and Microsoft Exchange) is the successor to the ACLs (access control lists) that we all know and … love …. With ACLs you had to assign users or groups cryptic permissions like readDLMemSubmitPermsBL, and you had to deal with permission inheritance from container to container, and you had to deal with Allow permissions vs. Deny permissions, and you had to – well, let’s just say that ACLs weren’t exactly a barrel of laughs.  In fact, using ACLs as a way to delegate permissions was just plain hard.
     

    Thanks to RBAC, however, it’s easy to delegate permissions in Lync Server 2010, and to delegate very specific permissions at that. With RBAC, your ability to carry out administrative tasks depends on the RBAC role (or roles) you have been assigned. For example, suppose there was an RBAC role that allowed you to manage archiving configuration settings and archiving policies, but only allowed you to manage archiving configuration settings and archiving policies. (As a matter of fact, there is such a role in Lync Server 2010: CsArchivingAdministrator.) Suppose you’ve been assigned that role. Can you create a new archiving policy and then assign that policy to a user? You bet you can; after all, that’s the whole idea. OK, so can you create a new dial plan and assign that to a user? Nope; that’s because the CsArchivingAdministrator role is limited to archiving-related activities, and users assigned that role can’t do anything but archiving-related activities: no creating and assigning dial plans; no disabling user accounts; no managing Response Group queues; etc. Do you need someone to help manage archiving activities, but you don’t want this same someone to be an all-powerful, full-fledged administrator? That’s fine: just assign them the CsArchivingAdministrator role.
     

    It really is just that easy.
     

    But wait: it gets even better. When you install Microsoft Lync Server 2010 the system automatically creates a number of RBAC roles for you (for example, CsArchivingAdministrator). You say none of those roles is exactly what you had in mind? That’s OK: Lync Server lets you create your own custom RBAC roles. Furthermore, you can also “scope” these roles any way you want. For example, you can limit user management activities to user accounts found in a specific OU; likewise, you can limit configuration activities to a specific site. The sky, as they say, is the limit.
     

    Get the idea? If not, don’t worry: in the next section of this article we’ll explain a little more about RBAC and how it works.
     

    A little more about how RBAC and how it works
     

    OK, we admit it: sometimes software companies (not us, of course) implement features in ways that might make sense to, say, a Klingon, but don’t make any sense at all to human beings. But good news, fellow humans: RBAC isn’t one of those features. Instead, Lync Server’s implementation is pretty darn simple and – dare we say? – easy to understand. As our many Klingon readers would say, “MajQa'.”
     

    Note. Which, as you probably know, is Klingon for “well-done.” Just like “Hab SoSlI' Quch!” is Klingon for “Your mother has a smooth forehead.” Hey, you can look it up.

     
    So how does RBAC work? Well, to begin with, RBAC determines administrative privileges through the use of RBAC roles. When you install Microsoft Lync Server 2010 a number of these RBAC roles are automatically created for you; these so-called “standard roles” include the following:
     

    • CsAdministrator
    • CsVoiceAdministrator
    • CsUserAdministrator
    • CsResponseGroupAdministrator
    • CsLocationAdministrator
    • CsArchivingAdministrator
    • CsViewOnlyAdministrator
    • CsServerAdministrator
    • CsHelpDesk

     
    In turn, each role is assigned a number of cmdlets; for example, the CsUserAdministrator role is assigned these cmdlets:
     

    • Disable-CsUser
    • Enable-CsUser
    • Get-CsAdUser
    • Get-CsUser
    • Get-CsUserClusterInfo
    • Move-CsUser
    • Move-CsLegacyUser
    • Set-CsUser
    • Grant-CsClientPolicy
    • Grant-CsClientVersionPolicy
    • Grant-CsConferencingPolicy
    • Grant-CsDialPlan
    • Grant-CsExternalAccessPolicy
    • Grant-CsHostedVoicemailPolicy
    • Grant-CsLocationPolicy
    • Grant-CsPinPolicy
    • Grant-CsVoicePolicy
    • Get-CsArchivingPolicy
    • Get-CsClientPolicy
    • Get-CsClientVersionPolicy
    • Get-CsConferencingPolicy
    • Get-CsExternalAccessPolicy
    • Get-CsHostedVoicemailPolicy
    • Get-CsLocationPolicy
    • Get-CsPinPolicy
    • Get-CsVoicePolicy
    • Get-CsClientPinInfo
    • Unlock-CsClientPin
    • Lock-CsClientPin
    • Set-CsClientPin
    • Get-CsClientVersionConfiguration
    • Get-CsDialPlan
    • Get-CsSite
    • Get-CsComputer
    • Get-CsNetworkInterface
    • Get-CsPool
    • Get-CsService
    • Get-CsSipDomain
    • Revoke-CsClientCertificate

     
    So do we really care which cmdlets have been assigned to which RBAC roles? Well, we should: these cmdlets represent the only cmdlets that – in this case – a CsUserAdministrator can run. Get-CsDialPlan and Grant-CsDialPlan have been assigned to the CsUserAdministrator role; that means that user administrators can run both of those two cmdlets. So far so good, right?
     

    However, note that New-CsDialPlan, Remove-CsDialPlan, and Set-CsDialPlan have not been assigned to the CsUserAdministrator role. What does that mean? That means that user administrators can get information about dial plans and they can assign (grant) dial plans to users; however, they can’t delete or modify existing dial plans, nor can they create new dial plans. That’s how the system works. When you try to perform an administrative task in Lync Server (either from the Windows PowerShell prompt or from the Lync Server Control Panel), the system checks to see which RBAC roles you have been assigned and, by extension, which cmdlets you’re allowed to run. If you try to run a cmdlet that hasn’t been assigned to your RBAC role, that command will fail.
     

    Period.
     

    And yes, RBAC roles are enforced by the Lync Server Control Panel. After all, each time you issue a command in the Control Panel you’re actually running a PowerShell cmdlet under the covers. (You may not have known that, but it’s true: Control Panel is really just a fancy, easy-to-use way to issue Windows PowerShell commands.)
     

    To show you what we mean, take a look at the Control Panel as accessed by a full-blown Lync Server administrator:
     

     

     
    Notice all the available management categories on the left-hand side of the Control Panel? Good. Now, let’s compare that by looking at the Control Panel as accessed by someone who holds the CsLocationAdministrator role:
     

      
     

    Why are there so few management categories shown in this picture? That’s easy: because, based on your RBAC role assignments, Control Panel only shows you the items that you’re allowed to manage. Needless to say, a Location administrator simply doesn’t have anywhere near as many administrative privileges as a full administrator.
     

    Cool, huh? We thought so, too.
     

    Of course, that leads to another question: how do you actually assign one of these RBAC roles to a user? If you’re anything like us (a scary thought, in and of itself), you probably stopped reading this article and immediately looked through the complete list of Lync Server cmdlets trying to find one that had a name which sounded vaguely like something you’d use to assign RBAC roles.
     

    However, before you searched through those 500+ cmdlets we should have mentioned one thing: there isn’t a cmdlet that assigns RBAC roles. Why isn’t there a cmdlet that assigns RBAC roles? To tell you the truth, we don’t know. But that’s OK, because – as it turns out – you don’t need a cmdlet to assign RBAC roles. Instead, RBAC roles are assigned based on your membership in an Active Directory security group. What’s that? Based on your membership in any Active Directory security group? Nope: based on your membership in a very specific Active Directory security group. As we noted earlier, when you install Lync Server the setup program automatically creates the following RBAC roles for you:
     

    • CsAdministrator
    • CsVoiceAdministrator
    • CsUserAdministrator
    • CsResponseGroupAdministrator
    • CsLocationAdministrator
    • CsArchivingAdministrator
    • CsViewOnlyAdministrator
    • CsServerAdministrator
    • CsHelpDesk

     
    Now, here’s a cool little trick: open up Active Directory Users and Computers, click on the Users container, then take a look at the security groups in that container. Tucked away along with all the other accounts in the Users container you should see the following universal security groups:
     

    • CsAdministrator
    • CsVoiceAdministrator
    • CsUserAdministrator
    • CsResponseGroupAdministrator
    • CsLocationAdministrator
    • CsArchivingAdministrator
    • CsViewOnlyAdministrator
    • CsServerAdministrator
    • CsHelpDesk

     
    Coincidence? Hardly.
     

    Note. Here’s a coincidence for you: Sometime in the 1930s a man named Joseph Figlock was walking down a street in Detroit when a baby fell through a window and landed on him. Joseph broke the baby’s fall, and both Joseph and the baby walked away unharmed. (Well, the baby probably just crawled away. But you know what we mean.) A year later Joseph was walking down the same street when the same baby fell through the same window and landed on him again! Once more, both walked away unharmed. (Being a year older, we’re assuming the baby could now walk.) Now that’s a coincidence!
     

    OK, it’s also – as near as anyone can tell – totally bogus. But as technical writers, we never let the facts get in the way of an important point.

     
    At any rate, and before we were so rudely interrupted (albeit by ourselves) we were about to actually make an important point: the RBAC roles that are assigned to you are based on your membership in an Active Directory security group that has the exact same name as the RBAC role. You say you’re a member of the CsResponseGroupAdministrator security group? Then that also means you’ve been assigned the CsResponseGroupAdministrator RBAC role. If you’re a member of the group then you also hold the corresponding role. If you hold an RBAC role then you must also be a member of the corresponding security group. It’s a simple as that.
     

    And you’re right: MajQa'!
     

    That, by the way, is why you don’t need a cmdlet in order to assign someone an RBAC role; after all, you don’t really assign roles anyway. You want Ken Myer to be a Response Group administrator? That’s fine: open up Active Directory Users and Computers, find the CsResponseGroupAdministrator group and add Ken Myer to the membership list. That’s all you have to do. If you decide that you don’t want Ken to be a Response Group administrator after all then just remove him from CsResponseGroupAdministrator. To be honest, it can’t get much simpler than that.
     

    Note. Yes, yes we know: it’s a Windows PowerShell world, and yet we keep talking about– gasp! – using a graphical user interface tool like Active Directory Users and Computers in order to manipulate RBAC roles and their associated security groups. If you just can’t stand the thought of using a GUI tool, well, take heart:  the Microsoft Link Server 2010 Script Warehouse contains a number of scripts related to RBAC, scripts that let you create a new Active Directory security group that can be used for a custom RBAC role, and scripts that let you add and remove members from an RBAC group. But don’t just take our word for it: see for yourself.

     
    Viewing RBAC roles
     

    As we noted earlier, when you install Lync Server you get – absolutely free! – a number of built-in RBAC roles. (You can also create your own custom roles, something we’ll discuss in the forthcoming Part 2 of this series.) How did we know that Lync Server provides you with a number of built-in RBAC roles? Well, for one thing we are experts on Microsoft Lync Server. (Assuming you have a very loose definition of the word “expert.”) For another, we knew about the Get-CsAdminRole cmdlet, the cmdlet that returns information about all your RBAC roles (both the built-in roles and any custom roles you create yourself). To see what we’re talking about, just run this command:
     

    Get-CsAdminRole

     
    In return, you should get back information similar to this for each role:
     

    Cmdlets        : {Name=Disable-CSUser, Name=Enable-CSUser, Name=Get-CSAdUser, Name=Get-CSUser...}
    ConfigScopes   : {Global}
    UserScopes     : {Global}
    SID            : S-1-5-21-1573807623-1597889489-1765977225-1142
    Identity       : CSUserAdministrator
    IsStandardRole : True

     
    If you want to see the information for a particular role, just include the –Identity parameter, like so:
     

    Get-CsAdminRole –Identity CsUserAdministrator

     
    Or use the –Filter parameter to use wildcards when returning RBAC roles. For example, suppose you’ve created several custom roles that all include the word Paris in the Identity (ParisVoiceAdministrators; ParisLocationAdministrators; etc.). Here’s how you’d get back information about all those custom roles:
     

    Get-CsAdminRole –Filter "*Paris*"

     
    Etc., etc.
     

    Tip. If you only want information about the built-in roles all you need to do is run the following command, which returns only the RBAC roles where the IsStandardRole property is equal to True ($True):
     

    Get-CsAdminRole | Where-Object {$_.IsStandardRole –eq $True}
     

    To get back information about custom roles you’ve created yourself, just look for cmdlets where the IsStandardRole property is equal to False ($False):
     

    Get-CsAdminRole | Where-Object {$_.IsStandardRole –eq $False}

     
    Viewing the cmdlets associated with an RBAC role
     

    As we just witnessed, calling Get-CsAdminRole shows you all of the RBAC roles configured for use in your organization; included in that information is a list of all the cmdlets associated with those RBAC roles.
     

    Well, sort of. For example, suppose we run the following command, a command that returns information for the RBAC role CsUserAdministrator:
     

    Get-CsAdminRole -Identity CsUserAdministrator
     

    When we run this command, and when we take a look at the data that gets returned, we should see something similar to this:
     

    Cmdlets        : {Name=Disable-CSUser, Name=Enable-CSUser, Name=Get-CSAdUser, Name=Get-CSUser...}
    ConfigScopes   : {Global}
    UserScopes     : {Global}
    SID            : S-1-5-21-1573807623-1597889489-1765977225-1142
    Identity       : CSUserAdministrator
    IsStandardRole : True
     

    Take a look at the value for the Cmdlets property. See the dreaded three dots at the end? That means there are more Cmdlets that have been assigned to the CsUserAdministrator role; unfortunately, though, all those cmdlets wouldn’t fit nicely on the screen. Consequently, PowerShell has … helpfully … shown you the first few cmdlets, then used the three dots to indicate that there are additional cmdlets assigned to this role. You can’t see them all.
     

    Grammar note. We seriously doubt that you are wondering what those three dots are called, but on the off-chance that you are wondering what those three dots are called, well, here you go: those three dots are known as an ellipsis. And if you’re now wondering, “Gee, do the authors of this article consider themselves part of the full space ellipsis school or the flush dots ellipsis school,” well, we happen to be flush dotters. And yes, you will have to pry those flush dots from our cold, dead fingers.
     

    Hey, there are some things in life you just have to take a stand on.

     
    Of course, regardless of whether you’re a full spacer or a flush dotter, the ellipsis can be a bit annoying: most likely you want to see all the cmdlets associated with an RBAC role, not just a handful of those cmdlets. But, hey, relax: there isn’t an ellipsis made that the authors can’t outwit:
     

    Get-CsAdminRole -Identity CsUserAdministrator | Select-Object –ExpandProperty Cmdlets
     

    What we’ve done here is grab all the property values for the CsUserAdministrator role and then pipe that information to the Select-Object cmdlet. From there we use the –ExpandProperty parameter to “expand” the value of the Cmdlets property. What does it mean to expand a property value? That’s an easy one: it just means that PowerShell is going to display all the values contained in that property, and in a nice, easy-to-read format. In other words, we’re going to get back a list of cmdlets that looks like this:
     

    Disable-CsUser
    Enable-CsUser
    Get-CsAdUser
    Get-CsUser
    Get-CsUserClusterInfo
    Move-CsUser
    Move-CsLegacyUser
    Set-CsUser
    Grant-CsClientPolicy
    Grant-CsClientVersionPolicy
    Grant-CsConferencingPolicy
    Grant-CsDialPlan
    Grant-CsExternalAccessPolicy
    Grant-CsHostedVoicemailPolicy
    Grant-CsLocationPolicy
    Grant-CsPinPolicy
    Grant-CsVoicePolicy
    Get-CsArchivingPolicy
    Get-CsClientPolicy
    Get-CsClientVersionPolicy
    Get-CsConferencingPolicy
    Get-CsExternalAccessPolicy
    Get-CsHostedVoicemailPolicy
    Get-CsLocationPolicy
    Get-CsPinPolicy
    Get-CsVoicePolicy
    Get-CsClientPinInfo
    Unlock-CsClientPin
    Lock-CsClientPin
    Set-CsClientPin
    Get-CsClientVersionConfiguration
    Get-CsDialPlan
    Get-CsSite
    Get-CsComputer
    Get-CsNetworkInterface
    Get-CsPool
    Get-CsService
    Get-CsSipDomain
    Revoke-CsClientCertificate
     

    Like we said: there isn’t an ellipsis made that we can’t outwit.
     

    Well, OK: there isn’t an ellipsis made that Select-Object and the –ExpandProperty parameter can’t outwit.
     

    Viewing RBAC role assignments
     

    If you want to make the best possible use of RBAC you obviously need to know the different roles that are available to you. At the same time, however, it’s equally important to know which roles have been assigned to which users. So how do you know which roles have been assigned to which users? Good question. And a question that has several answers, with some of those answers being a little better than others.
     

    To begin with, keep in mind that the RBAC role (or roles) that are assigned to you are based on your membership in the appropriate Active Directory security group. Are you a member of the CsUserAdministrator security group? Then congratulations: you also hold the CsUserAdministrator RBAC role. Are you a member of a custom Active Directory security group named RedmondVoiceAdministrators, a security group associated with a custom RBAC role named RedmondVoiceAdministrators? Then guess what: you also hold the RedmondVoiceAdministrators RBAC role.
     

    What does that all mean? Well, we’ll get back to that in just a second. In the meantime, let’s see how you can determine all the RBAC roles held by a specific user. How can you determine all the RBAC roles held by a specific user? The best way is to use the Get-CsAdminRoleAssignment cmdlet:
     

    Get-CsAdminRoleAssignment -Identity "kenmyer"
     

    The preceding command is going to return the names of all the RBAC roles held by the user with the SamAccountName kenmyer. And, yes, for better or worse you have to use the SamAccountName as the user identity; passing the user’s display name or SIP address or UPN isn’t going to do the trick:
     

    PS C:\> Get-CsAdminRoleAssignment "Ken Myer"

    Get-CsAdminRoleAssignment : User cannot be found in the Active Directory with the SAMAccountName:Ken Myer.

    At line:1 char:26

    + Get-CsAdminRoleAssignment <<<<  "Ken Myer"
        + CategoryInfo          : NotSpecified: (:) [Get-CsAdminRoleAssignment], ADObjectNotFoundException
        + FullyQualifiedErrorId : Microsoft.Rtc.Management.Authorization.ADObjectNotFoundException,Mic
       rosoft.Rtc.Management.Authorization.GetOcsRoleAssignmentCmdlet
     

    Of course, it’s theoretically possible that you might not have memorized the SamAccountNames for all your users; for that matter, it’s theoretically possible that you don’t even know what a SamAccountName is. (It’s basically the user’s logon name.) But that’s OK; here’s a tricky little command that does let you retrieve RBAC role assignments using the user’s display name:
     

    Get-CsUser "Ken Myer" | ForEach-Object {Get-CsAdminRoleAssignment –Identity $_.SamAccountName}
     

    What we’re doing here is using the Get-CsUser cmdlet to retrieve all the user account information for the user with the display name Ken Myer. We’re then piping that information to the ForEach-Object cmdlet, and asking ForEach-Object to take each user account passed to it (in this case, that’s just the Ken Myer account) and then call the Get-CsAdminRoleAssignment cmdlet, using the SamAccountName of the object currently in the pipeline ($_.SamAccountName) as the Identity. It’s a tiny bit convoluted, but that’s because Get-CsAdminRoleAssignment doesn’t directly accept pipelined input. And, convoluted or not, it works, which is all we really care about.
     

    OK, that’s one of the good answers we were talking about: as you can see, it’s very easy to retrieve the RBAC roles that have been assigned to a given user. But suppose you want to flip this task on its head, and instead see all the users who have been assigned a given role; for example, what if you’d like to see all the users who have been assigned the CsUserAdministrator role? What’s the answer to that question? Well, that turns out to be one of those answers that isn’t quite as good.
     

    Remember when we took time out to reiterate the fact that RBAC role assignments have a one-to-one correspondence to membership in selected Active Directory groups? We did that because one way to find out which users have been assigned the CsUserAdministrator role is to simply view the membership for the CsUserAdministrator group. That can be done by carrying out the following procedure:
     

    1. Open Active Directory Users and Computers.
    2. Click on the Users container.
    3. Right-click CsUserAdministrator and then click Properties.
    4. On the Members tab look at the users who are members of the group.

     
    That works just fine although, admittedly, it’s a bit cumbersome; on top of that, you can’t do any of it from the comfort and security of the Lync Server Management Shell. At this point in time, however, there’s no cmdlet that lets you retrieve all the users who have been assigned a specific RBAC role. Like we said, it’s an answer, and a solution. It just might not be the answer and solution you were hoping for.
     

    Note. But hey, cheer up: while there might not be a cmdlet that can return this information, there is a script that can list all the RBAC roles and all the users who have been assigned those roles. See the article How Do I List All the RBAC Roles and the Users that Hold Those Roles? for more information

     
    Is that all I need to know about RBAC and Microsoft Lync Server 2010?
     

    As a matter of fact it is … unless, of course, you’re interested in creating your own custom RBAC roles (and then, if you change your mind, interested in deleting those custom RBAC roles). For that information, you’re going to have to tune into the exciting conclusion of this two-part series: A Brief Introduction to Role-Based Access Control – Part 2 (coming soon).
     

    We know: the suspense is killing you, isn’t it? In that case, you better drop everything else and go read Part 2. Well, just as soon as we post it, that is.

  • 21 User Information Commands to Run Before You Die

     

     


    Things…

     

     

    21 User Information Commands to Run Before You Die

     

     

    Perhaps the hottest trend in the publishing world these days is the 1,001 Things approach: 1,001 Places to See Before You Die; 1,001 Movies You Must See Before You Die; 1,001 Books You Must Read Before You Die. So what’s wrong with these books? Nothing, nothing at all. However, they did get us to thinking. Imagine this scenario:

     

    It’s many years from now, and you’re lying on your death bed thinking, “Well, OK, so I did get to see the covered souks of Aleppo, and I did visit the Okavango Delta in Botswana. But I never got a chance to run any Microsoft Lync Server 2010 commands that return information about my users and their user accounts. I wasted my entire life!”

     

    Do you want this to happen to you? We didn’t think so. That’s why we’ve put together our own addition to the 1,001 Things genre: 21 User Information Commands to Run Before You Die!

     

    Note. Well, OK, if you want to get picky, sure, maybe that should be 1,001 User Information Commands to Run Before You Die. But, man, that’s a lot of work to write 1,001 user information commands. Because of that, we decided to get as close as we could to 1,001 commands and then stop. We like to think we got fairly close to 1,001 before we called it good enough.

     

    With our writing style, there’s always a good chance one or more of our readers will die of boredom at any given time. Therefore, we better get right to the 21 commands. Note that we won’t bother to explain how these commands work. For more information, take a look at the article Retrieving Active Directory and Microsoft Lync Server 2010 User Accounts.

     

    And, sure, go ahead and finish those 1,001 books first. But then take a look at that article.

     

    Here are the commands:

     

    Returning all the users in an OU

     

     

    This one is pretty straightforward: it returns a collection of all the Active Directory users accounts found in the Sales OU (as well as any child OUs of the Sales OU). Note that you must enter the Active Directory distinguished name of the OU in question:

     

    Get-CsAdUser –OU "OU=Sales,dc=litwareinc,dc=com"

     

    If you want a collection of all the user accounts that have been enabled for Lync Server (as opposed to all the user accounts, enabled for Communications Server or not) then use the Get-CsUser cmdlet instead:

     

    Get-CsUser –OU "OU=Sales,dc=litwareinc,dc=com"

     

    Although there will be some exceptions, for most of these commands you can use either Get-CsAdUser (if you want information about all your Active Directory user accounts) or Get-CsUser (if you want information only about accounts that have been enabled for Lync Server).

     

    Returning all the users who have disabled Active Directory user accounts

     

    Note that this returns users who have had their Active Directory accounts disabled. If you can wait a second, we’ll show you a command that returns users who have had their Lync Server accounts disabled.

     

    Get-CsAdUser | Where-Object {$_.UserAccountControl –match "AccountDisabled"}

     

     

    Returning all the users who work in a specific department

     

    This is another easy one: as you can see, it returns all the users who work in the Sales department. Need a list of all the users who work in the Human resources department? That’s fine; just replace Sales with Human Resources: –LdapFilter "Department=Human Resources".

     

    Here’s the command:

     

    Get-CsAdUser –LdapFilter "Department=Sales"

     

    Returning all the users with offices in a specific city

     

    In the world of LDAP (Lightweight Directory Access Protocol), the l (lower L) attribute is short for locality:

     

    Get-CsAdUser –LdapFilter "l=Redmond"

     

    Returning all the users who work in a specific country

     

    Here’s a command that returns all the users who work in the country (c) of Uruguay:

     

    Get-CsAdUser –LdapFilter "c=UY"

     

    What if you some of your users don’t work in Uruguay? That’s OK; just use the appropriate ISO-3166 country code. You can find a list of two-letter country codes here: http://www.iso.org/iso/english_country_names_and_code_elements.

     

    Returning all the users who have been enabled for Microsoft Lync Server

     

    Oh, here we go. This command returns a collection of users whose accounts have been enabled for Microsoft Lync Server:

     

    Get-CsAdUser –Filter {CsEnabled –eq $True}

     

    And, as promised, this one returns all the accounts that have not been enabled for Lync Server:

     

    Get-CsAdUser –Filter {CsEnabled –eq $False}

     

    Returning all the users who have been assigned a specific policy

     

    You say you created a per-user conferencing policy named RedmondConferencingPolicy only you can’t remember which users have actually been assigned that policy. This command should help:

     

    Get-CsUser –Filter {ConferencingPolicy –eq "RedmondConferencingPolicy"}

     

     

    Returning all the users who have been assigned a per-user voice policy

     

    Here’s a useful query: it returns a collection of all the users who have been assigned a per-user voice policy of some kind:

     

    Get-CsUser –Filter {VoicePolicy –ne $Null}

     

    See Command 7 if you’re interested in returning information about users who have been assigned a specific policy.

     

    Returning the user with a specified line URI

     

    Which user was assigned the line URI (425)555-1298? Beats us. But this command should tell you:

     

    Get-CsUser –Filter {LineUri –eq "TEL:+14255551298"}

     

    Returning all the users with a specific job title

     

    Remember that famous line from Shakespeare “First, kill all the lawyers”? Scholars now believe that that was a misprint. What Shakespeare actually said was, “First, find all the lawyers.” That line was inspired by the following command:

     

    Get-CsAdUser –LdapFilter "Title=Attorney"

     

    Returning all the users who have been assigned a line URI

     

    OK, we showed you how to find a user who had been assigned a specific line URI. But now you need to find a list of all the Lync Server-enabled users who’ve been assigned a line URI of some kind. Okey-doke:

     

    Get-CsUser –Filter {LineUri –ne $Null}

     

    And this command shows you all the Lync Server-enabled users who haven’t been assigned a line URI:

     

    Get-CsUser –Filter {LineUri –eq $Null}

     

    Returning all the users who have been enabled for Enterprise Voice

     

    This one’s so easy we’re almost too embarrassed to add it to the list.

     

    Like we said, almost too embarrassed:

     

    Get-CsUser -Filter {EnterpriseVoiceEnabled –eq $True}

     

    Returning all the users who work in a particular building

     

    This one is pretty handy; just make sure you configure the Office property in Active Directory Users and Computers:

     

    Get-CsUser –LdapFilter "physicalDeliveryOfficeName=Building 32"

     

    Returning all the users who have been assigned a PIN number

     

    Yes, you can use PIN numbers for dial-in conferencing authentication, but only if you’ve actually been assigned a PIN number. Here’s a command that returns a collection of all the users who’ve been assigned a PIN number:

     

    Get-CsUser | Get-CsClientPinInfo | Where-Object {$_.IsPinSet –eq $True}

     

    Returning all the users who have been assigned an Audio Conferencing Provider

     

    Audio Conferencing Providers enable users to conduct audio-only conferences with people both inside and outside the organization. How do you know if any of your users have been assigned an Audio Conferencing Provider? Well, you could walk around and ask them all. Or you could run this command:

     

    Get-CsUserAcp –Filter {AcpInfo –ne $Null}

     

    Returning all the users homed on a previous version of Communications Server

     

    Microsoft Lynjc Server 2010 is designed to play nicely with others; for example, you can simultaneously run both Communications Server “14” and Office Communications Server 2007 R2 as you slowly begin to migrate from 2007 R2 to the new product. Let’s assume you do that, and let’s assume you now need to know which users are homed on 2007 R2. If that’s the case, then this command should come in handy:

     

    Get-CsUser -OnOfficeCommunicationServer

     

    Returning a specified number of users

     

    This is really nice when you’re testing new commands or scripts. If you have 10,000 users in your organization, you might not want to return all 10,000 user accounts, especially if you’re trying out a new script for the first time. Fortunately, the –ResultSize parameter lets you specify the number of accounts to be returned. Only want to get back 10 accounts, even if you do have 10,000 users enabled for Lync Server? Then run this command:

     

    Get-CsUser –ResultSize 10

     

    Returning all the users homed on a specific Registrar pool

     

    You say you need to temporarily shut down one of your Registrar pools, and you’d like to run a command that returns a collection of all the user accounts that are homed on that pool? You mean a command like this one?

     

    Get-CsUser –Filter {RegistrarPool –eq "atl-cs-001.litwareinc.com"}

     

    Returning all the users who have been assigned a private line

     

    What’s a private line? Let’s quote from the Lync Server PowerShell help:

     

    “A private line is a phone number that is not published in Active Directory, and thus is not readily available to other people. In addition, this private line bypasses most in-bound call routing rules; for example, a call to a private line will not be forwarded to a person's delegates. Private lines are often used for personal phone calls or for business calls that should be kept separate from other team members.”

     

    Here’s how you can return a collection of all the users who have been assigned a private line:

     

    Get-CsUser –Filter {PrivateLine –ne $Null}

     

    Returning all the users who have not been assigned a Registrar pool

     

    Believe it or not, it can happen: sometimes you have users who have been enabled for Lync Server but who are not assigned to a Registrar pool (meaning that they can’t actually log on to Lync Server). Do you have any Lync Server-enabled users who aren’t assigned to a Registrar pool? There’s one way to find out:

     

    Get-CsUser -UnassignedUser

     

    Returning all the users whose user accounts were created on or after a specified date

     

    This one is cool, and one that we will explain, sort of. It’s a command that returns all the users whose Active Directory user accounts were created on June 1, 2010 or later:

     

    Get-CsAdUser –LDAPFilter "(WhenCreated>=20100601000000.0Z)"

     

    We won’t go into enormous detail in explaining how this command works; we’ll save that for a future article. (See? Yet another reason to live!) But we will explain what the date 20100601000000.0Z means:

     

    Digits

    Explanation

    2010

    This is simply the year, expressed in four digits.

    06

    This is the month, expressed in two digits. October would be listed as 10; March would be listed as 03; etc.

    01

    The day of the month, also expressed in two digits.

    00

    The hour of the day, based on a 24-hour clock. 3 AM would be listed as 03; 3 PM as 15; etc.

    00

    The minutes of the day, expressed in two digits. Thus 4:37 PM would list 16 for the hour and 37 for the minutes.

    00

    The seconds of the day, expressed in – well, right: expressed in two digits.

    .0Z

    A universal time designator.

     

    Put them altogether and you get a date that looks like this:

     

    20100601000000.0Z