passing by value or reference : powershell
more information :
PSVersion 5.1.19041.906
All files can be found here : [*]https://drive.google.com/drive/folders/1Ya0Xyxewgo6FtUHVbGqSASqXOFSlXvbR?usp=sharing
I would like to try to pass an object by reference, variable. In this object I would like to pass a bunch of information (containing different other variables/data).
Sometimes there's a need to return it back (one variable), by return $menuObjts.
At paragraphs 'INFO' are $menuObjts and $menuObjts[‘MENUS’] shown.
More information about these object I have tried to figure it out by gettype().fullname.
REMARK : in the code here I've used $global: for allowing to ACCESS and CHANGE the variable and be able to make a screenshot and use it for test purpose.
So my problem is to ACCESS and CHANCE values in $menuObjts[‘MENUS’], which is a part, element of $menuObjts.
Thanks to #Santiago Squarzon for his patience and quick reaction.
The idea is to create dynamically menus from the CSV file (what works) and calls the selected functions by name - $menus_.FUNCTION which are retrieved.
But now I would like to extend it and be able to create multi sub menus.
There are two seperate MENU_GRP elements :
$menuObjts.MENU_GRP
-- contains info about the current/active/selected one
($menuObjts.**MENUS** | Where-Object {[int]**$($_).MENU_GRP** -eq ...
-- $menuObjts.MENUS : contains all posible menus (CSV)
So I import a range menu-items by a CSV file.
So these $menus_ are added to $menuObjts.MENUS / $menuObjts[‘MENUS’]
There are other features in $menus_ such as MENU, PARENT, MENU_GRP, MENU_IDX, MENU_OFFSET, MENU_SEL_TYPE, nrElems, FUNCTION, info, status , SEL, RESTART, STOP
$global:menus_ = Import-Csv -Delimiter "," $($curPath)
$menuGRP_ = 0 # 0 - MAIN
$menus_.MENU
$nrRestarts = #($menus_ | Where-Object { [int]$_.RESTART -eq 1 -and [int]$_.MENU_GRP -eq 0 }).Count
write-host (" info : - nrRestarts: {0}" -f ($nrRestarts))
# SET : values in one object : $menuObjts
$global:menuObjts =[ordered]#{
MENUS = $menus_;
MENU_GRP = $menuGRP_;
MENU_SEL_TYPE = $null;
MENU_OFFSET = $null;
nrElems = $null;
sel_input = $null;
MENU_IDX = $null}
$menuObjts.MENUS?MENU_GRP = 0 or $menuObjts.MENUS?MENU_GRP = 6
$menuObjts.MENUS?MENU_OFFSET = -1 or $menuObjts.MENUS?MENU_OFFSET = 12
$menuObjts.MENUS?nrElems = 13 or $menuObjts.MENUS?nrElems = 4
$menuObjts.MENUS ? - ? because I don't know how to retrieve the underlying object and their features/data
So my problem is how to retrieve each element of $menus_ in $menuObjts.MENUS again.
The idea is that via one variable, the next one will be calculated ([*]see function updateMenuObjtsInfo )
So my question is how can I see by type, how to get the wanted data ...
information of variables/object Get-Variable
gettype()
$menuObjts
$menuObjts.MENUS
$menuObjts.MENUS | select -first 1
These are a few things I want to achieve, but this doesn't work proper ($_).MENU_GRP
$1stElementGrp_ = $($menus_ | Where { [int]$($_).MENU_GRP -eq $menuObjts.MENU_GRP }| Select -First 1 )
$menuOFFSET_ = $($1stElementGrp_).MENU_OFFSET
$menuNrElems_ = $($1stElementGrp_).nrElems
##### where $($menuObjts.MENUS).MENU_GRP -eq $menuObjts.MENU_GRP -> .MENU_OFFSET
$menuObjts.MENU_OFFSET = $($menuObjts.MENUS | Where-Object { [int]$($_).MENU_GRP -eq $menuObjts.MENU_GRP}| Select -First 1 ).MENU_OFFSET
$menuObjts.nrElems = #($menuObjts.MENUS | Where-Object { [int]$($_).MENU_GRP -eq $menuObjts.MENU_GRP -and [int]$($_).SEL -eq 1}).Count
Another idea … was adding methods, but I’m struggling with my (little) knowledge of Powershell.
(based on 4 Ways to Create PowerShell Objects | RidiCurious.com )
$menuObjts | Add-Member -MemberType ScriptMethod -Name "getMENUS_RESTART" -Value $( this.MENUS | Where-Object { [int]$_.RESTART -eq 1 -and [int]$_.MENU_GRP -eq $menuGRP_ })
INFO - $menuObjts :
Name Value
---- -----
MENUS {#{MENU;PARENT;MENU_GRP;MENU_IDX;MENU_OFFSET;MENU_SEL_TYPE;nrElems;FUNCTION;info;status;SEL;RESTART;STOP=typeInstallation;LICENSE;0;0;-1;0;13;f1;Windows-Defende...
MENU_GRP 0
MENU_SEL_TYPE
MENU_OFFSET
nrElems
sel_input
MENU_IDX
INFO - $menuObjts.MENUS :
MENU;PARENT;MENU_GRP;MENU_IDX;MENU_OFFSET;MENU_SEL_TYPE;nrElems;FUNCTION;info;status;SEL;RESTART;STOP
-----------------------------------------------------------------------------------------------------
typeInstallation;LICENSE;0;0;-1;0;13;f1;Windows-Defender has to be uninstalled
activate;;0;1;-1;0;13;f2;Windows has to be upgraded if working with an EVALUATION prod key;-1;0;0;0
NAME;HOST;0;2;-1;0;13;f3;F-SEC has to be configured as an isolated machine on the CSI server;-1;0;0;0
IP;;0;3;-1;0;13;f4;disable default Windows NTP service;-1;0;1;0
routes;;0;4;-1;0;13;f5;disable default Windows NTP service;-1;0;0;0
users;;0;5;-1;0;13;f6;disable default Windows NTP service;-1;0;0;0
ANTI VIRUS;SERVICEs;0;6;-1;0;13;f7;disable default Windows NTP service;-1;0;0;0
NTP;;0;7;-1;0;13;f8;;-1;0;0;0
MEINBERG;;0;8;-1;0;13;f9;;-1;0;0;0
addPATH;postgres;0;9;-1;0;13;f10;;-1;0;0;0
check;after CSI;0;10;-1;0;13;f11;;-1;0;0;0
execute;;0;11;-1;0;13;f12;;-1;0;0;1
quite;;0;12;-1;0;13;f13;;-1;0;0;1
WINDOWS DEFENDER;ANTI VIRUS;6;13;12;1;4;f14;;-1;0;0;0
F-SEC;;6;14;12;1;4;f15;;-1;0;0;0
execute;;6;15;12;1;4;f16;;-1;0;0;1
quite;;6;16;12;1;4;f17;;-1;0;0;1
Additional information [2021/05/04]
PS C:\Users\Administrator> $menuObjts.MENUS | Get-Member
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()
FUNCTION NoteProperty string FUNCTION=f1
info NoteProperty string info=Windows-Defender has to be uninstalled, before installing an other anti-virus program
MENU NoteProperty string MENU=typeInstallation
MENU_GRP NoteProperty string MENU_GRP=0
MENU_IDX NoteProperty string MENU_IDX=0
MENU_OFFSET NoteProperty string MENU_OFFSET=-1
...
PS C:\Users\Administrator> $menuObjts.MENUS
MENU : typeInstallation
PARENT : LICENSE
MENU_GRP : 0
MENU_IDX : 0
MENU_OFFSET : -1
MENU_SEL-TYPE :
nrElems : 13
FUNCTION : f1
info : Windows-Defender has ...
status : -1
SEL : 0
RESTART : 1
STOP :
MENU : activate
PARENT :
MENU_GRP : 0
...
I have the impression that $menus_ is added as a 'value' instead as an object to $menuObjts - Value : {#{MENU=
PS C:\Users\Administrator> $menuObjts.MENUS.PSobject.Properties
ReferencedMemberName : Length
ConversionType :
MemberType : AliasProperty
TypeNameOfValue : System.Int32
IsSettable : False
IsGettable : True
Value : 17
Name : Count
IsInstance : False
MemberType : Property
Value : 17
IsSettable : False
IsGettable : True
TypeNameOfValue : System.Int32
Name : Length
IsInstance : True
...
MemberType : Property
Value : {#{MENU=typeInstallation; PARENT=LICENSE; MENU_GRP=0; MENU_IDX=0; MENU_OFFSET=-1; MENU_SEL-TYPE=; nrElems=13;FUNCTION=f1; info=Windows-Defender has to be uninstalled, before installing an other anti-virus program;status=-1; SEL=0; RESTART=1; STOP=},
#{MENU=activate; PARENT=; MENU_GRP=0; MENU_IDX=1; MENU_OFFSET=-1; MENU_SEL-TYPE=; nrElems=13; FUNCTION=f2; info=Windows has to be upgraded if working with an EVALUATION prod key;status=-1; SEL=0; RESTART=0; STOP=},
#{MENU=NAME; PARENT=HOST; MENU_GRP=0; MENU_IDX=2; MENU_OFFSET=-1;MENU_SEL-TYPE=; nrElems=13; FUNCTION=f3; info=F-SEC has to be configured as an isolated machine on the CSI server;status=-1; SEL=0; RESTART=0; STOP=},
#{MENU=IP; PARENT=; MENU_GRP=0; MENU_IDX=3; MENU_OFFSET=-1; MENU_SEL-TYPE=;nrElems=13; FUNCTION=f4; info=disable default Windows NTP service; status=-1; SEL=0; RESTART=1; STOP=}...}
IsSettable : False
IsGettable : True
TypeNameOfValue : System.Object
Name : SyncRoot
IsInstance : True
...
First of all, I would recommend a good read on: Where-Object, about_Arrays and this good article on PS Objects
# Storing the CSV in the $csv var
$csv = #'
MENU;PARENT;MENU_GRP;MENU_IDX;MENU_OFFSET;MENU_SEL_TYPE;nrElems;FUNCTION;info;status;SEL;RESTART;STOP
typeInstallation;LICENSE;0;0;-1;0;13;f1;Windows-Defender has to be uninstalled
activate;;0;1;-1;0;13;f2;Windows has to be upgraded if working with an EVALUATION prod key;-1;0;0;0
NAME;HOST;0;2;-1;0;13;f3;F-SEC has to be configured as an isolated machine on the CSI server;-1;0;0;0
IP;;0;3;-1;0;13;f4;disable default Windows NTP service;-1;0;1;0
routes;;0;4;-1;0;13;f5;disable default Windows NTP service;-1;0;0;0
users;;0;5;-1;0;13;f6;disable default Windows NTP service;-1;0;0;0
ANTI VIRUS;SERVICEs;0;6;-1;0;13;f7;disable default Windows NTP service;-1;0;0;0
NTP;;0;7;-1;0;13;f8;;-1;0;0;0
MEINBERG;;0;8;-1;0;13;f9;;-1;0;0;0
addPATH;postgres;0;9;-1;0;13;f10;;-1;0;0;0
check;after CSI;0;10;-1;0;13;f11;;-1;0;0;0
execute;;0;11;-1;0;13;f12;;-1;0;0;1
quite;;0;12;-1;0;13;f13;;-1;0;0;1
WINDOWS DEFENDER;ANTI VIRUS;6;13;12;1;4;f14;;-1;0;0;0
F-SEC;;6;14;12;1;4;f15;;-1;0;0;0
execute;;6;15;12;1;4;f16;;-1;0;0;1
quite;;6;16;12;1;4;f17;;-1;0;0;1
'#|convertfrom-csv -Delimiter ';'
Get the first element of the array
$1stElementGrp_ = $csv[0] # Like this
$1stElementGrp_ = $csv | Select-Object -First 1 # Or Like this
Get the value of the property MENU_OFFSET and nrElems of the variable $1stElementGrp_
$menuOFFSET_ = $1stElementGrp_.MENU_OFFSET # $menuOFFSET_ returns -1
$menuNrElems_ = $1stElementGrp_.nrElems # $menuNrElems_ returns 13
Not sure what you're trying filter here
# $menuObjts.MENU_OFFSET = ($menuObjts.MENUS | Where-Object {
# [int]$($_).MENU_GRP -eq $menuObjts.MENU_GRP
# }| Select -First 1).MENU_OFFSET
#
# $menuObjts.nrElems = #($menuObjts.MENUS | Where-Object {
# [int]$($_).MENU_GRP -eq $menuObjts.MENU_GRP -and [int]$($_).SEL -eq 1}).Count
# }
Example: If you want to filter all the rows where MENU_OFFSET = -1
$csv | Where-Object {$_.MENU_OFFSET -eq -1} |
Select-Object MENU, PARENT, MENU_GRP, MENU_IDX, MENU_OFFSET |
Format-Table
Returns
MENU PARENT MENU_GRP MENU_IDX MENU_OFFSET
---- ------ -------- -------- -----------
typeInstallation LICENSE 0 0 -1
activate 0 1 -1
NAME HOST 0 2 -1
IP 0 3 -1
routes 0 4 -1
users 0 5 -1
ANTI VIRUS SERVICEs 0 6 -1
NTP 0 7 -1
MEINBERG 0 8 -1
addPATH postgres 0 9 -1
check after CSI 0 10 -1
execute 0 11 -1
quite 0 12 -1
Example: If you want to filter all the rows where MENU_GRP = 6 AND MENU matches the word 'WINDOWS'
$csv | Where-Object {$_.MENU_GRP -eq 6 -and $_.MENU -match 'Windows'} |
Select-Object MENU, PARENT, MENU_GRP, MENU_IDX, MENU_OFFSET |
Format-Table
Returns:
MENU PARENT MENU_GRP MENU_IDX MENU_OFFSET
---- ------ -------- -------- -----------
WINDOWS DEFENDER ANTI VIRUS 6 13 12
Related
Given below command giving the output as object
$output = (Invoke-AzVMRunCommand -ResourceGroupName $rgname -Name $vmname -CommandId 'RunPowerShellScript' -ScriptPath authoring.ps1).value
Output
Mode : Process
ContextDirectory :
ContextFile :
CacheDirectory :
CacheFile :
Settings : {}
Code : ComponentStatus/StdOut/succeeded
Level : Info
DisplayStatus : Provisioning succeeded
Message : #{cluster_name=test01; status=green; timed_out=False; number_of_nodes=3;
number_of_data_nodes=3;
active_primary_shards=25; active_shards=50; relocating_shards=0;
initializing_shards=0;
unassigned_shards=0; delayed_unassigned_shards=0; number_of_pending_tasks=0;
number_of_in_flight_fetch=0; task_max_waiting_in_queue_millis=0;
active_shards_percent_as_number=100.0}
Time :
Code : ComponentStatus/StdErr/succeeded
Level : Info
DisplayStatus : Provisioning succeeded
How to get the Message ==> number_of_nodes = 3 and number_of_data_nodes=3 in some variable so based on this value I need to perform some action.
Thanks
Ok, if as you tested $Output.Message is a multiline string like
#{cluster_name=test01; status=green; timed_out=False; number_of_nodes=3;
number_of_data_nodes=3;
active_primary_shards=25; active_shards=50; relocating_shards=0;
initializing_shards=0;
unassigned_shards=0; delayed_unassigned_shards=0; number_of_pending_tasks=0;
number_of_in_flight_fetch=0; task_max_waiting_in_queue_millis=0;
active_shards_percent_as_number=100.0}
you can convert this into a Hashtable like this:
$data = $Output.Message.Trim("#{}") -replace ';', [environment]::NewLine | ConvertFrom-StringData
If you print that out to screen with $data | Format-Table -AutoSize it looks like this:
Name Value
---- -----
number_of_nodes 3
task_max_waiting_in_queue_millis 0
number_of_data_nodes 3
status green
initializing_shards 0
active_shards_percent_as_number 100.0
cluster_name test01
delayed_unassigned_shards 0
active_shards 50
unassigned_shards 0
active_primary_shards 25
number_of_in_flight_fetch 0
timed_out False
relocating_shards 0
number_of_pending_tasks 0
With this Hashtable format, it is easy to get the values of the different keys.
For instance:
$data.number_of_nodes # --> 3
$data.number_of_data_nodes # --> 3
$data.cluster_name # --> test01
I'm attempting to add a custom column to the table output of some objects retrieved from our MECM instance.
The following code shows the precise format of the code, only gathering data from Get-Process instead.
$types = #{
"chrome" = "This is Chrome"
"winlogon" = "This is winlogon"
}
$procs = Get-Process | Select Name,Id | Where { ($_.Name -eq "winlogon") -or ($_.Name -eq "chrome") }
$procsCustom = $procs | Select Name,Id,#{
Name = "TestColumn"
Expression = {
$name = $_.Name
$types.$name
}
}
$procsCustom | Format-Table
This code functions as expected:
Name Id TestColumn
---- -- ----------
chrome 12428 This is Chrome
chrome 12448 This is Chrome
chrome 12460 This is Chrome
winlogon 880 This is winlogon
winlogon 5076 This is winlogon
When I do the same thing for my actual code:
$refreshTypes = #{
1 = "Manual Update Only"
2 = "Scheduled Updates Only"
4 = "Incremental Updates Only"
6 = "Incremental and Scheduled Updates"
}
$colls = Get-CMCollection | Where { ($_.RefreshType -eq 4) -or ($_.RefreshType -eq 6) }
$collsCustom = $colls | Select Name,RefreshType,#{
Name = "RefreshTypeFriendly"
Expression = {
$type = $_.RefreshType
$refreshTypes.$type
}
}
$collsCustom | Format-Table
The custom column is not populated:
Name RefreshType RefreshTypeFriendly
---- ----------- -------------------
Collection 1 6
Collection 2 4
The following code shows that $_.RefreshType is being parsed correctly:
$collsCustom = $colls | Select Name,RefreshType,#{
Name = "RefreshTypeFriendly"
Expression = {
$_.RefreshType
}
}
$collsCustom | Format-Table
Name RefreshType RefreshTypeFriendly
---- ---------- -------------------
Collection 1 6 6
Collection 2 4 4
The following code forces a fake value for $type in the expression scriptblock and shows that the rest of the code works:
$collsCustom = $colls | Select Name,RefreshType,#{
Name = "RefreshTypeFriendly"
Expression = {
$type = 1
$refreshTypes.$type
}
}
$collsCustom | Format-Table
Name RefreshType RefreshTypeFriendly
---- ----------- -------------------
Collection 1 6 Manual Update Only
Colleciton 2 4 Manual Update Only
So why in the world does the intended code output nothing in the custom column? Given the examples I've provided above, I'm stumped.
FWIW I've also tried using the array syntax ($refreshTypes[$type]) instead of the object property syntax ($refreshTypes.$type) but I get the same behavior.
Thanks for your time.
Environment:
Win10 x64 20H2
Powershell 5.1
Looks like your type isn't being interpreted as an int. Try casting $type to Int
Try this
Expression = {
[int]$type = $_.RefreshType
$refreshTypes.$type
}
I am using the following Powershell command in order to extract the name, the assigned RAM and RAM usage of each VMs in the server.
Get-VM | ft Name, Memorydemand, Memoryassigned
However the result of the memorydemand and memoryassigned are in Bytes but I want them to be in Megabytes. Is there a way for me to divide the results of the memorydemand and memoryassigned by 1048576 so that I can get their corresponding MB?
Also, is it also possible to get the average RAM Usage of a certain VM for the last one or two months? Even though Hyper-V is assigning dynamic memory, I just want to double-check.
There's a couple different approaches that I can think of to achieve this.
Use Select-Object to create calculated properties
Use the Select-Object command to create custom, calculated properties.
Get-VM | Select-Object -Property `
Name,
#{ Name = 'MemoryDemandMB'; Expression = { $PSItem.MemoryDemand/1MB } },
#{ Name = 'MemoryAssignedMB'; Expression = { $PSItem.MemoryAssigned/1MB } } |
Format-Table -Property Name, MemorydemandMB, MemoryassignedMB -AutoSize
Use Add-Member to augment the objects
You can use the Add-Member command to add two new properties to the objects. This actually augments the objects, rather than simply appending the properties for the lifetime of the pipeline.
Get-VM |
Add-Member -MemberType ScriptProperty -Name MemoryDemandMB -Value { $this.MemoryDemand/1MB } -PassThru |
Add-Member -MemberType ScriptProperty -Name MemoryAssignedMB -Value { $this.MemoryAssigned/1MB } -PassThru |
Format-Table -Property Name, MemorydemandMB, MemoryassignedMB -AutoSize
Results
Here's what the output looks like on my system.
Name MemoryDemandMB MemoryAssignedMB
---- -------------- ----------------
agent01 0 0
agent02 0 0
dc01 878 1058
denver01 0 0
london01 877 1070
MobyLinuxVM 0 0
munich01 1228 1638
sccm01 2213 2604
swarm01 0 0
UbuntuDesktop 0 0
Does PSCustomObject's know the order in which its properties are added?
# Order of properties
$o21 = New-Object PSCustomObject |
Add-Member NoteProperty a2 2 -passThru |
Add-Member NoteProperty a1 1 -passThru
$o21 | fl
a2 : 2
a1 : 1
$o12 = New-Object PSCustomObject |
Add-Member NoteProperty a1 1 -passThru |
Add-Member NoteProperty a2 2 -passThru
$o12 | fl
a1 : 1
a2 : 2
I want to read this order. How?
To get an ordered list of properties of an object in PowerShell, you can access the Properties collections through the hidden psobject memberset property:
PS C:\> $o12.psobject.Properties
MemberType : NoteProperty
IsSettable : True
IsGettable : True
Value : 1
TypeNameOfValue : System.Int32
Name : a1
IsInstance : True
MemberType : NoteProperty
IsSettable : True
IsGettable : True
Value : 2
TypeNameOfValue : System.Int32
Name : a2
IsInstance : True
Expand the Name property if you just want an ordered list of property names using Select-Object:
$PropertyNames = $o12.psobject.Properties |Select-Object -ExpandProperty Name
or using property enumeration (PowerShell 3.0+):
$PropertyNames = $o12.psobject.Properties.Name
To expand on Mathias' answer; if you are looking to get the property list for an array of objects, you'll need to do one of the following depending on how different the objects in the array are:
All objects in the array share the same properties:
This example will pull the first object of the array only and grab its property names
$PropertyNames = $o12[0].PSObject.Properties | Select-Object -ExpandProperty Name
Objects in the array do not share properties:
$PropertyNames = $o12 | ForEach-Object { $_.PSObject.Properties | Select-Object -ExpandProperty Name}
In both of the examples, the takeaway is that property enumeration will happen at the top level. If your top level is an object array and not a PSCustomObject/PSObject, then you'll return array properties and not the properties of the object/objects in the array:
PS> $obj.PSObject.Properties.Name
Count
Length
LongLength
Rank
SyncRoot
IsReadOnly
IsFixedSize
IsSynchronized
PS> $obj[0].PSObject.Properties.Name
Name
SamAccountName
Suppose I call Get-Service and want to assign a new column ID with the cmdlet output that prints incrementing integers so that:
ID Status Name DisplayName
-- ------ ---- -----------
0 Running AdobeARMservice Adobe Acrobat Update Service
1 Stopped AeLookupSvc Application Experience
2 Stopped ALG Application Layer Gateway Service
I'm trying to use Select-Object right now to add this column, but I don't quite understand how to iterate a variable in this sort of expression. Here's what I've got:
Get-Service |
Select-Object #{ Name = "ID" ; Expression= { } }, Status, Name, DisplayName |
Format-Table -Autosize
Is there a way to iterate integers within Expression= { }, or am I going about this problem the wrong way?
You can do it this way, though you will need to maintain some counter variable outside of the main expression.
$counter = 0
Get-Service |
Select-Object #{ Name = "ID" ; Expression= {$global:counter; $global:counter++} }, Status, Name, DisplayName |
Format-Table -Autosize
Another option, which is perhaps cleaner
Get-Service `
|% {$counter = -1} {$counter++; $_ | Add-Member -Name ID -Value $counter -MemberType NoteProperty -PassThru} `
| Format-Table ID
I asked the same question a different way and got the following answer
$x = 10
Get-Service |
Select-Object #{ Name = "ID" ; Expression={ (([ref]$x).Value++) }}, Status, Name, DisplayName | Format-Table -Autosize
It wasn't at all clear to me that the expression is being invoked within Select-Object's scope, not the pipe's. The [ref] qualifier bumps the increment's result up to the pipe's scope achieving the same result as explicitly specifying the variable as global.