Powershell: Sort percent number - powershell

Got a small problem with sorting percent numbers.
I just wrote a simple script to test:
function get-percent{
$obj1 = New-Object PSObject
$percentUsed1="{0:P0}" -f (100/100)
$obj1 | Add-Member -membertype NoteProperty -Name 'Percent' -Value $percentUsed1
$obj2 = New-Object PSObject
$percentUsed2="{0:P0}" -f (99/100)
$obj2 | Add-Member -membertype NoteProperty -Name 'Percent' -Value $percentUsed2
$obj3 = New-Object PSObject
$percentUsed3="{0:P0}" -f (8/100)
$obj3 | Add-Member -membertype NoteProperty -Name 'Percent' -Value $percentUsed3
$obj4 = New-Object PSObject
$percentUsed4="{0:P0}" -f (70/100)
$obj4 | Add-Member -membertype NoteProperty -Name 'Percent' -Value $percentUsed4
write $obj1
write $obj2
write $obj3
write $obj4
}
get-percent | sort-object 'Percent' -descending |ft
Here is what i get:
Percent
99%
8%
70%
100 %
Normally, I should have: 100 % , 99%, 70%...
Any ideas guys ?
Thx.

Try:
get-percent | sort-object { [INT]($_.percent -replace '%') } -descending
In this way sort-object can sort by [INT] and not by [String] as before.

Related

Summing an array of Objects in Powershell

I have an array of objects that are holding integer values.
$row = new-Object PSObject # create a new object to hold its data
$row | Add-Member -Name "sheet_number" -MemberType NoteProperty -Value 1
$row | Add-Member -Name "frame_number" -MemberType NoteProperty -Value 2
$row | Add-Member -Name "sheet_height" -MemberType NoteProperty -Value 1200
$row | Add-Member -Name "frame_height" -MemberType NoteProperty -Value 1200
$row | Add-Member -Name "frame_width" -MemberType NoteProperty -Value 3300
$row | Add-Member -Name "orientation" -MemberType NoteProperty -Value 0
$frames += $row
this is in a for loop intended to iterate through several times. But the sheet_number property should only have a couple values. what I need is to sum up the values in frame_width where the sheet_number is the same.
Pseudo Code:
sheet_width = sum of frame_width where sheet number = 1
Use a combination of Where-Object, ForEach-Object, and Measure-Object:
$sum = (
$frames |
Where-Object sheet_number -eq 1 |
ForEach-Object frame_width |
Measure-Object -Sum
).Sum
To do it for all sheet numbers, additionally use Group-Object:
$arrayOfSums =
$frames |
Group-Object sheet_number |
ForEach-Object {
($_.Group.frame_width | Measure-Object -Sum).Sum
}

Displaying the content of Subnet in a form of table in PowerShell

I am trying to display the PowerShell data in the following format.
Vnet Subnet
---- ------
AZRWUSVNET1 default
Corpvnet MgmtSubnet
Corpvnet Devsubnet
Corpvnet QASubnet
Corpvnet ProdSubnet
I have tried this below script
$a = Get-AzVirtualNetwork
$b = $a | Select-Object Name,Subnets
$ResourceList = #()
foreach ($item in $b)
{
$vnetname = ($b | Where-Object -Property Name -EQ ($item.Name))
$subnet = $vnetname.Subnets.Name
$vnetname = $item.Name
$VMObject = New-Object PSObject
$VMObject | Add-Member -MemberType NoteProperty -Name "Vnet" -Value $vnetname
$VMObject | Add-Member -MemberType NoteProperty -Name "Subnet" -Value $subnet
$ResourceList += $VMObject
}
$ResourceList
But I am getting the data in the following format
Vnet Subnet
---- ------
AZRWUSVNET1 default
Corpvnet {MgmtSubnet, Devsubnet, QASubnet, ProdSubnet…}
Can anyone please help here.
You can simplify your code significantly by using Select-Object to extract and annotate the Subnets values:
Get-AzVirtualNetwork |Select-Object -Property #{Name='Vnet';E=
Name'} -ExpandProperty Subnets |Select-Object Vnet,#{Name='Subnet';Expression={$_.Name}}
If you insist on using loops, you'll need a nested loop (since the individual subnets are nested inside each vnet):
$vNets = Get-AzVirtualNetwork | Select-Object Name, Subnets
$ResourceList=#()
foreach($vNet in $vNets){
foreach($subnet in $vNet.Subnets){
$VMObject = New-Object PSObject
$VMObject | Add-Member -MemberType NoteProperty -Name "Vnet" -Value $vNet.Name
$VMObject | Add-Member -MemberType NoteProperty -Name "Subnet" -Value $subnet.Name
$ResourceList += $VMObject
}
}
$ResourceList
I just tried this and it resolved
$vNets = Get-AzVirtualNetwork | Select-Object Name, Subnets
$ResourceList=#()
foreach($vNet in $vNets){
foreach($subnet in $vNet.Subnets){
#$subnatename=$vnet.subnets.Name
$VMObject = New-Object PSObject
$VMObject | Add-Member -MemberType NoteProperty -Name "Vnet" -Value $vNet.Name
$VMObject | Add-Member -MemberType NoteProperty -Name "Subnet" -Value $subnet.Name
$ResourceList += $VMObject
}
}
$ResourceList
Only change is $subnet.Name
Thanks

Selecting from objects within objects

I have an array of information in Powershell that is comprised of objects within objects. It goes down 4 objects deep. Is there a way to select a property from the bottom most object while remaining at the top level? Does this make sense what I am asking?
$result
active : active
security : #{waf=; acls=}
sealloaction :
siteDualFactorSettings : #{enabled=False; version=0}
login_protect : #{enabled=False; url_patterns=System.Object[]}
performance_configuration : #{advanced_caching_rules=; acceleration_level=standard; cache300x=False; cache_headers=System.Object[]}
$result.security
waf acls
--- ----
#{rules=System.Object[]} #{rules=System.Object[]}
$result.security.waf
rules
-----
{#{action=api.threats.action.block_request; action_text=Block; id=api.threats.sql_injection; name=SQL Injection}, #{action=api.threats.action.alert; action_text=Alert Only;}
To produce a mixed main/nested level selection:
foreach on the main array
select of the nested stuff + calculated properties to reference the iterated main array element
$results | ForEach {
$r = $_
$r.security.waf.rules | select *, #{N='domain'; E={$r.domain}}
}
Or as a one-liner:
$results | %{ $r = $_; $r.security.waf.rules | select *, #{N='domain'; E={$r.domain}} }
$myArray | where { $_.x.y.z -eq 'something' }
which is functionally equivalent to
foreach ($object in $myArray)
{
if ($object.x.y.z -eq 'something')
{
$object # Writes $object to the output stream
}
}
The easier way should be with $result | select ${L='label';E={$_.security.waf}} then you drill down to where you want.
For example:
$obj1_child = New-Object -TypeName psobject
$obj1_child | add-member -Name obj1_child -MemberType NoteProperty -Value "obj1_child"
$obj1_child | add-member -Name value1 -MemberType NoteProperty -Value "child_value1"
$obj1 = New-Object -TypeName psobject
$obj1 | add-member -Name obj1 -MemberType NoteProperty -Value "obj1"
$obj1 | add-member -Name child -MemberType NoteProperty -Value $obj1_child
$obj2 = New-Object -TypeName psobject
$obj2 | add-member -Name value1 -MemberType NoteProperty -Value "value1"
$parentObj = New-Object -TypeName psobject
$parentObj | Add-Member -MemberType NoteProperty -name string -value "parentObject"
$parentObj | Add-Member -MemberType NoteProperty -name OBJ1 -value $obj1
$parentObj | Add-Member -MemberType NoteProperty -name OBJ2 -value $obj2
$parentObj | select string,obj2, #{L='value of obj1 child';E={$_.obj1.child.value1}}
I create a $parentObj inside there's a string, and two objects ($obj1,$obj2), and inside $obj1 there's another object ($obj1_child). Now I want the string from $parentObj and the last string from $obj1_child this should work:
$parentObj | select string, #{L='value of obj1 child';E={$_.obj1.child.value1}}
Output:
>> $parentObj | select string, #{L='value of obj1 child';E={$_.obj1.child.value1}}
string value of obj1 child
------ -------------------
parentObject child_value1
Not sure if it's what you want. but it was what I was looking for when landed here, so may help someone else.
Reference:
https://devblogs.microsoft.com/scripting/access-objects-inside-other-objects-in-powershell-pipeline/
Two ways to do the same thing
$x = [PSCustomObject]#{
active = 'active';
security = [PSCustomObject]#{
waf = [PSCustomObject]#{
rules = #(
[PSCustomObject]#{
action='api.threats.action.block_request';
action_text='Block';
id='api.threats.sql_injection';
name='SQL Injection'},
[PSCustomObject]#{
action='api.threats.action.alert';
action_text='Alert Only';}
)
}
acls = [PSCustomObject]#{
rules = #(
[PSCustomObject]#{
action='api.threats.action.block_request';
action_text='Block';
id='api.threats.sql_injection';
name='SQL Injection'},
[PSCustomObject]#{
action='api.threats.action.alert';
action_text='Alert Only';}
)
}
}
}
$x.security.waf.rules | select *, #{name='domain'; ex={'foo'}}
$x | select -ExpandProperty security | select -ExpandProperty waf | select -ExpandProperty rules | select *, #{name='domain'; ex={'foo'}}

Powershell: File properties - Bytes to Mb

Currently have the following script that outputs some file data to a report. The file length is in bytes though and wondering how I can convert that to MB before outputting to the array.
$arr = #()
gci C:\stuff -recurse | ? {$_.PSIsContainer -eq $False} | % {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty Directory $_.DirectoryName
$obj | Add-Member NoteProperty Name $_.Name
$obj | Add-Member NoteProperty Length $_.Length
$obj | Add-Member NoteProperty created $_.creationtime
$obj | Add-Member NoteProperty Access $_.LastAccessTime
$obj | Add-Member NoteProperty LastWritten $_.LastWriteTime
$obj | Add-Member NoteProperty Extension $_.Extension
$obj | Add-Member NoteProperty Owner ((Get-ACL $_.FullName).Owner)
$arr += $obj
}
$arr | Export-CSV -notypeinformation "c:\files.csv"
When converting into MB, just put brackets around it:
$obj | Add-Member NoteProperty Length ($_.Length/1MB)
or maybe more useful:
$obj | Add-Member NoteProperty MB ("{0:N3}" -f ($_.Length/1MB))
to only show the first three digits after the point.

ConvertTo-XML doesn't display as expected

In a foreach loop, I am trying to gather information about a number of objects, and concatenate them all in one XML file.
foreach($Task in $Tasks) {
$OutputObj = New-Object -TypeName PSobject
$OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer
# does not work, $s contains '#document'
$s = ConvertTo-XML -InputObject $OutputObj
Write-Host $s
# works as expected
$OutputObj | Export-CliXML $OutPath
}
What am I doing wrong with ConvertTo-XML? How can I get it to produce the output that Export-CliXML? To be clear, I want to convert each $outputobj and append it to the output file.
Try this:
($Tasks | ForEach-Object {
$OutputObj = New-Object -TypeName PSObject
$OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer
Write-Output $OutputObj
} | ConvertTo-XML -NoTypeInformation).Save($OutPath)
or this:
$combined = #()
foreach($Task in $Tasks) {
$OutputObj = New-Object -TypeName PSobject
$OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer
$combined += $OutputObj
}
$combined | Export-CliXML $OutPath
What a result do you want to see?
To learn more about ConvertTo-XML read the article "Dress Up Your XML Output with the ConvertTo-XML Cmdlet" http://technet.microsoft.com/en-us/library/ff730921.aspx
Instead of
$s = ConvertTo-XML -InputObject $OutputObj
Try
$s = $OutputObj | ConvertTo-XML
I have the same problem with ConvertTo-CSV. The -InputObject parameter is not working as I expect. But piping the object(s) to the ConvertTo-CSV works as expected.