Not able to add data to csv file powershell - powershell

I am trying to add data to an csv file.
I am creating the csv with header first and then trying to add the rows. but it is returning blank csv file
$props=[ordered]#{
ServerName=''
SystemFolderPath=''
IdenityReference=''
FileSystemRights=''
}
New-Object PsObject -Property $props |
Export-Csv "C:\status_report.csv" -NoTypeInformation
$serverlist = Get-Content -Path "C:\ServerList.txt"
foreach($server in $serverlist)
{
$paths_list = $env:Path -Split ';'
Foreach ($sys_Path in $paths_list)
{
$Permissions = Get-Acl -Path $sys_Path
$Users_Permissions = $Permissions.Access | Where-Object {$_.IdentityReference}
#$Users_Permission
Foreach ($user in $Users_Permissions)
{
$IdenityReference = $user.IdentityReference.Value
$FileSystemRights = $user.FileSystemRights
$NewLine = "{0},{1},{2},{3}" -f $server,$sys_Path,$IdenityReference,$FileSystemRights
$NewLine | Export-Csv -Path "C:\status_report.csv" -Append -NoTypeInformation -Force
}
}
}
Please let me know what I am doing wrong here

The main reason why you're seeing this is because Export-Csv expects an object or object[] through the pipeline and you're passing a formatted string instead. This is specified on MS Docs:
Do not format objects before sending them to the Export-CSV cmdlet. If Export-CSV receives formatted objects the CSV file contains the format properties rather than the object properties.
PS /> 'server01,C:\Windows,Computer\User,FullControl' | ConvertTo-Csv
"Length"
"45"
Instead of appending to a CSV which is quite inefficient, unless there is a specific need for this, what you will want to do is collect the results first and then export them.
I'm not too sure why | Where-Object { $_.IdentityReference } is needed, I left it there but I don't think it's needed.
Regarding $serverlist, if you will run this on remote hosts you would be better of using Invoke-Command since it allows parallel invocations. The outer loop wouldn't be needed in that case:
$serverlist = Get-Content -Path "C:\ServerList.txt"
# Collect results here
$result = Invoke-Command -ComputerName $serverlist -ScriptBlock {
$paths_list = $env:Path -Split [System.IO.Path]::PathSeparator
foreach($sys_Path in $paths_list)
{
$Permissions = (Get-Acl -Path $sys_Path).Access
foreach($acl in $Permissions)
{
if(-not $acl.IdentityReference)
{
continue
}
[pscustomobject]#{
ComputerName = $env:ComputerName
SystemFolderPath = $sys_Path
IdenityReference = $acl.IdentityReference.Value
FileSystemRights = $acl.FileSystemRights
}
}
}
} -HideComputerName
$result | Export-Csv -Path "C:\status_report.csv" -NoTypeInformation

Accept Santiago above but this is what I did with what you wrote.
$props = [ordered]#{
ServerName = ''
SystemFolderPath = ''
IdenityReference = ''
FileSystemRights = ''
}
New-Object PsObject -Property $props |
Export-Csv "C:\status_report.csv" -NoTypeInformation
$serverlist = Get-Content -Path "C:\ServerList.txt"
$result = $serverlist | ForEach-Object {
foreach ($server in $_) {
$paths_list = $null
$paths_list = $env:Path -Split ';'
Foreach ($sys_Path in $paths_list) {
$Permissions = Get-Acl -Path $sys_Path
$Users_Permissions = $Permissions.Access | Where-Object { $_.IdentityReference }
#$Users_Permission
Foreach ($user in $Users_Permissions) {
$IdenityReference = $null
$FileSystemRights = $null
$IdenityReference = $user.IdentityReference.Value
$FileSystemRights = $user.FileSystemRights
[PSCustomObject]#{
Server = $server
Sys_Path = $sys_Path
Referecent = $IdenityReference
Rights = $FileSystemRights
}
$sys_Path = $null
}
}
}
}
$result | Export-Csv -Path "C:\status_report.csv" -NoTypeInformation

Santiago's answer is correct and contains all the required information for you to understand the issue you have here.
I just wanted to provide you with the minimum modifications to be done in your script:
Replace the $props custom object by a function (i.e CreateCustomObject)
function CreateCustomObject($val1, $val2, $val3, $val4) {
$NewObject = New-Object PSObject ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "ServerName" -Value $val1 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "SystemFolderPath" -Value $val2 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "IdenityReference" -Value $val3 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "FileSystemRights" -Value $val4 ;
return $NewObject ;
}
Replace the String Variable $NewLine by an Array
$NewLine = #()
$NewLine += CreateCustomObject $server $sys_Path $IdenityReference $FileSystemRights
Write to CSV only once data is collected (move the command to the end of the script)
So the final script will look something like that:
function CreateCustomObject($val1, $val2, $val3, $val4) {
$NewObject = New-Object PSObject ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "ServerName" -Value $val1 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "SystemFolderPath" -Value $val2 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "IdenityReference" -Value $val3 ;
Add-Member -InputObject $NewObject -MemberType NoteProperty -Name "FileSystemRights" -Value $val4 ;
return $NewObject ;
}
$serverlist = Get-Content -Path "C:\Temp\ServerList.txt"
$NewLine = #()
foreach($server in $serverlist) {
$paths_list = $env:Path -Split ';'
Foreach ($sys_Path in $paths_list) {
$Permissions = Get-Acl -Path $sys_Path
$Users_Permissions = $Permissions.Access | Where-Object {$_.IdentityReference}
#$Users_Permission
Foreach ($user in $Users_Permissions) {
$IdenityReference = $user.IdentityReference.Value
$FileSystemRights = $user.FileSystemRights
$NewLine += CreateCustomObject $server $sys_Path $IdenityReference $FileSystemRights
}
}
}
$NewLine | Export-Csv -Path "C:\temp\status_report.csv" -NoTypeInformation -Force

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

Assistance required with Powershell Script pls

I am trying to create a script which searches a list of computers/servers and identifies if a user profile exists for each member of an AD group called 'TestDisabledUsers'. Results are then piped to a CSV file with the format below.
ComputerName WMI_Connection Pingable Profile_Search
Computer1 Server IS Contactable TRUE User1 Exists ; User2 Exists ; User3 No Profile
Computer2 Server IS Contactable TRUE User1 No Profile ; User2 Exists ; User3 Exists
Computer3 Server IS Contactable TRUE User1 Exists ; User2 Exists ; User3 No Profile
Current script is not working at present as it only shows 1 user in the Profile_Search output :-(
Apologies if this is a simple solution, I'm not the best coder ;-) Any help very much appreciated.
So far I have the following powershell script :-
Clear-History
$ErrorActionPreference= 'silentlycontinue'
$outputFolderName = 'ProfileAudit ' + $(Get-Date -f dd-MM-yyyy)
$outputpath = "C:\temp\$outputFolderName"
If(!(test-path $outputpath))
{
New-Item -ItemType Directory -Force -Path $outputpath | out-null
}
$computers = Get-Content -path C:\Temp\svrs.txt
$report = #()
foreach ($computer in $computers) {
$Ping = Test-Connection -ComputerName $computer -Quiet -count 2
$wmi = gwmi win32_bios -ComputerName $computer
if ($wmi)
{
$WMIResult = 'Server IS Contactable'
$profiles = Get-ADGroupMember -Identity TestDisabledUsers | Foreach {$_.SamAccountName}
foreach ($profile in $profiles) {
$user = Get-CimInstance -ComputerName $computer -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $profile }
if ($user)
{
$profileexists = ("$profile Exists") -join ' ; '
#$user | Remove-CimInstance
}
else {
$profileexists = ("$profile No Profile") -join ' ; '
}
}
$tempreport = New-Object -TypeName PSObject
$tempreport | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer.ToUpper()
$tempreport | Add-Member -MemberType NoteProperty -Name WMI_Connection -Value $WMIResult
$tempreport | Add-Member -MemberType NoteProperty -Name Pingable -Value $Ping
$tempreport | Add-Member -MemberType NoteProperty -Name Profile_Search -Value $profileexists
$report += $tempreport
}
else
{
$WMIResult = 'Server NOT Contactable'
$tempreport = New-Object PSObject
$tempreport | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer.ToUpper()
$tempreport | Add-Member -MemberType NoteProperty -Name WMI_Connection -Value $WMIResult
$tempreport | Add-Member -MemberType NoteProperty -Name Pingable -Value $Ping
$tempreport | Add-Member -MemberType NoteProperty -Name Profile_Search -Value $null
$report += $tempreport
}
}
$CSVFileName = 'ProfileAudit ' + $(Get-Date -f dd-MM-yyyy) + '.csv'
$report | Export-Csv $outputpath\$CSVFileName -NoTypeInformation
Try the below. The main issue is that you're overwriting $profileexists instead of appending to it in the profile loop.
Clear-History
$outputpath = "C:\temp\ProfileAudit $(Get-Date -f dd-MM-yyyy)"
If(!(test-path $outputpath))
{
New-Item -ItemType Directory -Force -Path $outputpath | out-null
}
$report = Get-Content -path C:\Temp\svrs.txt | % {
$ping = Test-Connection -ComputerName $_ -Quiet -count 2
$profileexists = $null
if(gwmi win32_bios -ComputerName $_)
{
$WMIResult = 'Server IS Contactable'
$profiles = Get-ADGroupMember -Identity TestDisabledUsers | select -ExpandProperty SamAccountName
foreach ($profile in $profiles) {
$user = Get-CimInstance -ComputerName $_ -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $profile }
if ($user)
{
$profileexists += "$profile Exists;"
}
else {
$profileexists += "$profile No Profile;"
}
}
}
else
{
$WMIResult = 'Server NOT Contactable'
}
[pscustomobject]#{
ComputerName = $_.ToUpper()
WMI_Connection = $WMIResult
Pingable = $Ping
Profile_Search = $profileexists
}
}
$CSVFileName = 'ProfileAudit ' + $(Get-Date -f dd-MM-yyyy) + '.csv'
$report | Export-Csv $outputpath\$CSVFileName -NoTypeInformation

Powershell querying users in Active Directory

I am having a hard time figuring out a more efficient way of querying info from AD. As it stands I import a .csv file of active users from our student information system. Then I want to create a new .csv file of active users info from AD. As such, I am querying AD on every user (approx 10k students.) I have a feeling I could somehow accomplish this with one query, but no luck. The students match on a numeric ID that is stored in the AD title field. The code does work, however it takes hours to run. Here is what I use:
$Users = Import-Csv "c:\DASLExport.csv" -Header #("a") | Select a
$usersarray = #()
ForEach ($Row in $Users) {
$userSearchString = $Row.a
$currentUser = (Get-ADUser -Filter {Title -eq $userSearchString} -Properties title, SamAccountName, extensionAttribute1)
$UserObj = New-Object PSObject
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "ID" -Value $($currentUser.title)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Username" -Value $($currentUser.SamAccountName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Password" -Value $($currentUser.extensionAttribute1)
$usersarray += $UserObj
}
If($usersarray.count -gt 0) {$usersarray | Export-Csv -Path 'c:\users.csv' -NoTypeInformation}
I think, instead of query each user with Get-ADUser , Get all users with title at once and save it to a variable, Then query this variable instead.
Also, Regular Arrays are in fixed size, which mean that each time you insert new element you actually create new array and copy all the data into it, and you repeat it again and again, which take much time. so switch to ArrayList which is intend to grow, it will be much faster.
Check it yourself:
$ArrayList = New-Object System.Collections.ArrayList
$RegularArray = #()
Measure-Command { 1..10000 | % {[void]$ArrayList.Add($_)} }
Measure-Command { 1..10000 | % {$RegularArray += $_ } }
So For example try this:
$Users = Import-Csv "c:\DASLExport.csv" -Header #("a") | Select a
$ADUsers = Get-ADUser -Filter {Title -ne "$null"} -Properties title, SamAccountName, extensionAttribute1
$Usersarray = New-Object System.Collections.ArrayList
ForEach ($Row in $Users) {
$userSearchString = $Row.a
$currentUser = $ADUsers | ? {$_.Title -eq $userSearchString}
if (!$currentUser) {continue}
$UserObj = New-Object PSObject
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "ID" -Value $($currentUser.title)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Username" -Value $($currentUser.SamAccountName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Password" -Value $($currentUser.extensionAttribute1)
[void]$usersarray.Add($UserObj)
}
If($usersarray.count -gt 0) {$usersarray | Export-Csv -Path 'c:\users.csv' -NoTypeInformation}
While #Avshalom's answer is useful, it can be improved:
[CmdletBinding()]
param
(
[Parameter(Position = 0)]
[ValidateScript({Test-Path -Path $PSItem -PathType Leaf})]
[string]
$Path = 'C:\DASLExport.csv',
[Parameter(Position = 1)]
[ValidateScript({Test-Path -Path $PSItem -PathType Leaf -IsValid})]
[string]
$Destination = 'C:\users.csv'
)
$csv = Import-Csv -Path $Path -Header a
$users = #(Get-ADUser -Filter 'Title -ne "$null"' -Properties Title, SamAccountName, extensionAttribute1)
$collection = foreach ($row in $csv)
{
$title = $row.a
$user = $users.Where{$PSItem.Title -eq $title}
if (-not $user)
{
Write-Warning -Message "User $title not found."
continue
}
[pscustomobject]#{
ID = $user.Title
Username = $user.SamAccountName
Password = $user.extensionAttribute1
}
}
$collection | Export-Csv -Path $Destination -NoTypeInformation
You can assign the output of the foreach loop to a variable directly, avoiding the need to manage a list object (although if you do opt for a list, you should use System.Collections.Generic.List<Type> since ArrayList is deprecated). Additionally, you don't need to use a Select-Object statement since your csv was already loaded and it just processes it twice in that scenario. The biggest speed improvement is not querying AD thousands of times, keeping it in a single object, but MOSTLY by not using [array]/#().
Speed comparisons:
$L = 1..100000
Measure-Command {$col = foreach ($i in $L) { $i }}
~70ms
Measure-Command {$col = [System.Collections.Generic.List[int]]::new(); foreach ($i in $L) { $col.Add($i) }}
~110ms
Measure-Command {$col = #(); foreach ($i in $L) { $col += $i }}
~46 SECONDS

Export list/array to CSV in Powershell

done some googling but answers I have found seem to be more complex than what I need. I have a simple script to fetch DNS cache entries, and I'd like to export the results to a CSV in the most "powershell" manner. Code looks like this:
function Get-Dns
{
$domains = #()
$cmdOutput = Invoke-Command -ScriptBlock {ipconfig /displaydns}
ForEach ($line in $($cmdOutput -split "`r`n"))
{
if ($line -like '*Record Name*'){
$domain = $line -split ":"
$domains += $domain[1]
}
}
so I have an array, $domains, which I would like to use Export-CSV to essentially output a one column CSV with one domain per line. Using Export-CSV seems to just output the length of each element rather than the contents itself. Any help is appreciated!
The most PowerShell way:
(ipconfig /displaydns|where{$_-match'Record Name'})|%{$_.split(":")[1].Trim()}>dnscache.txt
"ipconfig /displaydns" is going to give you back a big'ol string array, which is going to be harder to work with. Try the native commandlets for DNS manipulation:
Get-DnsClientCache | Export-Csv -Path .\stuff.csv
If you're using Windows 7 or earlier, try this...
$dns_client_cache = #()
$raw_dns_data = ipconfig /displaydns
for ($element = 3; $element -le $raw_dns_data.length - 3; $element++) {
if ( $raw_dns_data[$element].IndexOf('Record Name') -gt 0 ) {
if ( $dns_entry ) { $dns_client_cache += $dns_entry }
$dns_entry = New-Object -TypeName PSObject
Add-Member -InputObject $dns_entry -MemberType NoteProperty -Name 'RecordName' -Value $raw_dns_data[$element].Split(':')[1].Trim()
} elseif ( $raw_dns_data[$element].IndexOf('Record Type') -gt 0 ) {
Add-Member -InputObject $dns_entry -MemberType NoteProperty -Name 'RecordType' -Value $raw_dns_data[$element].Split(':')[1].Trim()
} elseif ( $raw_dns_data[$element].IndexOf('Time To Live') -gt 0 ) {
Add-Member -InputObject $dns_entry -MemberType NoteProperty -Name 'TimeToLive' -Value $raw_dns_data[$element].Split(':')[1].Trim()
} elseif ( $raw_dns_data[$element].IndexOf('Data Length') -gt 0 ) {
Add-Member -InputObject $dns_entry -MemberType NoteProperty -Name 'DataLength' -Value $raw_dns_data[$element].Split(':')[1].Trim()
} elseif ( $raw_dns_data[$element].IndexOf('Section') -gt 0 ) {
Add-Member -InputObject $dns_entry -MemberType NoteProperty -Name 'Section' -Value $raw_dns_data[$element].Split(':')[1].Trim()
} elseif ( $raw_dns_data[$element].IndexOf('CNAME Record') -gt 0 ) {
Add-Member -InputObject $dns_entry -MemberType NoteProperty -Name 'CNAMERecord' -Value $raw_dns_data[$element].Split(':')[1].Trim()
}
}
$dns_client_cache | Export-Csv -Path .\dns_stuff.csv -Force -NoTypeInformation
Sorry! I know it's messy.
I ended up going with to export multiple value array to csv
$Data | %{$_} | export-csv -nti -Path C:\

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,""