Powershell doesn't return key value in hashtable - powershell

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.

Related

Powershell - how to extract a value from Object[]

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"

Why is this powershell -or filter not working?

I have an object saved to a variable with lots of records. I want to filter the object results based on two columns. The same value can appear in either column. The filter works correctly on the HostName column. But doesn't correctly filter on the RecordData column. There is a result there but the filter isn't retrieving it.
Below is an example of the results missing. All 4 rows should appear in the first query. I can't find what I'm doing wrong. Can someone help?
PS H:\> $DNSRecord | where { ($_.HostName -like "*gcod049*") -or ($_.RecordData -like "*gcod049*") }
HostName RecordType Type Timestamp TimeToLive RecordData
-------- ---------- ---- --------- ---------- ----------
gcod049.domainname.com A 1 0 00:20:00 10.26.101.49
gcod049 A 1 0 00:20:00 10.26.101.49
PS H:\> $DNSRecord | where { ($_.hostname -like "*ssrsdev*") }
HostName RecordType Type Timestamp TimeToLive RecordData
-------- ---------- ---- --------- ---------- ----------
ssrsdev.domainname.com CNAME 5 0 01:00:00 gcod049.domainname.com.
ssrsdev CNAME 5 0 01:00:00 gcod049.domainname.com.
**UPDATE: When I use Select or Select-Object with .ToString(), the value is completely different.
PS H:\> $DNSRecord |
Select HostName, RecordData, #{Name="RecordDataString";Expression={$_.RecordData.ToString()}} |
where { ($_.HostName -like "*gcod049*") -or ($_.RecordData -like "*gcod049*") }
HostName RecordData RecordDataString
-------- ---------- ----------------
gcod049.domainname.com DnsServerResourceRecordA DnsServerResourceRecordA
gcod049 DnsServerResourceRecordA DnsServerResourceRecordA
PS H:\> $DNSRecord |
Select-Object -Property HostName, RecordData, #{Name="RecordDataString";Expression={$_.RecordData.ToString()}} |
where { ($_.HostName -like "*ssrsdev*") -or ($_.RecordData -like "*ssrsdev*") }
HostName RecordData RecordDataString
-------- ---------- ----------------
ssrsdev.domainname.com DnsServerResourceRecordCName DnsServerResourceRecordCName
ssrsdev DnsServerResourceRecordCName DnsServerResourceRecordCName
Here the object get-member properties...
$DNSRecord | gm
TypeName: Microsoft.Management.Infrastructure.CimInstance#root/Microsoft/Windows/DNS/DnsServerResourceRecord
Name MemberType Definition
---- ---------- ----------
Clone Method System.Object ICloneable.Clone()
Dispose Method void Dispose(), void IDisposable.Dispose()
Equals Method bool Equals(System.Object obj)
GetCimSessionComputerName Method string GetCimSessionComputerName()
GetCimSessionInstanceId Method guid GetCimSessionInstanceId()
GetHashCode Method int GetHashCode()
GetObjectData Method void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context), void ISerializab...
GetType Method type GetType()
ToString Method string ToString()
DistinguishedName Property string DistinguishedName {get;}
HostName Property string HostName {get;}
PSComputerName Property string PSComputerName {get;}
RecordClass Property string RecordClass {get;}
RecordData Property CimInstance#Instance RecordData {get;set;}
RecordType Property string RecordType {get;}
Timestamp Property CimInstance#DateTime Timestamp {get;}
TimeToLive Property CimInstance#DateTime TimeToLive {get;set;}
Type Property uint16 Type {get;}
This is from the table view for type DnsServerResourceRecord's RecordData property, from the file DnsServerPsProvider.Format.ps1xml in the folder C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\DnsServer. It has a long list of if/else statements for this property. I have the dnsserver module from installing rsat in windows 10, but I don't know how to get get-dnsserverresourcerecord working.
But you can refer to $_.RecordData.HostNameAlias for CNAME types. Ironically, canonical name means "real name".
elseif( $_.RecordType -eq "CNAME")
{
$_.RecordData.HostNameAlias
}
$DNSRecord | where { $_.HostName -match 'gcod049' -or
$_.RecordData.HostNameAlias -match 'gcod049' }
Or sometimes it's easier to pipe to findstr /i to look for something.
Have you tried forcing powershell to evaluate the -or first with parenthesis inside the where { ... } like this:
$DNSRecord | where { (($_.HostName -like "*gcod049*") -or ($_.RecordData -like "*gcod049*")) }

Sort-Object produces different objects when using -Descending

Why does Sort-Object produce different objects when -Descending is used? The NoteProperty members are not the same.
Also, when writing to the console, the Name property does not appear unless -Descending is used. Why is that?
C:>Get-Type | Select-Object -Property BaseType,Name | gm
TypeName: Selected.System.RuntimeType
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
BaseType NoteProperty RuntimeType BaseType=System.Object
Name NoteProperty string Name=Registry
C:>Get-Type | Select-Object -Property BaseType,Name | Sort-Object -Property BaseType,Name | gm
TypeName: Selected.System.RuntimeType
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
BaseType NoteProperty object BaseType=null
Name NoteProperty string Name=_Activator
C:>Get-Type | Select-Object -Property BaseType,Name | Sort-Object -Property BaseType,Name -Descending | gm
TypeName: Selected.System.RuntimeType
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
BaseType NoteProperty RuntimeType BaseType=System.Xml.Xsl.XsltException
Name NoteProperty string Name=XsltCompileException
My apologies for not including information about Get-Type. https://gallery.technet.microsoft.com/scriptcenter/Get-Type-Get-exported-fee19cf7
This is not a cmdlet that is natively part of PoSH. It's either something the OP wrote or got from another source. If from another source than the OP should reach out to that author to ask what is to be expected.
If you do this same things using a built-in cmdlet, say Get-Date, we see all members are the same.
Get-Date |
Select-Object -Property BaseType,Name |
Sort-Object -Property BaseType, Name |
Get-Member
TypeName: Selected.System.DateTime
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
BaseType NoteProperty object BaseType=null
Name NoteProperty object Name=null
Get-Date | Select-Object -Property BaseType,Name |
Sort-Object -Property BaseType, Name -Descending |
Get-Member
TypeName: Selected.System.DateTime
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
BaseType NoteProperty object BaseType=null
Name NoteProperty object Name=null
So, would seem to point specifically to the implementation of this cmdlet the OP is looking for clarity on.
It is possible that your GetType invocation results in an array starting with $null as it has a try-catch block providing an error value of $null. Then, should any parameters be passed to the Get-Type, that null would be filtered out, but you pass none, so null is still present in the output. Then, as the result is piped to Select-Object, only the first object in the pipe is parsed to check if all the columns are present to display, and should the null be the first object, it has no properties thus nothing gets displayed.
To fix, add the | Where-Object {$_ -ne $null} into the Get-Type.ps1 script right before | Where-Object -FilterScript $WhereBlock in the last significant line. This will filter out any nulls produced by previous try-catch block, and you will only get an array of objects that have values.

powershell outputs argument with (#{Name=name}:String)

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.

Store output value in variable and do arithmetic operation in powershell

I am doing powershell script for getting memory value of some process in powershell also i need to divide the value by 1024 in order to convert KB/MB.
For example
PS >>$memory = Get-Process nginx | Select-Object WS | format-wide -Column 1
PS >>$memory
62541824
This value is in bytes. I need to convert this value into KB/MB. so I performed div operation after that.
PS >> $memory = $memory / 1024
Method invocation failed because [System.Object[]] doesn't contain a method named 'op_Division'.
At C:\script\tomcat-mem.ps1:3 char:13
+ $mem= $mem / <<<< 1024
+ CategoryInfo : InvalidOperation: (op_Division:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Any one help me to resolve this error.
Because you're piping to format-wide, you're returning an object rather than a type that can be trivially cast to an int. The following would be more suitable:
$memory = Get-Process nginx | Select-Object -ExpandProperty WS
$memory / 1024
Proof:
$m = get-process explorer | select -expandproperty ws
$m.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
$m = get-process explorer | select ws | format-wide -Column 1
$m.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
You can see that the first version has been implicitly converted to an Int32, therefore it's possible to run a division operation on it.