Author Posts

June 17, 2017 at 7:33 pm

Hello Eeryone,

I'll start by saying i'm pretty new to Powershell and am really hoping to learn and eventually become proficient, but for now a I have what I think to be a simple question...

I'm building a script to query a single host which is entered by the user and return a pile of information. In my case I have set up each piece of information to query independently so it can be stored in a variable (Username, RAM, OS, ect.), but in doing so I now have 19 queries so far that as you can imagine can a bit to run being that I still have 4 more queries to add I was wondering what I can do to circle back and make this process more efficient. My current run time so far is roughly 50, obviously id like to see that closer to 5-7, I was wondering if there is a way to have the script fire say 5 separate WMI queries to a single machine simultaneously rather than one at a time.

Maybe i'm looking at this all wrong? Would it be better to pull down an entire namespace at once and somehow extract data from it? So far i'm querying 3 name spaces on the target machine if that makes a difference.

June 17, 2017 at 10:04 pm

Without any line of your code it will quite hard to recommend something for you. But some general advices might help either.
If you fire some wmi queries to one machine it makes sense to combine these single queries in one cim session. That usually speeds up those tasks noticeably ... here is something I set up time ago for a customer:

function Get-WMIPCInfo {
    [CmdletBinding()]
    param(
        $ComputerName = $ENV:ComputerName
    )
    if (Test-Connection $ComputerName -Quiet -Count 1) {
        Write-Debug -Message "$ComputerName reachable"
        $so = New-CimSessionOption -Protocol DCOM 
        $WMIData = New-CimSession -CN $ComputerName -SessionOption $so
        $BIOS = Get-CimInstance -Class CIM_BIOSElement -CimSession $WMIData | Select-Object -Property *
        $OS = Get-CimInstance -Class CIM_OperatingSystem -CimSession $WMIData | Select-Object -Property *
        $ServiceBranch = (([wmiclass]"\\$($ComputerName)\root\default:stdRegProv").GetStringValue(2147483650,'SOFTWARE\Microsoft\Windows NT\CurrentVersion','ReleaseID')).svalue
        $DISK = Get-CimInstance -Class CIM_LogicalDisk -CimSession $WMIData | Where-Object {$_.DeviceID -eq "C:"} | Select-Object -Property *
        $Computer = Get-CimInstance -Class CIM_ComputerSystem -CimSession $WMIData | Select-Object -Property *
        $IPConfig = Get-CimInstance -Class Win32_NetworkAdapterConfiguration -CimSession $WMIData | Where-Object{$_.DefaultIPGateway -match "((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}"}  | Select-Object -Property *
        #$LoggedOnUser = (Get-CimInstance -Class CIM_Process -CimSession $WMIData -Filter "Name = 'explorer.exe'" -ErrorAction SilentlyContinue | Invoke-CimMethod -MethodName GetOwner).User
        $UserQuery = ( C:\Windows\System32\quser.exe /server:$ComputerName 2> null)
        if ($UserQuery) {
            $UserQuery[1].Trim() -match "^(\S+)\s+.*((\d{2}\.){2}\d{4}\s+\d{2}:\d{2})" | Out-Null
            $LoggedOnUser = $Matches[1]
            $LogonTime = Get-Date -Date $Matches[2]
        }
        Remove-CimSession $WMIData
        Write-Debug -Message "`$BIOS: $BIOS"
        Write-Debug -Message "`$OS:  $OS"
        Write-Debug -Message "`$DISK:  $DISK"
        Write-Debug -Message "`$Computer:  $Computer"
        Write-Debug -Message "`$IPConfig:  $IPConfig"
        Write-Debug -Message "`$LoggedOnUser:  $LoggedOnUser"
        $CIMOutput = [PSCustomObject]@{
            ComputerName = $BIOS.PSComputerName;
            Model = $Computer.Model;
            BIOSName    = $BIOS.Name;
            SMBIOSVersion = $BIOS.SMBIOSBIOSVersion;
            BIOSVersion  = $BIOS.BIOSVersion;
            ReleaseDate  = $BIOS.ReleaseDate;
            SerialNumber = $BIOS.SerialNumber;
            OSCaption   = $OS.Caption;
            OSVersion   = $OS.Version;
            ServiceBranch = $ServiceBranch;
            InstallDate  = $OS.InstallDate;
            LastBootUpTime = $OS.LastBootUpTime;
            PhysicalRAM = [math]::round((($Computer.TotalPhysicalMemory) / 1GB),2);
            DiskSize     =  [math]::round(($DISK.Size / 1GB),2);
            DiskFreeSpace =  [math]::round(($DISK.FreeSpace / 1GB),2);
            IPV4Address     = $IPConfig.IPAddress[0];
            DefaultGateway = $IPConfig.DefaultIPGateway[0];
            DNSDomain = $IPConfig.DNSDomain;
            DHCPEnabled = $IPConfig.DHCPEnabled;
            LoggedOnUser = $LoggedOnUser
            LogonTime = $LogonTime
        }
    $CIMOutput
    }
    else {
        Write-Warning "`tComputer '$($ComputerName)' unreachable`n" 
    }
}

... you can use as an inspiration. 😉

June 17, 2017 at 11:17 pm

So, if you're using Get-WmiObject, switch to Get-CimInstance. Faster. Establish a CimSession first, query against it, and then close it. Much faster. Make the connection over Dcom if needed (CimSessionOption), but don't use the old, deprecated Wmi commands.

It's not possible to query an entire namespace, and if it was, it would NOT be faster. Not the way the tech works.

June 18, 2017 at 2:57 am

I was able to clean it up quite a bit after I posted this by combining all of my "like" queries into one request, i'm evolving quickly. My second attempt ran in 7 seconds. I will deffinately look into switching to Get-CimInstance, im going to add some more info into this first. I have attached my remote query block below, this is just pulling basic system info. How can I segment a script into blocks of code? I would like to be able to control when, where, and why parts of my script execute, later adding call options as part of a GUI. I'm looking to have a call such as "Summary" that would just run this block and stop eliminating the need to have multiple script files.

$WMIOS = gwmi -comp $hostname -Query "SELECT FreePhysicalMemory,Name,OSArchitecture FROM Win32_OperatingSystem"
$freeMemory         = ($WMIOS.FreePhysicalMemory*1000/1gb).ToString(".00")
$OS                 = $WMIOS.Name.split('|')[0]
$Arch               = $WMIOS.OSArchitecture


$WMICS = gwmi -comp $hostname -Query "SELECT DNSHostName, Domain, Username FROM win32_computersystem"
$name               = $WMICS.DNSHostName
$domain             = $WMICS.Domain
$username           = $WMICS.Username


$WMIMEM = gwmi -comp $hostname -Query "SELECT Capacity FROM Win32_PhysicalMemory"
$totalMemory        = $WMIMEM.Capacity/1gb.ToString(".00")

$NetAdapter=(get-wmiobject win32_networkadapter -filter "netconnectionstatus = 2" -comp $hostname).Name

$FQDN = ($name+"."+$domain)

$filter = "(&(objectCategory=computer)(objectClass=computer)(cn=$hostname))"
$DN=([adsisearcher]$filter).FindOne().Properties.distinguishedname
$OU=(($DN -split ',(?=OU)') | Select-Object -skip 1) -join ',' -replace ".{27}$" -Replace ",OU=", " / " -Replace "OU=";

June 18, 2017 at 8:18 am

Unfortunately my first reply has been blocked – I don't know why. So I try again.
Here something I put together a while ago for a customer. You could use an inspiration or blue print and adapt it to your needs

function Get-WMIPCInfo {
    [CmdletBinding()]
    param(
        $ComputerName = $ENV:ComputerName
    )
    if (Test-Connection $ComputerName -Quiet -Count 1) {
        Write-Debug -Message "$ComputerName reachable"
        $so = New-CimSessionOption -Protocol DCOM 
        $WMIData = New-CimSession -CN $ComputerName -SessionOption $so
        $BIOS = Get-CimInstance -Class CIM_BIOSElement -CimSession $WMIData | Select-Object -Property *
        $OS = Get-CimInstance -Class CIM_OperatingSystem -CimSession $WMIData | Select-Object -Property *
        $ServiceBranch = (([wmiclass]"\\$($ComputerName)\root\default:stdRegProv").GetStringValue(2147483650,'SOFTWARE\Microsoft\Windows NT\CurrentVersion','ReleaseID')).svalue
        $DISK = Get-CimInstance -Class CIM_LogicalDisk -CimSession $WMIData | Where-Object {$_.DeviceID -eq $OS.SystemDrive} | Select-Object -Property *
        $Computer = Get-CimInstance -Class CIM_ComputerSystem -CimSession $WMIData | Select-Object -Property *
        $IPConfig = Get-CimInstance -Class Win32_NetworkAdapterConfiguration -CimSession $WMIData | Where-Object{$_.DefaultIPGateway -match "((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}"}  | Select-Object -Property *
        $UserQuery = ( C:\Windows\System32\quser.exe /server:$ComputerName 2> null)
        if ($UserQuery) {
            $UserQuery[1].Trim() -match "^(\S+)\s+.*((\d{2}\.){2}\d{4}\s+\d{2}:\d{2})" | Out-Null
            $LoggedOnUser = $Matches[1]
            $LogonTime = Get-Date -Date $Matches[2]
        }
        Remove-CimSession $WMIData
        Write-Debug -Message "`$BIOS: $BIOS"
        Write-Debug -Message "`$OS:  $OS"
        Write-Debug -Message "`$DISK:  $DISK"
        Write-Debug -Message "`$Computer:  $Computer"
        Write-Debug -Message "`$IPConfig:  $IPConfig"
        Write-Debug -Message "`$LoggedOnUser:  $LoggedOnUser"
        $CIMOutput = [PSCustomObject]@{
            ComputerName = $BIOS.PSComputerName;
            Model = $Computer.Model;
            BIOSName    = $BIOS.Name;
            SMBIOSVersion = $BIOS.SMBIOSBIOSVersion;
            BIOSVersion  = $BIOS.BIOSVersion;
            ReleaseDate  = $BIOS.ReleaseDate;
            SerialNumber = $BIOS.SerialNumber;
            OSCaption   = $OS.Caption;
            OSVersion   = $OS.Version;
            ServiceBranch = $ServiceBranch;
            InstallDate  = $OS.InstallDate;
            LastBootUpTime = $OS.LastBootUpTime;
            PhysicalRAM = [math]::round((($Computer.TotalPhysicalMemory) / 1GB),2);
            DiskSize     =  [math]::round(($DISK.Size / 1GB),2);
            DiskFreeSpace =  [math]::round(($DISK.FreeSpace / 1GB),2);
            IPV4Address     = $IPConfig.IPAddress[0];
            DefaultGateway = $IPConfig.DefaultIPGateway[0];
            DNSDomain = $IPConfig.DNSDomain;
            DHCPEnabled = $IPConfig.DHCPEnabled;
            LoggedOnUser = $LoggedOnUser
            LogonTime = $LogonTime
        }
    $CIMOutput
    }
    else {
        Write-Warning "`tComputer '$($ComputerName)' unreachable`n" 
    }
}

In my environment it used to run in less than a second.
Have fun! 😉