Sum Columns Using Powershell - 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,""

Related

Powershell add a new line in output CSV file if multiple entries found

I am doing an export of mailboxes from Office 365.
Some mailboxes the have the same primarysmtpaddress are active and also Inactive.
I enter code here have the following code:
$MBX = import-csv "c:\retention\LegalHold\test.csv"
$MBX | Add-Member -MemberType NoteProperty -Name Retention -Value NoPolicy
$MBX | Add-Member -MemberType NoteProperty -Name NoMailbox -Value $False
$MBX | Add-Member -MemberType NoteProperty -Name IsInactiveMailbox -Value $null
$MBX | Add-Member -MemberType NoteProperty -Name PrimarySmtpAddress -Value $null
$count=0
$fullcount = ($mbx | measure-object).count
foreach ($M in $MBX){
$policy = $null
$count++
$search = $m.custodian.tostring()
Write-progress -activity "Checking Mailbox $count out of $fullcount --- $search " -percentcomplete (($count / $fullcount)*100) -status "Processing"
Try{
$ErrorActionPreference = 'Stop'
$Policy = Invoke-Command -scriptblock {Get-EXOMailbox $m.custodian -IncludeInactiveMailbox -properties primarysmtpaddress,inplaceholds,isinactivemailbox,LitigationHoldEnabled,LitigationHoldDuration,Office}
#$Policy = Invoke-Command -scriptblock {Get-Mailbox $m.name -IncludeInactiveMailbox}
#$m.MBXFound = $True
$m.IsInactiveMailbox = $Policy.IsInactiveMailbox -join ','
$m.PrimarySmtpAddress = $Policy.PrimarySmtpAddress -join ','
}Catch{$m.NoMailbox = "$True"}
}
$MBX | export-csv C:\retention\legalhold\test_check.csv -NoTypeInformation
Instead of outputting to the CSV like this:
I'd like to have the mailboxes with an active and inactive mailbox on separate lines like this:
You would need to loop through the results of $Policy instead of just using -join.
$MBX = import-csv "c:\retention\LegalHold\test.csv"
$MBX | Add-Member -MemberType NoteProperty -Name Retention -Value NoPolicy
$MBX | Add-Member -MemberType NoteProperty -Name NoMailbox -Value $False
$MBX | Add-Member -MemberType NoteProperty -Name IsInactiveMailbox -Value $null
$MBX | Add-Member -MemberType NoteProperty -Name PrimarySmtpAddress -Value $null
$count=0
$fullcount = ($mbx | measure-object).count
$Output = foreach ($M in $MBX){
$policy = $null
$count++
$search = $m.custodian.tostring()
Write-progress -activity "Checking Mailbox $count out of $fullcount --- $search " -percentcomplete (($count / $fullcount)*100) -status "Processing"
Try{
$ErrorActionPreference = 'Stop'
$Policy = Invoke-Command -scriptblock {Get-EXOMailbox $m.custodian -IncludeInactiveMailbox -properties primarysmtpaddress,inplaceholds,isinactivemailbox,LitigationHoldEnabled,LitigationHoldDuration,Office}
#$Policy = Invoke-Command -scriptblock {Get-Mailbox $m.name -IncludeInactiveMailbox}
#$m.MBXFound = $True
$Policy | ForEach-Object {
$m.IsInactiveMailbox = $_.IsInactiveMailbox
$m.PrimarySmtpAddress = $_.PrimarySmtpAddress
$m
}
}Catch{$m.NoMailbox = "$True";$m}
}
$Output | export-csv C:\retention\legalhold\test_check.csv -NoTypeInformation

Not able to add data to csv file 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

How do I include on certain values when summing from a powershell array?

I want to get a sum for the total space a SQL server is using for Data and Log files.
From a few other sources on the internet I have the following code: (Yes, I'm a Powershell Noob)
$servers = "SQLSERVER1"
$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 "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 "Used(GB)" -Value $sused
$array += $obj
}
}
$array += write-output " "
$totalSize = ($array | Measure-Object 'Used(GB)' -Sum).Sum
$array += $totalsize
$array += write-output " "
}
$totalsize
This gives me the result of:
Processing Recovery from SQL-Group1-DB
Processing System from SQL-Group1-DB
Processing SQLInstall from SQL-Group1-DB
Processing OCTOPUS from SQL-Group1-DB
Processing SQL_DATA from SQL-Group1-DB
Processing SQL_LOG from SQL-Group1-DB
Processing TEMP_DB from SQL-Group1-DB
Processing SSS_X64FREV_EN-US_DV9 from SQL-Group1-DB
274.92
Of course that has included EVERY drive on the server.
I only want the SQL_DATA and SQL_LOG drives included.
Any ideas on how to achieve this?
(Happy to use entirely different code if it works)
TIA
If you do not want the info for all drives on the server, you could limit the results of the Get-WmiObject cmdlet in the $sysinfo variable by using a Where-Object{} clause like:
$sysinfo = Get-WmiObject Win32_Volume -ComputerName $server |
Where-Object { 'SQL_DATA', 'SQL_LOG' -contains $_.Label }

Powershell custom append object to csv file

I'm trying to output a custom object to a csv formatted text file as I loop through a for each. One object per line.
But nothing is written to the file.
Is it something with types to be converted ?
$rechten = Get-ADGroupMember -Identity $v -Recursive -ERRORACTION silentlycontinue | Get-ADUser -Property DisplayName -ERRORACTION silentlycontinue | Select-Object Name
Write-Host -ForegroundColor Yellow "ADgroup $v wordt uitgevlooid."
foreach ($rechtenhouder in $rechten) {
$objResults = New-Object PSObject
$objResults | Add-Member -MemberType NoteProperty -Name DirectoryPath -Value $objPath
$objResults | Add-Member -MemberType NoteProperty -Name Identity -Value $rechtenhouder.name
$objResults | Add-Member -MemberType NoteProperty -Name Systemrights -Value $accessRight.FileSystemRights
$objResults | Add-Member -MemberType NoteProperty -Name systemrightstype -Value $accessRight.accesscontroltype
$objResults | Add-Member -MemberType NoteProperty -Name isinherited -Value $accessRight.isinherited
$objResults | Add-Member -MemberType NoteProperty -Name inheritanceflags -Value $accessRight.inheritanceflags
$objResults | Add-Member -MemberType NoteProperty -Name rulesprotected -Value $objACL.areaccessrulesprotected
$objResults | Add-Member -MemberType NoteProperty -Name Adtype -Value "User"
$arrResults += $objResults
Add-Content $exportpathtxtappend $objresults
}
For your specific use exporting all objects at once or in batches would be the most efficient, but there are times were it would make sense to export a record one at a time to a CSV file which is what led me to this question, so I want to post my solution.
Use Export-CSV -Append to continually add to the end of a csv file.
foreach ($rechtenhouder in $rechten) {
$objResults = New-Object PSObject -Property #{
DirectoryPath = $objPath;
Identity = $rechtenhouder.name;
Systemrights = $accessRight.FileSystemRights;
systemrightstype = $accessRight.accesscontroltype;
isinherited = $accessRight.isinherited;
inheritanceflags = $accessRight.inheritanceflags;
rulesprotected = $objACL.areaccessrulesprotected;
Adtype = "User";
}
$objResults | Export-CSV $csvPath -Append -NoTypeInformation
}
This is useful if you are continually polling at set time intervals, but less so if you are iterating over a collection of objects, just export them all at once. For example, I would use this method of exporting for a script like below:
while($true){
$procs = Get-Process | Select-Object Name,CPU
$procs | Add-Member -type NoteProperty -Name "Timestamp" -Value $(Get-Date)
$procs | Export-CSV $csvPath -Append -NoTypeInformation
sleep -Seconds 60
}
First, I suggest you to create your object in a decent smarter way:
foreach ($rechtenhouder in $rechten) {
$objResults = New-Object PSObject -Property #{
DirectoryPath = $objPath;
Identity = $rechtenhouder.name;
Systemrights = $accessRight.FileSystemRights;
systemrightstype = $accessRight.accesscontroltype;
isinherited = $accessRight.isinherited;
inheritanceflags = $accessRight.inheritanceflags;
rulesprotected = $objACL.areaccessrulesprotected;
Adtype = "User";
}
$arrResults += $objResults
}
With this done, your $arrResults now contains your objects. This can easily exported to CSV files with PowerShells builtin Export-CSV:
$arrResults | Export-Csv -Path "C:/temp/text.csv"
Using Add-Content on every loop iteration is IMHO ineffective regarding performance. If your script runs for a long time and you want to save your current state in intervals, you could e.g. start an asynchronous job - let's say every 10th iteration - exporting your current array:
$i = 0
foreach ($rechtenhouder in $rechten) {
$objResults = New-Object PSObject -Property #{
DirectoryPath = $objPath;
Identity = $rechtenhouder.name;
Systemrights = $accessRight.FileSystemRights;
systemrightstype = $accessRight.accesscontroltype;
isinherited = $accessRight.isinherited;
inheritanceflags = $accessRight.inheritanceflags;
rulesprotected = $objACL.areaccessrulesprotected;
Adtype = "User";
}
$arrResults += $objResults
if ($i % 10 -eq 0) {
Start-Job -ScriptBlock {
param($T, $Path)
$T | Export-Csv -Path $Path
} -ArgumentList #($arrTest, "Path/to/script")
}
$i++
}

How can i get the disk details in csv format in windows server using powershell

Hi all I want to get details in below format
Hostname Drive_0 Drive_1 Drive_2
Name C: 99.899227142334 d: 99.899227142334 e: 99.899227142334
I can get this detail using below script but this works on PowerShell 3.0
how can I change to execute on PowerShell 2.0
$result = #()
$obj = new-object PSobject
$server = hostname
$obj |Add-Member -MemberType NoteProperty -Name "Hostname" -Value $server -ErrorAction SilentlyContinue
$z = Get-WmiObject -Class win32_Logicaldisk -Filter 'DriveType=3' | Select-Object -Property DeviceID, #{LABEL='TotalSize';EXPRESSION={$_.Size/1GB}}
$z3 = $z.DeviceID
$z4 = $z.TotalSize
$i = 0
foreach($z3 in $z3){
$z1 = $z.DeviceID[$i]
$z2 = $z.TotalSize[$i]
$zx = "$z1" + ": $z2"
$obj | Add-Member -MemberType NoteProperty -Name "Drive_$i" -Value $zx -ErrorAction SilentlyContinue
$i++
}
$result+=$obj
$result | Export-Csv "$env:userprofile\Desktop\Result.csv" -NoTypeInformation
You can change your code to the below. It's a bit neater and sorts out your loops and limits so you don't need to manage them
$result = #()
$obj = new-object PSobject
$server = hostname
$obj |Add-Member -MemberType NoteProperty -Name "Hostname" -Value $server -ErrorAction SilentlyContinue
$z = Get-WmiObject -Class win32_Logicaldisk -Filter 'DriveType=3' | Select-Object -Property DeviceID, #{LABEL='TotalSize';EXPRESSION={$_.Size/1GB}}
$i = 0
$z | % {
$z1 = $_.DeviceID
$z2 = $_.TotalSize
$zx = "$z1" + ": $z2"
$obj | Add-Member -MemberType NoteProperty -Name "Drive_$i" -Value $zx
$i++
}
$result+=$obj
$result | Export-Csv "$env:userprofile\Desktop\Result.csv" -NoTypeInformation
I also removed the -ErrorAction off the Add-Member as you should try and handle anything that crops up yourself, but add it back if you feel the need to.