PSObject sorting - sorting

I am trying to format a PSObject related to a question a few days back. My object looks like this:
New-Object PSObject -Property #{
"Version"= $winVersion.Caption
"Processor Name" = $processorInfo.Name
"Processor Manufacturer" = $processorInfo.Manufacturer
"Processor Max Clock Speed" = $processorInfo.MaxClockSpeed
} |format-list
The above gives the following output:
Processor Manufacturer : GenuineIntel
Processor Max Clock Speed : 2201
Version : Microsoft Windows 8 Pro
Processor Name : Intel(R) Core(TM) i7-2670QM CPU # 2.20GHz
However, this:
New-Object PSObject -Property #{
"Windows Version"= $winVersion.Caption
"Processor Name" = $processorInfo.Name
"Processor Manufacturer" = $processorInfo.Manufacturer
"Processor Max Clock Speed" = $processorInfo.MaxClockSpeed
} |format-list
gives the following output:
Processor Manufacturer : GenuineIntel
Processor Max Clock Speed : 2201
Processor Name : Intel(R) Core(TM) i7-2670QM CPU # 2.20GHz
Windows Version : Microsoft Windows 8 Pro
Not really a big deal, but I wonder why the formatting changes? It does not seem to be alphabetical in any way. Furthermore, I tried sorting the object with Sort-Object (from A-Z) but to no avail. Is it String related?

The order of a hashtable can't be predicted (in PowerShell V3.0 you can user the [ordered] accelerator to make an hashtable ordered), but in V2.0 you need to build your custom object like this to preserve properties order:
$o = New-Object PSObject
$o | add-member Noteproperty "Version" $winVersion.Caption
$o | add-member Noteproperty "Processor Name" $processorInfo.Name
$o | add-member Noteproperty "Processor Manufacturer" $processorInfo.Manufacturer
$o | add-member Noteproperty "Processor Max Clock Speed" $processorInfo.MaxClockSpeed
$o | format-list

You can still add a custom method to your object to provide the format you want, for example:
$test = New-Object PSObject -Property #{
"Processor Manufacturer"="GenuineIntel"
"Processor Max Clock Speed" = "2201"
"Version"="Microsoft Windows 8 Pro"
}
Add-Member -MemberType ScriptMethod -Name "show" -Value {echo $this.version;echo $this."processor manufacturer";echo $this."Processor Max Clock Speed"} -inputObject $test
$test.show()

Related

Is it possible to have powershell curl output only the content part? [duplicate]

In PowerShell, how do you get an object's property value by specifying its name (a string)? I want something like the following:
$obj = get-something
# View the object's members:
$obj | gm
# I could retrieve a property by doing so:
write-host $obj.SomeProp
# But for many purposes, I would really want to:
write-host $obj | Get-PropertyByName "SomeProp"
Is there something similar to "Get-PropertyByName" in PowerShell?
Sure
write-host ($obj | Select -ExpandProperty "SomeProp")
Or for that matter:
$obj."SomeProp"
Expanding upon #aquinas:
Get-something | select -ExpandProperty PropertyName
or
Get-something | select -expand PropertyName
or
Get-something | select -exp PropertyName
I made these suggestions for those that might just be looking for a single-line command to obtain some piece of information and wanted to include a real-world example.
In managing Office 365 via PowerShell, here was an example I used to obtain all of the users/groups that had been added to the "BookInPolicy" list:
Get-CalendarProcessing conferenceroom#example.com | Select -expand BookInPolicy
Just using "Select BookInPolicy" was cutting off several members, so thank you for this information!
You can get a property by name using the Select-Object cmdlet and specifying the property name(s) that you're interested in. Note that this doesn't simply return the raw value for that property; instead you get something that still behaves like an object.
[PS]> $property = (Get-Process)[0] | Select-Object -Property Name
[PS]> $property
Name
----
armsvc
[PS]> $property.GetType().FullName
System.Management.Automation.PSCustomObject
In order to use the value for that property, you will still need to identify which property you are after, even if there is only one property:
[PS]> $property.Name
armsvc
[PS]> $property -eq "armsvc"
False
[PS]> $property.Name -eq "armsvc"
True
[PS]> $property.Name.GetType().FullName
System.String
As per other answers here, if you want to use a single property within a string, you need to evaluate the expression (put brackets around it) and prefix with a dollar sign ($) to declare the expression dynamically as a variable to be inserted into the string:
[PS]> "The first process in the list is: $($property.Name)"
The first process in the list is: armsvc
Quite correctly, others have answered this question by recommending the -ExpandProperty parameter for the Select-Object cmdlet. This bypasses some of the headache by returning the value of the property specified, but you will want to use different approaches in different scenarios.
-ExpandProperty <String>
Specifies a property to select, and indicates that an attempt should
be made to expand that property
https://technet.microsoft.com/en-us/library/hh849895.aspx
[PS]> (Get-Process)[0] | Select-Object -ExpandProperty Name
armsvc
powershell variables
Try this :
$obj = #{
SomeProp = "Hello"
}
Write-Host "Property Value is $($obj."SomeProp")"
Here is an alternative way to get an object's property value:
write-host $(get-something).SomeProp
$com1 = new-object PSobject #Task1
$com2 = new-object PSobject #Task1
$com3 = new-object PSobject #Task1
$com1 | add-member noteproperty -name user -value jindpal #Task2
$com1 | add-member noteproperty -name code -value IT01 #Task2
$com1 | add-member scriptmethod ver {[system.Environment]::oSVersion.Version} #Task3
$com2 | add-member noteproperty -name user -value singh #Task2
$com2 | add-member noteproperty -name code -value IT02 #Task2
$com2 | add-member scriptmethod ver {[system.Environment]::oSVersion.Version} #Task3
$com3 | add-member noteproperty -name user -value dhanoa #Task2
$com3 | add-member noteproperty -name code -value IT03 #Task2
$com3 | add-member scriptmethod ver {[system.Environment]::oSVersion.Version} #Task3
$arr += $com1, $com2, $com3 #Task4
write-host "windows version of computer1 is: "$com1.ver() #Task3
write-host "user name of computer1 is: "$com1.user #Task6
write-host "code of computer1 is: "$com1,code #Task5
write-host "windows version of computer2 is: "$com2.ver() #Task3
write-host "user name of computer2 is: "$com2.user #Task6
write-host "windows version of computer3 is: "$com3.ver() #Task3
write-host "user name of computer3 is: "$com1.user #Task6
write-host "code of computer3 is: "$com3,code #Task5
read-host

How can i get an overview of UWP registered URIs and aliases?

I want to use the "URI style launching" for UWP apps (for example: MS ToDo) from a win32 Application, command line or UWP apps.
UWP has a specific shell URI-Schemas available to launch them.
For example you can press win+R and enter ms-todo: and MS ToDo will start.
Respectively open cmd and enter start ms-todo: and MS ToDo will start.
How can i get a list with all local available URIs?
tl;dr
They are listed in Settings > Apps > Default Apps > Choose default apps by protocol
Getting details for all apps with Powershell
$result = [System.Collections.ArrayList]#();
foreach ($appx in Get-AppxPackage)
{
$location = $appx.InstallLocation
$manifest = "$location\AppxManifest.xml"
if($location -ne $null -and (Test-Path $manifest -PathType Leaf))
{
[xml]$xml = Get-Content $manifest
$ns = new-object Xml.XmlNamespaceManager $xml.NameTable
$ns.AddNamespace("main", "http://schemas.microsoft.com/appx/manifest/foundation/windows10")
$ns.AddNamespace("uap", "http://schemas.microsoft.com/appx/manifest/uap/windows10")
$ns.AddNamespace("uap3", "http://schemas.microsoft.com/appx/manifest/uap/windows10/3")
$ns.AddNamespace("uap5", "http://schemas.microsoft.com/appx/manifest/uap/windows10/5")
$uriElements = $xml.SelectNodes("//uap:Extension[#Category = 'windows.protocol']/uap:Protocol/#Name", $ns)
$uris = $uriElements | select -ExpandProperty '#text'
$appIds = $xml.SelectNodes("//main:Application/#Id", $ns) | select -ExpandProperty '#text'
if ($appIds.Count -eq 0) {
continue;
}
$aliases = $xml.SelectNodes("//main:Extensions/uap5:Extension[#Category = 'windows.appExecutionAlias']/uap5:AppExecutionAlias/uap5:ExecutionAlias/#Alias", $ns) | select -ExpandProperty '#text'
#$result[$appx.Name] = $uris
$tmp = New-Object -TypeName PSObject
$tmp | Add-Member –MemberType NoteProperty –Name Name –Value "$($appx.Name)"
$tmp | Add-Member –MemberType NoteProperty –Name URIs –Value ($uris -join "`n")
$tmp | Add-Member –MemberType NoteProperty –Name Aliases –Value ($aliases -join "`n")
$tmp | Add-Member –MemberType NoteProperty –Name Package –Value "$($appx.PackageFamilyName)"
$tmp | Add-Member –MemberType NoteProperty –Name AppIds –Value ($appIds -join "`n")
$tmp | Add-Member –MemberType NoteProperty –Name Folder –Value $appx.InstallLocation
$null = $result.Add($tmp)
}
}
$result | Sort-Object -Property Name | Out-GridView
Result
The manual way for a single App:
I'm only aware of a slighly cumbersome manual way have to do do for every single app.
In this case "Microsoft.Todos"
Execute
Get-AppxPackage -Name Microsoft.Todos
Read Property "InstallationLocation"
C:\Program Files\WindowsApps\Microsoft.Todos_2.27.32662.0_x64__8wekyb3d8bbwe
Open AppxManifest.xml
C:\Program Files\WindowsApps\Microsoft.Todos_2.27.32662.0_x64__8wekyb3d8bbwe\AppxManifest.xml
Look for protocol entries:
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="ms-to-do">
<uap:DisplayName>ms-resource:app_name_ms_todo</uap:DisplayName>
</uap:Protocol>
</uap:Extension>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="ms-todo">
<uap:DisplayName>ms-resource:app_name_ms_todo</uap:DisplayName>
</uap:Protocol>
</uap:Extension>
As you can see "ms-to-do" and "ms-todo" are the associated URIs.

Errors with Get-ChildItem trying to scan network for all .mdb files

I am very new to using powershell and trying to execute a script that scans the entire network for all .mdb and .accdb files, for example, and generates a spreadsheet containing the data on them that I process elsewhere.
I put the sensistive data that I didnt want to provide in ()s
Here is my code:
#single threaded
import-module activedirectory
$arr = #()
$computers = Get-ADComputer -filter 'name -like "(employee computers)*"' | Select -Exp Name
foreach ($computer in $computers) {
Write-Host "Scanning" $computer "..."
gci \\$computer\c$\* -Include *.mdb, *.accdb -Recurse | ? {$_.PSIsContainer -eq $False} | % {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty Directory $_.DirectoryName
$obj | Add-Member NoteProperty Name $_.Name
$obj | Add-Member NoteProperty FullName $_.FullName
$obj | Add-Member NoteProperty Size $_.Length
$obj | Add-Member NoteProperty CreationTime $_.CreationTime
$obj | Add-Member NoteProperty LastWriteTime $_.LastWriteTime
$arr += $obj
Write-Host "Scanning..."
}}
$arr | Export-CSV -notypeinformation '(path)\EmployeeDBs.csv'
This has been working pretty well so far, but for certain machines and/or directories on some machines I am receiving the following error messages:
Get-ChildItem : The specified network name is no longer available
[Get-ChildItem], IOException
+ FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand
and
Get-ChildItem : An object at the specified path \\(employee computer)\c$ does not exist.
[Get-ChildItem], IOException
+ FullyQualifiedErrorId : ItemDoesNotExist,Microsoft.PowerShell.Commands.GetChildItemCommand
I have been googling around but havent had much luck in understanding these error messages. Would somebody be able to explain what the issues are?
I am thinking (hoping) that they are permissions problems because I am testing the scripts on my personal machine before I run them from the admin machine
Any insight is greatly appreciated!
EDIT: below is my edited code for asynchronous execution:
import-module activedirectory
$computers = Get-ADComputer -filter 'name -like "wa-150*"' | Select -Exp Name
Get-job | Remove-Job -Force
Remove-Item -path (path)\EmployeeDBs.txt
foreach ($computer in $computers) {
$scriptBlock = {gci \\$($args[0])\c$\Users\z*\Desktop\* -Include *.mdb, *.accdb -Recurse | ? {$_.PSIsContainer -eq $False} | % {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty Directory $_.DirectoryName
$obj | Add-Member NoteProperty Name $_.Name
$obj | Add-Member NoteProperty Size $_.Length
$obj | Add-Member NoteProperty CreationTime $_.CreationTime
$obj | Add-Member NoteProperty LastWriteTime $_.LastWriteTime
Write-Output -InputObject $obj
}
}
while ((Get-Job -State Running).Count -ge 20) {
Write-Host "Full - Waiting ... "
Start-Sleep -Seconds 5;
}
Start-Job -name $computer -ScriptBlock $scriptBlock -ArgumentList $computer
#Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $computer
}
Get-Job | Wait-Job | Receive-Job | Out-File -Append -FilePath '(path)\EmployeeDBs.txt'
Write-Host "Done"

Problems getting CPU Average in PowerShell from a server with two CPU's

I am trying to run the following on a remote machine with two physical CPU's and so it doesn't return any value.
Get-WmiObject Win32_Processor -computername X |
Measure-Object -property LoadPercentage -Average |
Select-Object -ExpandProperty Average
(Source: How to Determine the Average CPU Percentage using a Powershell Command and Output the CPU Percentage as a Numeric Percentage with no Labels)
This is the output when I run Get-WmiObject Win32_Processor -computername X:
Caption : Intel64 Family 6 Model 63 Stepping 4
DeviceID : CPU0
Manufacturer : GenuineIntel
MaxClockSpeed : 2195
Name : Intel(R) Xeon(R) CPU E7-4850 v3 # 2.20GHz
SocketDesignation : None
Caption : Intel64 Family 6 Model 63 Stepping 4
DeviceID : CPU1
Manufacturer : GenuineIntel
MaxClockSpeed : 2195
Name : Intel(R) Xeon(R) CPU E7-4850 v3 # 2.20GHz
SocketDesignation : None
And output when I run Get-WmiObject Win32_Processor -computername X | Measure-Object -property LoadPercentage -Average | Select-Object -ExpandProperty Average:
0
This is my first post on here. As the code I'm trying to use is from Stack Overflow, I decided to post here rather than on Server Fault.
EDIT: I am looking for a way to get average cpu load from all available cpu's on the remote machines being queried and so the script will ideally work no matter how many CPU's the remote machine has.
The solution for me was using invoke-command instead:
Invoke-Command -ComputerName X -Credential $creds {
Get-WmiObject Win32_Processor |
Measure-Object -property LoadPercentage -Average |
Select-Object -ExpandProperty Average
}
..though I actually ended up using Get-Counter to get cpu usage (which also has the samples and intervals parameters built in):
Invoke-Command -ComputerName X -Credential $creds {
((Get-Counter -counter '\Processor(_Total)\% Processor Time').CounterSamples).CookedValue
}

Get used CPU%, Disk% & memory % counters

I am trying to get these 3 counters thru PowerShell, could you help? Something like below:
Hostname1 : CPU% : 75%
Hostname1 : MEM% : 55%
Hostname1 : Disk1 % : 15%
Hostname1 : Disk2 % : 10%
Hostname1 : Disk3 % : 13%
Hostname1 : Disk4 % : 12%
Hostname2 : CPU% : 75%
Hostname2 : MEM% : 55%
Hostname2 : Disk1 % : 11%
Hostname2 : Disk2 % : 15%
Hostname2 : Disk3 % : 15%
Note: I could not find a counter for %used/memory so I am not going thru performance counters.
Likely your easiest method is using WMI. The below is a script I wrote up for you to demonstrate the ability.
You would need to work on the formatting and I left out disk stats - so some work required.
# Lets import our list of computers
$computers = get-Content .\computer-list.txt
# computer-list.txt is your hostnames each on a new line
# Lets create our variables
$HostInfo = #()
# Lets loop through our computer list from computers
foreach ($computer in $computers) {
# Lets get our stats
# Lets create a re-usable WMI method for CPU stats
$ProcessorStats = Get-WmiObject win32_processor -computer $computer
$ComputerCpu = $ProcessorStats.LoadPercentage
# Lets create a re-usable WMI method for memory stats
$OperatingSystem = Get-WmiObject win32_OperatingSystem -computer $computer
# Lets grab the free memory
$FreeMemory = $OperatingSystem.FreePhysicalMemory
# Lets grab the total memory
$TotalMemory = $OperatingSystem.TotalVisibleMemorySize
# Lets do some math for percent
$MemoryUsed = ($FreeMemory/ $TotalMemory) * 100
$PercentMemoryUsed = "{0:N2}" -f $MemoryUsed
# Lets throw them into an object for outputting
$objHostInfo = New-Object System.Object
$objHostInfo | Add-Member -MemberType NoteProperty -Name Name -Value $computer
$objHostInfo | Add-Member -MemberType NoteProperty -Name CPULoadPercent -Value $ComputerCpu
$objHostInfo | Add-Member -MemberType NoteProperty -Name MemoryUsedPercent -Value $PercentMemoryUsed
# Lets dump our info into an array
$HostInfo += $objHostInfo
}
# Lets output to the console
$HostInfo
This works:
$system = Get-WmiObject win32_OperatingSystem
$totalPhysicalMem = $system.TotalVisibleMemorySize
$freePhysicalMem = $system.FreePhysicalMemory
$usedPhysicalMem = $totalPhysicalMem - $freePhysicalMem
$usedPhysicalMemPct = [math]::Round(($usedPhysicalMem / $totalPhysicalMem) * 100,1)
$driveLetters = Get-WmiObject Win32_Volume | select DriveLetter
foreach ($driveLetter in $driveLetters)
{
$drive = Get-WmiObject Win32_Volume | where {$_.DriveLetter -eq $driveLetter.DriveLetter}
$usedDiskSpace = $drive.Capacity - $drive.FreeSpace
$usedDiskSpacePct = [math]::Round(($usedDiskSpace / $drive.Capacity) * 100,1)
}

Resources