Better way to query remote server registries? - powershell

I've built this small block of code to query and store the values of a group of servers, which seems to work fine, however I'd like to know if there is a "pure PowerShell" way to do this.
$eServers = Get-ExchangeServer
$Servers = $eServers | ?{$_.Name -like "Delimit_server_group"}
foreach ($server in $Servers)
{
[string]$Key1 = "\\$server\HKLM\SYSTEM\CurrentControlSet\Control\"
[string]$rKeys += (REG QUERY "$key1" /s)
}

You can use the RegistryKey class to open a remote registry:
$RemoteHKLM = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$server)
$RemoteKey = $RemoteHKLM.OpenSubKey('SYSTEM\CurrentControlSet\Control')
# Following will return all subkey names
$RemoteKey.GetSubKeyNames()
You'll have to implement recursive traversal yourself if you need functionality equivalent to reg query /s

Matthias' answer is probably your best option, but there are other approaches you could take as well. If you have PSRemoting enabled on your systems, you could for instance invoke remote commands like this:
$key = 'HKLM:\SYSTEM\CurrentControlSet\Control'
Invoke-Command -Computer $Servers -ScriptBlock {
Get-ChildItem $args[0] | Select-Object -Expand Name
} -ArgumentList $key

Related

PowerShell How to check if you are allowed to connect to a specific computer

I am making a script that connects to multiple servers using WMI. I have a $args1 that has a server that I have access to, CANCONNECT, and one that I cannot connect to CANT.
In the foreach loop I want to write something that does X if I cannot connect and does Y if it can connect. In the foreach block I have code that gets info from other servers and It works for everything that I can connect to.
How would I approach this / do this?
This is the code I have so far
$args1 = Get-Content .\names.txt
$ArrComputers = 'CANCONNECT', 'CANT'
# $OutArray = #()
$i = 0
foreach ($Computer in $ArrComputers)
{
....
}
I put a try catch statement in the foreach loop
For the try I did
$Networks = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $Computer | ? {$_.IPEnabled}
and catch I just set the thing to pass as Failed
Reference from here

Test-Path PowerShell Issue

I am trying to search several servers to see if a specific Registry key exists.
It looks like the code below is working, but as I start to add the final part of the key, it stops "finding" stuff. I can start to add a*, then ab* as the last key, but as soon as I get to the third character or even the full string that I know is there, it comes back False saying it did not find it.
$servers = Get-Content c:\input.txt | `
Select-Object #{l='ComputerName';e={$_}},#{l='KeyExist';e={Test-Path "HKLM:\System\CurrentControlSet\services\*abcdefg*" }}
$servers | Format-Table -AutoSize
Your problem is that you run Test-Path against the local computer for each remote server name. Unfortunately Test-Path doesn't support querying remote registries.
You could use WMI:
$RegProv = [wmiclass]"\\$servername\root\default:StdRegProv"
if($RegProv.EnumKey(2147483650,"System\CurrentControlSet\services").sNames -like 'abc*'){
# key starting with abc exists
}
Wrap it in your calculated property like this:
#{Name='KeyExists';Expression={[bool](([wmiclass]"\\$_\root\default:StdRegProv").EnumKey(2147483650,"System\CurrentControlSet\services").sNames -like 'abc*')}}
You can check the remote registry like this :
So for each server it will get the registry value and it will store the value in the arraylist and will display the final result.
Presently in your code, you are basically checking locally only.
#####Get Registry Value ####
$main = "LocalMachine"
$path= "registry key path"
$servers = Get-Content c:\input.txt #-- Get all the servers
$arr=New-Object System.Collections.ArrayList
foreach ($Server in $servers)
{
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($main, $Server)
$regKey= $reg.OpenSubKey($path)
$Value = $regkey.GetValue($key)
$arr.Add($Value)
}
$arr
Note: Change the placeholders accordingly

What's the fastest way to get online computers

I'm writing a function which returns all Online Computers in our network, so I can do stuff like this:
Get-OnlineComputers | % { get-process -computername $_ }
Now I basically got my function ready, but it's taking way too long.
I want to only return Computers which have WinRM active, but I also want to provide the option to get every computer even those which haven't got WinRM set up (switch parameter).
This is my function. first it creates a pssession to the domaincontroller, to get all computers in our LAN. then foreach computer, it will test if they have WinRM active or if they accept ping. if so, it gets returned.
$session = New-PSSession Domaincontroller
$computers = Invoke-Command -Session $session { Get-ADComputer -filter * } | select -ExpandProperty Name
$computers | % {
if ($IncludeNoWinRM.IsPresent)
{
$ErrorActionPreference = "SilentlyContinue"
$ping = Test-NetConnection $_
if ($ping.PingSucceeded -eq 'True')
{
$_
}
}
else
{
$ErrorActionPreference = "SilentlyContinue"
$WinRM = Test-WSMan $_
if ($WinRM)
{
$_
}
}
}
Is this the best way I can go to check my online computers? Does anyone have a faster and better idea?
Thanks!
Very Quick Solution is using the -Quiet Parameter of the Test-Connection cmdlet:
so for example:
$ping = Test-Connection "Computer" -Quiet -Count 1
if ($ping)
{
"Online"
}
else
{
"Offline"
}
if it's not enough fast for you, you can use the Send Method of the System.Net.NetworkInformation.Ping
here's a sample function:
Function Test-Ping
{
Param($computer = "127.0.0.1")
$ping = new-object System.Net.NetworkInformation.Ping
Try
{
[void]$ping.send($computer,1)
$Online = $true
}
Catch
{
$Online = $False
}
Return $Online
}
Regarding execute it on multiple computers, I suggest using RunSpaces, as it's the fastest Multithreading you can get with PowerShell,
For more information see:
Runspaces vs Jobs
Basic Runspaces implemenation
Boe Prox (master of runspaces) has written a function which is available from the Powershell Gallery. I've linked the script below.
He uses many of the answers already given to achieve the simultaneous examination of 100s of computers by name. The script gets WMI network information if test-connection succeeds. It should be fairly easy to adapt to get any other information you want, or just return the result of the test-connection.
The script actually uses runspace pools rather than straight runspaces to limit the amount of simultaneous threads that your loop can spawn.
Boe also wrote the PoSH-RSJob module already referenced. This script will achieve what you want in native PoSH without having to install his module.
https://gallery.technet.microsoft.com/scriptcenter/Speedy-Network-Information-5b1406fb

Powershell parallel set-variable

I have a script that functions the way I want it to but it's slow. I tried using the same method in a workflow with foreach parallel but the set-variable command is not something that can be used within a workflow. I wanted to see if the way I'm doing this is incorrect and if there's a better way to get what I'm doing. The reason I want to do parallel requests is because the script can take quite a long time to complete when expanded to 20+ servers as is does each server in turn where as being able to do them all in one go would be quicker.
Below is a dumbed down version of the script (that works without parallel foreach) but it's effectively what I need to get working:
$servers = #("server1", "server2");
foreach ($s in $servers) {
$counter_value = get-counter "\\$s\counter_name"
Set-Variable -name "{s}counter" -value $counter_value
write-host ${server1counter}
Commands not supported in workflows needs to be executed in an Inlinescript. Try (untested):
workflow t {
$servers = #("server1", "server2");
foreach -parallel ($s in $servers) {
inlinescript {
$counter_value = get-counter "\\$using:s\counter_name"
Set-Variable -name "$($using:s)counter" -value $counter_value
#write-host with a PerformanceCounterSampleSet isn't a good combination. You'll only get the typename since it's a complex type (multiple properties etc.)
write-host (Get-Variable "$($using:s)counter" -ValueOnly)
}
}
}
t

Convert VBS Script to List WMI Class Names to Powershell

Basically I'd like to use Powershell to dump a list of all available classes in the root\cimv2 namespace. I have a vbscript which accomplishes the task:
Set objWMIService = _
GetObject("winmgmts:{impersonationLevel=impersonate}root\cimv2")
Set colClasses = objWMIService.SubClassesOf
For Each objClass In colClasses
If Left(objClass.Path_.Class,6) = "Win32_" Then
WScript.Echo objClass.Path_.Class
End If
Next
I've been able to get powershell to retrieve the list, but I can't seem to figure out how to get it to Write-Host the names. This is where I'm at now:
$WMIService = Get-WmiObject -Namespace root\cimv2 -List
$aClasses = $WMIService.SubClassesOf
foreach ($Class in $aClasses) {
Write-Host $Class.Path_.Class
}
Powershell dumps a long list of nothing, so I know it's enumerated something. I've tried all sorts of $Class.x and haven't hit the magic one yet. Does anyone know?
Something similar to:
$WMIService = Get-WmiObject -Namespace root\cimv2 -List
$WMIService | where { $_.Name -like "Win32_*" } | foreach { $_.Name }
Will get you what you're looking for, I think.