PowerShell - Pass array as an optional parameter value - powershell

I'm pretty new to PowerShell.
I'm trying to pass an array of server names as an optional parameter value to get the last bootup time for a list of servers.
Example 1
$serverList = #('server1"', '"server2"', '"server3"', '"server4"', '"server5"')
Get-CimInstance -ComputerName $serverList -ClassName win32_operatingsystem | Select-Object csname, lastbootuptime
Example 2
Get-CimInstance -ComputerName server1,server2,server3,server4,server5 -ClassName win32_operatingsystem | Select-Object csname, lastbootuptime
In example 1, I get an error that says it can't connect to the
servers.
In example 2, it works.
I'm likely missing a key piece of fundamental knowledge as to why what I'm doing wasn't working. What am I missing?
Thanks for the help.

In the first example you are over-doing things with the quotes. (also server1 has an ending double quote, but no starting double quote..)
By putting the servers inside single quotes ', the text inside it is taken literally, so you are feeding the cmdlet with names like "server2", so including the double-qoute characters.
These quotes obviously don't belong to the server name.
BTW: Not an error, but you don't need the #() when creating the server names array.
This would be a better way of setting up your string array, where you can use either single or double quote characters, but not both:
$serverList = 'server1', 'server2', 'server3', 'server4', 'server5'
Get-CimInstance -ComputerName $serverList -ClassName win32_operatingsystem | Select-Object csname, lastbootuptime
You have also noticed that when used as parameters to a cmdlet, you don't even need the quotes, and the elements are interpreted as strings, as long as they do not contain space characters:
Get-CimInstance -ComputerName server1,server2,server3,server4,server5 -ClassName win32_operatingsystem | Select-Object csname, lastbootuptime

Related

Removing #{} from the output [duplicate]

This question already has answers here:
How do I write the value of a single property of a object?
(2 answers)
Closed 2 years ago.
The output of the following code is like #{Average = 2}.How can I get the output as Average = 2, Without the #{}.
$cpu = $ENV:COMPUTERNAME |Foreach-Object {
Get-WmiObject -computername $_ win32_processor | Measure-Object -property LoadPercentage -Average | Select Average
}
Firstly, I don't think you need to pipe $env:COMPUTERNAME to Foreach-Object, since its of type System.String, not an array or any other type of collection. Would be easier to just use -ComputerName $env:COMPUTERNAME directly. You can see what the type is with ($env:COMPUTERNAME).GetType(). Also have a look at about_environment_variables for more information about Windows environment variables in PowerShell.
Secondly, as #Mathias R. Jessen suggested in the comments, you should use -ExpandProperty to expand the property #{Average = 2} to 2.
Modified command
Get-WmiObject -ComputerName $env:COMPUTERNAME -Class Win32_Processor `
| Measure-Object -Property LoadPercentage -Average `
| Select-Object -ExpandProperty Average
You could also run Get-Help Select-Object -Parameter ExpandProperty to see what ExpandProperty does for Select-Object.
-ExpandProperty <String>
Specifies a property to select, and indicates that an attempt should be made to expand that property. Wildcards are permitted in the property name.
For example, if the specified property is an array, each value of the array is included in the output. If the property contains an object, the properties of that object are displayed in the output.
Required? false
Position? named
Default value None
Accept pipeline input? False
Accept wildcard characters? false
Also as a side note mentioned in the comments by #mklement0, WMI cmdlets(e.g. Get-WmiObject) have been superseded by CIM cmdlets(e.g. Get-CimInstance) in PowerShell v3 (released in September 2012).. This is also pointed out to you when you run Get-Help Get-WmiObject:
Starting in Windows PowerShell 3.0, this cmdlet has been superseded by Get-CimInstance
And also in this Use CIM cmdlets not WMI cmdlets article. Another reason is that the future is PowerShell Core, which doesn't support WMI cmdlets anymore. You can have a look at this answer for more information.
With all that said, here is the equivalent CIM command using Get-CimInstance.
Get-CimInstance -ClassName Win32_Processor `
| Measure-Object -Property LoadPercentage -Average `
| Select-Object -ExpandProperty Average
Which will work on both Windows PowerShell and PowerShell Core.

Get-WmiObject from AD OU

I have a simple problem that I can't seem to work through. I need to know what servers are still running server 2008/R2.
I know that Win32_OperatingSystem's Name property contains the information that I'm looking for. I would like to be able to run Get-WmiObject against a collection of servers in an OU.
There are two problems that I'm having:
I can't figure out how to redirect the output of Get-ADComputer to something that Get-WmiObject -ComputerName can use. I think Get-ADComputer is outputting objects of type Microsoft.ActiveDirectory.Management.ADComputer, and Get-WmiObject is looking for type System.Management.ManagementObject. Here's what I came up with but it doesn't appear to work.
Get-WmiObject Win32_OperatingSystem -ComputerName (Get-ADComputer -filter * -SearchBase "OU=Member Servers,DC=Company,DC=Com" | select #{L="ComputerName";e={$_."name"}}) -Property name, csname | select csname, name | Format-Table -AutoSize
My temp workaround: I was able to create a CSV that contains the list of server names. I was able to use the CSV to run Get-WmiObject against. However, the OU contains "dead" servers. So when I try to run Get-WmiObject using the CSV-list of servers that came from AD there are connection timeouts and PowerShell waits a period of time to see if the dead server will respond. This really slows down the operation & we are working to clean this up. Until that happens, Is there a way to only pass the server names that pass a Test-Connection to Get-WmiObject?
Get-WmiObject win32_operatingsystem -ComputerName (Get-Content C:\Users\user1\Desktop\Servers.csv) -Property name, csname | select csname, name | Format-Table -AutoSize
Pick the name component first then it will pass it to the next pipeline object (select -object)
Get-WmiObject Win32_OperatingSystem -ComputerName ((Get-ADComputer -filter * -SearchBase "OU=Member Servers,DC=Company,DC=Com").Name)
Note: -ComputerName: accepts a string object so you cannot pass a base type object directly to that.

Get-WMIObject include computer name

I'm trying out a script to go grab installed software on servers remotely. Problem is I want it to output certain attribs including the computer name but I can't seem to figure out how to get the name inserted.
Here is what I have so far...
$servers = Get-QADComputer -SearchRoot "OU=servers,OU=mydomain:-),DC=COM" | Select Name
...which works fine of course. Then...
$servers | % {Get-WMIObject -Class Win32Reg_AddREmovePrograms} | select Displayname,Version,InstallDate,PSComputerName
... which provides the full list of software installed on all servers in that OU but the PSComputerName becomes MY COMPUTER (the computer I run the query from - not the computername of the system being queried). The goal is to have the servername the software is installed on on each line item of software. I've asked professor Google and don't seem to see anything helpful (or anything that I understand anyway).
Hope this makes sense. semi-amateur PS script writer so hopefully this is easy for you guys. Thanks in advance for your help
Your command:
Get-WMIObject -Class Win32Reg_AddREmovePrograms
Does not specify computer to query, so it just query computer command being executed on. Thus PSComputerName display MY COMPUTER, as MY COMPUTER is computer being queried. You have to specify -ComputerName parameter to Get-WMIObject cmdlet to query specific computer. And -ComputerName parameter accept array of computer names, so you can put array of computer names to it instead of using ForEach-Object cmdlet and query one computer at time.
Since the object returned from the WMI call doesn't contain the computer you made the request on, you need to include it yourself from include your ForEach-Object (%) block. You could use Add-Member to add it yourself, then do your Select-Object outside like you're doing now:
$servers | % {
Get-WMIObject -Class Win32Reg_AddREmovePrograms -ComputerName $_ |
Add-Member -MemberType NoteProperty -Name ComputerName -Value $_ -PassThru
} | select Displayname,Version,InstallDate,ComputerName
Another way is to move the Select-Object to inside the block and do it within there, by creating a new property on the fly with a hashtable:
$servers | % {
Get-WMIObject -Class Win32Reg_AddREmovePrograms -computername $_ |
Select-Object Displayname,Version,InstallDate,#{Name='ComputerName';Expression={$_}}
}

Any idea why I can't seem to execute two Get-WMIObject commands in the same Scriptblock?

I'm having a hell of a time with what seems like an utterly bizarre issue. I'm basically trying to inventory a bunch of workstations via WinRM and the Invoke-Command PowerShell cmdlet, but I'm running into grief when I try to execute more than one Get-WMIObject call at a time.
In this case, I'm specifically trying to get the model and serial number of the workstations and pipe them out to a CSV, making it important that the two commands are executed in the same Scriptblock, giving me something very similar to the below, basically.
Invoke-Command -ScriptBlock { Get-WmiObject Win32_ComputerSystem | Select Model ; Get-WmiObject win32_SystemEnclosure | Select serialnumber } -ComputerName (Get-ADComputer -Server [domain I care about] -filter 'name -Like "[types of computers I care about]"' | Select-Object -expand Name)
Even when run locally, Get-WmiObject Win32_ComputerSystem | Select Model ; Get-WmiObject win32_SystemEnclosure | Select serialnumber only returns the first command. I've tried swapping them around and the first command executes, while the second does not. Some of my colleagues report that it works just fine for them, others see the same result as me, and it doesn't seem to be a version issue, as one of the people for whom this works is running the same version of PowerShell as I am. (5.0.10240.16384)
Screenshot below of a few different command combinations. Anyone have any idea what's going on here?
If you change the list in the select-object cmdlets so both include the properties output by each, you will get the result you want (I think).
Invoke-Command -ScriptBlock { Get-WmiObject Win32_ComputerSystem | Select Model,SerialNumber ; Get-WmiObject win32_SystemEnclosure | Select model,serialnumber } -ComputerName localhost
That will get you output to the screen which should include all of the info. If you just want objects you can just capture the output and see that all of the properties are there.
$objects=Invoke-Command -ScriptBlock { Get-WmiObject Win32_ComputerSystem | Select Model ; Get-WmiObject win32_SystemEnclosure | Select serialnumber } -ComputerName (Get-ADComputer -Server [domain I care about] -filter 'name -Like "[types of computers I care about]"' | Select-Object -expand Name)
If you execute $objects | format-list * you will see that you have two objects, one with a Model and one with a SerialNumber.
Outputting different objects to a single CSV is another issue altogether. You get columns in the CSV based on the properties in the first object, so you will lose the SerialNumber property in the CSV.

Using Wildcard with WMIC Version Query

I am an InfoSec admin with an okay amount of PowerShell experience. I'll keep it short and sweet:
([WMI] "\\$comp\root\CIMV2:CIM_DataFile.Name='$path'").Version)
I use this for calling file versions instead of using get-item VersionInfo.ProductVersion, since this does not always return an accurate value. It works well. However, when $path is equal to something like this:
C:\Windows\System32\Macromed\Flash\Flash*.ocx
The query doesn't work because the file is not found. I imagine this is due to the single quotes around the variable ignoring the wildcard.
I will admit that I did find a work around to my problem here (the answer posted by JPBlanc):
Powershell get-item VersionInfo.ProductVersion incorrect / different than WMI
However, I want to know if it is possible for me to use a wildcard with my existing script.
You can't pass a wildcard directly, but you can query the filesystem with that wildcard and then loop through the results. In both cases here, I'm assuming that you're doing this remotely.
$FlashFiles = invoke-command -computername $comp {Get-ChildItem C:\Windows\System32\Macromed\Flash\Flash*.ocx;};
foreach ($File in $FlashFiles) {
write-output "$($File.Fullname): $(([WMI] "\\$comp\root\CIMV2:CIM_DataFile.Name='$($File.FullName)'").Version)"
}
Or do it with a single pipeline:
invoke-command -computername $comp {Get-ChildItem C:\Windows\System32\Macromed\Flash\Flash*.ocx||foreach-object {write-output "$($_.Fullname): $(([WMI] "\\$comp\root\CIMV2:CIM_DataFile.Name='$($_.FullName)'").Version)"};
You can make the latter even faster by running the WMI query local to the remote computer (you could do it with the first too, but it's not as pretty)
invoke-command -computername $comp {Get-ChildItem C:\Windows\System32\Macromed\Flash\Flash*.ocx|foreach-object {write-output "$($_.Fullname): $(([WMI] "\\.\root\CIMV2:CIM_DataFile.Name='$($_.FullName)'").Version)"}};
The Name property of a CIM_DataFile can't contain wildcards. I don't believe any of them can.
However, you can specify the Drive, Path, and Extension to get a list:
Get-WmiObject -ComputerName $comp -Class CIM_DataFile -Filter "Drive='C:' AND Path='\\Windows\\System32\\Macromed\\Flash\\' AND Extension='ocx'"
The syntax of Path is a bit flaky. You need the trailing backslashes, for example.
You can also pipe to Where-Object for further filtering:
Get-WmiObject -ComputerName $comp -Class CIM_DataFile -Filter "Drive='C:' AND Path='\\Windows\\System32\\Macromed\\Flash\\' AND Extension='ocx'" |`
Where-Object { $_.FileName -like 'Flash*' } |`
ForEach-Object { $_.Name; $_.Version }