PowerShell Scripting
Group Policy change management made easy!
September 25, 2007
Back in the ‘80s, I remember doing everything on a PC inside a black walled window, typing commands into a DOS prompt. Then came Windows and the advent of the GUI. The days of the command prompt appeared to be over, especially for the Windows user. Until now—because a small group at Microsoft has decided to go back to the basics. They created a tool called PowerShell that combines the ease of use of a command prompt, the power of object manipulation, simple but flexible cmdlets, and the ability to easily access Microsoft .NET classes.
Installing and running PowerShell isn't all that exciting on its own, because most people's first commands are the familiar Dir or Cls commands from the DOS days. However, like a Swiss Army knife, the beauty of PowerShell is its ability to solve difficult problems with unbelievable ease. To illustrate these capabilities, we'll tackle a difficult Group Policy management challenge: managing and archiving Group Policy Objects (GPOs) listed in a Microsoft Excel spreadsheet.
I'll use PowerShell to demonstrate how easily you can accomplish this task with just a few lines of code. I chose this scenario because in almost every company I've visited, regardless of whether they use a third-party GPO management tool or the native Group Policy Management Console (GPMC) tool, everyone seems to maintain a list of GPOs (along with their status, change information, owner, etc.) in an Excel spreadsheet.
Step 1: Download and Install GpMC and powerShell
GPMC is the de facto management console for viewing, archiving, and analyzing GPOs in Active Directory (AD). Although we aren't going use the GPMC Microsoft Management Console (MMC) UI, we do need GPMC's COM automation DLL for our PowerShell script to call its APIs.
GPMC ships only with Windows Vista. If you're running an OS other than Vista, you need to download GPMC from http://www.microsoft.com/windowsserver2003/gpmc. Just install the file GPMC.msi; all the COM registrations are handled automatically and will then be easily accessible from PowerShell.
Next, download the appropriate version of PowerShell for your OS. You can download PowerShell from http://www.microsoft.com/windowsserver2003/technologies/management/powershell/download.mspx. Notice that PowerShell is supported on Windows XP SP2, Windows Server 2003, and Vista on both x32 and x64 platforms. Before installing PowerShell, make sure you have Microsoft .NET Framework
2.0 installed. The x86 platform version is available at http://www.microsoft.com/downloads/details.aspx?familyid=0856eacb-4362-4b0d-8edd-aab15c5e 04f5&displaylang=en (with links on the page to other platforms).
Step 2: Create a Sample GpO Spreadsheet
Most administrators document their GPOs in some form, whether in an Excel spreadsheet, a database, or even a Notepad file. If you haven't documented your GPOs, now is a good time to start.
As Figure 1 shows, I used four GPOs. My sample Excel spreadsheet describes specific attributes of each GPO, such as GPOName, GPOGUID, Domain, Owner, Department, Change Control status, and Description. You'll need to list the GPOs that exist in your AD, or create test GPOs in your AD for this exercise and update the GPOName, GPOGUID, and Domain columns with your own GPOs that you want to back up. After completing the Excel spreadsheet, save it as a comma-separated value (CSV) file called GPOList.csv.
PowerShell has built-in cmdlets that let you import the contents of a .csv file, then navigate to individual items inside the spreadsheet as objects. This is one of the key differences between PowerShell and typical UNIX shells or other scripting languages such as Perl. Whereas UNIX shells and Perl operate on data as pipes of text to pass forward, PowerShell allows the infinitely more flexible feature of storing and passing object references that can be queried, manipulated, searched, and operated on as collections. PowerShell was originally designed as a .NET scripting language—this underlying infrastructure is obvious in PowerShell's ability to inherit .NET's capabilities for data manipulation, while keeing the technology accessible.
In your sample spreadsheet, you can create anynumber of columns with any amount of informationfor each GPO. For consistency, we'll designate thefirst column as GPOName because that's what we'lluse as our unique identifier.
Step 3: Create the powerShell Script
We'll start with a PowerShell script that calls GPMC's COM APIs for initiating a GPO backup. Listing 1 contains this code, called BackupGPO.ps1. First, the script creates a reference to the GPMC COM Automation object. In VBScript, you'd call the function CreateObject—for example, Set GPM = CreateObject("GPMgmt.GPM"). PowerShell has an equivalent function called New-Object; passing in the -comobject GPMgmt.GPM parameter, as callout A in Listing 1 shows, initializes the GPMC COM object.
A useful PowerShell feature is that for any cmdlet, parameter, or object, if you enter the first few characters of the cmdlet or parameter and press Tab, PowerShell fills in the closest match. If you continue to press Tab, you'll cycle through all the possible cmdlets, parameters, or object attributes. For example, after you set the $GPM variable at callout A in Listing 1, if you enter $GPM. and press Tab, you'll see all the COM functions that GPMC has exposed.
We also need to grab the constants from the GPMC object and create a variable that can be passed into our COM functions; the code at callout B accomplishes this task. In addition, we need to use the DNS names from the sample .csv file to obtain the domain object GPMDomain via a call to the GetDomain GPMC API; the code at callout C accomplishes this task.
Next, we need to call the GPMC API to select the appropriate GPO, as the code at callout D does. Notice that this line of code uses a new operand (i.e., $_.) and specifies the column name of the .csv file we created earlier. The $_ operand lets us access sets of data within each column, stored by PowerShell as .NET objects.
The last line in our GPMC API calls, which callout E shows, is the final call to actually do the backup. In this example, I hard-coded the backup location to C:backup. This location can be passed in or be part of the columns in the passed-in .csv file. The PowerShell command is
$Result = $GPO.Backup ("C:\backup", $_ .Description)
Note that when you pass in a directory name to a COM-based function call, you need to use two backslashes because single backslashes are interpreted as escape characters. Make sure the directory C:backup exists before you call the PowerShell script.
Finally, we add a single line of code at the beginning to name our filter and put the code we've written so far in a block to allow objects to be passed in as a filter. Functions are typically created with parameters that are passed in. In our case, the whole .csv file is passed in and we're dynamically accessing all the objects within the file. Using functions doesn't make sense because we aren't specifying static parameters. Filters just take whatever is passed in—the code inside the filter handles and makes assumptions about the data to use.
If we wanted to get more advanced, we could add error checks to make sure the $GPMResult variable is valid and that no exceptions have been thrown, to determine the script's success or failure. However, I wanted to keep the example simple.
Step 4: execute the Backup Filter
Now we get to see PowerShell's magic and flexibility. First, start PowerShell and change the PowerShell policy to allow execution of scripts. To do so, enter
PS C:> Set-ExecutionPolicy -executionPolicy Unrestricted
Next, load the PowerShell code from Listing 1 by "dot-sourcing" the PowerShell file. This action essentially loads the filter we've created into the current PowerShell runspace.
As I already explained, passing a .csv file as a parameter only passes the reference to the .csv file in a function. You then have to write code in the function to manually parse the contents of the .csv file. Passing the .csv file in as a filter lets us access all of the file's data elements as .NET objects inside the filter.
Enter
PS C:> . C:PSDemoBackupGPOs.ps1
Then, import the .csv file and pass it into the Do-GPOBackup filter we wrote. Enter
PS C:> import-csv C:PSDemoGPOList.csv | Do-GPOBackup
Figure 2 shows the output.
The PowerShell script that we created can take as input any .csv file for performing operations on GPOs. We can also add a parameter check to operate only on GPOs that match a certain domain name. For example, for the domain Americas, enter
PS C:> import-cvs C:PSDemoGPOList.csv | {where $_.Domain = "Americas"} | DoGPOBackup
Another option is to take the result and pipe it to a graph to identify the GPOs that were backed up, time them to see which ones are taking the longest to back up, and pinpoint which ones are creating the greatest performance problems. The possibilities are now endless. We could modify the PowerShell script so that instead of performing backups, we could perform periodic restores of critical GPOs by scheduling them via Task Scheduler. In addition, simply changing the spreadsheet contents would dynamically change the list of GPOs being operated on, backed up, or restored.
Back to Basics
My simple example demonstrates PowerShell's extreme flexibility. Using this example as a foundation, you can automate the reporting, analysis, creation, and even provisioning of new GPOs. You can also use PowerShell to link GPOs to organizational units (OUs) in AD. Because Microsoft used PowerShell as the back end to write Exchange Server 2007's management UI, you can designate something as simple as an Excel spreadsheet or as intricate as Exchange's management UI as your preferred UI. PowerShell's simple but immensely useful command prompt lets you truly go back to basics.
About the Author
You May Also Like