I have bunch of server names. Then i am looping thru these names and creating sheet by each server name. But getting error. below is my code
create excel spreadsheet
$xlsx = Join-Path -Path (Get-Location).Path -ChildPath "ClusterUsageReport-$(get-date -UFormat '%Y-%m-%d-%H-%M-%S').xlsx"
$xl = new-object -ComObject Excel.Application
$workbook = $xl.Workbooks.Add()
$i = 1
$clusters = ('Hcohesity01','Hcohesity05')
foreach ($vip in $clusters){
### create excel woorksheet
$worksheet = $workbook.Worksheets.Item($i)
$worksheet.Name = "$vip-Storage Growth"
$worksheet.activate | Out-Null
$i ++
### there are more scripts below this line, which create charts in each sheet ####
}
$xl.visible = $true
$worksheet.columns.autofit() | Out-Null
$worksheet.usedRange.rows(1).Font.Bold = $True
$workbook.SaveAs($xlsx,51) | Out-Null
Message i am getting
Invalid index. (Exception from HRESULT: 0x8002000B (DISP_E_BADINDEX))
At C:\raju\scripts\cluster_usage_in_excel.ps1:25 char:1
$worksheet = $workbook.Worksheets.Item($i)
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
Message coming from line $worksheet = $workbook.Worksheets.Item($i) , any idea how do i achieve this ?
Below is entire script .. can you point where needs update ??
usage: ./graphStorageGrowth.ps1 -vip mycluster -username myuser [ -domain mydomain.net ] [ -days 60 ]
### process commandline arguments
[CmdletBinding()]
param (
[Parameter(Mandatory = $True)][string]$username,
[Parameter()][int32]$days = 60
)
### constants
$TB = (1024*1024*1024*1024)
$GB = (1024*1024*1024)
### source the cohesity-api helper code
. ./cohesity-api
### create excel spreadsheet
$xlsx = Join-Path -Path (Get-Location).Path -ChildPath "ClusterUsageReport-$(get-date -UFormat '%Y-%m-%d-%H-%M-%S').xlsx"
$xl = new-object -ComObject Excel.Application
$workbook = $xl.Workbooks.Add()
$i = 0
$clusters = ('Hcohesity01','hcohesity03')
foreach ($vip in $clusters){
### create excel woorksheet
while($xlsx.Worksheets.Count -lt $i) { $xlsx.Worksheets.Add() }
#$worksheet = $workbook.Worksheets.Item($i)
$worksheet.Name = "$vip-Storage Growth"
$worksheet.activate()
### headings for data rows
$row = 1
$worksheet.Cells.Item($row,1) = 'Date'
$worksheet.Cells.Item($row,2) = 'Usage in Tib'
$row++
### authenticate
apiauth -vip $vip -username $username -domain corpads.local
### calculate startTimeMsecs
$startTimeMsecs = $(timeAgo $days days)/1000
### get cluster info
$clusterInfo = api get cluster?fetchStats=true
$clusterId = $clusterInfo.id
### collect $days of write throughput stats
#$stats = api get statistics/timeSeriesStats?schemaName=kBridgeClusterStats`&entityId=$clusterId`&metricName=kSystemUsageBytes`&startTimeMsecs=$startTimeMsecs`&rollupFunction=average`&rollupIntervalSecs=86400
$stats = api get "statistics/timeSeriesStats?endTimeMsecs=1662609600000&entityId=$clusterId&metricName=kMorphedUsageBytes&metricUnitType=0&range=day&rollupFunction=average&rollupIntervalSecs=86400&schemaName=kBridgeClusterStats&startTimeMsecs=$startTimeMsecs"
### populate excel worksheet with the throughput stats
foreach ($stat in $stats.dataPointVec){
$day = usecsToDate (($stat.timestampMsecs)*1000)
$consumed = $stat.data.int64Value/$TB
$worksheet.Cells.Item($row,1) = "$day".split()[0]
$worksheet.Cells.Item($row,2) = "{0:N2}" -f $consumed
$row++
}
### create excel chart
$chartData = $worksheet.Range("A1").CurrentRegion
$chart = $worksheet.Shapes.AddChart().Chart
$chart.chartType = 4
$chart.SetSourceData($chartData)
$chart.HasTitle = $true
$chart.ChartTitle.Text = "Storage Consumption Last $days Days"
$chart.Parent.Top = 50
$chart.Parent.Left = 150
$chart.Parent.Width = 600
$i ++
}
$xl.visible = $true
#[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl)
$worksheet.columns.autofit() | Out-Null
$worksheet.usedRange.rows(1).Font.Bold = $True
$workbook.SaveAs($xlsx,51) | Out-Null
What I need to do is to extract the data in the excel row and output them into different rows on Excel. After that, I will need to use the extracted data and perform certain conditions on the extracted data.
This is my current script:
To open excel and apply the formulas
$excel = New-Object -ComObject excel.application
$filepath = 'D:\testexcel.xlsx'
$workbook = $excel.workbooks.open("$filepath")
$worksheet = $workbook.worksheets.item(1)
$excel.Visible = $true
$rows = $worksheet.range("A1").currentregion.rows.count
$worksheet.range("S1:S$rows").formula = $worksheet.range("S1").formula
Function to find the row, apply the formula and output it
function test123(){
param([string]$test123)
$sourcefile = "D:\testexcel.xlsx"
$sheetname = "abc"
$excel = new-object -comobject excel.application
$excel.Visible = $true
$excelworkbook = $excel.Workbooks.open($sourcefile, 2, $true)
$excelworksheet = $excelworkbook.worksheets.item($sheetname)
$row = 1
$column = 1
$found = $false
while(($excelworksheet.cells.item($row, $column).value() -ne $null) -and($found -eq $false)){
if(($excelworksheet.cells.item($row, $column).value()).toupper() -eq $test123.ToUpper()){
write-host $excelworksheet.cells.item($row, $column).value() $excelworksheet.cells.item($row, $column+1).value(),
$excelworksheet.cells.item($row, $column +2).value() $found = $true
}
$row += 1
}
#close workbook
$excelworkbook.close()
$excel.quit()
}
test123 -test123 "Test123"
Please guide me and tell me if this is the right way to do it... Thanks
Please have a look into the ImportExcel module by Douge Finke. This module has the capability to do what you need.
Get it from PowerShell gallery: Install-Module -Name ImportExcel
Github link: https://github.com/dfinke/ImportExcel
you can then do Get-Help Import-Excel -Examples which has pretty good examples.
I was able to find here the code for Word Document files, how could I use /adjust the same set of code to run Excel files
Thanks
$objWord = New-Object -comobject Word.Application
$objWord.Visible = $false
$list = Get-ChildItem "C:\Users\john\foldername\*.*" -Include *.doc*
foreach($item in $list){
$objDoc = $objWord.Documents.Open($item.FullName,$true)
$objSelection = $objWord.Selection
$wdFindContinue = 1
$FindText = "1911"
$MatchCase = $False
$MatchWholeWord = $true
$MatchWildcards = $False
$MatchSoundsLike = $False
$MatchAllWordForms = $False
$Forward = $True
$Wrap = $wdFindContinue
$Format = $False
$wdReplaceNone = 0
$ReplaceWith = "456"
$wdFindContinue = 1
$ReplaceAll = 2
$a = $objSelection.Find.Execute($FindText,$MatchCase,$MatchWholeWord, `
$MatchWildcards,$MatchSoundsLike,$MatchAllWordForms,$Forward,`
$Wrap,$Format,$ReplaceWith,$ReplaceAll)
$objDoc.Save()
$objDoc.Close()
}
$objWord.Quit()
Based on this answer, you could do something like this:
$folderPath = "C:\Users\john\foldername\*"
$fileType = "*.xls*"
$excel = New-Object -ComObject Excel.Application
$textToReplace = #{
# "TextToFind" = "TextToReplaceWith"
"This1" = "That1"
"This2" = "That2"
"This3" = "That3"
}
Function findAndReplace($wsheet, $FindText, $ReplaceWith) {
#simple Replace to execute on all columns of a Worksheet object
$wsheet.Columns.Replace($FindText, $ReplaceWith) > $null
}
Function findAndReplaceMulti($wsheet, $lookupTable) {
#apply multiple Replace on the same Worksheet object
$lookupTable.GetEnumerator() | ForEach-Object {
findAndReplace $wsheet $_.Key $_.Value
}
}
Function findAndReplaceWholeWb($wbook, $lookupTable) {
#apply multiple Replace in all Worksheets
$wbook.Worksheets | ForEach-Object {
findAndReplaceMulti $_ $lookupTable
}
}
Get-ChildItem -Path $folderPath -Recurse -Filter $fileType | ForEach-Object {
$excel.Visible = $False
Write-Host "Processing `"$($_.Name)`"..."
$wbook = $excel.Workbooks.Open($_.FullName)
findAndReplaceWholeWb $wbook $textToReplace
$wbook.Close($True)
}
$excel.Quit()
$excel = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
I open a CSV file in Excel and run this macro to change the background color. I am trying to convert this part of code to PowerShell.
lrow = Range("G" & Rows.Count).End(xlUp).Row
Set MR = Range("G2:G" & lrow)
For Each cell In MR
If UCase(Trim(cell.Value)) = "FALSE" Then
cell.Interior.ColorIndex = 3
End If
Next
Any help converting this code to PowerShell.
Thanks
SR
You could write something like this:
$objExcel = New-Object -ComObject Excel.Application
$objExcel.Visible = $true
$objExcel.DisplayAlerts = $false
$filePath = "c:\logs\2015-04-23.csv"
$xlsFilePath = Get-Item -Path $filePath | % { Join-Path (Split-Path $_ -Parent) "$($_.BaseName).xls" }
$workBook = $objExcel.Workbooks.Open($filePath)
$workSheet = $WorkBook.sheets | select -First 1
$xlup = -4162
$lrow = $workSheet.cells.Range("G" + $workSheet.Rows.Count).End($xlup).Row
$workSheet.cells.Range("G2:G" + $lrow) | % {
$value = $_.Text
if($value.ToUpper() -eq "TRUE"){
$_.Interior.ColorIndex = 3
}
}
$WorkBook.SaveAs($xlsFilePath, 18)
$objExcel.Quit()
If you have a very large file, it is faster to search values using powershell then updating the Excel sheet. The following example looks a bit funny but executes much faster.
$filePath = "c:\logs\2015-04-23.csv"
$rowAliases = 97..122 | foreach { ([char]$_).ToString().ToUpper() }
$selectedRow = "G"
$selectedName = (Get-Content $filePath -ReadCount 1 -TotalCount 1).Split(",")[$rowAliases.IndexOf($selectedRow)]
$startRow = 2
$rowCount = 1;
$objExcel = New-Object -ComObject Excel.Application
$objExcel.Visible = $true
$objExcel.DisplayAlerts = $false
$xlsFilePath = Get-Item -Path $filePath | % { Join-Path (Split-Path $_ -Parent) "$($_.BaseName).xls" }
$workBook = $objExcel.Workbooks.Open($filePath)
$workSheet = $WorkBook.sheets | select -First 1
Import-Csv -Path $filePath | % {
if($rowCount -ge $startRow){
[string]$value = $_ | select -ExpandProperty $selectedName
if($value.ToUpper() -eq "TRUE"){
$workSheet.cells.Item($rowCount + 1, $selectedIndex + 1).Interior.ColorIndex = 3
}
}
$rowCount ++
}
$WorkBook.SaveAs($xlsFilePath, 18)
$objExcel.Quit()
how can I save the below script after it has run?
Script is from: Powershell Disk Usage Report
$erroractionpreference = "SilentlyContinue"
$a = New-Object -comobject Excel.Application
$a.visible = $True
$b = $a.Workbooks.Add()
$c = $b.Worksheets.Item(1)
$c.Cells.Item(1,1) = "Server Name"
$c.Cells.Item(1,2) = "Drive"
$c.Cells.Item(1,3) = "Total Size (GB)"
$c.Cells.Item(1,4) = "Free Space (GB)"
$c.Cells.Item(1,5) = "Free Space (%)"
$d = $c.UsedRange
$d.Interior.ColorIndex = 19
$d.Font.ColorIndex = 11
$d.Font.Bold = $True
$intRow = 2
$colComputers = get-content "c:\servers.txt"
foreach ($strComputer in $colComputers)
{
$colDisks = get-wmiobject Win32_LogicalDisk -computername $strComputer -Filter "DriveType = 3"
foreach ($objdisk in $colDisks)
{
$c.Cells.Item($intRow, 1) = $strComputer.ToUpper()
$c.Cells.Item($intRow, 2) = $objDisk.DeviceID
$c.Cells.Item($intRow, 3) = "{0:N0}" -f ($objDisk.Size/1GB)
$c.Cells.Item($intRow, 4) = "{0:N0}" -f ($objDisk.FreeSpace/1GB)
$c.Cells.Item($intRow, 5) = "{0:P0}" -f ([double]$objDisk.FreeSpace/[double]$objDisk.Size)
$intRow = $intRow + 1
}
}
According to https://social.technet.microsoft.com/Forums/windowsserver/en-US/919459dc-3bce-4242-bf6b-fdf37de9ae18/powershell-will-not-save-excel-file, this will work, but I am unable to:
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$xlFixedFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlWorkbookDefault
$Excel = New-Object -comobject Excel.Application
$Excel.Visible = $true
################
$Excel.workbooks.OpenText($file,437,1,1,1,$True,$True,$False,$False,$True,$False)
$Excel.ActiveWorkbook.SaveAs($env:tmp + "\myfile.xls", $xlFixedFormat)
$Excel.Workbooks.Close()
$Excel.Quit()
This worked for me :
$workbook.Close($false)
$excel.Quit()
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($workSheet)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
Remove-Variable -Name excel
To properly and completely close Excel, you also need to release COM references. In my own testing have found removing the variable for Excel also ensures no remaining references exist which will keep Excel.exe open (like if you are debugging in the ISE).
Without performing the above, if you look in Task Manager, you may see Excel still running...in some cases, many copies.
This has to do with how the COM object is wrapped in a “runtime callable wrapper".
Here is the skeleton code that should be used:
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$workbook = $excel.Workbooks.Add()
# or $workbook = $excel.Workbooks.Open($xlsxPath)
# do work with Excel...
$workbook.SaveAs($xlsxPath)
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
# no $ needed on variable name in Remove-Variable call
Remove-Variable excel
Got it working! - Special thanks to #Matt
Complete script that is working:
$erroractionpreference = "SilentlyContinue"
$a = New-Object -comobject Excel.Application
$a.visible = $True
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$xlFixedFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlWorkbookDefault
$a.Visible = $true
$b = $a.Workbooks.Add()
$c = $b.Worksheets.Item(1)
$c.Cells.Item(1,1) = "Server Name"
$c.Cells.Item(1,2) = "Drive"
$c.Cells.Item(1,3) = "Total Size (GB)"
$c.Cells.Item(1,4) = "Free Space (GB)"
$c.Cells.Item(1,5) = "Free Space (%)"
$d = $c.UsedRange
$d.Interior.ColorIndex = 19
$d.Font.ColorIndex = 11
$d.Font.Bold = $True
$intRow = 2
$colComputers = get-content "c:\servers.txt"
foreach ($strComputer in $colComputers)
{
$colDisks = get-wmiobject Win32_LogicalDisk -computername $strComputer -Filter "DriveType = 3"
foreach ($objdisk in $colDisks)
{
$c.Cells.Item($intRow, 1) = $strComputer.ToUpper()
$c.Cells.Item($intRow, 2) = $objDisk.DeviceID
$c.Cells.Item($intRow, 3) = "{0:N0}" -f ($objDisk.Size/1GB)
$c.Cells.Item($intRow, 4) = "{0:N0}" -f ($objDisk.FreeSpace/1GB)
$c.Cells.Item($intRow, 5) = "{0:P0}" -f ([double]$objDisk.FreeSpace/[double]$objDisk.Size)
$intRow = $intRow + 1
}
}
$a.workbooks.OpenText($file,437,1,1,1,$True,$True,$False,$False,$True,$False)
$a.ActiveWorkbook.SaveAs("C:\Users\Username\Desktop\myfile.xls", $xlFixedFormat)
$a.Workbooks.Close()
$a.Quit()
As mentioned in MSDN documentation here, the ReleaseComObject call only decrements the reference counter of that COM object by 1. If your scripts has multiple references of the same COM object, It will not release the object.
The documentation recommends using FinalReleaseComObject method to completely release the COM object and close the Excel process once in for all.
Just be sure to call this method only when you are done with the COM reference, as not doing so may lead to bugs which are hard to debug.
Creating the Excel file:
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $True
......
Closing down the Excel:
$Excel.Close()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
spps -n Excel
This solves my issue when $excel.Quit() does not quit and OneDrive won't upload the file. In my case I just need some automation and after the job is done it is quite fine that all the Excel processes are killed.
$excel.Quit()
# Check and you will see an excel process still exists after quitting
# Remove the excel process by piping it to stop-process
# Warning: This Closes All Excel Processes
Get-Process excel | Stop-Process -Force