Take Command of Server 2008 with Windows PowerShell – Part 4

In the last couple of weeks you were introduced to the basics of Windows PowerShell, Microsoft’s new command shell environment.

In Part 1 I talked about what PowerShell is and showed you how to install and access it on Windows Server 2008.

In Part 2 we went over PowerShell cmdlets and PowerShell providers. I showed you how to execute cmdlets and work with the objects returned and also how to use the different PowerShell providers to work with server resources inaccessible from the FileSystem provider.

In Part 3 we learned how to customize the user interface with the PowerShell profile.

And now that you are familiar with the capabilities of PowerShell, it is time to learn how to really unleash the power of PowerShell with PowerShell scripting.

Today I’ll show you how to take advantage of the script engine to automate the performance of Administrative tasks on the Server.

Understanding PowerShell Scripting

If you followed along with the PowerShell customization exercise that I showed you in the last article, you already know how to create PowerShell scripts. The profile file created in that exercise is actually a PowerShell Script.

In essence, a PowerShell script is simply a text file containing a list of PowerShell commands saved with the file extension .ps1.

For security purposes, script execution is disabled by default. You can set the script execution policy with the Set-ExecutionPolicy cmdlet.

The parameters available for the Set-ExecutionPolicy cmdlet are: -restricted, -unrestricted, -remotesigned and –allsigned. Enter the command: Get-Help Set-ExecutionPolicy –detailed for details on the Set-ExecutionPolicy cmdlet parameters.

In order to exhibit the capabilities of the PowerShell script engine we will go over a sample PowerShell script. The example script was created to display the potential of PowerShell scripting for performing administrative tasks in a server environment.

When the example script is ran, it behaves like a program. It first lists all the printers installed on the server and then prompts you to select one or all of the printers.

Once a printer is selected an additional menu is displayed for operations that can be performed with the printer or printers. When an action is selected, it is performed, then the program begins again at the beginning until you enter ‘exit’ at one of the prompts.

If we examine the script, we can see that it is broken up into three sections. The first section is dedicated to the function definition portion of the script. The second section covers the process of prompting the user. The third section is where the operation chosen is performed on the selected printer.

  function cancelPrinterJobs {  	$objPrinter = $args[0]  	$objCancel = $objPrinter.CancelAllJobs()  	if ($objCancel.ReturnValue -eq 0) {  		write-host "Cancelled all jobs on"$objPrinter.Name  	}  	else {  		write-host "Cancel of all jobs on"$objPrinter.Name"failed"  	}  }    function pausePrinter {  	$objPrinter = $args[0]  	$objPause = $objPrinter.Pause()  	if ($objPause.ReturnValue -eq 0) {  		write-host $objPrinter.Name"pause"  	}  	else {  		write-host "Pause of"$objPrinter.Name"failed"  	}  }    function resumePrinter {  	$objPrinter = $args[0]  	$objResume = $objPrinter.Resume()  	if ($objResume.ReturnValue -eq 0) {  		write-host $objPrinter.Name" resumed"  	}  	else {  		write-host "Resume of"$objPrinter.Name"failed"  	}  }    function testPrinter {  	$objPrinter = $args[0]  	$objTest = $objPrinter.PrintTestPage()  	if ($objTest.ReturnValue -eq 0) {  		write-host $objPrinter.Name" printed a test page"  	}  	else {  		write-host $objPrinter.Name" test page failed"  	}  }    function setDefaultPrinter {  	$objPrinter = $args[0]  	$objDefault = $objPrinter.SetDefaultPrinter()  	if ($objDefault.ReturnValue -eq 0) {  		write-host $objPrinter.Name" Set as default."  	}  	else {  		write-host "Failed to set"$objPrinter.Name"as default"  	}  }    function writePrinterInfo {  	$objPrinter = $args[0]  	$strComputerName = $args[1]  	$colPrinters = get-wmiobject -class "Win32_PrinterConfiguration" -namespace "rootCIMV2" -computername $strComputerName    	write-host $objPrinter.Name "Printer Information"  	write-host  	write-host "Name: " $objPrinter.Name  	write-host "Driver Name: " $objPrinter.DriverName  	write-host "Port Name: " $objPrinter.PortName  	write-host "Shared: " $objPrinter.Shared  	write-host "Share Name: " $objPrinter.ShareName  	write-host "Queued: " $objPrinter.Queued  	write-host "Status: " $objPrinter.Status    	foreach ($objPrinter in $colPrinters) {  		if ($objPrinter.Name -eq $strPrinterName) {  			write-host "Driver Version: " $objPrinter.DriverVersion  			write-host "Paper Size: " $objPrinter.PaperSize  			write-host "X Resolution: " $objPrinter.XResolution  			write-host "Y Resolution: " $objPrinter.YResolution  			write-host  			break  		}  	}  }  

In the first section, six functions are defined. Each function performs an action with a printer. The function cmdlet is used to define each function. Immediately after the function cmdlet the function name is supplied, followed by the commands of the function enclosed in curly brackets.

Functions in PowerShell do not support defining the arguments. Instead, functions accept a virtually unlimited number of space-separated objects as arguments. The arguments can be accessed within the function with the $args variable.

When PowerShell executes a script, commands are executed as the script is read rather than loaded to memory and then executed. Therefore, a function must be defined in the script before it is ever called.

It is a good idea to always define all your functions first in a PowerShell script. If a function is called within another function the called function must be defined first.

The first commands of every function are to assign the expected argument to a variable for script readability. All the functions expect a WMI Printer object as the first argument. Only the last function expects a second argument, the computer name.

The first five functions each execute a method of the printer object and assign it to a variable. The return value is checked for successful execution with an “if” statement and if the conditions in the parentheses are met the Write-Host cmdlet in the curly brackets is executed to display a success message.

If the successful condition is not met a failure message is displayed from the command in the curly brackets of the else statement.

After the argument variables are assigned in the final function the Get-WMIObject cmdlet is used to assign the Win32_PrinterConfiguration object to a variable. Then select properties from the supplied Printer object are displayed.

After that the Foreach-Object cmdlet is used to loop through the items of the PrinterConfiguration object then checks if its name matches that of the Printer object supplied to the function. When a match is found select properties from the PrinterConfiguration object are displayed and the loop is exited with the break cmdlet.

  cls    $strComputerName = "."  $colPrinters = get-wmiobject -class "Win32_Printer" -namespace "rootCIMV2" -computername $strComputerName | sort-object "Name"    do {  	$printerIndex = 0  	write-host  	write-host "Installed Printers"  	write-host  	write-host "0) All Printers"    	foreach ($objPrinter in $colPrinters) {  		$printerIndex = $printerIndex + 1  		write-host $printerIndex") "$objPrinter.Name  	}    	write-host      	$selectedPrinterIndex = read-host "Select the printer(s) you wish to work with or type 'exit' to exit"  	if ($selectedPrinterIndex -eq "exit") {  		break  	}  	$validated = 0  	do {  		if ($selectedPrinterIndex -gt $printerIndex) {  			$selectedPrinterIndex = read-host "Please enter a valid printer number"  		}  		else {	$validated = 1 }  	}  	while ($validated -lt 1)    	write-host  	write-host "Printer Operations"  	write-host  	write-host "0) Display Printer(s) Information"  	write-host "1) Cancel Printer(s) Jobs"  	write-host "2) Pause Printer(s)"  	write-host "3) Resume Printer(s)"  	write-host "4) Test Printer(s)"  	if ($selectedPrinterIndex -gt 0) {  		write-host "5) Set Printer As Default"  	}  	write-host    	$selectedOperationIndex = read-host "Select an action to perform on the printer or type 'exit' to exit"  	if ($selectedOperationIndex -eq "exit") {  		break  	}  	$validated = 0  	do {  		if ($selectedOperationIndex -gt 5) {  			$selectedOperationIndex = read-host "Please enter a valid operation number"  		}  		else {  			if ($selectedPrinterIndex -eq 0) {  				if ($selectedOperationIndex -eq 5) {  					$selectedOperationIndex = read-host "Please enter a valid operation number"  				}  				else {	$validated = 1 }  			}  			else {	$validated = 1 }  		}  	}  	while ($validated -lt 1)  

The second section of the script is where execution begins. The program begins by clearing the console with the cls cmdlet. Next a variable is assigned with the computer name.

When a computer name is expected as a cmdlet parameter the dot used will be interpreted as the local machine. The computer name variable is then used when assigning the WMI Win32_Printer object to a variable.

The DoWhile-Object is executed next and is closed at the end of the script with while (1). This tells the program to loop execution until the loop is exited with the break cmdlet.

The next four commands display the printer menu title and first option as 0 for selecting all printers. A variable is then created to keep track of the printer index before the foreach-object cmdlet is used to loop through the Printer objects of the Win32_Printer object. Each time through the loop one is added to the printer index then it is displayed with the name of the current Printer object.

After the loop a blank line is displayed to separate the menu items from the prompt. The prompt is displayed with the Read-Host cmdlet and the input is assigned to a variable to represent the index of the selected printer. The printer index variable is then checked if the value is exit and exits the main program loop if so.

A variable is then assigned the value of 0 to represent the input has not been validated. A Do While loop is set to loop until the validation variable equals 1. Enclosed in the loop is an if object that is used to check if the printer index exceeds the number of printers, if so, If the input is invalid the script prompts again for a valid printer index until a valid input is provided by the user.

The second menu is then displayed. An “if” statement is used to check if the printer index variable is greater than 0 before the operation to set as the default printer is displayed.

The user is then prompted to enter an operation. Validation for the second menu is done using the same logic as the first menus validation. The difference is an “if” statement to check to make sure the operation index supplied is not to set all printers to default.

  	if ($selectedPrinterIndex -gt 0) {  		$objPrinter = $colPrinters[$selectedPrinterIndex - 1]  	}    	write-host  	switch ($selectedOperationIndex) {  		0 {  			if ($selectedPrinterIndex -eq 0) {  				foreach ($objPrinter in $colPrinters) {  					writePrinterInfo $objPrinter $strComputerName  				}  			}  			else {	writePrinterInfo $objPrinter $strComputerName }  		}  		1 {  			if ($selectedPrinterIndex -eq 0) {  				foreach ($objPrinter in $colPrinters) {  					cancelPrinterJobs $objPrinter  				}  			}  			else {	cancelPrinterJobs $objPrinter }  		}  		2 {  			if ($selectedPrinterIndex -eq 0) {  				foreach ($objPrinter in $colPrinters) {  					pausePrinter $objPrinter  				}  			}  			else {	pausePrinter $objPrinter }  		}  		3 {  			if ($selectedPrinterIndex -eq 0) {  				foreach ($objPrinter in $colPrinters) {  					resumePrinter $objPrinter  				}  			}  			else {	resumePrinter $objPrinter }  		}  		4 {  			if ($selectedPrinterIndex -eq 0) {  				foreach ($objPrinter in $colPrinters) {  					testPrinter $objPrinter  				}  			}  			else {	testPrinter $objPrinter }  		}  		5 {  			setDefaultPrinter $objPrinter  		}  	}  }  while (1)  cls  

The final section of the script handles performing the selected operation with the selected printer or printers. It starts off by assigning the selected printer object to a variable using the selected printer index if the user did not select all printers.

The control of which operation is to be executed is handled by the Switch cmdlet. Within the curly brackets of the Switch cmdlet the commands executed are the commands within the curly brackets of the number in the Switch cmdlet that match the operation index provided by the user.

In all cases but the last, an if statement is used to check if the selected operation is to be performed on all printers. If all printers have been selected a foreach loop is used to pass all the Printer objects to the proper function otherwise only the Printer object of the selected printer is passed to the function.

The fifth case does not check if all printers were selected because the input validation prevents the option from being able to be selected if all printers are selected. After the switch statement the program’s infinite Do While loop is closed off the screen cleared and execution of the script ends.

Now that you are so familiar with how to create dynamic PowerShell scripts, you probably want to know how to execute a script. To demonstrate, go ahead and copy the commands from all three sections of the script example into notepad and save it to ‘C:PrinterOperations.ps1’. From PowerShell enter the command below to execute the script.

  & “C:PrinterOperations.ps1”  

With the command above, variable names and functions used in the script will be destroyed when execution completes. If you wish to have script variables and functions available within PowerShell after the script has finished executing you can use dot-sourcing.

The command below will run the script and when execution completes you will have access to the variables and functions created will still be available.

  & . “C:PrinterOperations.ps1”  

I hope that you’ve enjoyed reading my series on Windows PowerShell.

Keep in mind that there are many additional resources and a number of informative references available on this topic. The owner’s manual, articles, and many useful scripting examples are available at Microsoft TechNet’s Script Center.