Selecting from objects within objects - powershell

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'}}

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

Powershell - convert new-object created with PSObject to number or integer

I have a script that uses APIs to pull data from our MDM about phone devices. It creates a .csv file with many columns. The IMEI column shows as (for instance) 3.343434+14 instead of showing 334343412345678912345. Can anyone help show me how to have this output properly in the .csv file? I can manipulate the column properties after the fact, but would rather it just came out correct. It appears the output is coming through as general when I really want it to be a number/integer.
I can't figure out where to possible enter [int] (if that's even what is required to fix this).
$data = $response.Device
$data | foreach {
$serial = $_.SerialNumber
$phone = $_.PhoneNumber
$ownership = $_.Ownership
$enrollstat = $_.EnrollmentStatus
$compliant = $_.ComplianceStatus
$user = $_.UserName
$asset = $_.AssetNumber
$getlast = $_.LastSeen
$imei = $_.Imei
$lastdate = [DateTime]$getlast
try{$lastdate = Get-Date $getlast}
catch{write-host "NULL Date $serial"}
$object = New-Object -TypeName PSObject
$object | Add-Member -Name 'Serial Number' -MemberType NoteProperty -Value $serial
$object | Add-Member -Name 'Phone Number' -MemberType Noteproperty -Value $phone
$object | Add-Member -Name 'IMEI' -MemberType NoteProperty -Value $imei
$object | Add-Member -Name 'Ownership' -MemberType Noteproperty -Value $ownership
$object | Add-Member -Name 'Enrollment Status' -MemberType Noteproperty -Value $enrollstat
$object | Add-Member -Name 'Compliance Status' -MemberType Noteproperty -Value $compliant
$object | Add-Member -Name 'User' -MemberType Noteproperty -Value $user
$object | Add-Member -Name 'Asset Number' -MemberType Noteproperty -Value $asset
$object | Add-Member -Name 'Last Seen Date' -MemberType NoteProperty -Value $lastdate
I would like the .cvs column to show the entire IMEI number and not have decimals nor be truncated.
I was unable to replicate your problem, but I wanted to showcase the proper way to go about your goal:
# assumption: $response is the result of some REST API call using `Invoke-RestMethod`
# `Invoke-RestMethod` will automatically turn xml/json responses into objects
$response = [pscustomobject]#{
Device = [pscustomobject]#{
SerialNumber = 'abc123'
PhoneNumber = '+18005551234'
Ownership = 'UNIQUEID'
EnrollmentStatus = $true
ComplianceStatus = $false
UserName = 'USERID'
AssetNumber = 123456
LastSeen = '2019-03-29T12:00:00'
Imei = 334343412345678912345
}
}
$response.Device | Select-Object -Property #(
#{
Name = 'Serial Number'
Expression = {$_.SerialNumber}
}
#{
Name = 'Phone Number'
Expression = {$_.PhoneNumber}
}
#{
Name = 'IMEI'
Expression = {$_.Imei}
}
#{
Name = 'Ownership'
Expression = {$_.Ownership}
}
#{
Name = 'Enrollment Status'
Expression = {$_.EnrollmentStatus}
}
#{
Name = 'Compliance Status'
Expression = {$_.ComplianceStatus}
}
#{
Name = 'User'
Expression = {$_.UserName}
}
#{
Name = 'Asset Number'
Expression = {$_.AssetNumber}
}
#{
Name = 'Last Seen Date'
Expression = {$_.LastSeen}
}
) | Export-Csv -Path C:\Temp\test.csv -NoTypeInformation
See: Select-Object documentation on -Property for an explanation of technique.

Append values to Powershell object

I have a PS object and couldnt figure out a way to append values to my object.
$object = New-Object PSObject
Add-Member -InputObject $object -MemberType NoteProperty -Name Col1 -Value ""
Add-Member -InputObject $object -MemberType NoteProperty -Name Col2 -Value ""
Add-Member -InputObject $object -MemberType NoteProperty -Name Type -Value ""
1..10 |ForEach{
$src=$_
11..20 | ForEach{
$dst = $_
$object.Col1=$src
$object.Col2=$dst
$object.Type="New"
}
}
I want my result like
col1 col2 Type
---- ---- ----
1 11 New
1 12 New
1 13 New
1 14 New
...
Use a PSCustomObject:
$values = 1..10 | % {
[pscustomobject]#{ col1=1; col2=10+$_; Type="New" }
}
The output you want is a list of objects, not a single object. You generate that by creating the objects inside the loop. #Burt_Harris already showed you one way to do that (using a type accelerator [PSCustomObject]), but of course you can also use New-Object to the same end:
$list = 1..10 | ForEach-Object {
$src = $_
11..20 | ForEach-Object {
$prop = #{
Col1 = $src
Col2 = $_
Type = 'New'
}
New-Object -Type PSObject -Property $prop
}
}
Create the property hashtable as an ordered hashtable if you want the properties to appear in a particular order in the output (PowerShell v3 and newer only):
$prop = [ordered]#{
Col1 = $src
Col2 = $_
Type = 'New'
}
The list of objects can be captured by assigning the pipeline output to a variable ($list = ...).

Selecting objects from a Custom Object

I have a custom PS Object that is something like the below:
ID Folder
MyServer01 \\Server\Share\Share\MyServer01
MyServer02 \\Server\Share\Share\MyServer02
Naturally the object itself is rather large, with over 1000 entries. I need to be able to select a specific row of the object based on querying the ID.
I thought something like this would work but I'm not having much luck:
$obj | Select-Object | Where-Object ($_.ID -eq "MyServer01")
I need it to return the entire row, so the above (assuming it worked) would return:
MyServer01 \\Server\Share\Share\MyServer01
EDIT:
foreach ($mf in $Folders.Tables[0]) {
$Info = New-Object System.Object
$Info | Add-Member -Type NoteProperty -Name ID -Value $mf.ID
$Info | Add-Member -Type NoteProperty -Name Folder -Value $mf.Folder
$obj += $Info
}
Use a hashtable for storing your objects:
$obj = #{}
foreach ($mf in $Folders.Tables[0]) {
$Info = New-Object -Type System.Object
$Info | Add-Member -Type NoteProperty -Name ID -Value $mf.ID
$Info | Add-Member -Type NoteProperty -Name Folder -Value $mf.Folder
$obj[$mf.ID] = $Info
}
Don't append to an array in a loop, as that tends to perform poorly.
If your code doesn't depend on the objects being created explicitly as System.Object I'd also recommend to create them as custom objects:
$obj = #{}
foreach ($mf in $Folders.Tables[0]) {
$Info = New-Object -Type PSCustomObject -Property #{
'ID' = $mf.ID
'Folder' = $mf.Folder
}
$obj[$mf.ID] = $Info
}