Combine output into a single table - powershell

I'm using this code which I have modified to remove some items that I don't need and I am trying to combine the output into a single table. I have gotten this far:
$SCVMs | ForEach-Object {
$VMName = Get-SCVirtualMachine $_.Name | Select -Expand Name
$ReportData = $ReportData + (Get-SCVirtualMachine $_.Name |
Get-SCVirtualHardDisk |
Select #{Label="VM Name";Expression={$VMName}},
#{Label="VHD Name";Expression={$_.Name}},
#{Label="VHD Location";Expression={$_.Location}},
#{Label="Max Disk Size (GB)";Expression={($_.MaximumSize/1GB)}},
#{Label="Disk Space Used (GB)";Expression={"{0:N2}" -f ($_.Size/1GB)}},
#{Label="Disk Space Used (%)";Expression={[math]::Round((($_.Size/1GB)/($_.MaximumSize/1GB))*100)}},
#{Label="Free Disk Space (GB)";Expression={"{0:N2}" -f (($_.MaximumSize/1GB) - ($_.Size/1GB))}} |
ConvertTo-Html -as Table -Fragment)
}
The report displays the hard drives for a particular VM in the same table but it creates a separate table for each VM. I would like to generate a single table for all the VMs with a separate row for each hard drive in the VM.
I believe the trick lies in how I select the objects and pipe them along but I'm just not experienced enough to see how to do it.
I don't need the report in HTML, CSV would be fine.

You pipe each object into ConvertTo-Html, so you get a table fragment for each object. Instead of doing that (and appending in a loop on top of that) put ConvertTo-Html outside the ForEach-Object loop.
Change this:
$SCVMs | ForEach-Object {
$VMName = ...
$ReportData = $ReportData + (Get-SCVirtualMachine $_.Name |
Get-SCVirtualHardDisk |
Select ... |
ConvertTo-Html -as Table -Fragment)
}
into this:
$ReportData = $SCVMs | ForEach-Object {
$VMName = ...
Get-SCVirtualMachine $_.Name |
Get-SCVirtualHardDisk |
Select ...
} | ConvertTo-Html -as Table -Fragment
or this (if you need to append to $ReportData):
$ReportData += $SCVMs | ForEach-Object {
$VMName = ...
Get-SCVirtualMachine $_.Name |
Get-SCVirtualHardDisk |
Select ...
} | ConvertTo-Html -as Table -Fragment
To switch to CSV output you just replace ConvertTo-Html with ConvertTo-Csv or Export-Csv.

Just take out the intermediate variables and remove the ConvertTo-Html from inside your loop. Better still export direct to CSV. Something like this:
$SCVMs | ForEach-Object {
$VMName = Get-SCVirtualMachine $_.Name | Select -Expand Name
$ReportData = $ReportData + (Get-SCVirtualMachine $_.Name | Get-SCVirtualHardDisk | Select `
#{Label="VM Name";Expression={$VMName}}, `
#{Label="VHD Name";Expression={$_.Name}}, `
#{Label="VHD Location";Expression={$_.Location}}, `
#{Label="Max Disk Size (GB)";Expression={($_.MaximumSize/1GB)}}, `
#{Label="Disk Space Used (GB)";Expression={"{0:N2}" -f ($_.Size/1GB)}}, `
#{Label="Disk Space Used (%)";Expression={[math]::Round((($_.Size/1GB)/($_.MaximumSize/1GB))*100)}}, `
#{Label="Free Disk Space (GB)";Expression={"{0:N2}" -f (($_.MaximumSize/1GB) - ($_.Size/1GB))}} | ConvertTo-HTML -as Table -Fragment) }

Related

How to get VM which has not Raw physical disk and sort those VM by name provisionedspace?

I need to get VM List which is to be sort by provisionedspace and also those VM should not have RawPhysical. I have tried the below code
Get-Datastore -Name "$DSName" | Get-VMHost | get-vm | Select-Object -Property Name, Provisionedspacegb | sort -Property Provisionedspacegb | select -First 3 | Select Name
The above which is used to get VM list sort by provisionedspacegb
Get-Datastore -Name "$DSName" | Get-VMHost | Get-VM | Get-HardDisk | Where-Object {$_.DiskType -eq "RawPhysical" } | Select Parent
The above which is used to get VM list which does not have Physical Disk
I need those two code in a single line powershell code..
Whenever working with a lot of pipe operators, take a step back and consider divide et impera -approach. That is, break the script in more manageable parts. I don't have VMWare available, but try the following idea:
# Get a list of all the VMs
$allVms= Get-Datastore -Name "$DSName" | Get-VMHost | get-vm
# Array for those we actually want
$rawVms = #()
# Iterate the VM collection. Look for such VMs that have whatever disk config
foreach($vm in $allVms) {
# This filtering statement is likely to be incorrect. Tune accordingly
if($vm | get-harddisk | ? { $_.DiskType -eq "RawPhysical" }).Count -gt 0 {
# If the VM has raw disk, add it into desired VMs list
$rawVms += $vm
}
}
# Do stuff to the filtered collection
$rawVms | Select-Object -Property Name, Provisionedspacegb | ` # ` = line break for readability
sort -Property Provisionedspacegb | select -First 3 | Select Name
The actual syntax is likely to be a bit different.

Reading txt-file, change rows to columns, save txt file

I have a txt files (semicolon separated) containing over 3 million records where columns 1 to 4 have some general information. Columns 5 and 6 have detailed information. There can be up to 4 different detailed information for the same general information in columns 1 to 4.
My sample input:
Server;Owner;Company;Username;Property;Value
Srv1;Dave;Sandbox;kwus91;Memory;4GB
Srv1;Dave;Sandbox;kwus91;Processes;135
Srv1;Dave;Sandbox;kwus91;Storage;120GB
Srv1;Dave;Sandbox;kwus91;Variant;16
Srv2;Pete;GWZ;aiwq71;Memory;8GB
Srv2;Pete;GWZ;aiwq71;Processes;234
Srv3;Micael;P12;mxuq01;Memory;16GB
Srv3;Micael;P12;mxuq01;Processes;239
Srv3;Micael;P12;mxuq01;Storage;160GB
Srv4;Stefan;MTC;spq61ep;Storage;120GB
Desired output:
Server;Owner;Company;Username;Memory;Processes;Storage;Variant
Srv1;Dave;Sandbox;kwus91;4GB;135;120GB;16
Srv2;Pete;GWZ;aiwq71;8GB;234;;
Srv3;Micael;P12;mxuq01;16GB;239;160GB;
Srv4;Stefan;MTC;spq61ep;;;120GB;
If a values doesn't exist for general information (Columns 1-4) it has to stay blank.
My current code:
$a = Import-csv .\Input.txt -Delimiter ";"
$a | FT -AutoSize
$b = #()
foreach ($Server in $a.Server | Select -Unique) {
$Props = [ordered]#{ Server = $Server }
$Owner = ($a.where({ $_.Server -eq $Server})).Owner | Select -Unique
$Company = ($a.where({ $_.Server -eq $Server})).Company | Select -Unique
$Username = ($a.where({ $_.Server -eq $Server})).Username | Select -Unique
$Props += #{Owner = $Owner}
$Props += #{Company = $Company}
$Props += #{Username = $Username}
foreach ($Property in $a.Property | Select -Unique){
$Value = ($a.where({ $_.Server -eq $Server -and
$_.Property -eq $Property})).Value
$Props += #{ $Property = $Value }
}
$b += New-Object -TypeName PSObject -Property $Props
}
$b | FT -AutoSize
$b | Export-Csv .\Output.txt -NoTypeInformation -Delimiter ";"
After a lot of trying and getting errors: My script works.
But it takes a lot of time.
Is there a possibility to make performance better for around 3 Million lines in txt file? I'm calculating with more or less 2.5 Million unique values for $Server.
I'm running Windows 7 64bit with PowerShell 4.0.
try Something like this:
#Import Data and create empty columns
$List=import-csv "C:\temp\file.csv" -Delimiter ";"
#get all properties name with value not empty
$ListProperty=($List | where Value -ne '' | select property -Unique).Property
#group by server
$Groups=$List | group Server
#loop every rows and store data by group and Property Name
$List | %{
$Current=$_
#Take value not empty and group by Property Name
$Group=($Groups | where Name -eq $Current.Server).Group | where Value -ne '' | group Property
#Add all property and first value not empty
$ListProperty | %{
$PropertyName=$_
$PropertyValue=($Group | where Name -eq $PropertyName | select -first 1).Group.Value
$Current | Add-Member -Name $PropertyName -MemberType NoteProperty -Value $PropertyValue
}
$Current
} | select * -ExcludeProperty Property, Value -unique | export-csv "c:\temp\result.csv" -notype -Delimiter ";"

powershell out-file how to rid of extra spaces on each line

I run this command and I get all computer hostnames in the names.txt file.
Each hostname in the file is on a separate line, but every hostname is followed with white spaces which cause an issue when I try to read this file. How can I output to this file without getting the white spaces on each line?
Get-ADComputer -Filter * | Select-Object -property name | Sort-Object -Property name | out-file -filepath C:\temp\names.txt
You have the problem that you don't just have names, you have objects with the property 'name', and you also have the problem that Out-File runs complex objects through some kind of formatting before sending them to the file.
To fix both, expand the name out to just text, and generally use Set-Content instead:
Get-ADComputer -filter * | Select-Object -ExpandProperty Name | Sort-Object | Set-Content C:\temp\names.txt
or in short form
Get-ADComputer -filter * | Select -Expand Name | Sort | sc C:\temp\names.txt
or
(Get-ADComputer -filter *).Name | sort | sc C:\temp\names.txt
expandproperty should get rid of the #()
Get-ADComputer -Filter * | sort Name | Select -expandproperty Name | %{ $_.TrimEnd(" ") } | out-file -filepath C:\temp\names
Untested no AD#home
Piping it through this should work (before piping to the out-file):
EDIT: Piping through % { $_.name } should convert #{name=VALUE} to VALUE:
% { $_ -replace ' +$','' } | % { $_.name }
Like this:
Get-ADComputer -Filter * | Select-Object -property name | Sort-Object -Property name | % { $_ -replace ' +$','' } | % { $_.name } | out-file -filepath C:\temp\names.txt

Exchange Get-MailboxFolderStatistics FolderSize to MB

Morning folks, and what a sad day it is to be British.
Anyway, I'm trying to get MailboxFolderStatistics's FolderSize to MB.
The following line:
Get-MailboxFolderStatistics Joe.Bloggs |
Where-Object { $_.FolderPath -ne "/Deletions" } |
Select-Object FolderPath, #{ N = "FolderSize (MB)"; E = { $_.FolderSize.ToMB() } }
works fine when I'm using Exchange Management Shell.
But if I'm using a remote PS session into one of my Exchange boxes, I don't get anything for FolderSize.
Any ideas?
It's because the Exchange Management Shell you run on the server includes a type named Microsoft.Exchange.Data.ByteQuantifiedSize that gets converted to a System.String through remoting. The former exposes a ToMB() method, the latter does not.
I have written a workaround, but maybe there is a simpler and/or prettier method :
Get-MailboxFolderStatistics Joe.Bloggs |
Where-Object { $_.FolderPath -ne "/Deletions" } |
Select-Object FolderPath, #{
N = "FolderSize (MB)";
E = {
"{0:N2}" -f ((($_.FolderSize -replace "[0-9\.]+ [A-Z]* \(([0-9,]+) bytes\)","`$1") -replace ",","") / 1MB)
}
}
This uses a regular expression to turn the ugly string (example : 3.712 KB (3,801 bytes)) into a usable number. On my system , is not a valid digit grouping symbol so I had to remove it from the string, too.
You can use the following lines to get the $_.FolderSize represented in [decimals]
Select-Object #{
N = "FS_MB";
E = {
[math]::round( ([decimal](($_.FolderSize -replace "[0-9\.]+ [A-Z]* \(([0-9,]+) bytes\)","`$1") -replace ",","") / 1MB),2)
}
}
Typically when looking at folder sizes there is desire to sort them by size descending. To achieve this we need to know the FolderAndSubfolderSize in Bytes and store that in a bigint property as opposed to System.String. It's not rocket science to convert Bytes to Kb,Mb,Gb so I won't go into that here.
In-line syntax for dynamically adding a new property FolderAndSubfolderSizeBytes (I've used backticks to split over several lines for legibility only)
Get-EXOMailbox -Identity user.name#domain.com`
| Get-EXOMailboxFolderStatistics `
| Select Name,FolderAndSubfolderSize,#{`
name="FolderAndSubfolderSizeBytes";`
expression={((($_.FolderAndSubfolderSize -replace '^(.*\()(.*)(\sbytes\))$','$2').Replace(',','')) -as [bigint])}} `
| Sort-Object -Property FolderAndSubfolderSizeBytes -Descending | ft
Long-hand add new properties to variable object for later re-use
$mb = Get-EXOMailbox -Identity user.name#domain.com | Get-EXOMailboxFolderStatistics
foreach ($folder in $mb) {
$folder | Add-Member -NotePropertyName FolderSizeBytes -NotePropertyValue ((($folder.FolderSize -replace '^(.*\()(.*)(\sbytes\))$','$2').Replace(',','')) -as [bigint])
$folder | Add-Member -NotePropertyName FolderAndSubfolderSizeBytes -NotePropertyValue ((($folder.FolderAndSubfolderSize -replace '^(.*\()(.*)(\sbytes\))$','$2').Replace(',','')) -as [bigint])
}
$mb | Select Name,FolderPath,FolderAndSubfolderSize,FolderAndSubfolderSizeBytes | Sort-Object -Property FolderAndSubfolderSizeBytes -Descending | ft

Append results or text to text file by using Powershell

I have a collection of objects with properties: ProductName and PartName. The content of collection is output to a file first:
$colProducts | sort-object ProductName | `
Select-object ProductName PartName | `
Format-Table -autosize ProductName, PartName | `
Out-File myProducts.txt
So far so good. However, I have trouble to append a text message to the result file like this:
Add-Content myProducts.txt "`nParts in more than one Product`n"
I found that the appended text is not readable at the end. One thing I notice is that the output of the first collection to a file is in Unicode, and the second one code (add-content) is in ASCII if only to a new file.
After this, I would like to continue to add the following information the same result file:
$colProducts | Group-object PartName | sort-object PartName | `
Where-Object {$_.Count -gt 1 } | `
Select-object ProductName PartName | `
Format-Table -autosize ProductName, PartName | `
Out-File myProducts.txt
The above codes will overwrite to the result file. I need to append to the file. Greatly appreciate help!
Update: It is good to know -Append option. How about Add-Content? It seems adding some unreadable chars to the file after Out-File from collection.
I would first try:
$colProducts | Group-object PartName | sort-object PartName | `
Where-Object {$_.Count -gt 1 } | `
Select-object ProductName PartName | `
Format-Table -autosize ProductName, PartName | `
Out-File -Append myProducts.txt
And then look at this to get a feel for what you were encountering.
Essentially, Out-File (and Out-File -Append) gives you Unicode by default and Add-Content gives ASCII by default. My advice would be stick to the same command and you shouldn't have a problem.
And, of course, help Out-File -Detailed! Always check out the powershell examples because they help a great deal, not just to figure out their common usage, but to grok them as well.
Try:
$colProducts | Group-object PartName | sort-object PartName | `
Where-Object {$_.Count -gt 1 } | `
Select-object ProductName PartName | `
Format-Table -autosize ProductName, PartName | `
Out-File -Append myProducts.txt
Another option:
$colProducts | sort-object ProductName | `
Select-object ProductName PartName | `
Format-Table -autosize ProductName, PartName | `
Out-String | Add-Content myProducts.txt
Add-Content myProducts.txt "`nParts in more than one Product`n"