Array in a foreach loop - powershell

What am I doing wrong here?
The mailbox has an active an inactive mailbox so it will return two mailboxes.
However, when trying to capture the output, I am only getting the last account in the array
Note, this is a simplified version of a larger script, but kept it simple for this example.
$guid = import-csv "c:\temp\Mailboxes.csv"
$DAta = New-Object psobject
$Data | Add-Member -MemberType NoteProperty -Name alias -Value $null
$Data | Add-Member -MemberType NoteProperty -Name guid -Value $null
$mbxcol = #()
#$data = $null
foreach ($G in $Guid){
$mbx = Get-mailbox $g.alias -IncludeInactiveMailbox
$data.alias = $mbx.alias
$data.guid = $mbx.guid
$MBXCol += $Data
}
$mbxcol

As explained in comments, every array element is a reference of the same object ($Data), a simple way to demonstrate using Object.ReferenceEquals Mehod with this example:
foreach ($item in 0..10) {
$data.Alias = 'mailbox{0}' -f $item
$data.Guid = [guid]::NewGuid()
$mbxcol += $data
}
[object]::ReferenceEquals($data, $mbxcol[0]) # -> True
As for how to simplify and make your code more efficient, do not add elements (+=) to a fixed collection (#( )):
$result = (Import-Csv "c:\temp\Mailboxes.csv").Alias |
Get-Mailbox -IncludeInactiveMailbox |
Select-Object Alias, Guid

A much more simple example of your code is:
$guid = ("A","B")
$Data = New-Object psobject
$Data | Add-Member -MemberType NoteProperty -Name alias -Value $null
$mbxcol = #()
foreach ($G in $Guid){
$mbx = $g
$data.alias = $mbx
$MBXCol += $Data
}
$mbxcol
As #Santiago mentioned in his comment, $Data is a reference to an object, so each time you update it, you overwrite it, even if it is in an array. To fix this, instantiate the object each loop as follows:
$guid = ("A","B")
$mbxcol = #()
foreach ($G in $Guid){
$Data = New-Object psobject
$Data | Add-Member -MemberType NoteProperty -Name alias -Value $null
$mbx = $g
$data.alias = $mbx
$MBXCol += $Data
}
$mbxcol

Related

Splitting an array to different variables

I have an XML file with multiple same-name fields like this:
<XMLDAT>
<Interpret>Crow Jonathan</Interpret>
<Interpret>Mcnabney Douglas</Interpret>
<Interpret>Haimovitz Matt</Interpret>
<Interpret>Sitkovetski Dmitri</Interpret>
</XMLDAT>
I'm trying to split these into a separate variable for each Interpret so I can be able to export it as a CSV file. Ex.
[xml]$XML = Get-Content -Path C:\TestFile.xml
$Interpret = $XML.XMLDAT.Interpret
$interpret1 = ""
$interpret2 = ""
$interpret3 = ""
$interpret4 = ""
$DATACOLLECTION = #()
$DATA = New-Object PSObject
Add-Member -inputObject $DATA -memberType NoteProperty -name "Interpret1" -value $interpret1
Add-Member -inputObject $DATA -memberType NoteProperty -name "Interpret2" -value $interpret2
Add-Member -inputObject $DATA -memberType NoteProperty -name "Interpret3" -value $interpret3
Add-Member -inputObject $DATA -memberType NoteProperty -name "Interpret4" -value $interpret4
$DATACOLLECTION += $DATA
$DATACOLLECTION | Export-Csv -append -path C:\test.csv -NoTypeInformation
I'm not sure how to proceed into splitting these into their own variables.
PowerShell supports multi-target variable assignments:
[xml]$XML = Get-Content -Path C:\TestFile.xml
$interpret1, $interpret2, $interpret3, $interpret4 = $XML.XMLDAT.Interpret
But you don't really need all those variables :)
You could construct the final object by dynamically adding all the "Interpret" node values to a hashtable and then convert that to an object:
[xml]$XML = Get-Content -Path C:\TestFile.xml
$properties = [ordered]#{}
$XML.XMLDAT.Interpret |ForEach-Object -Begin {$number = 1} -Process {
$properties["Interpret$($number++)"] = "$_"
}
#( [pscustomobject]$properties ) |Export-Csv -Append -Path C:\test.csv -NoTypeInformation
Was able to get desired result using this ForEach loop:
ForEach ($Interprets in $Interpret){
$interpret1 = $Interpret[0]
$interpret2 = $Interpret[1]
$interpret3 = $Interpret[2]
$interpret4 = $Interpret[3]
}
I would just do one column with four rows:
$xml.xmldat.interpret | % { [pscustomobject]#{Interpret = $_} }
Interpret
---------
Crow Jonathan
Mcnabney Douglas
Haimovitz Matt
Sitkovetski Dmitri

Sharepoint - Export all data to csv with powershell

I am attempting to use a script provided by Thriggle See His Answer Here and am having some issues with it. It works almost flawlessly for what I am doing - EXCEPT - it doesn't export the Created By, Created Date, Modified By, and Modified Date.
Is there a way to add those fields into the script?
Here is his script:
$url = "$url"
$listName = "$list"
$path ="c:\ColumnsOfList.csv"
$web = Get-SPWeb $url
$list = $web.Lists.TryGetList($listName)
$fields = $list.ContentTypes | %{ $_.FieldLinks } | select Name, DisplayName
$items = #() #array to store objects representing list items
$list.items | %{
$item = $_;
$hash = #{}; #hash table to store field name-value pairs
$fields | %{ $hash[$_.DisplayName] = $item[$_.Name] };
$items += new-object psobject -Property $hash }
$items | Export-Csv -Path $path
You could try this one.
Here is my test code.
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
#Get the Web
$web = Get-SPWeb -identity "http://sp:12001"
#Get the Target List
$list = $web.Lists["OrderDetails"]
#Array to Hold Result - PSObjects
$ListItemCollection = #()
#Get All List items where Status is "In Progress"
$list.Items | foreach {
$ExportItem = New-Object PSObject
$ExportItem | Add-Member -MemberType NoteProperty -name "Title" -value $_["Title"]
$ExportItem | Add-Member -MemberType NoteProperty -Name "OrderDate" -value $_["OrderDate"]
$ExportItem | Add-Member -MemberType NoteProperty -name "CreatedBy" -value $_["Author"]
$ExportItem | Add-Member -MemberType NoteProperty -name "Created" -value $_["Created"]
#Add the object with property to an Array
$ListItemCollection += $ExportItem
}
#Export the result Array to CSV file
$ListItemCollection | Export-CSV "C:\Lee\ListData.csv" -NoTypeInformation
#Dispose the web Object
$web.Dispose()

Trying to create a custom object list? hash? - Unsure

I'm trying trying to get two properties from two separate commands and add them to a variable to be able to further evaluate.
I was told a custom object would work...
Clear-Host
Add-PSSnapin citrix* -ErrorAction SilentlyContinue
$DRSrvs = Get-XAServer drptsw00* | select -ExpandProperty servername
$hash = $null
$hash = #{}
foreach ($DR in $DRSrvs) {
$hash = New-Object PsObject -Property #{
servername = $DR
Logins = (Get-XALoadEvaluator -ServerName $DR).LoadEvaluatorName
}
}
A hashtable is for mapping (unique) keys to values. If you need to map different servernames to login names use a hashtable, otherwise use custom objects. Either way you need to handle the data structures correctly.
Hashtable:
$hash = #{}
foreach ($DR in $DRSrvs) {
$hash[$DR] = (Get-XALoadEvaluator -ServerName $DR).LoadEvaluatorName
}
Custom object list:
$list = foreach ($DR in $DRSrvs) {
New-Object PsObject -Property #{
servername = $DR
Logins = (Get-XALoadEvaluator -ServerName $DR).LoadEvaluatorName
}
}
Assigning something to a variable in a loop replaces the previous value in that variable with each iteration, leaving you with just the last value after the loop finishes.
I used this method and got a very clean output. Citrix SDK for Powershell if very funny and has lots of gotchas.
Clear-Host
Add-PSSnapin citrix* -ErrorAction SilentlyContinue
$OutputData = $null
$OutputData = #()
$Srvs = Get-XAServer Srv123* | Select-Object -ExpandProperty ServerName
$object = New-Object PSObject
Add-Member -InputObject $object -MemberType NoteProperty -Name Servername -Value ""
Add-Member -InputObject $object -MemberType NoteProperty -Name LoadEval -Value ""
foreach ($Srv in $Srvs) {
$servername= $Srv
$LoadEval = ((Get-XALoadEvaluator -ServerName $Srv).LoadEvaluatorName)
$appObject = New-Object System.Object
$appObject |
Add-Member -MemberType NoteProperty -Name "ServerName" -Value $servername -PassThru |
Add-Member -MemberType NoteProperty -Name "LoadEval" -Value $LoadEval
$outputData += $appObject
}

How do I create a custom array in powershell?

I am trying to sort out arrays in PS. The problem I am trying to solve is to return a list of replicated VMs and some basic stats.
Having read through a multitude of sites and suggestions the best I could get is the following script:
$myArray = #()
$vms = get-vm | where-object { $_.replicationstate -ne "Disabled" }
foreach ($vm in $vms)
{
$vmRepl = Get-VMReplication
$replfreq = (New-TimeSpan -seconds $vmRepl.replicationfrequencysec)
$lastrepl = $vmRepl.lastreplicationtime
$nextrepl = $lastrepl + $replfreq
$secfrom = [math]::Round((New-TimeSpan -start $vmRepl.lastreplicationtime).TotalSeconds)
$secto = [math]::Round((New-TimeSpan -end ($vmRepl.lastreplicationtime + $replfreq)).TotalSeconds)
$obj = New-Object System.Object
$obj | Add-Member -MemberType NoteProperty -Name Name -Value $vmRepl.Name
$obj | Add-Member -MemberType NoteProperty -Name ReplicationFrequencySec -Value $vmRepl.replicationfrequencysec
$obj | Add-Member -MemberType NoteProperty -Name SecondsSinceLastRepl -Value $secfrom
$obj | Add-Member -MemberType NoteProperty -Name SecondsUntilNextRepl -Value $secto
$obj | Add-Member -MemberType NoteProperty -Name LastReplication -Value $lastrepl
$obj | Add-Member -MemberType NoteProperty -Name NextReplication -Value $nextrepl
$myArray += $obj
}
write-output $myArray | ft -AutoSize
This works when I only have one VM, but when there are multiple ones the output appears within curly braces.
I think I am on the right track finally. I just need someone to help me sort out the remaining piece(s) of the puzzle.
The other weird thing is that the New-TimeSpan stops working with multiple VMs.
Thanks in advance.
Braden
The biggest probem with your script is : you start a foreach loop but you don't use any element from the array you're looping through. You just loop through the same data for each item in the array.
Basicly the current script retreives a list of VMs, then for each entry you fetch the replication status of all the machines in the array. Then you do some processing on this set and then add this set to a new object (and this goes on for each entry in your list). For a good explanation on the usage of foreach see
http://blogs.technet.com/b/heyscriptingguy/archive/2014/04/28/basics-of-powershell-looping-foreach.aspx
I would also suggest to use [PSCustomObject] instead of new-object / add-member : it's easier to use, the code is easier to read and it also maintains the order of the properties you set with it (since you're using get-vm I assume you have PS3 or higher)
I think you might be overwriting the same object ($obj) in each foreach() iteration.
Try this instead:
$VMs = Get-Vm | Where-Object {$_.ReplicationState -ne 'Disabled'}
$MyVmReplicationStatus = foreach ($VM in $VMs){
$VMReplStatus = Get-VMReplication
$LastRepTime = $VMReplStatus.LastReplicationTime
$ReplFreqSecs = $VMReplStatus.ReplicationFrequencySec
$ReplFrequency = (New-TimeSpan -Seconds $ReplFreqSecs)
$Props = #{
Name = $VMReplStatus.Name
ReplicationFrequencySec = $ReplFreqSecs
SecondsSinceLastRepl = [System.Math]::Round((New-TimeSpan -Start $LastRepTime).TotalSeconds)
SecondsUntilNextRepl = [System.Math]::Round((New-TimeSpan -End ($LastRepTime + $ReplFrequency)).TotalSeconds)
LastReplication = $LastRepTime
NextReplication = $LastRepTime + $ReplFrequency
}
New-Object -TypeName psobject -Property $Props
}
Write-Output -InputObject $MyVmReplicationStatus | Format-Table -AutoSize

Sum Columns Using Powershell

I have written the following PowerShell script for getting disk space information for servers in our environment.
$servers = Get-Content E:\POC.txt
$array = #()
foreach($server in $servers){
$sysinfo = Get-WmiObject Win32_Volume -ComputerName $server
for($i = 0;$i -lt $sysinfo.Count; $i++){
$sname = $sysinfo[$i].SystemName
$servername = $server
$label = $sysinfo[$i].Label
if(($label) -and (!($label.Contains("FILLER")))){
write-host "Processing $label from $server"
$name = $sysinfo[$i].Name
$capacity = [math]::round(($sysinfo[$i].Capacity/1GB),2)
$fspace = [math]::round(($sysinfo[$i].FreeSpace/1GB),2)
$sused = [math]::round((($sysinfo[$i].Capacity - $sysinfo[$i].FreeSpace)/1GB),2)
$fspacepercent = [math]::Round((($sysinfo[$i].FreeSpace*100)/$sysinfo[$i].Capacity),2)
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "SystemName" -Value $sname
$obj | Add-Member -MemberType NoteProperty -Name "ServerName" -Value $server
$obj | Add-Member -MemberType NoteProperty -Name "Label" -Value $label
$obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $name
$obj | Add-Member -MemberType NoteProperty -Name "Capacity(GB)" -Value $capacity
$obj | Add-Member -MemberType NoteProperty -Name "FreeSpace(GB)" -Value $fspace
$obj | Add-Member -MemberType NoteProperty -Name "Used(GB)" -Value $sused
$obj | Add-Member -MemberType NoteProperty -Name "FreeSpace%" -Value $fspacepercent
$array += $obj
}
}
$array += write-output " "
$totalSize = ($array | Measure-Object 'Capacity(GB)' -Sum).Sum
$array += $totalsize
$array += write-output " "
}
$filename = "E:\VolumeReport.csv"
$array | Export-CSV $filename -NoTypeInformation
One additional requirement here is to get the sum of the columns for Capacity, Size and Freespace for each server. I tried using Measure-Object but no success.
No values are getting outputted here. Just blank. Please look into this and kindly assist.
Let try this on for size shall we.
$servers = Get-Content E:\POC.txt
$propertyOrdered = "SystemName","ServerName","Label","Name","Capacity(GB)","FreeSpace(GB)","Used(GB)","FreeSpace%"
$filename = "C:\temp\VolumeReport.csv"
('"{0}"' -f ($propertyOrdered -join '","')) | Set-Content $filename
foreach($server in $servers){
$sysinfo = Get-WmiObject Win32_Volume -ComputerName $server
$serverDetails = #()
for($i = 0;$i -lt $sysinfo.Count; $i++){
$sname = $sysinfo[$i].SystemName
$servername = $server
$label = $sysinfo[$i].Label
if(($label) -and (!($label.Contains("FILLER")))){
write-host "Processing $label from $server"
$name = $sysinfo[$i].Name
$capacity = [math]::round(($sysinfo[$i].Capacity/1GB),2)
$fspace = [math]::round(($sysinfo[$i].FreeSpace/1GB),2)
$sused = [math]::round((($sysinfo[$i].Capacity - $sysinfo[$i].FreeSpace)/1GB),2)
$fspacepercent = [math]::Round((($sysinfo[$i].FreeSpace*100)/$sysinfo[$i].Capacity),2)
$props = #{
"SystemName" = $sname
"ServerName" = $server
"Label" = $label
"Name" = $name
"Capacity(GB)" = $capacity
"FreeSpace(GB)" = $fspace
"Used(GB)" = $sused
"FreeSpace%" = $fspacepercent
}
# Build this server object.
$serverDetails += New-Object PSObject -Property $props
}
}
# Output current details to file.
$serverDetails | Select $propertyOrdered | ConvertTo-Csv -NoTypeInformation | Select-Object -Skip 1 | Add-Content $filename
#Calculate Totals and append to file.
$totals = '"","","","Totals",{0},{1},{2},""' -f ($serverDetails | Measure-Object -Property "Capacity(GB)" -Sum).Sum,
($serverDetails | Measure-Object -Property "FreeSpace(GB)" -Sum).Sum,
($serverDetails | Measure-Object -Property "Used(GB)" -Sum).Sum
$totals | Add-Content $filename
}
Part of the issue here is that you were mixing object output and static string output which most likely would have been holding you back. I tidied up the object generation in a way that should be 2.0 compliant. Not that what you were going was wrong in anyway but this is a little more pleasing to the eye then all the Add-Members
I removed $array since it did not have a place anymore since the logic here is constantly output data to the output file as supposed to storing it temporarily.
For every $server we build an array of disk information in the variable $serverDetails. Once all the disks have been calculated (using your formulas still) we then create a totals line. You were not really clear on how you wanted your output so I guessed. The above code should net output like the following. (It looks a lot nicer in Excel or in a csv aware reader. )
"SystemName","ServerName","Label","Name","Capacity(GB)","FreeSpace(GB)","Used(GB)","FreeSpace%"
"server01","server01","System Reserved","\\?\Volume{24dbe945-3ea6-11e0-afbd-806e6f6e6963}\","0.1","0.07","0.03","71.85"
"","","","Totals",0.1,0.07,0.03,""
"server02","server02","System Reserved","\\?\Volume{24dbe945-3ea6-11e0-afbd-806e6f6e6963}\","0.1","0.07","0.03","69.27"
"server02","server02","images","I:\","1953.12","152.1","1801.02","7.79"
"server02","server02","Data","E:\","79.76","34.59","45.18","43.36"
"","","","Totals",2032.98,186.76,1846.23,""