PowerShell Object contains value from previous loop cycle - list

Situation
The main object is from type $type_RGS which may contain 1-n objects from type $type_RgsQueue. $type_RgsQueue may contain 1-n objects from type $type_RgsAgentGroup. (RGS -> Queue -> Agent)
I created These custom types as followed:
# Contains Attribute AgentGroup (as List – Purpose: use .add() )
$Type_RgsQueue= [ordered]#{
Name = ""
Fwd_Target = New-Object -TypeName psobject -Property $Type_ForwardTarget
Timeout = "" #sek
Fwd_Overflow = New-Object -TypeName psobject -Property $Type_ForwardTarget
Overflow = "" # number
AgentGroups = New-Object System.Collections.Generic.List[System.Object]
}
$Type_RgsAgentGroup= [ordered]#{
Name = ""
AgentAlertTime = ""
DistributionGroup = ""
RoutingMethod = "Attendant"
}
#Mainobject – Contains Attribute Queues (as List – Purpose: use .add() )
$Type_RGS = [ordered]#{
Name = ""
…
#Queues = #()
Queues = New-Object System.Collections.Generic.List[System.Object]
}
As you can see I used this time New-Object System.Collections.Generic.List[System.Object]. My idea behind this is, that I can easily add multiple objects from type $type_RgsAgentGroup / $type_RgsQueue
Before the foreach-loopstarts, I create the object $AllRgsObject where I intend to save all created $type_RGS-objects:
$AllRgsObject = New-Object -TypeName psobject -Property $Type_AllRgs
foreach ($csv in $list)
{
During each cycle I create a new $type_RGS-object ($RgsObject)
$RgsObject = $null # just for testing
$RgsObject = New-Object -TypeName psobject -Property $Type_Rgs
...
...
... and create aswell a new $type_rgsQueue-object ($rgsQueueObject)...
$RgsQueueObject = New-Object -TypeName psobject -Property $Type_RgsQueue
$RgsQueueObject.Name = "$($csv.Displayname)$($QueueSuffix)$($QueueCounter)"
do{
... and corresponding object from type $type_RgsAgentGroup ($RgsAgentGroupObject)
$RgsAgentGroupObject = $null # just for testing
$RgsAgentGroupObject = New-Object -TypeName psobject -Property $Type_RgsAgentGroup
...
$RgsQueueObject.AgentGroups.Add($RgsAgentGroupObject)
}while (…)
$RgsObject.Queues.Add($RgsQueueObject)
}while (…)
$AllRgsObject.items += $RgsObject
} // END FOREACH
Issue:
After the first foreach-Loop, the object $rgsQueueObjectstill contain the values from the previous run. Even if I set $rgsQueueObject = $null the values Comeback if the object is newly created.
It seems that there is reference to the previous object, but I don't understand whats going on here.
I assume it's related to New-Object System.Collections.Generic.List[System.Object] because this the first time I use this type, but I liked the Approach how I can use .add() instead of +=
Any idea whats going on there? If you need more Details, please let me know. I'll try to explain it in more Detail.
Best regards

Related

Powershell Define Powershell Objects

I like to define my variables in a structered fashion. Most MSDN blogs however do not do this.
For example:
[object]myObj = ...
Is this the correct default format for all objects in Powershell?
Use PsObject like this:
$o = new-Object PsObject -property #{Name='donald'; Kind='duck' }
You pass a hashtable as argument for -property parameter. Also you can create empty object and add properties later:
$o = New-Object PsObject
$o | Add-Member NoteProperty project myproj.csproj
$o | Add-Member NoteProperty Success $true
You can of course use pipe to Add-Member
$o = New-Object PsObject
# ...
$o |
Add-Member NoteProperty project myproj.csproj -pass |
Add-Member NoteProperty Success $true
If I understand the question, you're asking a couple things:
Can you explicitly specify the type of a variable?
What type does Powershell use if you don't specify one yourself?
Powershell will certainly let you specify a type explicitly, but it will also infer types. Note that since all types inherit from System.Object, explicitly specifying [object] in a combined declaration/assignment statement has no value that I can see. The type system will still infer an appropriate child type. For example:
$x = 3
$x.GetType() # Returns 'Int32'
Remove-Variable x
[object] $x = 3
$x.GetType() # Returns 'Int32'
Remove-Variable x
[valuetype] $x = 3
$x.GetType() # Returns 'Int32'
Remove-Variable x
[int] $x = 3
$x.GetType() # Returns 'Int32'
If you split up the declaration and assignment, you can create a variable of type Object:
Remove-Variable x
$x = new-object -TypeName Object
$x.GetType() # Returns 'Object'
...but once you assign a value, the variable gets a new inferred type anyway:
$x = 3
$x.GetType() # Returns 'Int32'
While the type system will happily infer Int32 when you specify Object, explicit types win when the inferred type would be incompatible. For example:
$x = 3 # Gets inferred type 'Int32'
[string] $x = 3 # Gets explicit type 'String'
$x = 'x' # Gets inferred type 'String'
[char] $x = 'x' # Gets explicit type 'Char'
If your question is more geared toward defining and using custom object types, Stej's answer is excellent.
Since Powershell 3 one can also parse HashTables to
Custom Objects like:
[PSObject] $Piza = [PSCustomObject] #{
Ingredients = 4
}
Or if you like to define more detailed types in your object
you could use the -AsCustomObject Parameter from New-Module
[PSObject] $Piza = New-Module -AsCustomObject -ScriptBlock {
[Guid]$id = [Guid]::NewGuid()
[String] $Name = 'Macaroni'
Function TestFunction() {}
# Dont forget to export your members and functions
# as this is built up as a module and stuffed into
# an object later
Export-ModuleMember -Function * -Variable *
}
As there are no things as classes in posh you can add
custom classnames and namespaces to your object
that you can query later (pseudo instance ;)
$Piza.PSObject.TypeNames.Insert(0, 'Pizas.Macaroni')
If I understand your question properly, you are asking
How to create custom object in powershell?
In powershell, it is able to create an instance of .Net object by using New-Object. Like this
$ie = New-Object -ComObject InternetExplorer.Application
$ie.Navigate2("http://stackoverflow.com/")
$ie.Visible = $true
The code above create a com object which navigate your ie to stackoverflow.
And, If you want to create your own object, PSObject would be your choice. Like below
$obj = New-Object PSObject -Property #{
Name = "Your Name"
Age = 30
}
When you call the object by using "$obj"
$obj
Age Name
--- ----
30 Your Name
If you are using powershell 3.0, you can get the same result with less typing like below
[PSCustomObject] #{
Name = "Your Name"
Age = 30
}
Creation of the PSObject object is the basis for access to all objects from the scripting language and provides an abstraction for the cmdlet developer and
It differs in different version of powershell..
New-Object PSObject –Property [HashTable]
$Object = New-Object PSObject`
$Object | add-member Noteproperty LineNumber $LineNumber
$Object | add-member Noteproperty Date $TodayDate
You can also create fast objects like that:
$myVar = "" | select column1, column2, column3
this will create object with 3 NoteProperties
and you have access to every property on normal basis like
$myVar.column1
P.S. take under consideration that this is Selecte.System.String not System.Management.Automation.PSCustomObject.
Still enough for keeping rows of data, exporting, accessing data cells.
Update: More flexible solution and still easy to implement would be Hashtable created like below.
$myVar2 = #{
column1=value1
column2=value2
column3=value3
}
This will create variable of type System.Collections.Hashtable
with some nice methods
regards
hermitagup

Create Customer Objects and Properties in an Array

Thank you for your help in advance.
What i need to do is to add Multiple properties and sub-properties in an array of objects.
To be specific, i am using PowerNSX to get some data of the NSX Cluster prepared for NSX, this is acomplished by using the Command Get-NsxClusterStatus, the return value of this command will be something like the below:
featureId : com.vmware.vshield.firewall
featureVersion : 5.5
updateAvailable : false
status : GREEN
message :
installed : true
enabled : true
allowConfiguration : false
featureId : com.vmware.vshield.vsm.messagingInfra
updateAvailable : false
status : GREEN
installed : true
enabled : true
allowConfiguration : false
featureId : com.vmware.vshield.vsm.vdr_mon
featureVersion : 5.5
updateAvailable : false
status : UNKNOWN
installed : false
enabled : true
allowConfiguration : false
featureId : com.vmware.vshield.vsm.vxlan
featureVersion : 5.5
updateAvailable : false
status : GREEN
installed : true
enabled : true
allowConfiguration : false
featureId : com.vmware.vshield.vsm.nwfabric.hostPrep
featureVersion : 6.3.4.7087695
updateAvailable : false
status : GREEN
installed : true
enabled : true
allowConfiguration : false
If i used the Get-NsxClusterStatus command in a foreach loop to get all the info for all the Cluster then and add them to an array, i will have multiple output from the above, As an example:
$AllClusters = Get-Cluster
$AllNsxClusterInfo = #()
foreach ($Cluster in $AllClusters) {
$NsxClustersInfo = $Cluster | Get-NsxClusterStatus
$AllNsxClusterInfo += $NsxClustersInfo
}
The output of the $AllNsxClusterInfo will be the same as the above example but multiple time one for each cluster.
What i need is to have a foreach loop and add within the $AllNsxClusterInfo array a property for each cluster and in this property i will have another 4 properties as follow (firewall, messagingInfra, vxlan, hostPrep) and in each of these 4 properties i will have the required data
The idea is to organize the returned value for future use in the script i am creating.
at the moment what i am doing (Which is failing) is as follow:
$GetAllClusters = Get-Cluster
$PreparedClusters = #()
$PreparedClusterInfo = #()
$i = 1
$Cluster = Get-Cluster -Name HQ-Prod
$CheckClusterPreparation = $Cluster | Get-NsxClusterStatus | Where-Object {$_.featureId -Match "com.vmware.vshield.vsm.nwfabric.hostPrep"}
$ClusterName = $Cluster.name
If ($CheckClusterPreparation.installed -Match "True") {$PreparedClusters += $Cluster.name}
$GetPreparedClusterInfo = $Cluster | Get-NsxClusterStatus
$ClsuterNumber = "ClusterNumber" + $i
$PreparedClusterInfo += New-Object -TypeName PSObject -Property #{ClusterNumber=$ClsuterNumber}
$PreparedClusterInfo.$ClusterName += New-Object -TypeName PSObject -Property #{Feature="hostPrep"}
$PreparedClusterInfo.$ClusterName.hostPrep += New-Object -TypeName PSObject -Property #{Value=($GetPreparedClusterInfo | Where-Object {$_.featureId -Match "com.vmware.vshield.vsm.nwfabric.hostPrep"})}
$PreparedClusterInfo.$ClusterName += New-Object -TypeName PSObject -Property #{Feature="vxlan"}
$PreparedClusterInfo.$ClusterName.vxlan += New-Object -TypeName PSObject -Property #{Value=($GetPreparedClusterInfo | Where-Object {$_.featureId -Match "com.vmware.vshield.vsm.vxlan"})}
$PreparedClusterInfo.$ClusterName += New-Object -TypeName PSObject -Property #{Feature="firewall"}
$PreparedClusterInfo.$ClusterName.firewall += New-Object -TypeName PSObject -Property #{Value=($GetPreparedClusterInfo | Where-Object {$_.featureId -Match "com.vmware.vshield.firewall"})}
$PreparedClusterInfo.$ClusterName += New-Object -TypeName PSObject -Property #{Feature="messagingInfra"}
$PreparedClusterInfo.$ClusterName.messagingInfra += New-Object -TypeName PSObject -Property #{Value=($GetPreparedClusterInfo | Where-Object {$_.featureId -Match "com.vmware.vshield.vsm.messagingInfra"})}
What i need at the end it when i want to retrive the VXLAN info of cluster number 2 i would use the following:
$PreparedClusterInfo.ClusterNumber2.vxlan
Hope i was able to explain my self clearly and thank you for your help
I think that you misunderstand how to construct a PSObject, and I think that you need to know what hashtables are.
Constructing a PSObject (or [PSCustomObject] using the type accelerator). You construct an object to have various properties (which you name), and can assign values for those properties. Take the first object that you create as an example:
$PreparedClusterInfo += New-Object -TypeName PSObject -Property #{ClusterNumber=$ClsuterNumber}
This creates an object using the hashtable #{ClusterNumber=$ClsuterNumber} to define the properties and their values. That hashtable has 1 key/value combination, so the object will have one property. That property will be named 'ClusterNumber', and will have a value of $ClsuterNumber (which I'm guessing is a typo for $ClusterNumber, and will use that going forward).
You could reference that object and see:
ClusterNumber: ClusterNumber1
...since 'ClusterNumber1' was the value of $ClusterNumber when the object was created. You then proceed to create more and more objects.
I think what would serve you better here is a hashtable. A hashtable, in simple terms, is a reference dictionary. You define keys (on the left), and the value for that key (on the right). Then when you reference the key later it will tell you the value. Such as:
$MyHashTable = #{
'Animal' = 'Dog'
'Food' = 'Cake'
'Vehicle' = 'HD Fatboy'
}
I defined the keys for that hashtable as Animal, Food, and Vehicle. I can then reference those later to get their value, such as $MyHashTable.Food will return Cake. Another way to do this is to create an empty hashtable, and then add Key/Value pairs to it as such:
$MyHashTable = #{}
$MyHashTable.Add('Animal','Dog')
$MyHashTable.Add('Food','Cake')
$MyHashTable.Add('Vehicle','HD Fatboy')
Or you could even do it this way:
$MyHashTable = #{}
$MyHashTable.'Animal' = 'Dog'
$MyHashTable.'Food' = 'Cake'
$MyHashTable.'Vehicle' = 'HD Fatboy'
For your purposes I would make a hashtable where the cluster numbers are the keys, and the value is a nested hashtable where you can define your various other properties such as 'vxlan'.
$PreparedClusterInfo = #{}
<Other code to collect cluster info>
$PreparedClusterInfo.$ClusterNumber = #{
hostPrep = $GetPreparedClusterInfo | Where-Object {$_.featureId -Match "com.vmware.vshield.vsm.nwfabric.hostPrep"}
vxlan = $GetPreparedClusterInfo | Where-Object {$_.featureId -Match "com.vmware.vshield.vsm.vxlan"}
firewall = $GetPreparedClusterInfo | Where-Object {$_.featureId -Match "com.vmware.vshield.firewall"}
messagingInfra = $GetPreparedClusterInfo | Where-Object {$_.featureId -Match "com.vmware.vshield.vsm.messagingInfra"}
}
You then repeat this for your other clusters once you have redefined $ClusterNumber and $GetPreparedClusterInfo.
At this point you can indeed reference $PreparedClusterInfo like you want to be able to and it will return the value you expect:
PS C:\WINDOWS\system32> $PreparedClusterInfo.ClusterNumber2.vxlan
featureId : com.vmware.vshield.vsm.vxlan
featureVersion : 5.5
updateAvailable : false
status : GREEN
installed : true
enabled : true
allowConfiguration : false

Dynamically creating the name of an existing object to Set one of its properties

I have the following code in Powershell:
function New-Row-Object-Instance {
New-Object PSObject -Property #{
Zeros = 0
Tens = 0
Twentys = 0
Thirtys = 0
Fortys = 0
Fiftys = 0
Sixtys = 0
}
$Row_Details = New-Row-Object-Instance
I Updated $Row_Details with some values. Now I have the following Labels on a Windows Form that is displayed: $Zeros, $Tens, $Twentys, $Thirtys etc.
I want to update the Labels on Form with values using the property Content.
So $Zeros.Content = 2 and so forth
foreach ($property in $Row_Details.PSObject.Properties) {
$property.Name >> $OutFile
# following creates the label names $Zeros,....
$v = -join('$',$property.Name)
# following gives error.. no property named Content
$v.Content = 2
# following gives error.. no property named Content
(-join('$',$property.Name)).Content = 2
# following gives error.. no property named Content
$v | Set-ItemProperty -Name "Content" -Value 2
# following does not update the Labels on the Form itself
Set-Variable $v -Value #{Content = "2"}
# Cannot use Set-ItemProperty -inputObject $v because cannot name property
}
I can hard code name of each Label, but was trying to do it dynamically or Elegantly...
Not sure I really understand your question, but I'll give it a try with a small example that might put you on a track to follow:
$object = New-Object PSObject -Property #{
Zeros = 0;
Tens = 0;
}
$Zeros = New-Object PSObject -Property #{
Content = "0"
}
$Tens = New-Object PSObject -Property #{
Content = "0"
}
$Zeros
$Tens
$object.PSObject.Properties | %{
$property = $_.Name
$expression = "`$$($property).Content = `"2`""
Invoke-Expression $expression
}
$Zeros
$Tens
The question is unclear to me, but maybe this helps
If you just want to modify the values for each of them you an do this:
foreach ($property in $Row_Details.PSObject.Properties)
{
$property.Value = 0
}
If you want to make new variables for each item:
foreach ($property in $Row_Details.PSObject.Properties)
{
New-Variable -Name $property.Name -Value $property.Value
}
You should be able to use Get-Variable for this is interact with those other objects. Shrinking your examples down to two you can proof of concept this fairly easily.
# Simulate your controls by creating object with those respective properties
$Zeros = New-Object PSObject -Property #{Content = 0}
$Tens = New-Object PSObject -Property #{Content = 0}
$Row_Details = New-Object PSObject -Property #{
Zeros = 1
Tens = 2
}
# Display the current Contents
write-host "Zeros: $($Zeros.Content)"
write-host "Tens: $($Tens.Content)"
foreach ($property in $Row_Details.PSObject.Properties){
$singleVariable = Get-Variable $property.name
$singleVariable.Value.content = $property.Value
}
# Show the updated Contents
write-host "Zeros: $($Zeros.Content)"
write-host "Tens: $($Tens.Content)"
The results of this being
Zeros: 0
Tens: 0
Zeros: 1
Tens: 2
The only problem I see is that all of this exists in the same scope so you my example should work as intended. However, depending where your variables are defined, you might have scope issues. If that happens you just need to experiment with the -Scope parameter of Get-Variable

How to add extra information to a custom PSObject property

Based on an export file and some checks I'm creating an array using the following block of code:
$Result += New-Object PSObject -Property #{
FQDN = $($Server.FQDN)
IP = $($Server.IP)
Description = $($Server.Description)
Remarks = $("")
}
When I reuse this $Result, for another check, how can I add extra information in the "Remarks" property?
If ($Result.IP.Contains($IP.number)){
$Result.Remarks += "Attention for this server" | Where-Object $Result.IP -eq $IP.number
}
You need to loop through the $Result array and modify the specific element with the correct IP value:
# Loop through array
$Result = foreach($object in $Result)
{
# Check if array element is interesting
if($object.IP -eq $IP.Number)
{
# Modify the object in question
$object.Remarks += "Attention!"
}
# Write the (potentially modified) object back to the array
$object
}

Powershell - Splitting multiple lines of text into individual records

Fairly new to PowerShell and wondering if someone could provide a hand with the following. Basically I have a .txt document with a number of records separated by a special character ($)(e.g)
ID
Date Entered
Name
Title
Address
Phone
$
ID
Date Entered
Name
Title
Address
Phone
$
I want to split each item between the $ into individual "records" so that I can loop through each record. So for example I could check each record above and return the record ID for all records that didn't have a phone number entered.
Thanks in advance
If each record contains a fixed number of properties you can take this approach. It loops through creating custom objects while skipping the dollar sign line.
$d = Get-Content -Path C:\path\to\text\file.txt
for ($i = 0; $i -lt $d.Length; $i+=7) {
New-Object -TypeName PsObject -Property #{
'ID' = $d[$i]
'Date Entered' = $d[$i+1]
'Name' = $d[$i+2]
'Title' = $d[$i+3]
'Address' = $d[$i+4]
'Phone' = $d[$i+5]
}
}
I once had the same requirement... this is how I did it
$loglocation = "C:\test\dump.txt"
$reportlocation = "C:\test\dump.csv"
$linedelimiter = ":"
$blockdelimiter = "FileSize"
$file = Get-Content $loglocation
$report = #()
$block = #{}
foreach ($line in $file)
{
if($line.contains($linedelimiter))
{
$key = $line.substring(0,$line.indexof($linedelimiter)).trimend()
$value = $line.substring($line.indexof($linedelimiter)+1).trimstart()
$block.Add($key,$value)
if ($block.keys -contains $blockdelimiter)
{
$obj = new-object psobject -property $block
$report += $obj
$block = #{}
}
}
}
$report
$report | Export-Csv $reportlocation -NoTypeInformation
So you cycle through each line, define key and value and add the object to a hashtable. Once the keys contains the blockdelimiter a new object gets written to an array and the hashtable gets cleared.
In my case the linedelimiter was a colon and the blockdelimiter was a valid record so you will have to make some changes. Let me know if this approach suits your needs and you can't find what to do.
P.S. By default only the noteproperties of the first object in the array will be shown so you will have to pipe the array to Select-Object and add all properties needed.
Grts.

Resources