Multithreaded Remote Registry Gathering with Powershell

Gather specific subkey values or an entire registry key’s subkey values with powershell and multithreading.

Introduction

I’ve become a bit enamored with a multithreaded function I found recently on the Internet. (After reading through a good portion of the Powershell Deep Dive book I’m almost certain the roots of that function are from the legendary Boe Prox). Using this function as a template I’ve released my own function which gathers several bits of system information from WMI using within runspaces. As gathering remote registry information is really just a wmi connection I modified this same script to gather anything from a remote system’s registry as well. This is the resulting function.

As always (in my general scripts at least), this function supports supplying or prompting for alternate credentials. The default examples included in the comment based help show a very nice use case scenario for such a multithreaded powershell function, gathering remote time server information from a server. If you had to do this for 800 servers then this might be very handy indeed.

Many times you may need more than just one subkey value from a remote registry so instead of adding additional connections and processing I included the ability to just skip the subkey altogether and return all of the subkey values found under the key instead.

Downloads

Download the script from the technet gallery (more frequently updated)

Download the script from this site (less frequently updated)

Comments (12)

  1. 1:07 PM, 08/29/2013noel blanc  / Reply

    Hello Sir,

    To day, i play with 7,800 remote XPSP3 get/set a value in registry (activate log for print service)and i create process (net stop / net start spooler) with WMI , trottlelimite 500 : 2H07 with STA. I don’t test again wthout STA. And each thread write result in the same file with out-file -append. All things are OK in the file.
    note : Without STA and trottlelimite=20, i write to the console a RC (carried return) is loosed, so two info on a line on the screen. MTA ? i don’t know. Nevermind :STA now for me. With big machine with many CPU, and for sample, with works in a database on a unique computer, perhaps MTA is more fast. But i don’t have this type of job.

    About STA/MTA,I find these informations on the net :
    http://www.techvanguards.com/com/concepts/multithreading.asp
    http://www.codeguru.com/cpp/com-tech/activex/apts/article.php/c5529/Understanding-COM-Apartments-Part-I.htm#more
    http://www.codeguru.com/cpp/com-tech/activex/apts/article.php/c5533/Understanding-COM-Apartments-Part-II.htm
    Perhaps you know…
    I think i never undestand why too many connectserver requests wmi hang the PS console V3. Too complex for me.
    I can’t do more about runspacepool. I do only copy/paste of your work and other “good” people.
    Respectfully.
    Noel

  2. 3:50 AM, 08/24/2013noel blanc  / Reply

    Hello,
    Thanks for your reply. You undestand very well. In the contexte of “massive parallele with runspace”, i thougth that get-wmiobject in the scriptblock is too bad ( too much CPU,RAM and Time ). I use “direct” wmi requests in runspacepool for great speed.
    Thanks for the alternative.
    For the issue, have you some idea? why do a same code and mecanisme hang with big trottlelimit even if “sender” computer’s ressources are large? it hangs with connecserver WMI request but not with “net use” in the same code and conditions?
    Sorry if my english is confusing….
    Respectfully.

    • 11:08 PM, 08/25/2013Zachary Loeber  / Reply

      Hello,

      I wish I had an answer for you but I do not. Are you certain that the connections from the source computer are getting properly released when the task is complete? When running the code I’d open up a cmd prompt and run netstat commands to see how many connections are open. There are only about 65k ports on a system which can be used. If they start getting all used up you will start seeing what appears to be resource consumption issues.

      • 2:44 PM, 08/26/2013noel blanc  / Reply

        Hello Sir,
        I think i find the key in :
        http://newsqlblog.com/2012/05/22/concurrency-in-powershell-multi-threading-with-runspaces/
        $throttleLimit=200
        $sessionState = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
        $runspacePool = [runspacefactory]::CreateRunspacePool(1, $throttleLimit, $sessionState, $host)
        $runspacePool.ApartmentState = “STA” # the key seems to be here but i don’t undestand the link with WMI request connectserver
        $runspacePool.Open()

        I don’t know if it’s hasard. When i tested that, i was very busy and now, when i write this, i doubt about all parameters of the test, number of computers in the list and trottlelimite used.
        Tomorow, I’ll test with the “real” conditons, more computers.
        During the wait time, what do you think about this property in the contexte of direct WMi request? I kown, nobody can read the code and tests are complexes.
        Respectfully.

        • 11:25 PM, 08/27/2013Zachary Loeber  / Reply

          Hey Noel,

          I’m not very familiar (yet!) with apartment states when it comes to runspace pools. I’ve actually read the article you referenced previously. I double-checked and powergui (my script editor of choice for debugging one off functions) runs in STA mode by default. When you test STA vs. MTA let me know how it turns out.

          • 3:10 PM, 08/28/2013noel blanc 

            Hello Sir,
            Today i can test with 7,800 remote computers XPSP3; I launch script from a notebook W2K8R2SP1 with PS v3 end i do it with ps console not ps_ise. Someone says that one is sta and other is mta but i don’t kown which is which.

            First, i constate two things :
            – for a runspacepool new created
            PS C:\ $throttleLimit=200
            PS C:\ $sessionState = [system.management.automation.
            runspaces.initialsessionstate]::CreateDefault()
            PS C:\ $runspacePool = [runspacefactory]::CreateRunsp
            acePool(1, $throttleLimit, $sessionState, $host)
            PS C:\ $runspacePool.ApartmentState
            Unknown <<<< ?????
            – and for the console
            PS C:\$Host.Runspace.ApartmentState
            Unknown <<<< ?????
            So, i don't know how to be sure of ApartmentState for the two object.
            Second, the scriptblock used for te test :
            net use \\ip pwd /u:user
            copy local \\ip
            net use \\ip /d
            connectserver WMI resquest
            createprocess wmi resquet

            Third, my 4 tests :
            a – without $runspacePool.ApartmentState = “STA” before $runspacePool.Open() :
            – with the same script and trottlelimite = 30 for 7,800 remote computers XPSP3 : NOK (PS no respond, no progress, visible with wireshark on the network :only one 50 connectserver, and no createprocess on the net )

            b – without $runspacePool.ApartmentState = “STA” before $runspacePool.Open() :
            – with the same script and trottlelimite = 30 for 7,800 remote computers XPSP3 : OK
            it takes more 14 hours with trottlelimite = 20 without sta

            c – with $runspacePool.ApartmentState = “STA” before $runspacePool.Open() :
            – with the same script and trottlelimite = 500 for 7,800 remote computers XPSP3 : OK
            it takes 5H30 ( more 14 hours with trottlelimite = 20 without sta )
            all the time ps turns : ( a little more ) 500 process 2000 threads 90%RAM 90%CPU, 100,000 handles ( values evolue a little, positive/negative )

            d – with $runspacePool.ApartmentState = “STA” before $runspacePool.Open() :
            – new scriptblock :
            connectserver, provider remote registry
            GetWordValue/SetWordValue WMI request
            no net use/copy
            – i must leave office before it finish but i see it during 1 hour
            – with a new script and trottlelimite = 500 for 7,800 remote computers XPSP3 : OK
            before to leave my office, i can see in taskmgr :
            all the time ps turns : 52 process 800 threads 99%CPU (maxi green 1/3 red) no more RAM used (1,5G)

            I can't explain any thing, just i constate. In the future, I would do more work like that, for information that is not in our inventory of our large park of computer distributed throughout the French territory. For this works, i'll use "sta" and trottlelimite = 500, while the script.

            During many weeks i use the script without sta and with trottlelimit=200 to make "net use/copy" without error. PS hangs only when i introduced connectserver wmi request.

            So, if you can test, constate, and …. explain sta/mta and consequence, I would be very grateful.
            Now with a web traductor….
            I really appreciated that you to respond me constructively. I have post on different sites but no relevant return.
            i don't know if you can undestand my poor english.
            Respectfully.

          • 11:21 PM, 08/28/2013Zachary Loeber 

            Mr Blanc, you have done all the hard work my friend. I’m grateful that you chose to share your experiences. I’m almost certain that the PS console runs in MTA mode by default. I found a great conversation on the differences between MTA/STA here: http://stackoverflow.com/questions/127188/could-you-explain-sta-and-mta.

            Regardless, I’ll force the apartment state to be STA in the runspace in my scripts moving forward. Thanks again good sir!

          • 3:15 PM, 08/28/2013noel blanc 

            I make a mistake in the last post.
            The good texte is :
            b – without $runspacePool.ApartmentState = “STA” before $runspacePool.Open() :
            – with the same script and trottlelimite = 20 for 7,800 remote computers XPSP3 : OK
            it takes more 14 hours with trottlelimite = 20 without sta
            If you can edit …
            Best regards

          • 10:52 PM, 08/28/2013Zachary Loeber 

            Good to hear it is working for you. I’ll look to incorporate those changes in my scripts moving forward!

  3. 9:34 AM, 08/20/2013noel blanc  / Reply

    hello,
    Your script is very helpfull for me.
    I modify the scriptblock because i need to use wmi to connect to 1,000 computers with different credentials and create a remote process on each computer.
    It’s work very well with trottlelimit is less than 25 ( 20 for me ) . But powershell hangs if trottlelimit is more, like 200 for example.
    My scriptblock use, for each remote computer, 3 different parameters, address_Ip and user and password.
    It connect wia wmi with credentials and launch a process with an instance of createprocess.
    For investigation, i replace the wmi request by simple commands “net use ip user pwd” and “copy via cmd/smb” and it’s work very well with trottlelimit 200.
    In the case of trottleIimit 200 and connect/createprocess with wmi, i also see with wireshark the sending of WMI connects but nothing about the createprocess with wmi. And i see these createprocess on the network with trottellimite 20.
    I think my scriptblock is not wrong. I think Wmi has a limit but i don’t find anything about this limitation on internet. I find only this : http://us.generation-nt.com/answer/advanced-wmi-multiple-wmi-connections-network-bottleneck-help-25992852.html
    I put my scripblock here if you can/want test wmi and the remote creteprocess and trottlelimit more 30 :
    $cmd=”cmd.exe”
    $locator = New-Object -ComObject “WbemScripting.SWbemLocator”
    $oRemoteWmi = $locator.ConnectServer($adrIp,”root\cimv2″,$user,$lepwd)
    $oRemoteClassProcess=$oRemoteWmi.Get(“Win32_Process”)
    $oPS=$oRemoteWmi.Get(“Win32_ProcessStartup”)
    $oMethodeCreate=$oRemoteClassProcess.Methods_|?{$_.name -eq ‘create’}
    $oInParam=$oMethodeCreate.inParameters.SpawnInstance_()
    ($oInParam.Properties_ | where {$_.name -eq “CommandLine”}).Value = $cmd
    ($oInParam.Properties_ | where {$_.name -eq “CurrentDirectory”}).Value = “c:\”
    ($oInParam.Properties_ | where {$_.name -eq “ProcessStartupInformation”}).Value = $oPS
    $outParam = $oRemoteClassProcess.ExecMethod_(“Create”, $oInParam)

    #lecture code du pid
    $remotePid = $null
    $remotePid = ($outParam.Properties_ | ?{$_.name -eq “ProcessId”}).Value
    Write-Host “$domaine $adrIp remotePid = $remotePid”
    I use W2K8R2SP1 with PS V3 on the “sender”.
    Thank you very much
    Ps : i’m french and don’t write very well… so if you can’t undestand what i write, forget me 😉

    • 9:38 PM, 08/23/2013Zachary Loeber  / Reply

      Perhaps I’m misreading your code but are you just trying to track down some remote processes and return their PID? If so, it looks like you are overcomplicating your code by quite a bit. Try this function out instead (recode to fit your needs)

      Function Get-RemoteProcess
      {
      < # .SYNOPSIS Retrieve remote process information. .DESCRIPTION Retreives remote process information with WMI and, optionally, a different credential. .PARAMETER Name The process name to return. Accepted via pipeline. .PARAMETER ComputerName Computer with process to check .PARAMETER PromptForCredential Set this if you want the function to prompt for alternate credentials .PARAMETER Credential Pass an alternate credential .NOTES Name : Get-RemoteProcess Version : 1.0.0 Aug 11th 2013 - First release Author : Zachary Loeber Disclaimer : This script is provided AS IS without warranty of any kind. I disclaim all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall I be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the script or documentation. Copyright : I believe in sharing knowledge, so this script and its use is subject to : http://creativecommons.org/licenses/by-sa/3.0/ .LINK http://www.the-little-things.net/ .LINK http://nl.linkedin.com/in/zloeber .EXAMPLE $Cred = Get-Credential Get-process | Get-Remoteprocess -ComputerName 'testserver1' -Credential $Cred | Measure-Object Description: ------------------ Returns a count of all processs on testserver1 with the same name as those found on the local system using alternate credentials. .EXAMPLE Get-Remoteprocess -ComputerName 'testserver1' -PromptForCredentials $true Description: ------------------ Returns all processs on testserver1 prompting for credentials (once). #>
      [CmdletBinding()]
      param(
      [Parameter( Position=0,
      ValueFromPipelineByPropertyName=$true,
      ValueFromPipeline=$true,
      HelpMessage=”The service name to return.” )]
      [Alias(‘ProcessName’)]
      [string[]]
      $Name,

      [parameter( HelpMessage=”Computer with service to check” )]
      [string]
      $ComputerName = $env:computername,

      [parameter( HelpMessage=”Set this if you want the function to prompt for alternate credentials” )]
      [switch]
      $PromptForCredential,

      [parameter( HelpMessage=”Pass an alternate credential” )]
      [System.Management.Automation.PSCredential]
      $Credential
      )
      BEGIN
      {
      if ($PromptForCredential)
      {
      $Credential = Get-Credential
      }
      }
      PROCESS
      {
      $procresults = @()
      $processes = @()
      if ($Name)
      {
      $processes += $Name
      }
      $wmiparams = @{
      Namespace = ‘root\CIMV2’
      Class = ‘Win32_Process’
      ComputerName = $ComputerName
      ErrorAction = ‘Stop’
      }
      if ($Credential -ne $null)
      {
      $wmiparams.Credential = $Credential
      }
      if ($processes.count -ge 1)
      {
      try
      {
      $procresults = Get-WmiObject @wmiparams | where {$_.ProcessName -contains $processes}
      }
      catch
      {
      Write-Warning -Message (‘{0}: {1}’ -f $ComputerName, $_.Exception.Message)
      }
      }
      else
      {
      try
      {
      $procresults = Get-WmiObject @wmiparams
      }
      catch
      {
      Write-Warning -Message (‘{0}: {1}’ -f $ComputerName, $_.Exception.Message)
      }
      }
      }
      END
      {
      $procresults
      }
      }

      Get-RemoteProcess

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Follow

Get every new post delivered to your Inbox

Join other followers