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
Good Technique
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.
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.
Good Technique
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
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
Good Technique
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.
Good Technique
Hi josiahwww,
Another alternative is to use the .NET System.Management.ManagementObjectSearcher class directly -- its PowerShell type shortcut is [WMISearcher]. For example:
...etc. (not tested, but it gives you the basic idea).
HTH,
Bill
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()
HTH,
Bill
Good Technique
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.
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.
Good Technique
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
Good Technique
OK, I did some testing. Here is the test script:
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
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 }
Bill
Good Technique
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?
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?