I'm trying to run the command Get-VMNetworkAdapter on a list of VMs
I'm getting the list with the command:
Get-VM –ComputerName (Get-ClusterNode –Cluster clustername)|select name
and it looks fine, when I'm using
$vmm=Get-VM –ComputerName (Get-ClusterNode –Cluster clustername)|select name
foreach ($item in $vmm)
{Get-VMNetworkAdapter -VMName $item}
it gives me the exception
nvalidArgument: (#{Name=vmname}:String)
like it adds all those symbols..
What is the proper way to lose them?
You need to expand the property. Select doesn't remove the object otherwise:
$vmm = Get-VM –ComputerName (Get-ClusterNode –Cluster clustername) `
| Select-Object -ExpandProperty name
To explain what -ExpandProperty does:
First of all, the drawback of -ExpandProperty is that you can only do it to one property at a time.
Select-Object normally wraps the results in another object so that properties remain properties. If you say $x = Get-ChildItem C:\Windows | Select-Object Name, then you get an object array with one property: Name.
PS C:\> $x = Get-ChildItem C:\Windows | Select-Object Name
PS C:\> $x
Name
----
45235788142C44BE8A4DDDE9A84492E5.TMP
8A809006C25A4A3A9DAB94659BCDB107.TMP
.
.
.
PS C:\> $x[0].Name
45235788142C44BE8A4DDDE9A84492E5.TMP
PS C:\> $x[0].GetType().FullName
System.Management.Automation.PSCustomObject
Notice the header? Name is a property of the object.
Also, the base object with it's type is still kind of there:
PS C:\> $x | Get-Member
TypeName: Selected.System.IO.DirectoryInfo
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Name NoteProperty string Name=45235788142C44BE8A4DDDE9A84492E5.TMP
TypeName: Selected.System.IO.FileInfo
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Name NoteProperty string Name=bfsvc.exe
Normally, that's all great. Especially because we normally want multiple properties of the object.
Sometimes, however, not what we want. Sometimes, we want an array that's the same type as the property we selected. When we use it later we want just that property and nothing else and we want it to be the exact same type as the property and nothing else.
PS C:\> $y = Get-ChildItem C:\Windows | Select-Object -ExpandProperty Name
PS C:\> $y
45235788142C44BE8A4DDDE9A84492E5.TMP
8A809006C25A4A3A9DAB94659BCDB107.TMP
.
.
.
PS C:\> $y[0].Name
PS C:\> $y[0]
45235788142C44BE8A4DDDE9A84492E5.TMP
PS C:\> $y.GetType().FullName
System.Object[]
PS C:\> $y[0].GetType().FullName
System.String
Notice there's no header, and any calls to a Name property fail; there is no Name property anymore.
And, there's nothing left over from the original object:
PS C:\> $y | Get-Member
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
.
.
.
.
Basically, here it's the equivalent of doing this:
$z = Get-ChildItem C:\Windows | ForEach-Object { $_.Name }
Which I think is how you had to do it in PowerShell v1.0 or v2.0... it's been too many years since I've used that to remember right.
Related
I'm trying to figure out how to grab the value associated with AzureWebJobsStorage variable from a Object[] in powershell. Here's the logic so far:
$azAppSettingsOutput = az functionapp config appsettings list --name somename --resource-group myResource --subscription subscription | ConvertFrom-Json
Write-Output $azAppSettingsOutput.GetType().name
Write-Output $azAppSettingsOutput | Get-Member
Write-Output $azAppSettingsOutput.name
This is the output I see:
PS C:\Users\me> .\test.ps1
Object[]
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
name NoteProperty string name=APPINSIGHTS_INSTRUMENTATIONKEY
slotSetting NoteProperty bool slotSetting=False
value NoteProperty string value=asdfasdf-asdf-asdf-asdf-asdf-asdf
APPINSIGHTS_INSTRUMENTATIONKEY
AzureWebJobsStorage
FUNCTIONS_EXTENSION_VERSION
FUNCTIONS_WORKER_RUNTIME
WEBSITE_RUN_FROM_PACKAGE
StorageTableName
PS C:\Users\me\>
I know I can loop through like this:
foreach($setting in $azAppSettingsOutput) {
Write-Output $setting.name
$value = $setting.value
And then I can add an if statement to check if the name matches "AzureWebJobsStorage" but just wondering if there's a simpler way.
Thanks.
Use the Where-Object cmdlet to filter your data:
$azAppSettingsOutput |Where-Object name -eq 'AzureWebJobsStorage'
This will filter out any objects except for those where the name property equals "AzureWebJobsStorage"
PS C:\Users\kris> $hashx=#{}
PS C:\Users\kris> $(Get-CimInstance Win32_Process | Select-Object ProcessId, Name) | ForEach-Object { $hashx[$_.ProcessId]=$_.Name }
PS C:\Users\kris> $hashx
Name Value
---- -----
1292 svchost.exe
6032 StartMenuExperienceHost.exe
428 smss.exe
4736 powershell.exe
2580 svchost.exe
5628 explorer.exe
5164 taskhostw.exe
PS C:\Users\kris> $hashx['5164']
PS C:\Users\kris> $hashx.5164
PS C:\Users\kris> $hashx."5164"
PS C:\Users\kris> $hashx.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Hashtable System.Object
Can anyone explain me what i do wrong? i'm beginner in powershell, and i don't understand why it returns null value by key?
Get-CimInstance Win32_Process returns ProcessId property as type System.UInt32. You will need to cast your key retrieval values to that type or convert the ProcessId values to System.Int32. The reason is by default the shell interprets unquoted or uncast whole numbers as System.Int32 provided the number is less than or equal to [int32]::maxvalue or System.Int64 otherwise.
In your case, you can simply use the syntax below if you don't mind working with Uint32:
$hashx[[uint32]5164]
Personally, I would convert the ProcessId value to System.Int32 (using accelerator [int]) when adding it to the hash table:
Get-CimInstance Win32_Process |
Select-Object ProcessId, Name | ForEach-Object {
$hashx[[int]$_.ProcessId] = $_.Name
}
# Now the keys can be accessed using Int32 numbers
$hashx[5164]
As an aside, you can discover property types yourself with the Get-Member command:
Get-CimInstance win32_Process |
Select -First 1 -Property ProcessId | Get-Member
TypeName: Selected.Microsoft.Management.Infrastructure.CimInstance
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
ProcessId NoteProperty uint32 ProcessId=0
Notice the definition of ProcessId shows type uint32.
I have some code:
$output = [PSCustomObject]#{
Name = $ws.UsedRange.Columns.Item(1).Value2
Department = $ws.UsedRange.Columns.Item(3).Value2
}
$output | GM
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Department NoteProperty System.Object[,] Department=System.Object[,]
Name NoteProperty System.Object[,] Name=System.Object[,]
I need to sort and filter my $output, but I can't. Nothing happens. Probably doing something wrong.
PS> $output
Name Department
---- ----------
{Numbers, 1,2,3,4,5,6,7...} {Sales,IT,Accounting,Developers...}
And my condition:
PS> $output | Sort-Object Department -Descending | Where-Object {$_.Department -eq "Sales"}
Name Department
---- ----------
{Numbers, 1,2,3,4,5,6,7...} {Sales,IT,Accounting,Developers...}
You created a single object with 2 properties, each of which contains all values of its associated column. Since Sort-Object and Where-Object sort and filter lists of objects by their properties there's nothing for these cmdlets to do here.
What you actually want to do is create one object per row.
$output = foreach ($row in $ws.UsedRange.Rows) {
[PSCustomObject]#{
Name = $row.Columns.Item(1).Value2
Department = $row.Columns.Item(3).Value2
}
}
Untested, since I don't have MS Office at hand here.
I need a hint how to get the upgrade code from an installed MSI out of the registry. Actually I'm having the product code, which can be retrieved from HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\.
Now I want to retrieve the upgrade code (based on the product code) from HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes.
My problem is that the product code is used as value-name, which means I've a REG_SZ where the name is the product code guid and the value is empty.
One way to retrieve the product code might be:
PS HKLM:\SOFTW...Codes> Get-ItemProperty * | select -First 1 | gm
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
42F79228D77BA4A4EB5150F3DC090CE3 NoteProperty System.String 42F79228D77BA4A4EB5150F3DC090CE3=
...
How can I check if a PSCustomObject has the property 42F79228D77BA4A4EB5150F3DC090CE3?
Does anybody knows if there is a more elegant way?
This is how you can check. Working on that elegant solution...
$properties = Get-ItemProperty * | select -first 1 | Get-Member | Where-object {$_.MemberType -eq "NoteProperty"}
if("42F79228D77BA4A4EB5150F3DC090CE3" -in $properties.Name){
Write-Output "It's in there!"
}
Edit
This is a bit more elegant. It goes to the HKLM path, and checks for a PSChildName (Registry Key) that is the same as the code.
If found, it will return the Name and property. If not found, the variable $codeExists will be $null.
$code = "42F79228D77BA4A4EB5150F3DC090CE3"
$codeExists = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes" | Where-Object {$_.PSChildName -eq $code}
if($codeExists){
Write-Output "It's in there!"
}
I am setting the variable 'a' like this:
$a=dir -recurse c:\temp
If I now examine this object with 'get-member' like this:
$a|get-member
I get back the type, but also all the methods and other properties like this:
TypeName: System.IO.FileInfo
Name MemberType Definition
---- ---------- ----------
Mode CodeProperty System.String Mode{get=Mode;}
AppendText Method System.IO.StreamWriter AppendText()
CopyTo Method System.IO.FileInfo CopyTo(string destFileName), System.IO.FileInfo CopyTo(string destFileName, bool...
[...]
Which is nice; but sometimes I just want to get hold of the type (I'll look up whatever it does afterwards).
So I tried this:
$a|get-member|select-object -Property typename
The output suprised me initially: because what you get back is the typename for each individual item in the collection- and the types (although clearly related) are not identical:
TypeName
--------
System.IO.DirectoryInfo
System.IO.DirectoryInfo
System.IO.DirectoryInfo
[...]
System.IO.FileInfo
[...]
Then I thought about this; and it sort of made sense - this is a collection of Objects that I'm piping through the Object-Pipeline; but then it made me think:
What was 'Get-Member' actually telling me previously ? When it said the type was 'System.IO.FileInfo' - but actually the collection contains a mixture of object types ?
Whatever it is 'Get-Member' is displaying - how do I get at that exact thing ?
I can almost (sort-of, but actually its just wrong) get what I thought I was initially after with this:
$a|get-member|select-object -Property typename -first 1
But this just peeks at the 'first' object; and in fact gives me a different answer to what 'Get-Member' output for me.
So what is the 'TypeName' that 'Get-Member' is showing- and where is that stored ?
Is the value of 'dir' (Get-ChildItem against a filepath) simply a collection of objects, or is it a parent object (with its own set of 'scalar' properties) and a single property referencing a collection of associated objects ?
That's a lot of questions in one, let's see if we can make some sense of this.
The TypeName that Get-Member displays for each distinct type of object (we'll get back to that), comes from a hidden property that all objects in PowerShell carry, called pstypenames:
PS C:\> $something = 1
PS C:\> $something.pstypenames
System.Int32
System.ValueType
System.Object
PS C:\>
So, pstypenames is an ordered list of all the types in that objects type hierarchy. If we change the value of pstypenames, you'll see that reflected in the output from Get-Member:
PS C:\> $something.pstypenames.Insert(0,"MonoJohnny.CustomTypeName")
PS C:\> Get-Member -InputObject $something
TypeName: MonoJohnny.CustomTypeName
Name MemberType Definition
---- ---------- ----------
...
So, if you want the TypeName for an object, as displayed by Get-Member, you can always do:
$something.pstypenames[0]
As shown above, this value can be manipulated, so if you want the true type of an object at runtime, use the GetType() method:
$something.GetType().FullName
The reason that Get-Member only shows you the entire list of properties for a System.IO.FileInfo object once is that it (rightly so) assumes that all other objects of type System.IO.FileInfo will have the exact same members - no need to duplicate that output.
I you have multiple distinct types in a collection and pipe those to Get-Member, it'll only show you the members for the first object it encounters with a unique type name (remember, the value of pstypenames[0]). This is the case when you pipe Get-ChildItem to Get-Member, since Get-ChildItem on the filesystem provider only returns two different types of objects - FileInfo objects and DirectoryInfo objects.
For builtin types this is totally fine, but with custom objects that you create in PowerShell, this can be quite annoying.
Consider the following example:
PS C:\> $Object1 = New-Object psobject -Property #{ Prop1 = 1 }
PS C:\> $Object2 = New-Object psobject -Property #{ Prop2 = 2 }
Now, $Object1 and $Object2 are clearly 2 different kinds of objects - they have different property names. But what happens when we pipe them to Get-Member in the same pipeline:
PS C:\> $Object1,$Object2 |Get-Member
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Prop1 NoteProperty System.Int32 Prop1=1
Since the underlying type of both objects are System.Management.Automation.PSObject, the value of pstypenames is also the same for both, and Get-Member can't distinguish between the two.
Now, all of a sudden, the ability to manipulate pstypenames without actually fiddling with the type system comes in handy:
PS C:\> $Object1.pstypenames.Insert(0,"ObjectType1")
PS C:\> $Object2.pstypenames.Insert(0,"ObjectType2")
PS C:\> $Object1,$Object2 |Get-Member
TypeName: ObjectType1
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Prop1 NoteProperty System.Int32 Prop1=1
TypeName: ObjectType2
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Prop2 NoteProperty System.Int32 Prop2=2
Sweet!
You can also inject a custom type name during object creation by specifying PSTypeName as a string property:
PS C:\> $Object1 = New-Object psobject -Property #{ Prop1 = 1; PSTypeName = 'ObjectType1' }
PS C:\> $Object2 = New-Object psobject -Property #{ Prop2 = 2; PSTypeName = 'ObjectType2' }