OK, so I may be in the minority, but I don't like when the scripts I write bleed all over my screen. And when I use GWMI, I find that they bleed more often than not. See, I often work with systems that I don't have access to, and when I use GWMI (even with the ever popular "-ErrorAction SIlentlyContinue", a personal friend of mine), my scripts leave blood all over the screen. So, I went out to figure out the issue. It appears that the error returned by GWMI with you don't have access to a computer is not returned at the PowerShell level, but through the OS itself. So when you have a group of computers and run something like:
Get-WmiObject -computer $Server Win32_OperatingSystem -ErrorAction SilentlyContinue
You get something back like:
Get-WmiObject : Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
At line:1 char:14
+ Get-WmiObject <<<< -computer $Server Win32_OperatingSystem
+ CategoryInfo : NotSpecified: (:) [Get-WmiObject], UnauthorizedAccessException
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
(OK, yeah, it's exactly like that, thanks to cut and paste and a bit of formatting.) Anyway, since I have a script that calls WMI for OS info, disk info, boot info, ... in fact, info about several other things, all this bleeding on my screen was giving me heartaches.
So I went about trying to find a way around it. Turns out that other commands fail, but catch the issue at a higher level: for example, Get-ChildItem gives "Cannot find path '\\ServerName\c$' because it does not exist." and works with -ErrorAction. While this would work, what if the item really doesn't exist and Windows is installed on the D: drive? That wouldn't help much, and I wanted to find a way to shunt my WMI requests only if the server had an access denied.
I finally found that the DOS command running "net use \\ServerName\C$" will return the denied access error, and you can keep it from bleeding on the screen, but you have to do it wisely. First, you need to use what .Net calls "[Diagnostics.Process] Start()". To use this, I created the following function:
function Chk-ServerAccess {
param($Server, $Share)
$PSAccessTest = New-Object System.Diagnostics.Process
$PSAccessTest.StartInfo.FileName = "NET"
$PSAccessTest.StartInfo.Arguments = ' VIEW
\\'+ $Server + '\' + $Share+ '$'
$PSAccessTest.StartInfo.RedirectStandardError = $true
$PSAccessTest.StartInfo.UseShellExecute = $FALSE
$PSAccessTest.Start() | Out-Null
$StdErr = $PSAccessTest.StandardError.ReadToEnd()
-not ($StdErr -match 'Access is denied.')
} # Chk-ServerAccess
This function returns Boolean True if you can access the server and Boolean false if you can't. You initialize a variable of type "System.Diagnostics.Process". Then you set the command (file name) as "NET" (ignoring the fact this isn't really a file, but an internal Windows OS command). Build the arguments using the variable for your server name and an admin share you should be able to access on the server (you can also build it with "VIEW\\$Server\$Share`$", if you prefer expanding variables inside the text string). Make sure you redirect standard error - we don't really care about standard output, but we need to catch the error. We also need to make sure we set UseShellExecute to false so we can redirect any output (including errors).
Once we are ready, we run the command by starting it ($PSAccessTest.Start() | Out-Null) - note that we pipe this to Out-Null - we do this so we don't have a spurious "True" returned by our script accidentally. Once run, we capture the error output into a variable ($StdErr, in this case) and test to see if it matches 'Access is denied'. If it doesn't, we return true, meaning you have rights to the admin share you wish to access on the server. And it doesn't throw any blood to the screen - making it possible to then run your GWMI commands without having your friends wondering what your script is doing to their servers.
I just love when you start a script and it ends with nothing but white output on the screen. Hope this helps someone else out there ...
Will Martin