Good Technique

Ask your PowerShell-related questions, including questions on cmdlet development!
Forum rules
Do not post any licensing information in this forum.

Any code longer than three lines should be added as code using the 'Select Code' dropdown menu or attached as a file.
This topic is 14 years and 8 months old and has exceeded the time allowed for comments. Please begin a new topic or use the search feature to find a similar but newer topic.
Locked
User avatar
josiahwww
Posts: 8
Last visit: Thu Jul 02, 2009 3:22 am

Good Technique

Post by josiahwww »

Hello,

For my first large Powershell script I want to convert a very large and successful VBscript that monitors all my servers by collecting data using several WMI Win32 classes and e-mails me an nice report in HTML detailing any issues. I am hoping to reduce the amount of code I have to write, this is supposed to be on of the selling points of Powershell. I want to pass credentials when i execute the script so this is the technique I have choosen so far.

if ($user -ne "") { $MyCred = Get-Credential $user Get-WmiObject -Class Win32_NetworkAdapterConfiguration -computername $hostname -Credential $MyCred
Get-WmiObject -Class Win32_Service -computername $hostname -Credential $MyCred
Get-WmiObject -Class Win32_LogicalDisk -computername $hostname -Credential $MyCred
}else{
Get-WmiObject -Class Win32_NetworkAdapterConfiguration -computername $hostname
Get-WmiObject -Class Win32_Service -computername $hostname
Get-WmiObject -Class Win32_LogicalDisk -computername $hostname}

It seems a waste to have to re-write almost the same code in the else statement. I would like to get rid of the else{} statement and was trying something like this. I know this syntax won't work, I am just trying to get the idea across. I also know there are several others ways to handle this
but was hoping to move from the traditional scripting format to the batch/pipeline technique.

Get-WmiObject -Class Win32_Service -computername $hostname ({if($user -ne ""){-Credential $MyCred})


Here is how i do it with VBScript, re-use is very simple between win32 classes, also a single connection is created and not several. I know I can use this same method in Powershell but then what would be the point of learning Powershell ;-)

'### Connect to WMI default namespaceIf sHostName = sComputer Then Set oWMI = oWMIConnect.ConnectServer(sComputer, "root/cimv2")ElseIf (sUser = "") And (sPwd = "") Then Set oWMI = oWMIConnect.ConnectServer(sComputer, "root/cimv2", , , , ,WBEM_FLAG_CONNECT_USE_MAX_WAIT)Else Set oWMI = oWMIConnect.ConnectServer(sComputer, "root/cimv2", sUser, sPwd, , ,WBEM_FLAG_CONNECT_USE_MAX_WAIT)End If
'### Query Win32 Class and Get propertiesDim colClass, oClassSet colClass = oWMI.ExecQuery("Select * from Win32_Service")
Get some data....

Set colClass = oWMI.ExecQuery("Select * from Win32_LogicalDisk")
Get some data...

Thanks
Josiah
josiahwww2009-07-01 18:57:31
User avatar
josiahwww
Posts: 8
Last visit: Thu Jul 02, 2009 3:22 am

Good Technique

Post by josiahwww »

Hello,

For my first large Powershell script I want to convert a very large and successful VBscript that monitors all my servers by collecting data using several WMI Win32 classes and e-mails me an nice report in HTML detailing any issues. I am hoping to reduce the amount of code I have to write, this is supposed to be on of the selling points of Powershell. I want to pass credentials when i execute the script so this is the technique I have choosen so far.

if ($user -ne "") { $MyCred = Get-Credential $user Get-WmiObject -Class Win32_NetworkAdapterConfiguration -computername $hostname -Credential $MyCred
Get-WmiObject -Class Win32_Service -computername $hostname -Credential $MyCred
Get-WmiObject -Class Win32_LogicalDisk -computername $hostname -Credential $MyCred
}else{
Get-WmiObject -Class Win32_NetworkAdapterConfiguration -computername $hostname
Get-WmiObject -Class Win32_Service -computername $hostname
Get-WmiObject -Class Win32_LogicalDisk -computername $hostname}

It seems a waste to have to re-write almost the same code in the else statement. I would like to get rid of the else{} statement and was trying something like this. I know this syntax won't work, I am just trying to get the idea across. I also know there are several others ways to handle this
but was hoping to move from the traditional scripting format to the batch/pipeline technique.

Get-WmiObject -Class Win32_Service -computername $hostname ({if($user -ne ""){-Credential $MyCred})


Here is how i do it with VBScript, re-use is very simple between win32 classes, also a single connection is created and not several. I know I can use this same method in Powershell but then what would be the point of learning Powershell ;-)

'### Connect to WMI default namespaceIf sHostName = sComputer Then Set oWMI = oWMIConnect.ConnectServer(sComputer, "root/cimv2")ElseIf (sUser = "") And (sPwd = "") Then Set oWMI = oWMIConnect.ConnectServer(sComputer, "root/cimv2", , , , ,WBEM_FLAG_CONNECT_USE_MAX_WAIT)Else Set oWMI = oWMIConnect.ConnectServer(sComputer, "root/cimv2", sUser, sPwd, , ,WBEM_FLAG_CONNECT_USE_MAX_WAIT)End If
'### Query Win32 Class and Get propertiesDim colClass, oClassSet colClass = oWMI.ExecQuery("Select * from Win32_Service")
Get some data....

Set colClass = oWMI.ExecQuery("Select * from Win32_LogicalDisk")
Get some data...

Thanks
Josiah
josiahwww2009-07-01 18:57:31
User avatar
jhicks
Posts: 1789
Last visit: Mon Oct 19, 2015 9:21 am

Good Technique

Post by jhicks »

One thing you could do is write a function to get the WMI information:Function Get-WMIData {Param([string]$computername=$env:computername, [string]$namespace="rootcimv2", [string]$class="Win32_OperatingSystem", [System.Management.Automation.PSCredential]$credential ) $cmd="Get-WmiObject -Namespace $namespace -class $class -computername $computername" if ($credential) { Write-Host "Adding credentials $($credential.username)" -foregroundcolor cyan $cmd=$cmd + " -credential `$credential" } Write-Host $cmd -foregroundcolor CYAN #run the command and results will be written to the pipeline Invoke-Expression $cmd } #end function$cred=Get-Credential "jdhitsolutionsadministrator"$computer="JDHIT-DC01""win32_logicaldisk","win32_computersystem","win32_operatingsystem" | foreach { Get-WMIData -class $_ -computername $computer -cred $cred} I think if you make quick calls like this the connection is reused.
User avatar
abqbill
Posts: 138
Last visit: Mon Sep 28, 2020 1:20 pm

Good Technique

Post by abqbill »

Hi josiahwww,

Another alternative is to use the .NET System.Management.ManagementObjectSearcher class directly -- its PowerShell type shortcut is [WMISearcher]. For example:

Code: Select all

$searcher = [WMISearcher] ""
$searcher.Scope.Options.Impersonation = "Impersonate"
if (($username -ne "") -and ($password -ne "")) {
  $searcher.Scope.Options.Username = $username
  $searcher.Scope.Options.Password = $password
}
$searcher.Scope.Path = "computernamerootcimv2"
$searcher.Query = "select * from Win32_OperatingSystem"
$searcher.Get()
$searcher.Query = "select * from Win32_Service"
$searcher.Get()
...etc. (not tested, but it gives you the basic idea).

HTH,

Bill
User avatar
josiahwww
Posts: 8
Last visit: Thu Jul 02, 2009 3:22 am

Good Technique

Post by josiahwww »

All,

Thank you for the input. Looks like I'll use the Jhicks advice, I started to go this route but was wishing that Powershell supported some type of dynamic parameter addition. I suppose this would be the technique.

param([string]$user, [string]$hostname)

$cmd = "Get-WmiObject -class Win32_Service -computername `$hostname"

if($user -ne ""){
$myCred = Get-Credential $user
$cmd = $cmd + "-credential `$myCred"
}

Invoke-Expression $cmd

Thanks everyone for the feed back.
User avatar
abqbill
Posts: 138
Last visit: Mon Sep 28, 2020 1:20 pm

Good Technique

Post by abqbill »

This has me curious...I wonder which solution performs better? I'll have to do some testing when I get back to my office.Bill
User avatar
abqbill
Posts: 138
Last visit: Mon Sep 28, 2020 1:20 pm

Good Technique

Post by abqbill »

OK, I did some testing. Here is the test script:

Code: Select all

$list = @("dc1","dc2","dc3","dc4","dc5","dc6")

$searcher = [WMISearcher] ""
$searcher.Scope.Options.Impersonation = "Impersonate"

function test-searcher {
  foreach ($computer in $list) {
    $searcher.Scope.Path = "$computerrootcimv2"
    $searcher.Query = "select * from Win32_OperatingSystem"
    $os =  $searcher.Get()
    $searcher.Query = "select * from Win32_Service"
    $service = $searcher.Get()
  }
}

function test-gwmi {
  foreach ($computer in $list) {
    $os = get-wmiobject Win32_OperatingSystem -computer $computer
    $service = get-wmiobject Win32_Service  -computer $computer
  }
}

measure-command { test-searcher }
measure-command { test-gwmi }
I ran the test several times. On average, the ManagementObjectSearcher calls finished in a little over 1 second, but the get-wmiobject calls took nearly 12 seconds.

Bill
jvierra
Posts: 15439
Last visit: Tue Nov 21, 2023 6:37 pm
Answers: 30
Has voted: 4 times
Been upvoted: 33 times

Good Technique

Post by jvierra »

In VBSCript or other scripting languuages teh scripting support for WMI allows for caching of teh "winmgmts" object. After the first call we will reuse the object even if we release it so performance is pretty good.

It looks like the Get-WMIObject does not do this. It is getting then releasing a reference to WMI NET classs but the class is released after teh CmdLet returns the result.

Using the searcher we can keep a copy of the WMI management classes alive and easily reuse the objects on the local (client) side of the DCOM connection. This give performance similar to the WMI scripting model.

I am hoping that WinRM will allow us to grab directly at remote WMI classes which will, or should, further performance on the client side and over the network. A little more tweeking of teh WMI engine should then give us an extremely efficient management tool that eliminates any need for direct - and unsecured - access to a remote machine for management data. Well----wonder of wonders --- will it hapen in my lifetime?
This topic is 14 years and 8 months old and has exceeded the time allowed for comments. Please begin a new topic or use the search feature to find a similar but newer topic.
Locked