Utility SpotlightSMS Cmdlets for Windows PowerShell

Don Brown

Download the code for this article: Utility2007_11.exe (1211KB)

There used to be no way to manage your Microsoft System Management Server (SMS) 2003 clients from the command line. Luckily, among the many great technologies that have come along lately to help administrators manage complexity, change, and configuration, Windows PowerShell arrived to solve that problem.

To take advantage of it, I wrote a small "utility," SMS2003PowerShellSnapinSample (which you can find in the code download that accompanies this column, available online at technetmagazine.com/code07.aspx). The utility really boils down to a collection of six cmdlets assembled into a single Windows PowerShellTM snap-in. Using these cmdlets, you can configure SMS client local policy from Windows PowerShell for the local machine or groups of remote machines.

The more I played with SMS client local policy, the more this made sense. As a side benefit, I also came to understand more about how SMS works. I learned that by careful application of SMS client local policy, the SMS client could be controlled in a whole new way: machines reporting to the same site did not all have to be configured the same. Ideas started to flow—from setting certain machines so that permission isn't required to remotely control the machine, to changing how often the client checks for new policy, to disabling specific components at various times of the day—the sky is the limit using SMS client local policy. Let's take a closer look.

About SMS Advanced Client Local Policy

Programming Resources

All functional SMS 2003 Advanced Clients have a configuration policy, which is basically a list of settings that drive their various components. All client, inventory collection, and software distribution settings (among others) are included in these configuration policies. The policies themselves are created on the site server and handed down to the Advanced Client through the SMS management point.

The actual body of an SMS policy is similar to a Managed Object Format (MOF) file in that it contains a set of instances that will be compiled into the \\.\root\CCM WMI (Windows® Management Instrumentation) namespaces on the Advanced Client. The various other agents will then read these settings, which are located in the \\.\root\ccm\policy\machine\requestedconfig namespace. When applying local policy via MOF, however, you can only compile a MOF on a single machine and it must be run locally (or rather, when you are logged on directly to the machine). But WMI, because of its distributed nature, is accessible both locally and remotely, which yields a greater array of options to assist SMS administrators. This means that you can connect to WMI on a remote machine as easily as you can connect to WMI on your local machine, given proper administrative rights.

SMS client policy consists of configuration settings for the various client components, but it may also include instructions for executing software package content. As with Active Directory® Group Policy, SMS 2003 Advanced Client Policy obtained from an SMS management point may be overridden by SMS client local policy. You can't override all parts of a policy, but some really interesting properties can be overridden. This gives SMS administrators a greater degree of control over the configuration and operations of the SMS Client because it allows exceptions to the standard configuration applied at the SMS site.

Take the example of a secure and consolidated environment, where SMS administrators manage both servers and workstations as clients of a single SMS primary site. In this imaginary environment, a security policy might state that a user must be prompted for permission before help desk technicians can take remote control over their machine. That's obviously a problem if the help desk technicians want to use remote control to attach to the servers: typically, no users are logged on to the servers, so there's nobody to grant remote control permission. By careful application of local policy, though, the requirement for user permission may be overridden on specific clients. You can probably imagine dozens of other circumstances where SMS local policy may be useful in granting an exception to configuration of a particular SMS client agent.

Inside the Windows PowerShell Snap-In

Once you have some familiarity with the basics of Windows PowerShell, you can add to the sample source code provided with this column to expand what you can do to SMS client local policy from the command line. There are specific attributes that should be applied to a class before it is exposed to Windows PowerShell. You need to decide what the cmdlet is about and what action should be carried out. This is known as the "Verb-Noun" pairing. Some common verbs you'll see in Windows PowerShell are Add, Get, and Set. The noun portion describes the object you want to act upon. Cmdlets usually have parameters, which are declared in a Cmdlet class as public properties of various types. Lastly, the ProcessRecord function is the main place where you will do most of your work. Figure 1 shows a generic template you can use for cmdlets of your own.

Figure 1 Cmdlet template

[Cmdlet( "Verb", "Noun", SupportsShouldProcess = true )]
public class Verb_Noun : PSCmdlet
    [Parameter( ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, 
        HelpMessage = "Parameter" )]
    [Alias( "param" )]
    public string Parameter
        get { return MyParameter; }
        set { MyParameter = value; }
    private string MyParameter;

    protected override void ProcessRecord( )
        //Do your stuff here!

Once you've compiled your snap-in, you register it with Windows PowerShell by including a class in your project that has the RunInstaller attribute set and a few specific properties defined. See the source code accompanying this article for specifics. To register your snap-in, you will need to use the InstallUtil.exe tool included with the Microsoft® .NET Framework. Figure 2 shows the syntax you use to register your snap-in. Note that in Windows Vista®, this is an operation that requires elevation of privileges, so you will want to open Windows PowerShell with the option of "Open as administrator," or use the Script Elevation PowerToys for Windows Vista (which you can download at technetmagazine.com/issues/2007/06/UtilitySpotlight).

Figure 2 Installing the snap-in from Windows PowerShell

PS> set-alias installutil $env:windir\Microsoft.NET\Framework\v2.0.50727\installutil 
PS> installutil C:\MySMSTools\SMS2003PowerShellSnapinSample.dll 
Microsoft (R) .NET Framework Installation utility Version 2.0.50727.42 
Copyright (C) Microsoft Corporation. All rights reserved. 
Running a transacted installation. 
The transacted install has completed.

The next step is to find out if your snap-in is acknowledged by Windows PowerShell. Use the Get-PSsnapin cmdlet with the Registered parameter and Windows PowerShell summarizes the currently loaded snap-ins, followed by a list of registered snap-ins to be added. Your snap-in should be found in this list:

PS> Get-PSsnapin -registered

Now you can add the Windows PowerShell snap-in to the shell using the Add-PSsnapin cmdlet, as follows:

PS> add-pssnapin SMS2003PowerShellSnapinSample

If the operation is successful, you should be able to execute the cmdlets in your Windows PowerShell snap-in.

To see all the cmdlets that are made available by any Windows PowerShell snap-in, just specify the particular snap-in with the Get-Command cmdlet, passing the name of your snap-in as the value for the PSsnapin parameter. The sample snap-in accompanying this column displays six cmdlets, including the Verb-Noun Cmdlet template, as Figure 3 shows.

Figure 3 Displaying the cmdlets in a snap-in

Figure 3** Displaying the cmdlets in a snap-in **(Click the image for a larger view)

In the sample source code, I've written help only for the Get-SMSServerConnection cmdlet to demonstrate how it is constructed. If you want to expand the help, you will have an example to model after in the XML file. Figure 4 shows the help output.

Figure 4 Sample Get-Help output

PS > get-help Get-SMSServerConnection


    This cmdlet establishes a connection to the specified SMS primary site server using your current credentials. An object of type "SMSProvider" is returned through the pipeline.

    Get-SMSServerConnection [-SMSServerName] [<string>] [<CommonParameters>]

    This Cmdlet makes a connection to the specified SMS primary site server.  An object of type "SMSProvider" is returned. The "SMSProvider" object is not serializable and is used only to forward on through the pipeline to other cmdlets.


Now that you've seen how to install the Windows PowerShell snap-in, how to find out what commands are present in the snap-in, and how to get help on a typical cmdlet, let's start using the cmdlets. To show a list of all collections at an SMS primary site server, you can use the following command:

PS > Get-SMSServerConnection -server MYSMSSERVER | Get-Collections | Format-Table Name

Note the pipeline. This is a core part of how Windows PowerShell works, and it's a very powerful tool. It may be possible to do just about anything in Windows PowerShell on one line. The Get-SMSServerConnection cmdlet establishes a connection with the SMS server. Since the Get-Collections cmdlet has a single input parameter of the type that is returned by Get-SMSServerConnection, you can simply pass the output from the Get-SMSServerConnection cmdlet to the Get-Collections cmdlet. This is how the pipeline works, and it's a good example of how you can pass complex objects from one cmdlet to another. You can also store objects in variables, and if you were doing this in a Windows PowerShell script, it might look something like this:

$SMS = Get-SMSServerConnection 
     -server MYSMSSERVER
Get-Collections -SMSServerProvider $SMS

The output might be a little bit hard to read without some nice formatting of the output, because Get-Collections returns an object of type SMSCollections (which is basically an array of objects of type SMSCollection). In short, not a very pretty display, until you either filter out collections you aren't interested in seeing or format the display in a table of only those properties you care about. You do this by passing the output from the Get-Collections cmdlet through the pipeline to the Format-Table cmdlet, which can display only those properties that you specify. For example, you may append | Format-Table Name, Members.

Ah, but there's a better way to show all the members of a particular collection, and in this sample it is Get-CollectionMembers. This cmdlet returns an array of strings, representing the name of each member of the collection. As you might have guessed, this is a great candidate for passing down the pipeline to the next cmdlet.

Up to this point, there has been no SMS Advanced Client local policy—just connections to the SMS primary site server and enumerating collections and collection members. Included in this sample Windows PowerShell snap-in are two last cmdlets, named Enable-SoftwareDistribution and Disable-SoftwareDistribution respectively. This is where the SMS client local policy comes into play. These last two cmdlets manipulate the SMS client local policy for the Software Distribution Client Agent component on the SMS Advanced Client. As you might have guessed by the verb part of the cmdlet, Disable places a client local policy override that specifies the Software Distribution Client Agent should be disabled as well. In the same way, the verb Enable removes all client local policy overrides, returning the Software Distribution Client Agent back to its normal state, which is defined by the SMS policy handed down by the SMS management point. Figure 5 shows an example of what a Windows PowerShell one-liner might look like that places an SMS client local policy override on the Software Distribution Client Agent that forces it to be disabled for all members of the Windows Server 2003 Systems custom collection. The one-liner to remove all SMS client local policy overrides for the Software Distribution Client Agent on that same collection is also listed. The Disable-SoftwareDistribution and Enable-SoftwareDistribution cmdlets can also be used on their own, if you only have a few machines on which you want to manipulate SMS client local policy.

Figure 5 Assembling the cmdlets—examples

PS >Get-SMSServerConnection -server MYSMSSERVER | Get-Collections | where-object {$_.Name -eq "Windows Server 2003 Systems"} | Get-CollectionMembers | Disable-SoftwareDistribution 
PS >
PS > Get-SMSServerConnection -server MYSMSSERVER | Get-Collections | where-object {$_.Name -eq "Windows Server 2003 Systems"} | Get-CollectionMembers | Enable-SoftwareDistribution
PS >
PS >Disable-SoftwareDistribution –hosts SMSCLIENT1, SMSCLIENT2, SMSCLIENT3
PS >
PS >Enable-SoftwareDistribution –hosts SMSCLIENT1, SMSCLIENT2, SMSCLIENT3

Next Steps

The sample source code that comes with this article can provide you a good starting point in applying SMS client local policy overrides to groups of computers. The sample here only touches on the SMS Software Distribution Client Agent, and then only the Enabled property. The other client agents have many more properties you can apply overrides to in the same way. Now that you have a common starting point, the next logical step is to look at other parts of the SMS client local policy. Think about groups of managed systems that might be good candidates for SMS client local policy overrides and what local policy overrides make sense for you to use. One final note: it's good practice to know what you might be getting yourself into, so always test everything thoroughly in a lab environment.

Don Brown is a Senior Premier Field Engineer at Microsoft and has been working and supporting with SMS (ahem, SCCM) for as long as he can remember. Contact him at donbrown@microsoft.com.

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.