Add-PivotTable.ps1

function Add-PivotTable {
    <#
    .SYNOPSIS
        Add a pivot table to an Excel worksheet
 
    .DESCRIPTION
        Add a pivot table to an Excel worksheet
 
        Note:
            Each time you call this function, you need to save and re-create your Excel Object.
            If you attempt to modify the Excel object, save, modify, and save a second time, it will fail.
            See Save-Excel Passthru parameter for a workaround
  
    .PARAMETER Path
        Path to an xlsx file to add the pivot table to
 
        If Path is specified and you do not use passthru, we save the file
 
    .PARAMETER Excel
        ExcelPackage to add the pivot table to
 
        We do not save the ExcelPackage upon completion. See Save-Excel.
          
    .PARAMETER WorksheetName
        If specified, use this worksheet as the source.
             
    .PARAMETER StartRow
        The top row for pivottable data. If not specified, we use the dimensions start row
 
    .PARAMETER StartColumn
        The leftmost column for pivottable data. If not specified, we use the dimensions start column
 
    .PARAMETER EndRow
        The bottom row for pivottable data. If not specified, we use the dimensions' end row
 
    .PARAMETER EndColumn
        The rightmost column for pivottable data. If not specified, we use the dimensions' end column
 
    .PARAMETER PivotTableWorksheetName
        Name for the WorkSheet we create for the pivottable
 
    .PARAMETER PivotData
        Pivot data
         
    .PARAMETER PivotRows
        Pivot on these rows
 
    .PARAMETER PivotColumns
        Pivot on these columns
 
    .PARAMETER PivotFunction
        If specified, use this summary mode for pivot values (defaults to Count mode)
 
    .PARAMETER ChartType
        If specified, add a chart with this type
 
    .PARAMETER ChartTitle
        Optional chart title
 
    .PARAMETER ChartWidth
        Width of the chart
 
    .PARAMETER ChartHeight
        Height of the chart
 
    .PARAMETER Passthru
        If specified, pass the ExcelPackage back
 
    .EXAMPLE
     
        Get-ChildItem C:\ -file | Export-XLSX -Path C:\temp\files.xlsx
         
        Add-PivotTable -Path C:\Temp\files.xlsx -PivotRows Extension -PivotValues Length -ChartType PieExploded3D
 
        # Get files, create an xlsx in C:\temp\ps.xlsx
         
        # Take existing xlsx and add a pivot chart
            # Pivot rows on 'Extension'
            # Pivot values on 'Length'
            # Add an exploding pie chart!
 
        #This example gives you a pie chart breaking down storage by file extension
 
    .EXAMPLE
 
        #Create an xlsx.
            Get-ChildItem C:\ -file | Export-XLSX -Path C:\temp\files.xlsx
 
        # Open the excel file, add a pivot table (this won't save), pass through the excel object, save.
            New-Excel -Path C:\temp\files.xlsx |
                Add-PivotTable -PivotRows Extension -PivotValues Length -ChartType Pie -ChartTitle "Space per Extension" -ChartWidth 800 -ChartHeight 600 -Passthru |
                Save-Excel -Close
 
    .NOTES
        Thanks to Doug Finke for his example
        This function borrows heavily if not everything from Doug:
            https://github.com/dfinke/ImportExcel
 
        Thanks to Philip Thompson for an expansive set of examples on working with EPPlus in PowerShell:
            https://excelpslib.codeplex.com/
 
    .LINK
        https://github.com/RamblingCookieMonster/PSExcel
 
    .FUNCTIONALITY
        Excel
    #>

    [OutputType([OfficeOpenXml.ExcelPackage])]
    [cmdletbinding(DefaultParameterSetName = 'Excel')]
    param(

        [parameter( Position = 0,
                    ParameterSetName = 'Excel',
                    Mandatory=$true,
                    ValueFromPipeline=$true,
                    ValueFromPipelineByPropertyName=$false)]
        [OfficeOpenXml.ExcelPackage]$Excel,

        [parameter( Position = 0,
                    ParameterSetName = 'File',
                    Mandatory=$true,
                    ValueFromPipeline=$true,
                    ValueFromPipelineByPropertyName=$false)]
        [validatescript({Test-Path $_})]
        [string]$Path,

        [parameter( Position = 1,
                    Mandatory=$false,
                    ValueFromPipeline=$false,
                    ValueFromPipelineByPropertyName=$false)]
        [string]$WorkSheetName,

        [parameter( Position = 2,
                    Mandatory=$false,
                    ValueFromPipeline=$false,
                    ValueFromPipelineByPropertyName=$false)]
        [string]$PivotTableWorksheetName = 'PivotTable1',

        [parameter(
                    Mandatory=$false,
                    ValueFromPipeline=$false,
                    ValueFromPipelineByPropertyName=$false)]
        [int]$StartRow,
        
        [parameter(
                    Mandatory=$false,
                    ValueFromPipeline=$false,
                    ValueFromPipelineByPropertyName=$false)]
        [int]$StartColumn,
        
        [parameter(
                    Mandatory=$false,
                    ValueFromPipeline=$false,
                    ValueFromPipelineByPropertyName=$false)]
        [int]$EndRow,

        [parameter(
                    Mandatory=$false,
                    ValueFromPipeline=$false,
                    ValueFromPipelineByPropertyName=$false)]
        [int]$EndColumn,

        [string[]]$PivotRows,
        [string[]]$PivotColumns,
        [string[]]$PivotValues,
        [OfficeOpenXml.Table.PivotTable.DataFieldFunctions]$PivotFunction = [OfficeOpenXml.Table.PivotTable.DataFieldFunctions]::Count,

        [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType,
        [string]$ChartTitle,
        [int]$ChartWidth = 600,
        [int]$ChartHeight = 400,

        [switch]$Passthru
    )
    Process
    {

        Write-Verbose "PSBoundParameters: $($PSBoundParameters | Out-String)"    
        $SourceWS = @{}
        if($PSBoundParameters.ContainsKey( 'WorkSheetName') )
        {
            $SourceWS.Add('Name',$WorkSheetName)
        }

        Try
        {
            if($PSCmdlet.ParameterSetName -like 'File')
            {
                $Excel = New-Excel -Path $Path -ErrorAction Stop
            }

            $WorkSheets = @( $Excel | Get-Worksheet @SourceWS -ErrorAction Stop )
        }
        Catch
        {
            Throw "Could not get worksheets to search: $_"
        }

        If($WorkSheets.Count -eq 0)
        {
            Throw "Something went wrong, we didn't find a worksheet"
        }

        Foreach($SourceWorkSheet in $WorkSheets)
        {
            if($WorkSheets.count -gt 1)
            {
                $PivotTableWorksheetName = "$PivotTableWorksheetName-$($SourceWorkSheet.Name)"
            }

            Try
            {
                if( @( $Excel.WorkBook.Worksheets | Select -ExpandProperty Name -ErrorAction SilentlyContinue) -contains $PivotTableWorksheetName)
                {
                    Write-Error "Skipping existing worksheet '$PivotTableWorksheetName'"
                    continue
                }
                else
                {
                    Write-Verbose "Adding pivot worksheet $PivotTableWorksheetName"
                    $PivotWorkSheet = $Excel.Workbook.Worksheets.Add($PivotTableWorksheetName)
                }
            }
            Catch
            {
                Throw "Could not add PivotTable: $_"
            }
            
            #Get the coordinates
                $dimension = $SourceWorkSheet.Dimension
            
                If(-not $StartRow)
                {
                    $StartRow = $dimension.Start.Row
                }
                If(-not $StartColumn)
                {
                    $StartColumn = $dimension.Start.Column
                }
                If(-not $EndRow)
                {
                    $EndRow = $dimension.End.Row
                }
                If(-not $EndColumn)
                {
                    $EndColumn = $dimension.End.Column
                }

                $Start = ConvertTo-ExcelCoordinate -Row $StartRow -Column $StartColumn
                $End = ConvertTo-ExcelCoordinate -Row $EndRow -Column $EndColumn
                $RangeCoordinates = "$Start`:$End"
                
                Write-Verbose "Adding pivot table over data range '$RangeCoordinates' with name PT$PivotTableWorksheetName"

            #Pivot! Borrowed from Doug Finke - thanks Doug!
                #$PivotWorkSheet.View.TabSelected = $True
                $PivotTable = $PivotWorkSheet.PivotTables.Add($PivotWorkSheet.Cells["A1"], $SourceWorkSheet.Cells[$RangeCoordinates], "PT$PivotTableWorksheetName")
            
                if($PivotRows)
                {
                    Write-Verbose "Adding PivotRows $PivotRows"

                    foreach ($Row in @($PivotRows | Select -Unique))
                    {
                        [void]$PivotTable.RowFields.Add($PivotTable.Fields[$Row])
                    }
                }

                if($PivotColumns)
                {
                    Write-Verbose "Adding PivotColumns $PivotColumns"

                    foreach ($Column in @($PivotColumns | Select -Unique))
                    {
                        [void]$PivotTable.ColumnFields.Add($PivotTable.Fields[$Column])
                    }
                }

                if($PivotValues)
                {
                    Write-Verbose "Adding PivotValues $PivotValues"

                    foreach ($Value in @($PivotValues | Select -Unique))
                    {
                        $field = $PivotTable.DataFields.Add($PivotTable.Fields[$Value])
                        $field.Function = $PivotFunction
                    }
                }

                if($ChartType)
                {

                    Write-Verbose "Adding $ChartType chart"
                    $chart = $PivotWorkSheet.Drawings.AddChart("PC$PivotTableWorksheetName", $ChartType, $PivotTable)
                    $chart.SetPosition(1, 0, 6, 0)
                    $chart.SetSize($ChartWidth, $ChartHeight)
                    if($ChartTitle)
                    {
                        $chart.title.text = $ChartTitle
                    }
                }

            if($PSCmdlet.ParameterSetName -like 'File' -and -not $Passthru)
            {
                Write-Verbose "Saving '$($Excel.File)'"
                $Excel.save()
                $Excel.Dispose()
            }
            if($Passthru)
            {
                $Excel
            }
        }
    }
}