Powershell hash table storing values of "System.Collections.DictionaryEntry" - powershell

When I execute the command:
$var = #{a=1;b=2}
in Powershell (Version 3), $var ends up with a value of {System.Collections.DictionaryEntry, System.Collections.DictionaryEntry}. Why is this happening? How can I store the values I want to store?

That's because your ISE is enumerating the collection to create the variable-treeview, and the objects returned from a HashtableEnumerator which you get from $var.GetEnumerator() are DictionaryEntry-objects.
$var = #{a=1;b=2}
#Collection is a Hashtable
$var | Get-Member -MemberType Properties
TypeName: System.Collections.Hashtable
Name MemberType Definition
---- ---------- ----------
Count Property int Count {get;}
IsFixedSize Property bool IsFixedSize {get;}
IsReadOnly Property bool IsReadOnly {get;}
IsSynchronized Property bool IsSynchronized {get;}
Keys Property System.Collections.ICollection Keys {get;}
SyncRoot Property System.Object SyncRoot {get;}
Values Property System.Collections.ICollection Values {get;}
#Enumerated objects (is that a word?) are DictionaryEntry(-ies)
$var.GetEnumerator() | Get-Member -MemberType Properties
TypeName: System.Collections.DictionaryEntry
Name MemberType Definition
---- ---------- ----------
Name AliasProperty Name = Key
Key Property System.Object Key {get;set;}
Value Property System.Object Value {get;set;}
Your value (1 and 2) is stored in the Value-property in the objects while they Key is the ID you used (a and b).
You only need to care about this when you need to enumerate the hashtable, ex. When you're looping through every item. For normal use this is behind-the-scenes-magic so you can use $var["a"].

Related

How can I find the 'sub properties' of an object in powershell?

I guess I just don't know the proper name of whatever I'm looking for, but this was probably asked a thousand times before.
I started PowerShell not long ago, and I'm struggling to understand where I can find the 'sub properties'(if you can call it those) of an object.
For instance, I was using the VMware PowerCLI, and was tried to figure out how I can find the IP address of a VM.
So for example, I was using the Get-VM command, and when I piped it into get member, I got the following:
PS C:\Users\eitan.rapaport> get-vm "*VRA*" | gm
TypeName: VMware.VimAutomation.ViCore.Impl.V1.VM.UniversalVirtualMachineImpl
Name MemberType Definition
---- ---------- ----------
ConvertToVersion Method T VersionedObjectInterop.ConvertToVersion[T]()
Equals Method bool Equals(System.Object obj)
GetClient Method VMware.VimAutomation.ViCore.Interop.V1.VIAutomation VIObjectCoreInterop.GetClient()
GetConnectionParameters Method VMware.VimAutomation.ViCore.Interop.V1.VM.RemoteConsoleVMParams RemoteConsoleVMIn..
GetHashCode Method int GetHashCode()
GetType Method type GetType()
IsConvertableTo Method bool VersionedObjectInterop.IsConvertableTo(type type)
LockUpdates Method void ExtensionData.LockUpdates()
ObtainExportLease Method VMware.Vim.ManagedObjectReference ObtainExportLease.ObtainExportLease()
ToString Method string ToString()
UnlockUpdates Method void ExtensionData.UnlockUpdates()
CoresPerSocket Property int CoresPerSocket {get;}
CustomFields Property System.Collections.Generic.IDictionary[string,string] CustomFields {get;}
DatastoreIdList Property string[] DatastoreIdList {get;}
DrsAutomationLevel Property System.Nullable[VMware.VimAutomation.ViCore.Types.V1.Cluster.DrsAutomationLevel] ..
ExtensionData Property System.Object ExtensionData {get;}
Folder Property VMware.VimAutomation.ViCore.Types.V1.Inventory.Folder Folder {get;}
FolderId Property string FolderId {get;}
Guest Property VMware.VimAutomation.ViCore.Types.V1.VM.Guest.VMGuest Guest {get;}
GuestId Property string GuestId {get;}
HAIsolationResponse Property System.Nullable[VMware.VimAutomation.ViCore.Types.V1.Cluster.HAIsolationResponse]..
HardwareVersion Property string HardwareVersion {get;}
HARestartPriority Property System.Nullable[VMware.VimAutomation.ViCore.Types.V1.Cluster.HARestartPriority] H..
Id Property string Id {get;}
MemoryGB Property decimal MemoryGB {get;}
MemoryMB Property decimal MemoryMB {get;}
Name Property string Name {get;}
Notes Property string Notes {get;}
NumCpu Property int NumCpu {get;}
PersistentId Property string PersistentId {get;}
PowerState Property VMware.VimAutomation.ViCore.Types.V1.Inventory.PowerState PowerState {get;}
ProvisionedSpaceGB Property decimal ProvisionedSpaceGB {get;}
ResourcePool Property VMware.VimAutomation.ViCore.Types.V1.Inventory.ResourcePool ResourcePool {get;}
ResourcePoolId Property string ResourcePoolId {get;}
Uid Property string Uid {get;}
UsedSpaceGB Property decimal UsedSpaceGB {get;}
VApp Property VMware.VimAutomation.ViCore.Types.V1.Inventory.VApp VApp {get;}
Version Property VMware.VimAutomation.ViCore.Types.V1.VM.VMVersion Version {get;}
VMHost Property VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost VMHost {get;}
VMHostId Property string VMHostId {get;}
VMResourceConfiguration Property VMware.VimAutomation.ViCore.Types.V1.VM.VMResourceConfiguration VMResourceConfigu..
VMSwapfilePolicy Property System.Nullable[VMware.VimAutomation.ViCore.Types.V1.VMSwapfilePolicy] VMSwapfile..
As you can see, nothing mentions anything about IP.
I was researching this online, and have found that I should run the following command:
Get-VM | Select Name, #{N="IP Address";E={#($_.guest.IPAddress[0])}}
Which leads me to my question. How can I find the properties under a certain member/property of a command? How could I research the 'sub properties' of 'guest' in that example?
Okay, found it.
In that instance it would be get-vm "*VRA*" | select -ExpandProperty guest | gm
I've been dealing with this for the last two days, so I figured I'd share my solution:
First, I'm saving the VM object to a variable to make it easy to access without searching every time.
$vm = Get-vm -Name steve
To get all the properties of the VM Object
$vm | Select-Object *
If you just want to get the Name you can access it like this:
$vm.Name
If you want to get the Properties of the Guest Object (which is a property of the VM object):
$vm.Guest | Select-Object *
You can access the Properties of the Guest Object through the VM Object like this:
$vm.Guest.VmName
Want the IP addresses of the guest?
$vmip = $vm.Guest.IPAddress
And, because this took me a while to figure out, if that returns ipv4 and ipv6, and you want to filter out the ipv6:
$vmip = $vm.Guest.IPAddress | ?{$_ -notmatch ':'}
Another method of introspection in PowerShell is to just get the class name and then look it up in the API.
PS C:\> $Files = Get-ChildItem *.* -File
PS C:\> $Files[0].GetType().FullName
System.IO.FileInfo
You can then search for that class with either C# or PowerShell to find the API listing.
You can do a similar thing with the VMWare PowerCLI doc. The "all types" list on the left is a list of all the classes used by the module.

Update DLL RC info from powershell

I'm trying to update/add some RC information in DLL directly from the Powershell.
I have found how I can get it but I didn't found how set some field like SpecialBuild or PrivateBuild.
PS C:\> (gi .\mydll.dll).VersionInfo | fl
OriginalFilename : mydll.dll
FileDescription : mydll.dll
...............................
PrivateBuild : 32572
SpecialBuild : NOT_HOTFIX
FileVersionRaw : 17.3.0.12013
ProductVersionRaw : 17.3.0.12013
PS C:\> (gi .\mydll.dll).VersionInfo | gm
TypeName : System.Diagnostics.FileVersionInfo
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Comments Property string Comments {get;}
.................................................
PrivateBuild Property string PrivateBuild {get;}
ProductBuildPart Property int ProductBuildPart {get;}
ProductMajorPart Property int ProductMajorPart {get;}
ProductMinorPart Property int ProductMinorPart {get;}
ProductName Property string ProductName {get;}
ProductPrivatePart Property int ProductPrivatePart {get;}
ProductVersion Property string ProductVersion {get;}
SpecialBuild Property string SpecialBuild {get;}
FileVersionRaw ScriptProperty System.Object FileVersionRaw {get=New-Object System.Version -ArgumentList #(...
ProductVersionRaw ScriptProperty System.Object ProductVersionRaw {get=New-Object System.Version -ArgumentList #(...
As you can see, properties are get only here.
So do you know how I can update theses fields ?
Thanks in advance for your help ;)
Update fields possible with a tool like Resource Hacker, or verpatch, but it's not recommended. If the dll is yours, it's much easier to update it before compilation.
One of the downsides of patching compiled dll is that if it's signed, the signature will no longer be valid. Also, sometimes patching may result in a corrupted dll.
Is there any particular reason why you want to change it after compilation?

FilterScript in PowerShell

I'm using Windows Server 2012 R2 with PowerShell v5 and I've stumbled on some PowerShell behaviour I don't understand.
The following line working correctly and returning results as expected:
Get-WindowsFeature | where -Property "InstallState" -eq "Installed"
This working correctly and returning results as previous:
Get-WindowsFeature | where {$_.Installed}
The following is NOT working, even though it should:
Get-WindowsFeature | where {$_.Available}
But this one is working:
Get-WindowsFeature | where -Property "InstallState" -eq "Available"
I've seen the same behaviour with PowerShell v3 on Windows 7 as well.
Please explain it to me.
Get-WindowsFeature | Get-Member -MemberType Property
TypeName: Microsoft.Windows.ServerManager.Commands.Feature
Name MemberType Definition
---- ---------- ----------
AdditionalInfo Property hashtable AdditionalInfo {get;}
BestPracticesModelId Property string BestPracticesModelId {get;}
DependsOn Property string[] DependsOn {get;}
Depth Property int Depth {get;}
Description Property string Description {get;}
DisplayName Property string DisplayName {get;}
EventQuery Property string EventQuery {get;}
FeatureType Property string FeatureType {get;}
Installed Property bool Installed {get;}
InstallState Property Microsoft.Windows.ServerManager.Commands.InstallState InstallState {get;}
Name Property string Name {get;}
Notification Property Microsoft.Windows.ServerManager.ServerComponentManager.Internal.Notification[] Notification {g...
Parent Property string Parent {get;}
Path Property string Path {get;}
PostConfigurationNeeded Property bool PostConfigurationNeeded {get;}
ServerComponentDescriptor Property psobject ServerComponentDescriptor {get;}
SubFeatures Property string[] SubFeatures {get;}
SystemService Property string[] SystemService {get;}
There is no "Available" property.

powershell missing member methods in array

I have (yet another) powershell query. I have an array in powershell which i need to use the remove() and split commands on.
Normally you set an array (or variable) and the above methods exist. On the below $csv2 array both methods are missing, i have checked using the get-member cmd.
How can i go about using remove to get rid of lines with nan. Also how do i split the columns into two different variables. at the moment each element of the array displays one line, for each line i need to convert it into two variables, one for each column.
timestamp Utilization
--------- -----------
1276505880 2.0763250000e+00
1276505890 1.7487730000e+00
1276505900 1.6906890000e+00
1276505910 1.7972880000e+00
1276505920 1.8141900000e+00
1276505930 nan
1276505940 nan
1276505950 0.0000000000e+00
$SystemStats = (Get-F5.iControl).SystemStatistics
$report = "c:\snmp\data" + $gObj + ".csv"
### Allocate a new Query Object and add the inputs needed
$Query = New-Object -TypeName iControl.SystemStatisticsPerformanceStatisticQuery
$Query.object_name = $i
$Query.start_time = $startTime
$Query.end_time = 0
$Query.interval = $interval
$Query.maximum_rows = 0
### Make method call passing in an array of size one with the specified query
$ReportData = $SystemStats.get_performance_graph_csv_statistics( (,$Query) )
### Allocate a new encoder and turn the byte array into a string
$ASCII = New-Object -TypeName System.Text.ASCIIEncoding
$csvdata = $ASCII.GetString($ReportData[0].statistic_data)
$csv2 = convertFrom-CSV $csvdata
$csv2
There is no Remove or Split method on .NET's Array type, or added by the PowerShell wrapper around an Array instance. This is quite easy to show:
PS[64bit] E:\> $a = 1,2,3,4,5
PS[64bit] E:\> $a.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
PS[64bit] E:\> Get-Member -InputObject $a
TypeName: System.Object[]
Name MemberType Definition
---- ---------- ----------
Count AliasProperty Count = Length
Address Method System.Object&, mscorlib, Version=2.0.0.0, Culture=neutral, PublicK...
Clone Method System.Object Clone()
CopyTo Method System.Void CopyTo(array array, int index), System.Void CopyTo(arra...
Equals Method bool Equals(System.Object obj)
Get Method System.Object Get(int )
GetEnumerator Method System.Collections.IEnumerator GetEnumerator()
GetHashCode Method int GetHashCode()
GetLength Method int GetLength(int dimension)
GetLongLength Method long GetLongLength(int dimension)
GetLowerBound Method int GetLowerBound(int dimension)
GetType Method type GetType()
GetUpperBound Method int GetUpperBound(int dimension)
GetValue Method System.Object GetValue(Params int[] indices), System.Object GetValu...
Initialize Method System.Void Initialize()
Set Method System.Void Set(int , System.Object )
SetValue Method System.Void SetValue(System.Object value, int index), System.Void S...
ToString Method string ToString()
IsFixedSize Property System.Boolean IsFixedSize {get;}
IsReadOnly Property System.Boolean IsReadOnly {get;}
IsSynchronized Property System.Boolean IsSynchronized {get;}
Length Property System.Int32 Length {get;}
LongLength Property System.Int64 LongLength {get;}
Rank Property System.Int32 Rank {get;}
Arrays in .NET, and PowerShell, are fixed size. To remove an element you need to copy all but the element to be removed, in PSH this can be done with Where-Object:
$newArray = $oldArray | Where-Object {some-condition-on-$_}
Similarly Select-Object with -First and -Skip parameters can be used to select elements before or after (resp3ectively) an index.
NB System.Array does implement System.Collections.ILst but the explicit implementation of IList.Remove just throws a NotImplementedException.

How do you add a property to a function object on the PowerShell PSDrive "function:"?

I've attempted using the $function:foo value and get-item function:foo. All attempts succeed in modifying the temporary function object, but the additional property is missing when reassigned to the stored function (either through $function:foo = ... or set-item function:foo ...).
Here are the results of my attempts (all fail):
Setup
$=>function foo { "foo" }
$=>$f = $function:foo
$=>$f = $f | add-member noteproperty bar BARvalue -pass
$=>$f | gm b*
TypeName: System.Management.Automation.ScriptBlock
Name MemberType Definition
---- ---------- ----------
bar NoteProperty System.String bar=BARvalue
#1
$=>set-item function:f $f -force
$=>$function:foo | gm b*
>
#2
$=>$function:f = $f
$=>$function:foo | gm b*
>
#3
$=>$f = get-item function:foo
$=>$f | gm
TypeName: System.Management.Automation.FunctionInfo
Name MemberType Definition
---- ---------- ----------
Equals Method System.Boolean Equals(Object obj)
GetHashCode Method System.Int32 GetHashCode()
GetType Method System.Type GetType()
ToString Method System.String ToString()
PSDrive NoteProperty System.Management.Automation.PSDriveInfo PSDrive=Function
PSIsContainer NoteProperty System.Boolean PSIsContainer=False
PSPath NoteProperty System.String PSPath=Microsoft.PowerShell.Core\Function::foo
PSProvider NoteProperty System.Management.Automation.ProviderInfo PSProvider=Microsoft....
CmdletBinding Property System.Boolean CmdletBinding {get;}
CommandType Property System.Management.Automation.CommandTypes CommandType {get;}
DefaultParameterSet Property System.String DefaultParameterSet {get;}
Definition Property System.String Definition {get;}
Description Property System.String Description {get;set;}
Module Property System.Management.Automation.PSModuleInfo Module {get;}
ModuleName Property System.String ModuleName {get;}
Name Property System.String Name {get;}
Options Property System.Management.Automation.ScopedItemOptions Options {get;set;}
Parameters Property System.Collections.Generic.Dictionary`2[[System.String, mscorli...
ParameterSets Property System.Collections.ObjectModel.ReadOnlyCollection`1[[System.Man...
ScriptBlock Property System.Management.Automation.ScriptBlock ScriptBlock {get;}
Visibility Property System.Management.Automation.SessionStateEntryVisibility Visibi...
$=>$f = $f | add-member noteproperty bar barValue -pass
$=>$f | gm b*
TypeName: System.Management.Automation.FunctionInfo
Name MemberType Definition
---- ---------- ----------
bar NoteProperty System.String bar=barValue
$=>set-item function:foo $f
$=>$function:foo | gm b*
>
Not sure what I'm doing wrong. It seems like the properties are being stripped out when reassigned. Is that correct? the defined behavior? I haven't seen any documentation saying that FunctionInfo objects or ScriptBlocks are treated unusually. Is this some esoteric corner of the language?
My first thought is when you are attaching this property to an object, you are attaching it to a specific instance of that object. When your variable losses the reference to that object, any knowledge of that new property is lost.
My guess is the next time you get that item, you are creating a new FunctionInfo object with foo's properties (as stored in the function provider).
When you call Get-Item or Get-ChildItem, it returns object references to the .NET types that represent the underlying items. Those items do not exist in memory indefinitely (imagine a FileInfo object for every file on every local drive and every mapped drive living in memory.. ouch). Since PowerShell is creating a new instance every time you call Get-Item, you are getting the basic FunctionInfo object.
If you want to add a property to all items of a particular type, you can with PowerShell's extensible type system. You can create a custom .ps1xml file and load that into a PowerShell session that can add a property to every instance of a type. Some great examples on the PowerShell Team Blog are here -> Hate Add-Member and Leveraging the PowerShell Type Extensions to Get Documentation.
EDIT (addressing comment): I understand what you are trying to do, but the failure is due to the new property being "grafted" on to a PSObject wrapper that allows for the on-the-fly addition of properties. The new property is never really part of that FunctionInfo object that you are retrieving from the PSDrive.
In addition, the function provider (the function: psdrive), does not have a mechanism for storing that additional property. It knows and works with FunctionInfo objects. When you ask for an item from the Function: PSDrive, the Function provider returns a FunctionInfo object. When you save a function to the Function: PSDrive, the provider can only store values for properties it knows how to handle. One could write a custom provider that would handle new properties from the PSObject wrapper, but that is not part of the default functionality in this provider.
EDIT #2: Ok, this has been bugging me. I blogged about a workaround.