Friday, August 29, 2008

Get All Shared Printers with WMI and PowerShell

I whipped up this handy dandy script yesterday for work, and I think it makes a good simple example script, so I'm posting it here:

# Finds all servers that are hosting printers.
#
# Author: Tojo2000 
#
# Note: Requires a text file with one server per line named 'winserv.txt'.

$credential = Get-Credential
$servers = gc winserv.txt | sort

echo "ServerName`tShareName`tName`tDriverName`tStatus" > .\printers.txt

foreach ($server in $servers) {
  echo $server
  $ping = gwmi Win32_PingStatus -Filter "address = `"$server`""

  if ((!$ping.StatusCode) -and (!$ping.PrimaryAddressResolutionStatus)) {
    $printers = gwmi -Credential $credential -ComputerName $server -class Win32_Printer -Filter 'Shared = "True"'

    foreach ($printer in $printers) {
      if ($printer) {
        $output = $server, $printer.ShareName, $printer.Name, $printer.DriverName, $printer.Status
        echo ($output | Join-String2 "`t") >> .\printers.txt
      }
    }
  }
}

__________________________________________

# Join-String2
# Args:
#   $join_string: the string to paste between each element of the string array
#
# Returns:
#   The resulting string to the pipeline

function global:Join-String2 ([string]$join_string) {
  $output = '';
  $input_array = @($input);
  
  foreach ($string in $input_array[0..($input_array.Length - 2)]) {
    $output += $string;
  $output += $join_string;
  }
  $output += $input_array[-1];
  
  Write-Output $output;
}


There are a couple of things worthy of note here.  The Get-Credential cmdlet pops up a prompt to enter a username and password and stores it as a SecureString This is vastly more secure than storing the string in your script or typing it in to the console window.  Get-WMIObject, like many cmdlets, has a -Credential option that takes a credential as an input and runs the cmdlet under that account.  You can also specify a username here, and Get-Credential will be invoked to prompt you for the password.

The Join-String2 function is because there is already a cmdlet that is part of the PowerShell Comunity Extensions called Join-String, but apparently it freaks out if any of the values being printed are null, so I created a function that does a basic join.

The -Filter option of Get-WMIObject is basically just the "where clause" of the wmi query, which is to say that

gwmi Win32_Printer -Filter 'Shared = "True"'

is the same thing as

gwmi -Query 'Select * From Win32_Printer Where Shared = "True"'

It's just a lot more PowerShell-ish and a little less verbose.

No comments: