{"id":249,"date":"2008-01-28T19:45:58","date_gmt":"2008-01-29T03:45:58","guid":{"rendered":"http:\/\/testblog.sapien.com\/index.php\/2008\/01\/28\/got-powershell-revisited\/"},"modified":"2008-07-08T02:15:57","modified_gmt":"2008-07-08T10:15:57","slug":"got-powershell-revisited","status":"publish","type":"post","link":"https:\/\/dev.sapien.com\/blog\/2008\/01\/28\/got-powershell-revisited\/","title":{"rendered":"Got-PowerShell Revisited"},"content":{"rendered":"<p>Last <a target=\"_blank\" href=\"\/current\/2008\/1\/23\/is-powershell-installed.html\">week<\/a> I showed a PowerShell function that used WMI to remotely query a machine to determine if PowerShell was installed. Well, like most things Microsoft there&rsquo;s often more than one way to accomplish a task. Here is a different version of essentially the same function. This version, however, uses the .NET framework and the Microsoft.Win32.Registry class:<\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">Function Get-PowerShellInstall {<br \/>\nParam ([string]$computer=&quot;localhost&quot;) <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">$ErrorActionPreference=&quot;SilentlyContinue&quot;<br \/>\n$DebugPreference=&quot;SilentlyContinue&quot; <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">#provide some traps to handle connection errors<br \/>\nTrap [system.IO.IOException] {<br \/>\nWrite-Warning &quot;Failed to connect to $computer&quot;<br \/>\nWrite-Debug &quot;IOException with $computer&quot;<br \/>\n$script:failed=$True<br \/>\ncontinue    <br \/>\n} <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">Trap [system.UnauthorizedAccessException] {<br \/>\nWrite-Warning &quot;Access violation on $computer&quot;<br \/>\nWrite-Debug &quot;UnauthorizedAccessException on $computer&quot;<br \/>\n$script:denied=$True<br \/>\ncontinue<br \/>\n} <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">#The registry class doesn&rsquo;t accept localhost as<br \/>\n#a computer name so if passed, get the actual<br \/>\n#computer name <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">if ($computer -match &quot;localhost&quot;) {<br \/>\n$computer = $env:computername<br \/>\n} <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">$computer=$computer.ToUpper()<br \/>\nWrite-Debug &quot;Connecting to $computer&quot; <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">$regbase=[Microsoft.Win32.RegistryKey]::`<br \/>\nOpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::`<br \/>\nLocalMachine,$computer) <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">Write-Debug &quot;Continuing with function&quot;<br \/>\nIf ($script:denied) {<br \/>\nWrite-Debug &quot;access denied&quot;<br \/>\nRemove-Variable denied -Scope Script<br \/>\nReturn<br \/>\n} <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">If ($script:failed) {<br \/>\nWrite-Debug &quot;failed to connect&quot;<br \/>\nRemove-Variable failed -Scope Script<br \/>\nReturn<br \/>\n} <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">#bail out if the OpenRemoteBaseKey method failed.<br \/>\nif (!$regbase) {<br \/>\nWrite-Debug &quot;regbase not found&quot;<br \/>\nreturn<br \/>\n} <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">$basePath=&quot;software\\microsoft\\powershell\\1&quot;<br \/>\n$ShellPath=&quot;shellids\\Microsoft.PowerShell&quot;<br \/>\n$EnginePath=&quot;PowerShellEngine&quot; <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">Write-Debug &quot;basepath=$basepath&quot;<br \/>\nWrite-Debug &quot;shellpath=$shellpath&quot;<br \/>\nWrite-Debug &quot;enginepath=$enginepath&quot; <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">Write-Debug &quot;Opening (Join-Path $basePath $ShellPath)&quot;<br \/>\n$regkey=$regbase.opensubkey((Join-Path $basePath $ShellPath)) <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">if ($regkey) {<br \/>\nWrite-Debug &quot;`$regkey is found on $computer&quot;<br \/>\n$InstallPath=$regkey.GetValue(&quot;path&quot;)<br \/>\nWrite-Debug &quot;Install path is $InstallPath&quot;<br \/>\n#only get other values if able to get a value for $InstallPath<br \/>\nif ($InstallPath) {<br \/>\n$Installed=$True<br \/>\n$ExecutionPolicy=$regkey.GetValue(&quot;ExecutionPolicy&quot;)<br \/>\nWrite-Debug &quot;found execution policy of $ExecutionPolicy&quot;<br \/>\n#if ExecutionPolicy key is missing then assume Restricted<br \/>\nif (-not $ExecutionPolicy) {<br \/>\nWrite-Debug &quot;Execution policy is missing&quot;<br \/>\n$ExecutionPolicy=&quot;Restricted&quot;<br \/>\n}<br \/>\n$regkey=$regbase.opensubkey((Join-Path $basePath $EnginePath))<br \/>\n$PowerShellVersion=$Regkey.GetValue(&quot;PowerShellVersion&quot;)<br \/>\nWrite-Debug &quot;Found version of $PowerShellVersion&quot;<br \/>\n$RunTimeVersion=$RegKey.GetValue(&quot;RunTimeVersion&quot;)<br \/>\nWrite-Debug &quot;Found RunTimeVersion of $RunTimeVersion&quot;<br \/>\n} #end If ($InstallPath)<br \/>\n}<br \/>\nelse {<br \/>\nWrite-Debug &quot;`$regkey is NOT found on $computer &quot;<br \/>\n$InstallPath=$Null<br \/>\n$Installed=$False<br \/>\n$ExecutionPolicy=$Null<br \/>\n$PowerShellVersion=$Null<br \/>\n$RunTimeVersion=$Null<br \/>\n} #end if ($regkey) <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">Write-Debug &quot;Creating custom object&quot; <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">$obj=New-Object PSObject<br \/>\n$obj | Add-Member -MemberType NoteProperty -Name &quot;Computer&quot; -Value $computer.ToUpper()<br \/>\n$obj | Add-Member -MemberType NoteProperty -Name &quot;Installed&quot; -Value $Installed<br \/>\n$obj | Add-Member -MemberType NoteProperty -Name &quot;InstallPath&quot; -Value $InstallPath<br \/>\n$obj | Add-Member -MemberType NoteProperty -Name &quot;ExecutionPolicy&quot; -Value $ExecutionPolicy<br \/>\n$obj | Add-Member -MemberType NoteProperty -Name &quot;Version&quot; -Value $PowerShellVersion<br \/>\n$obj | Add-Member -MemberType NoteProperty -Name &quot;RunTimeVersion&quot; -Value $RunTimeVersion <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">write $obj<br \/>\nWrite-Debug &quot;end of function&quot;<br \/>\n}<\/font><\/p>\n<p>Again, don&rsquo;t try to copy and paste.  Grab the attached file instead. There are some noticeable differences between this version and the one using WMI. This version has a few traps to handle different types of connection errors. If you attempt to connect to a machine that isn&rsquo;t available, the function uses the Warning pipeline to display a message.  You&rsquo;ll get a slightly different error message if there is a permissions problem. By using Write-Warning, assuming your shell&rsquo;s $WarningPreference is still set the to default of Continue,  you can run an expression like this:<\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">get-content c:\\servers.txt | foreach-object {get-powerShellinstall $_} | Select computer,Installed,Version | convertto-html | out-file c:\\test\\psh.html<\/font><\/p>\n<p>You can still see the warnings, but it they won&rsquo;t be a part of the end result. I&rsquo;ve added a few other sample usages in the attached file.<\/p>\n<p>I&rsquo;ve also added some debug lines for tracing code execution. If you need to troubleshoot, change $debugpreference in the function to Continue and you&rsquo;ll the debug lines written to the debug pipeline.<\/p>\n<p>One last major advantage to this version is that even though it doesn&rsquo;t explicitly allow for alternate credentials, as long as a I have a secure channel to the remote computer, this script will work. What this means is I can map a drive to a server like this:<\/p>\n<p><font face=\"Courier New\" color=\"#0000ff\" style=\"color: rgb(0, 0, 255);\">net use * <\/font><a href=\"file:\/\/server01\/c$\"><font face=\"Courier New\" color=\"#0000ff\" style=\"color: rgb(0, 0, 255);\">\\server01\\c$<\/font><\/a><font face=\"Courier New\" color=\"#0000ff\" style=\"color: rgb(0, 0, 255);\"> \/user:mydom\\auser <\/font><a href=\"mailto:P@ssw0rd\"><font face=\"Courier New\" color=\"#0000ff\" style=\"color: rgb(0, 0, 255);\">P@ssw0rd<\/font><\/a><\/p>\n<p>Once the drive is mapped, I can use the PowerShell function:<\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\" style=\"color: rgb(0, 0, 255);\">Get-powershellinstall server01<\/font><\/p>\n<p>And it will work.<\/p>\n<p>If you come up with any interesting ways to use this function, please let me know.<\/p>\n<p>&nbsp;<\/p>\n<div class=\"wlWriterSmartContent\" id=\"scid:0767317B-992E-4b12-91E0-4F059A8CECA8:a5edcda6-5bd1-43c0-9d9c-2841ec60802b\" style=\"margin: 0px; padding: 0px; display: inline;\"><span class=\"sizeLess20\">Technorati Tags: <\/span><a rel=\"tag\" href=\"http:\/\/technorati.com\/tags\/PowerShell\"><span class=\"sizeLess20\">PowerShell<\/span><\/a><span class=\"sizeLess20\">, <\/span><a rel=\"tag\" href=\"http:\/\/technorati.com\/tags\/Registry\"><span class=\"sizeLess20\">Registry<\/span><\/a><span class=\"sizeLess20\">, <\/span><a rel=\"tag\" href=\"http:\/\/technorati.com\/tags\/Win32\"><span class=\"sizeLess20\">Win32<\/span><\/a><span class=\"sizeLess20\">, <\/span><a rel=\"tag\" href=\"http:\/\/technorati.com\/tags\/.NET\"><span class=\"sizeLess20\">.NET<\/span><\/a><\/div>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last week I showed a PowerShell function that used WMI to remotely query a machine to determine if PowerShell was installed. Well, like most things Microsoft there&#8217;s often more than one way to accomplish a task. Here is a different version of essentially the same function. This version, however, uses the .NET framework and the Microsoft.Win32.Registry class.<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[25],"tags":[28,31,36,54,35],"class_list":["post-249","post","type-post","status-publish","format-standard","hentry","category-windows-powershell","tag-powershell","tag-registry","tag-remote","tag-win32","tag-wmi"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/249","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/comments?post=249"}],"version-history":[{"count":0,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/249\/revisions"}],"wp:attachment":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/media?parent=249"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/categories?post=249"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/tags?post=249"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}