<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Spreadsheet Archives - LucD notes</title>
	<atom:link href="https://www.lucd.info/category/spreadsheet/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.lucd.info/category/spreadsheet/</link>
	<description>My PowerShell ramblings</description>
	<lastBuildDate>Thu, 03 Jan 2013 14:40:45 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>

<image>
	<url>https://www.lucd.info/wp-content/uploads/2018/12/cropped-120px-Tibetan_Dharmacakra-32x32.png</url>
	<title>Spreadsheet Archives - LucD notes</title>
	<link>https://www.lucd.info/category/spreadsheet/</link>
	<width>32</width>
	<height>32</height>
</image> 
<atom:link rel="hub" href="https://pubsubhubbub.appspot.com"/><atom:link rel="hub" href="https://pubsubhubbub.superfeedr.com"/><atom:link rel="hub" href="https://websubhub.com/hub"/>	<item>
		<title>Export-Xlsx, the sequel, and ordered data</title>
		<link>https://www.lucd.info/2013/01/03/export-xls-the-sequel-and-ordered-data/</link>
					<comments>https://www.lucd.info/2013/01/03/export-xls-the-sequel-and-ordered-data/#comments</comments>
		
		<dc:creator><![CDATA[LucD]]></dc:creator>
		<pubDate>Thu, 03 Jan 2013 13:20:52 +0000</pubDate>
				<category><![CDATA[ordered]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Spreadsheet]]></category>
		<category><![CDATA[Excel]]></category>
		<category><![CDATA[Export-Xls]]></category>
		<category><![CDATA[Export-Xlsx]]></category>
		<guid isPermaLink="false">http://www.lucd.info/?p=4289</guid>

					<description><![CDATA[Some time ago I published the Export-Xls function to the world in my [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Some time ago I published the <strong>Export-Xls</strong> function to the world in my <a href="https://www.lucd.info/2010/05/29/beyond-export-csv-export-xls/" target="_blank">Beyond Export-Csv: Export-Xls</a> post. And it has been one of the more popular functions on my blog.</p>
<p style="padding-left: 60px;"><a href="https://www.lucd.info/2013/01/03/export-xls-the-sequel-and-ordered-data/ps-2-excel/" rel="attachment wp-att-4299"><img fetchpriority="high" decoding="async" class="alignnone size-full wp-image-4299" alt="PS-2-Excel" src="https://lucd.info/wp-content/uploads/2013/01/PS-2-Excel.png" width="650" height="243" srcset="https://www.lucd.info/wp-content/uploads/2013/01/PS-2-Excel.png 650w, https://www.lucd.info/wp-content/uploads/2013/01/PS-2-Excel-300x112.png 300w" sizes="(max-width: 650px) 100vw, 650px" /></a></p>
<p>Just before Christmas 2012 I got a comment from Gilbert where he informed me that he had written an <strong>Export-Xlsx</strong> function and had borrowed some of the parameters that were on the original <strong>Export-Xls</strong> function. We exchanged a couple of emails with suggestions and ideas, and yesterday the finalised <strong>Export-Xlsx</strong> function was posted on Gilbert&#8217;s <a href="https://www.itpilgrims.com/" target="_blank">ITPilgrims</a> blog (which you should follow btw) in his <a href="https://www.itpilgrims.com/2013/01/export-xlsx/" target="_blank">Export-Xlsx</a> post.</p>
<p>In this post I will publish this new <strong>Export-Xlsx</strong> function with some annotations, and I will also show how you can now export your data in the order you want, provided you&#8217;re using <strong>PowerShell v3</strong>.</p>
<p><span id="more-4289"></span></p>
<h2>The script</h2>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">Function Export-Xlsx {

&lt;#
.SYNOPSIS
  Exports data to an Excel workbook
.DESCRIPTION
  Exports data to an Excel workbook and applies cosmetics. 
  Optionally add a title, autofilter, autofit and a chart.
  Allows for export to .xls and .xlsx format. If .xlsx is
  specified but not available (Excel 2003) the data will
  be exported to .xls.
.NOTES
  Author:  Gilbert van Griensven
  Based on
  https://www.lucd.info/2010/05/29/beyond-export-csv-export-xls/
.PARAMETER InputData
  The data to be exported to Excel
.PARAMETER Path
  The path of the Excel file. 
  Defaults to %HomeDrive%\Export.xlsx.
.PARAMETER WorksheetName
  The name of the worksheet. Defaults to filename
  in $Path without extension.
.PARAMETER ChartType
  Name of an Excel chart to be added.
.PARAMETER Title
  Adds a title to the worksheet.
.PARAMETER SheetPosition
  Adds the worksheet either to the 'begin' or 'end' of
  the Excel file. This parameter is ignored when creating
  a new Excel file.
.PARAMETER ChartOnNewSheet
  Adds a chart to a new worksheet instead of to the
  worksheet containing data. The Chart will be placed after
  the sheet containing data. Only works when parameter
  ChartType is used.
.PARAMETER AppendWorksheet
  Appends a worksheet to an existing Excel file.
  This parameter is ignored when creating a new Excel file.
.PARAMETER Borders
  Adds borders to all cells. Defaults to True.
.PARAMETER HeaderColor
  Applies background color to the header row. 
  Defaults to True.
.PARAMETER AutoFit
  Apply autofit to columns. Defaults to True.
.PARAMETER AutoFilter
  Apply autofilter. Defaults to True.
.PARAMETER PassThrough
  When enabled returns file object of the generated file.
.PARAMETER Force
  Overwrites existing Excel sheet. When this switch is
  not used but the Excel file already exists, a new file
  with datestamp will be generated. This switch is ignored
  when using the AppendWorksheet switch.
.EXAMPLE
  Get-Process | Export-Xlsx D:\Data\ProcessList.xlsx
.EXAMPLE
  Get-ADuser -Filter {enabled -ne $True} | 
  Select-Object Name,Surname,GivenName,DistinguishedName | 
  Export-Xlsx -Path 'D:\Data\Disabled Users.xlsx' -Title 'Disabled users of Contoso.com'
.EXAMPLE
  Get-Process | Sort-Object CPU -Descending | 
  Export-Xlsx -Path D:\Data\Processes_by_CPU.xlsx
.EXAMPLE
  Export-Xlsx (Get-Process) -AutoFilter:$False -PassThrough |
  Invoke-Item
#&gt;

  [CmdletBinding()]
  Param (
    [Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True)]
    [ValidateNotNullOrEmpty()]
    $InputData,
    [Parameter(Position=1)]
    [ValidateScript({
        $ReqExt = [System.IO.Path]::GetExtension($_)
        (          $ReqExt -eq &quot;.xls&quot;) -or
        (          $ReqExt -eq &quot;.xlsx&quot;)
      })]
    $Path = (Join-Path $env:HomeDrive &quot;Export.xlsx&quot;),
    [Parameter(Position=2)] $WorksheetName = [System.IO.Path]::GetFileNameWithoutExtension($Path),
    [Parameter(Position=3)]
    [ValidateSet(&quot;xl3DArea&quot;,&quot;xl3DAreaStacked&quot;,&quot;xl3DAreaStacked100&quot;,&quot;xl3DBarClustered&quot;,
      &quot;xl3DBarStacked&quot;,&quot;xl3DBarStacked100&quot;,&quot;xl3DColumn&quot;,&quot;xl3DColumnClustered&quot;,
      &quot;xl3DColumnStacked&quot;,&quot;xl3DColumnStacked100&quot;,&quot;xl3DLine&quot;,&quot;xl3DPie&quot;,
      &quot;xl3DPieExploded&quot;,&quot;xlArea&quot;,&quot;xlAreaStacked&quot;,&quot;xlAreaStacked100&quot;,
      &quot;xlBarClustered&quot;,&quot;xlBarOfPie&quot;,&quot;xlBarStacked&quot;,&quot;xlBarStacked100&quot;,
      &quot;xlBubble&quot;,&quot;xlBubble3DEffect&quot;,&quot;xlColumnClustered&quot;,&quot;xlColumnStacked&quot;,
      &quot;xlColumnStacked100&quot;,&quot;xlConeBarClustered&quot;,&quot;xlConeBarStacked&quot;,&quot;xlConeBarStacked100&quot;,
      &quot;xlConeCol&quot;,&quot;xlConeColClustered&quot;,&quot;xlConeColStacked&quot;,&quot;xlConeColStacked100&quot;,
      &quot;xlCylinderBarClustered&quot;,&quot;xlCylinderBarStacked&quot;,&quot;xlCylinderBarStacked100&quot;,&quot;xlCylinderCol&quot;,
      &quot;xlCylinderColClustered&quot;,&quot;xlCylinderColStacked&quot;,&quot;xlCylinderColStacked100&quot;,&quot;xlDoughnut&quot;,
      &quot;xlDoughnutExploded&quot;,&quot;xlLine&quot;,&quot;xlLineMarkers&quot;,&quot;xlLineMarkersStacked&quot;,
      &quot;xlLineMarkersStacked100&quot;,&quot;xlLineStacked&quot;,&quot;xlLineStacked100&quot;,&quot;xlPie&quot;,
      &quot;xlPieExploded&quot;,&quot;xlPieOfPie&quot;,&quot;xlPyramidBarClustered&quot;,&quot;xlPyramidBarStacked&quot;,
      &quot;xlPyramidBarStacked100&quot;,&quot;xlPyramidCol&quot;,&quot;xlPyramidColClustered&quot;,&quot;xlPyramidColStacked&quot;,
      &quot;xlPyramidColStacked100&quot;,&quot;xlRadar&quot;,&quot;xlRadarFilled&quot;,&quot;xlRadarMarkers&quot;,
      &quot;xlStockHLC&quot;,&quot;xlStockOHLC&quot;,&quot;xlStockVHLC&quot;,&quot;xlStockVOHLC&quot;,
      &quot;xlSurface&quot;,&quot;xlSurfaceTopView&quot;,&quot;xlSurfaceTopViewWireframe&quot;,&quot;xlSurfaceWireframe&quot;,
      &quot;xlXYScatter&quot;,&quot;xlXYScatterLines&quot;,&quot;xlXYScatterLinesNoMarkers&quot;,&quot;xlXYScatterSmooth&quot;,
      &quot;xlXYScatterSmoothNoMarkers&quot;)]
    [PSObject] $ChartType,
    [Parameter(Position=4)] $Title,
    [Parameter(Position=5)] [ValidateSet(&quot;begin&quot;,&quot;end&quot;)] $SheetPosition = &quot;begin&quot;,
    [Switch] $ChartOnNewSheet,
    [Switch] $AppendWorksheet,
    [Switch] $Borders = $True,
    [Switch] $HeaderColor = $True,
    [Switch] $AutoFit = $True,
    [Switch] $AutoFilter = $True,
    [Switch] $PassThrough,
    [Switch] $Force
  )
  
  Begin {
    Function Convert-NumberToA1 {
      Param([parameter(Mandatory=$true)] [int]$number)
      $a1Value = $null
      While ($number -gt 0) {
        $multiplier = [int][system.math]::Floor(($number / 26))
        $charNumber = $number - ($multiplier * 26)
        If ($charNumber -eq 0) { $multiplier-- ; $charNumber = 26 }
        $a1Value = [char]($charNumber + 96) + $a1Value
        $number = $multiplier
      }
      Return $a1Value
    }

    $Script:WorkingData = @()
  }
  
  Process {
    $Script:WorkingData += $InputData
  }
  
  End {
    $Props = $Script:WorkingData[0].PSObject.properties | % { $_.Name }
    $Rows = $Script:WorkingData.Count+1
    $Cols = $Props.Count
    $A1Cols = Convert-NumberToA1 $Cols
    $Array = New-Object 'object[,]' $Rows,$Cols

    $Col = 0
    $Props | % {
      $Array[0,$Col] = $_.ToString()
      $Col++
    }

    $Row = 1
    $Script:WorkingData | % {
      $Item = $_
      $Col = 0
      $Props | % {
        If ($Item.($_) -eq $Null) {
          $Array[$Row,$Col] = &quot;&quot;
        } Else {
          $Array[$Row,$Col] = $Item.($_).ToString()
        }
        $Col++
      }
      $Row++
    }

    $xl = New-Object -ComObject Excel.Application
    $xl.DisplayAlerts = $False
    $xlFixedFormat = [Microsoft.Office.Interop.Excel.XLFileFormat]::xlWorkbookNormal

    If ([System.IO.Path]::GetExtension($Path) -eq '.xlsx') {
      If ($xl.Version -lt 12) {
        $Path = $Path.Replace(&quot;.xlsx&quot;,&quot;.xls&quot;)
      } Else {
        $xlFixedFormat = [Microsoft.Office.Interop.Excel.XLFileFormat]::xlWorkbookDefault
      }
    }

    If (Test-Path -Path $Path -PathType &quot;Leaf&quot;) {
      If ($AppendWorkSheet) {
        $wb = $xl.Workbooks.Open($Path)
        If ($SheetPosition -eq &quot;end&quot;) {
          $wb.Worksheets.Add([System.Reflection.Missing]::Value,$wb.Sheets.Item($wb.Sheets.Count)) | Out-Null
        } Else {
          $wb.Worksheets.Add($wb.Worksheets.Item(1)) | Out-Null
        }
      } Else {
        If (!($Force)) {
          $Path = $Path.Insert($Path.LastIndexOf(&quot;.&quot;),&quot; - $(Get-Date -Format &quot;ddMMyyyy-HHmm&quot;)&quot;)
        }
        $wb = $xl.Workbooks.Add()
        While ($wb.Worksheets.Count -gt 1) { $wb.Worksheets.Item(1).Delete() }
      }
    } Else {
      $wb = $xl.Workbooks.Add()
      While ($wb.Worksheets.Count -gt 1) { $wb.Worksheets.Item(1).Delete() }
    }

    $ws = $wb.ActiveSheet
    Try { $ws.Name = $WorksheetName }
    Catch { }

    If ($Title) {
      $ws.Cells.Item(1,1) = $Title
      $TitleRange = $ws.Range(&quot;a1&quot;,&quot;$($A1Cols)2&quot;)
      $TitleRange.Font.Size = 18
      $TitleRange.Font.Bold=$True
      $TitleRange.Font.Name = &quot;Cambria&quot;
      $TitleRange.Font.ThemeFont = 1
      $TitleRange.Font.ThemeColor = 4
      $TitleRange.Font.ColorIndex = 55
      $TitleRange.Font.Color = 8210719
      $TitleRange.Merge()
      $TitleRange.VerticalAlignment = -4160
      $usedRange = $ws.Range(&quot;a3&quot;,&quot;$($A1Cols)$($Rows + 2)&quot;)
      If ($HeaderColor) {
        $ws.Range(&quot;a3&quot;,&quot;$($A1Cols)3&quot;).Interior.ColorIndex = 48
        $ws.Range(&quot;a3&quot;,&quot;$($A1Cols)3&quot;).Font.Bold = $True
      }
    } Else {
      $usedRange = $ws.Range(&quot;a1&quot;,&quot;$($A1Cols)$($Rows)&quot;)
      If ($HeaderColor) {
        $ws.Range(&quot;a1&quot;,&quot;$($A1Cols)1&quot;).Interior.ColorIndex = 48
        $ws.Range(&quot;a1&quot;,&quot;$($A1Cols)1&quot;).Font.Bold = $True
      }
    }

    $usedRange.Value2 = $Array

    If ($Borders) {
      $usedRange.Borders.LineStyle = 1
      $usedRange.Borders.Weight = 2
    }

    If ($AutoFilter) { $usedRange.AutoFilter() | Out-Null }

    If ($AutoFit) { $ws.UsedRange.EntireColumn.AutoFit() | Out-Null }

    If ($ChartType) {
      [Microsoft.Office.Interop.Excel.XlChartType]$ChartType = $ChartType
      If ($ChartOnNewSheet) {
        $wb.Charts.Add().ChartType = $ChartType
        $wb.ActiveChart.setSourceData($usedRange)
        Try { $wb.ActiveChart.Name = &quot;$($WorksheetName) - Chart&quot; }
        Catch { }
        $wb.ActiveChart.Move([System.Reflection.Missing]::Value,$wb.Sheets.Item($ws.Name))
      } Else {
        $ws.Shapes.AddChart($ChartType).Chart.setSourceData($usedRange) | Out-Null
      }
    }

    $wb.SaveAs($Path,$xlFixedFormat)
    $wb.Close()
    $xl.Quit()

    While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($usedRange)) {}
    While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($ws)) {}
    If ($Title) { While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($TitleRange)) {} }
    While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb)) {}
    While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl)) {}
    [GC]::Collect()

    If ($PassThrough) { Return Get-Item $Path }
  }
}</pre><p></p>
<h4>Annotations</h4>
<p><strong>Line 76-80</strong>: Parameter validation code block that checks if the requested file is of the type XLS or XLSX</p>
<p><strong> Line 81</strong>: The default save location is in the user&#8217;s home drive and the default filename is Export.Xslx</p>
<p><strong>Line 82</strong>: The default worksheetname is the name of the file</p>
<p><strong>Line 84-102</strong>: The enumerated accepted charttypes.</p>
<p><strong>Line 117-128</strong>: An internal helper function that converts a number to an Excel column name. For example the number <strong>28</strong> is converted to the column <strong>AB</strong>.</p>
<p><strong>Line 133-135</strong>: Copy the data that was passed to the function to the internal WorkingData array. Note that the function can be used in a pipeline, so the Process block will collect all data passed through the pipeline into this WorkingData array.</p>
<p><strong>Line 138-163</strong>: The input data is collected into a 2-dimensional array.</p>
<p><strong>Line 169-175</strong>: If the function discovers that the local Excel installation is not Excel 2010, then the filetype will be changed to XLS.</p>
<p><strong>Line 178-184</strong>: This part of the code configures the requested location of the worksheet. The accepted values are <strong>begin</strong> and <strong>end</strong>.</p>
<p><strong>Line 186-188</strong>: If the <strong>Force</strong> switch was not used in the call and the requested file already exists, the function will add a timestamp to the name, to guarantee a unique filename.</p>
<p><strong>Line 201-224</strong>: Handles the worksheet <strong>Title</strong> and the <strong>Header Colour</strong>.</p>
<p><strong>Line 228-231</strong>: Handles the <strong>Borders</strong> switch.</p>
<p><strong>Line 233</strong>: Handles the <strong>AutoFilter</strong> switch</p>
<p><strong>Line 235</strong>: Handles the <strong>AutoFit</strong> switch</p>
<p><strong>Line 237-248</strong>: Creates a chart, if requested, and handles the <strong>ChartOnNewSheet</strong> switch.</p>
<p><strong>Line 254-259</strong>: This avoids having to kill the Excel process by clearing all the allocated COM object</p>
<h2>Sample Usage</h2>
<h3>Export-Xlsx</h3>
<p>The <strong>Export-Xlsx</strong> function provides several of the parameters that were there in the <strong>Export-Xls</strong> function, and adds a couple of new ones. Let&#8217;s explore.</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">Get-Process |
Select Name,Id,Handles |
Export-Xlsx -Path C:\report.xlsx</pre><p></p>
<p>This produces an <strong>XLSX</strong> file with 1 worksheet, notice the <strong>default worksheet name</strong> and how <strong>Autofit</strong>, <strong>Autofilter </strong>and<strong> Borders</strong> are selected by default.</p>
<p><a href="https://www.lucd.info/2013/01/03/export-xls-the-sequel-and-ordered-data/export-xlsx-1/" rel="attachment wp-att-4291"><img decoding="async" class="alignnone  wp-image-4291" alt="export-xlsx-1" src="https://lucd.info/wp-content/uploads/2013/01/export-xlsx-1.jpg" width="353" height="284" srcset="https://www.lucd.info/wp-content/uploads/2013/01/export-xlsx-1.jpg 441w, https://www.lucd.info/wp-content/uploads/2013/01/export-xlsx-1-300x241.jpg 300w" sizes="(max-width: 353px) 100vw, 353px" /></a></p>
<p>Now lets try to add a <strong>chart</strong>.</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">Get-Process |
Select ProcessName,WorkingSet -First 10 |
Export-Xlsx -Path C:\report.xlsx -ChartType xlColumnClustered</pre><p></p>
<p>The result looks like this</p>
<p><a href="https://www.lucd.info/2013/01/03/export-xls-the-sequel-and-ordered-data/export-xlsx-2/" rel="attachment wp-att-4292"><img decoding="async" class="alignnone  wp-image-4292" alt="export-xlsx-2" src="https://lucd.info/wp-content/uploads/2013/01/export-xlsx-2.jpg" width="663" height="334" srcset="https://www.lucd.info/wp-content/uploads/2013/01/export-xlsx-2.jpg 829w, https://www.lucd.info/wp-content/uploads/2013/01/export-xlsx-2-300x150.jpg 300w" sizes="(max-width: 663px) 100vw, 663px" /></a></p>
<p>You can also use multiple values in the chart.</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">Get-Process | 
Select ProcessName,WorkingSet,VirtualMemorySize -First 10 |
Export-Xlsx -Path C:\report.xlsx -ChartType xlColumnClustered -WorksheetName MyGraph -ChartOnNewSheet</pre><p></p>
<p>And this produces an XLSX file with on one worksheet the data</p>
<p><a href="https://www.lucd.info/2013/01/03/export-xls-the-sequel-and-ordered-data/export-xlsx-3b/" rel="attachment wp-att-4293"><img loading="lazy" decoding="async" class="alignnone  wp-image-4293" alt="export-xlsx-3b" src="https://lucd.info/wp-content/uploads/2013/01/export-xlsx-3b.jpg" width="545" height="285" srcset="https://www.lucd.info/wp-content/uploads/2013/01/export-xlsx-3b.jpg 681w, https://www.lucd.info/wp-content/uploads/2013/01/export-xlsx-3b-300x156.jpg 300w" sizes="auto, (max-width: 545px) 100vw, 545px" /></a></p>
<p>And on a separate worksheet the chart.</p>
<p><a href="https://www.lucd.info/2013/01/03/export-xls-the-sequel-and-ordered-data/export-xlsx-3/" rel="attachment wp-att-4294"><img loading="lazy" decoding="async" class="alignnone  wp-image-4294" alt="export-xlsx-3" src="https://lucd.info/wp-content/uploads/2013/01/export-xlsx-3.jpg" width="709" height="376" srcset="https://www.lucd.info/wp-content/uploads/2013/01/export-xlsx-3.jpg 1266w, https://www.lucd.info/wp-content/uploads/2013/01/export-xlsx-3-300x159.jpg 300w, https://www.lucd.info/wp-content/uploads/2013/01/export-xlsx-3-1024x543.jpg 1024w" sizes="auto, (max-width: 709px) 100vw, 709px" /></a></p>
<h3>Ordered data</h3>
<p>With <strong>PowerShell v3</strong> the <strong>[ordered]</strong> cast was made available as an accelerator. The functionality that this brings was something that was asked by many users in the previous PowerShell versions.</p>
<p>To use this accelerator is quite easy. The following code snippet will show you how it is done and what the effect on the results is.</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">Get-Process | %{
$object = @{
Name = $_.Name
Handles = $_.Handles
Id = $_.Id
ProcessName = $_.ProcessName
}
New-Object PSObject -Property $object
} |
Export-Xlsx -Path C:\report-not-ordered.xlsx

Get-Process | %{
$object = [ordered]@{
Name = $_.Name
Handles = $_.Handles
Id = $_.Id
ProcessName = $_.ProcessName
}
New-Object PSObject -Property $object
} |
Export-Xlsx -Path C:\report-ordered.xlsx</pre><p></p>
<p>Both XLSX files next to each other show the difference, without the [ordered] cast the order in which the properties appear is not controllable.</p>
<p><a href="https://www.lucd.info/2013/01/03/export-xls-the-sequel-and-ordered-data/export-xlsx-ordered/" rel="attachment wp-att-4295"><img loading="lazy" decoding="async" class="alignnone  wp-image-4295" alt="export-xlsx-ordered" src="https://lucd.info/wp-content/uploads/2013/01/export-xlsx-ordered.jpg" width="537" height="510" srcset="https://www.lucd.info/wp-content/uploads/2013/01/export-xlsx-ordered.jpg 671w, https://www.lucd.info/wp-content/uploads/2013/01/export-xlsx-ordered-300x284.jpg 300w" sizes="auto, (max-width: 537px) 100vw, 537px" /></a></p>
<p>With the <strong>[ordered]</strong> cast, the properties appear in the same order as we specified them in the script. A great improvement !</p>
<p>Enjoy !</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lucd.info/2013/01/03/export-xls-the-sequel-and-ordered-data/feed/</wfw:commentRss>
			<slash:comments>35</slash:comments>
		
		
			</item>
		<item>
		<title>Beyond Export-Csv: Export-Xls</title>
		<link>https://www.lucd.info/2010/05/29/beyond-export-csv-export-xls/</link>
					<comments>https://www.lucd.info/2010/05/29/beyond-export-csv-export-xls/#comments</comments>
		
		<dc:creator><![CDATA[LucD]]></dc:creator>
		<pubDate>Sat, 29 May 2010 19:00:04 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Spreadsheet]]></category>
		<category><![CDATA[export]]></category>
		<category><![CDATA[XLS]]></category>
		<guid isPermaLink="false">http://www.lucd.info/?p=2261</guid>

					<description><![CDATA[Warning: this post has no &#8220;virtual&#8221; content ! This time I post a [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><span style="color: #ffffff;"><span style="background-color: #ff0000;">Warning</span></span>: this post has no &#8220;<em>virtual</em>&#8221; content !</p>
<p>This time I post a function that allows you to export your data to a &#8220;real&#8221; spreadsheet (XLS format) instead of a CSV file.</p>
<p>The reason for posting this function was a series of threads in the <a href="https://communities.vmware.com/community/vmtn/vsphere/automationtools/powercli;jsessionid=CF7ABD969A7E6F5BA5E110A012442FA7?view=discussions" target="_blank">PowerCLI Community</a> by <a href="https://communities.vmware.com/people/Sureshadmin?view=overview" target="_blank">Suresh</a>. Over several threads he has been collecting scripts that create various reports on his vSphere environment. Ultimately he wanted to have a spreadsheet where <a href="https://communities.vmware.com/thread/269701?tstart=15" target="_blank">each report </a>would be stored on a worksheet.</p>
<p>PowerShell has the very handy <strong>Export-Csv</strong> cmdlet to create CSV files but afaik nothing for creating XLS files <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f641.png" alt="🙁" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p><span id="more-2261"></span></p>
<p>That&#8217;s when I decided to &#8220;<em>pimp</em>&#8221; my <strong>XLS</strong> creating function I already had to something more suited for general use.</p>
<p>The function uses Excel through a COM object. So yes, you do need to have Excel installed on the client where you use the function. The function first exports the data to a CSV file with the Export-Csv cmdlet. And then does a copy/paste to the worksheet in the XLS file.</p>
<p><strong><span style="background-color: #ffff00;">Update June 16th 2010</span></strong>: the function has a new parameter <strong>-ChartType</strong> which allows the addition of a chart on the worksheet.<br />
<strong> </strong></p>
<p><strong><span style="background-color: #ffff00;">Update November 2nd 2010</span></strong>: a complete overhaul of the function</p>
<ul>
<li>The function now uses the <strong>clipboard</strong>, instead of a temporary CSV file, to hold the data that needs to be written to the worksheet.</li>
<li>The function places the array on the clipboard in such a way that it can be <strong>correctly pasted</strong> into the Worksheet. Thanks to my colleague <strong>Géry</strong> for his algorithm.</li>
<li>The function uses the <strong>Textbox</strong> trick as described in <a href="https://thoughtfulcode.wordpress.com/2010/09/03/copy-and-paste-with-clipboard-from-powershell/" target="_blank">Copy and Paste with Clipboard from PowerShell</a>. With this method we don&#8217;t need to start an new PowerShell thread with the STA attribute. And this method is considerably<strong> faster</strong> !</li>
</ul>
<h2>The script</h2>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">#requires -version 2

function Export-Xls{
	&lt;#
.SYNOPSIS
	Saves Microsoft .NET Framework objects to a worksheet in an XLS file
.DESCRIPTION
	The Export-Xls function allows you to save Microsoft .NET Framework objects
	to a named worksheet in an Excel file (type XLS). The position of the
	worksheet can be specified.
.NOTES
	Author:  Luc Dekens
.PARAMETER InputObject
	Specifies the objects to be written to the worksheet. The parameter accepts
	objects through the pipeline.
.PARAMETER Path
	Specifies the path to the XLS file.
.PARAMETER WorksheetName
	The name for the new worksheet. If not specified the name will
	be &quot;Sheet&quot; followed by the &quot;Ticks&quot; value
.PARAMETER SheetPosition
	Specifies where the new worksheet will be inserted in the series of
	existing worksheets. You can specify &quot;begin&quot; or &quot;end&quot;. The default
	is &quot;begin&quot;.
.PARAMETER ChartType
	Specifies the type of chart you want add to the worksheet.
	All types in the [microsoft.Office.Interop.Excel.XlChartType]
	enumeration are accepted.
.PARAMETER NoTypeInformation
	Omits the type information from the worksheet. The default is to
	include the &quot;#TYPE&quot; line.
.PARAMETER AppendWorksheet
	Specifies if the worksheet should keep or remove the existing
	worksheet in the spreadsheet. The default is to append.
.EXAMPLE
	PS&gt; $data = Get-Process | Select-Object Name, Id, WS
	PS&gt; Export-Xls $data C:\Reports\MyWkb.xls -WorksheetName &quot;WS&quot; -AppendWorksheet:$false
.EXAMPLE
	PS&gt; $data = Get-Process | Select-Object Name, Id, WS
	PS&gt; Export-Xls $data C:\Reports\MyWkb.xls -SheetPosition &quot;end&quot;
.EXAMPLE
	PS&gt; $data = Get-Process | Select-Object Name, Id, WS
	PS&gt; Export-Xls $data C:\Reports\MyWkb.xls -WorksheetName &quot;WS&quot; -ChartType &quot;xlColumnClustered&quot;
#&gt;
	param(
	[parameter(ValueFromPipeline = $true,Position=1)]
	[ValidateNotNullOrEmpty()]
	$InputObject,
	[parameter(Position=2)]
	[ValidateNotNullOrEmpty()]
	[string]$Path,
	[string]$WorksheetName = (&quot;Sheet &quot; + (Get-Date).Ticks),
	[string]$SheetPosition = &quot;begin&quot;,
	[PSObject]$ChartType,
	[switch]$NoTypeInformation = $true,
	[switch]$AppendWorksheet = $true
	)

	begin{
		[System.Reflection.Assembly]::LoadWithPartialName(&quot;Microsoft.Office.Interop.Excel&quot;)
		if($ChartType){
			[microsoft.Office.Interop.Excel.XlChartType]$ChartType = $ChartType
		}

		function Set-ClipBoard{
			param(
				[string]$text
			)
			process{
				Add-Type -AssemblyName System.Windows.Forms
				$tb = New-Object System.Windows.Forms.TextBox
				$tb.Multiline = $true
				$tb.Text = $text
				$tb.SelectAll()
				$tb.Copy()
			}
		}

		function Add-Array2Clipboard {
			param (
				[PSObject[]]$ConvertObject,
				[switch]$Header
			)
			process{
				$array = @()

				if ($Header) {
					$line =&quot;&quot;
					$ConvertObject | Get-Member -MemberType Property,NoteProperty,CodeProperty | Select -Property Name | %{
						$line += ($_.Name.tostring() + &quot;`t&quot;)
					}
					$array += ($line.TrimEnd(&quot;`t&quot;) + &quot;`r&quot;)
				}
				else {
					foreach($row in $ConvertObject){
						$line =&quot;&quot;
						$row | Get-Member -MemberType Property,NoteProperty | %{
							$Name = $_.Name
							if(!$Row.$Name){$Row.$Name = &quot;&quot;}
							$line += ([string]$Row.$Name + &quot;`t&quot;)
						}
						$array += ($line.TrimEnd(&quot;`t&quot;) + &quot;`r&quot;)
					}
				}
				Set-ClipBoard $array
			}
		}

		$excelApp = New-Object -ComObject &quot;Excel.Application&quot;
		$originalAlerts = $excelApp.DisplayAlerts
		$excelApp.DisplayAlerts = $false
		if(Test-Path -Path $Path -PathType &quot;Leaf&quot;){
			$workBook = $excelApp.Workbooks.Open($Path)
		}
		else{
			$workBook = $excelApp.Workbooks.Add()
		}
		$sheet = $excelApp.Worksheets.Add($workBook.Worksheets.Item(1))
		if(!$AppendWorksheet){
			$workBook.Sheets | where {$_ -ne $sheet} | %{$_.Delete()}
		}
		$sheet.Name = $WorksheetName
		if($SheetPosition -eq &quot;end&quot;){
			$nrSheets = $workBook.Sheets.Count
			2..($nrSheets) |%{
				$workbook.Sheets.Item($_).Move($workbook.Sheets.Item($_ - 1))
			}
		}
		$sheet.Activate()
		$array = @()
	}

	process{
		$array += $InputObject
	}

	end{
		Add-Array2Clipboard $array -Header:$True
		$selection = $sheet.Range(&quot;A1&quot;)
		$selection.Select() | Out-Null
		$sheet.Paste()
		$Sheet.UsedRange.HorizontalAlignment = [microsoft.Office.Interop.Excel.XlHAlign]::xlHAlignCenter
		Add-Array2Clipboard $array
		$selection = $sheet.Range(&quot;A2&quot;)
		$selection.Select() | Out-Null
		$sheet.Paste() | Out-Null
		$selection = $sheet.Range(&quot;A1&quot;)
		$selection.Select() | Out-Null

		$sheet.UsedRange.EntireColumn.AutoFit() | Out-Null
		$workbook.Sheets.Item(1).Select()
		if($ChartType){
			$sheet.Shapes.AddChart($ChartType) | Out-Null
		}
		$workbook.SaveAs($Path)
		$excelApp.DisplayAlerts = $originalAlerts
		$excelApp.Quit()
		Stop-Process -Name &quot;Excel&quot;
	}
}</pre><p></p>
<h4>Annotations</h4>
<p><strong>Line 65-77</strong>: This short function place a string in the Textbox control and then uses the Copy method to send the string to the Clipboard. <a href="https://thoughtfulcode.wordpress.com/2010/09/03/copy-and-paste-with-clipboard-from-powershell/" target="_blank">Function</a> by <a href="https://thoughtfulcode.wordpress.com/" target="_blank">Brian Reiter</a>.</p>
<p><strong>Line 79-107</strong>: This concept for this function came from my colleague <strong>Géry</strong>. The trick is to separate the properties from the array by the TAB character. That way a paste in Excel will place the properties in different cells. The first part of the function handles the property names (or the column headers in Excel), the second part prepares the actual data.</p>
<p><strong>Line 110-111,155</strong>: The Excel alerts are temporarily disabled to avoid pop-up messages. The original setting is restored just before leaving the function.</p>
<p><strong>Line 112-117:</strong> The function tests if the XLS file exists or not.</p>
<p><strong>Line 118</strong> A new worksheet is added in position 1.</p>
<p><strong>Line 119-121</strong>: If the AppendWorksheet switch is $false, all the other worksheets are removed.</p>
<p><strong>Line 123-128</strong>: These lines move the worksheet to the end of the list if the SheepPosition parameter states &#8220;end&#8221;.</p>
<p><strong>Line 134</strong>: All the objects from the InputObject are copied to the local $array variable.</p>
<p><strong>Line 138</strong>: The property names of the $array variable are copied to the clipboard</p>
<p><strong>Line 139-141</strong>: The column headers (the property names) are pasted to the Excel worksheet</p>
<p><strong>Line 142</strong>: The column headers are aligned.</p>
<p><strong>Line 143</strong>: The data in the $array variable is copied to the clipboard</p>
<p><strong>Line 144-146</strong>: The data is pasted into the Excel worksheet 1 row below the headers.</p>
<p><strong>Line 147</strong>: Unselect the range that was just copied.</p>
<p><strong>Line 150</strong>: The width of all columns in the worksheet is changed so all cell values are visible.</p>
<p><strong>Line 158</strong>: Although the Quit method is called, the Excel process stays active. The only way to get rid of it is through a Stop-Process cmdlet.</p>
<h2>Examples</h2>
<p>Some sample runs I did with Excel 2003.</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">$data = Get-Process | Select-Object Name, Id, WS
Export-Xls $data D:\Reports\MyWkb.xls</pre><p></p>
<p>This produces the following</p>
<p><a href="https://lucd.info/wp-content/uploads/2010/05/export-xls-ex11.png"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2299" title="export-xls-ex1" src="https://lucd.info/wp-content/uploads/2010/05/export-xls-ex11.png" alt="" width="398" height="217" srcset="https://www.lucd.info/wp-content/uploads/2010/05/export-xls-ex11.png 398w, https://www.lucd.info/wp-content/uploads/2010/05/export-xls-ex11-300x163.png 300w" sizes="auto, (max-width: 398px) 100vw, 398px" /></a></p>
<p>In this installation Excel was configured to start a new spreadsheet with 3 worksheets.</p>
<p>But we can get rid of those by using the <strong>-AppendWorksheet</strong> switch.</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">$data = Get-Process | Select-Object Name, Id, WS
Export-Xls $data D:\Reports\MyWkb.xls -AppendWorksheet:$false</pre><p></p>
<p>Now we only have the worksheet we just created.</p>
<p><a href="https://lucd.info/wp-content/uploads/2010/05/export-xls-ex21.png"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2300" title="export-xls-ex2" src="https://lucd.info/wp-content/uploads/2010/05/export-xls-ex21.png" alt="" width="335" height="221" srcset="https://www.lucd.info/wp-content/uploads/2010/05/export-xls-ex21.png 335w, https://www.lucd.info/wp-content/uploads/2010/05/export-xls-ex21-300x197.png 300w" sizes="auto, (max-width: 335px) 100vw, 335px" /></a></p>
<p>You can use the function in a pipeline</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">Get-Process | Select-Object Name, Id, WS | Export-Xls -Path D:\Reports\MyWkb.xls -WorksheetName &quot;My new sheet&quot;</pre><p></p>
<p>This gives</p>
<p><a href="https://lucd.info/wp-content/uploads/2010/05/export-xls-ex31.png"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2301" title="export-xls-ex3" src="https://lucd.info/wp-content/uploads/2010/05/export-xls-ex31.png" alt="" width="358" height="223" srcset="https://www.lucd.info/wp-content/uploads/2010/05/export-xls-ex31.png 358w, https://www.lucd.info/wp-content/uploads/2010/05/export-xls-ex31-300x186.png 300w" sizes="auto, (max-width: 358px) 100vw, 358px" /></a></p>
<p>And you can specify that the new worksheet should be at the end</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">Get-Process | Select-Object Name, Id, WS | `
   Export-Xls -Path D:\Reports\MyWkb.xls -WorksheetName &quot;Another sheet&quot; -SheetPosition &quot;end&quot;</pre><p></p>
<p>This gives</p>
<p><a href="https://lucd.info/wp-content/uploads/2010/05/export-xls-ex41.png"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2302" title="export-xls-ex4" src="https://lucd.info/wp-content/uploads/2010/05/export-xls-ex41.png" alt="" width="457" height="223" srcset="https://www.lucd.info/wp-content/uploads/2010/05/export-xls-ex41.png 457w, https://www.lucd.info/wp-content/uploads/2010/05/export-xls-ex41-300x146.png 300w" sizes="auto, (max-width: 457px) 100vw, 457px" /></a></p>
<h2>Charts</h2>
<p>With the <strong>-ChartType</strong> parameter you can create a chart on the worksheet.</p>
<p>The <strong>accepted values</strong> for this parameter are all the values present in the <strong>microsoft.Office.Interop.Excel.XlChartType</strong> enumtype. If you want to produce a list with the acceptable values you can do something like this.</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">[System.Reflection.Assembly]::LoadWithPartialName(&quot;Microsoft.Office.Interop.Excel&quot;) | Out-Null

[microsoft.Office.Interop.Excel.XlChartType] | gm -Static -MemberType Property | %{$_.Name}</pre><p></p>
<p>It will produce a rather long list of available chart types.<br />
<a href="https://lucd.info/wp-content/uploads/2010/05/XLS-charttypes.png"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2359" title="XLS-charttypes" src="https://lucd.info/wp-content/uploads/2010/05/XLS-charttypes.png" alt="" width="374" height="645" /></a></p>
<p>Some sample runs</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">$data = Get-Process | Sort-Object -Property WorkingSet -Descending | `
  Select-Object Name, @{N=&quot;WS&quot;;E={$_.WorkingSet/1MB}} -First 10
Export-Xls $data C:\Temp\MyWkb.xls -AppendWorksheet:$false -WorksheetName &quot;WS&quot; -ChartType &quot;xlColumnClustered&quot;</pre><p></p>
<p>Produces</p>
<p><a href="https://lucd.info/wp-content/uploads/2010/05/XLS-chart-1.png"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2360" title="XLS-chart-1" src="https://lucd.info/wp-content/uploads/2010/05/XLS-chart-1.png" alt="" width="631" height="318" srcset="https://www.lucd.info/wp-content/uploads/2010/05/XLS-chart-1.png 789w, https://www.lucd.info/wp-content/uploads/2010/05/XLS-chart-1-300x150.png 300w" sizes="auto, (max-width: 631px) 100vw, 631px" /></a></p>
<p>This short script selects the working set and the peak working set and add a line graph.</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">$data = Get-Process | Select-Object -First 10 Name, @{N=&quot;WS&quot;;E={$_.WorkingSet/1MB}},@{N=&quot;PM&quot;;E={$_.PeakWorkingSet/1MB}}
Export-Xls $data C:\Temp\MyWkb.xls -AppendWorksheet:$false -WorksheetName &quot;WS&quot; -ChartType &quot;xlLine&quot;</pre><p></p>
<p>And this produces</p>
<p><a href="https://lucd.info/wp-content/uploads/2010/05/XLS-chart-21.png"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-2746" title="XLS-chart-2" src="https://lucd.info/wp-content/uploads/2010/05/XLS-chart-21.png" alt="" width="710" height="274" srcset="https://www.lucd.info/wp-content/uploads/2010/05/XLS-chart-21.png 887w, https://www.lucd.info/wp-content/uploads/2010/05/XLS-chart-21-300x115.png 300w" sizes="auto, (max-width: 710px) 100vw, 710px" /></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lucd.info/2010/05/29/beyond-export-csv-export-xls/feed/</wfw:commentRss>
			<slash:comments>65</slash:comments>
		
		
			</item>
	</channel>
</rss>
