I'm trying to create a script that pulls print device from a group of servers housed in a text file. The script works fine except it only pulls one device from one server then the script completes. I'm trying to get this to work then loop in another command to combine all the data from all the sheets and look for dissimilarities between the server(s).
clear-host
# Get list of servers from text file
$sites = Get-Content -Path "User\user$\user\Documents\Working Folder\2132023\test.txt"
$counter = 4
# And here
foreach ($site in $sites) {
$result = Get-Printer -ComputerName $site | Select Name, DriverName, PortName, ShareName
#Create an Excel object
$ExcelObj = New-Object -comobject Excel.Application
$ExcelObj.Visible = $true
# Add a workbook
$ExcelWorkBook = $ExcelObj.Workbooks.Add()
$ExcelWorkSheet = $ExcelWorkBook.Worksheets.Item(1)
# Rename the worksheet
$ExcelWorkSheet.Name = $site
# Fill in the head of the table
$ExcelWorkSheet.Cells.Item(1, 1) = 'Device Name'
$ExcelWorkSheet.Cells.Item(1, 2) = 'Driver Name'
$ExcelWorkSheet.Cells.Item(1, 3) = 'Port Name'
$ExcelWorkSheet.Cells.Item(1, 4) = 'Share Name'
# Make the table head bold, set the font size and the column width
$ExcelWorkSheet.Rows.Item(1).Font.Bold = $true
$ExcelWorkSheet.Rows.Item(1).Font.size = 15
$ExcelWorkSheet.Columns.Item(1).ColumnWidth = 28
$ExcelWorkSheet.Columns.Item(2).ColumnWidth = 28
$ExcelWorkSheet.Columns.Item(3).ColumnWidth = 28
$ExcelWorkSheet.Columns.Item(4).ColumnWidth = 28
# Fill in Excel cells with the data obtained from the server
$ExcelWorkSheet.Columns.Item(1).Rows.Item($counter) = $result.Name
$ExcelWorkSheet.Columns.Item(2).Rows.Item($counter) = $result.DriverName
$ExcelWorkSheet.Columns.Item(3).Rows.Item($counter) = $result.PortName
$ExcelWorkSheet.Columns.Item(4).Rows.Item($counter) = $result.ShareName
$counter++
}
# Save the report and close Excel:
$ExcelWorkBook.SaveAs('\User\User\Documents\Working Folder\2132023\test.xlsx')
$ExcelWorkBook.Close($true)
That is because you are cfeating a new Excel COM object inside the loop.
Put that part above the loop, and inside create a new worksheet for each server and fill the data.
Because Get-Printer may very well return more that one object, you need to loop over the results from that too.
Try
# use full absolute path here
$outFile = 'X:\Somewhere\Documents\Working Folder\2132023\test.xlsx'
if (Test-Path -Path $outFile -PathType Leaf) { Remove-Item -Path $outFile -Force }
# Create an Excel object
$ExcelObj = New-Object -comobject Excel.Application
$ExcelObj.Visible = $true
# Add a workbook
$ExcelWorkBook = $ExcelObj.Workbooks.Add()
# Get list of servers from text file
$sites = Get-Content -Path "X:\Somewhere\Documents\Working Folder\2132023\test.txt"
foreach ($site in $sites) {
$counter = 2
# Add a sheet
$ExcelWorkSheet = $ExcelWorkBook.Sheets.Add()
# make this the the active sheet
$ExcelWorkSheet.Activate()
# Rename the worksheet
$ExcelWorkSheet.Name = $site
# Fill in the head of the table
$ExcelWorkSheet.Cells.Item(1, 1) = 'Device Name'
$ExcelWorkSheet.Cells.Item(1, 2) = 'Driver Name'
$ExcelWorkSheet.Cells.Item(1, 3) = 'Port Name'
$ExcelWorkSheet.Cells.Item(1, 4) = 'Share Name'
# Make the table head bold, set the font size and the column width
$ExcelWorkSheet.Rows.Item(1).Font.Bold = $true
$ExcelWorkSheet.Rows.Item(1).Font.size = 15
$ExcelWorkSheet.Columns.Item(1).ColumnWidth = 28
$ExcelWorkSheet.Columns.Item(2).ColumnWidth = 28
$ExcelWorkSheet.Columns.Item(3).ColumnWidth = 28
$ExcelWorkSheet.Columns.Item(4).ColumnWidth = 28
# Fill in Excel cells with the data obtained from the server
Get-Printer -ComputerName $site | Select-Object Name, DriverName, PortName, ShareName | ForEach-Object {
$ExcelWorkSheet.Columns.Item(1).Rows.Item($counter) = $_.Name
$ExcelWorkSheet.Columns.Item(2).Rows.Item($counter) = $_.DriverName
$ExcelWorkSheet.Columns.Item(3).Rows.Item($counter) = $_.PortName
$ExcelWorkSheet.Columns.Item(4).Rows.Item($counter) = $_.ShareName
$counter++
}
}
# Save the report and close Excel:
$ExcelWorkBook.SaveAs($outFile)
$ExcelWorkBook.Close($true)
$ExcelObj.Quit()
# Clean up the used COM objects
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelWorkSheet)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelWorkBook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelObj)
$null = [System.GC]::Collect()
$null = [System.GC]::WaitForPendingFinalizers()
P.S. The code would run faster if you set $ExcelObj.Visible = $false
Related
I am working on xlsx file and i need to read values from column A and display the values in column B
For an example column A has 100 rows and some of them have a string. At column B (Also 100 rows) i have also values. I want to run in a loop a search for all the cells in column A, Store them and print the corresponding values in column B
I want to search for # and display 1,2,7 from B
I need an object that holds the values from A and object for B (For further actions)
The code below search in all the columns and display the values.
What i need is to read only from a specific column. and i need an object that holds the values from A and B
$data holds the data of column A.
I want to in a loop and search for data and then display the same data in the same row in column B?
$ExcelFile = "C:\Temp\SharedFolder\Test.xlsx"
$excel = New-Object -ComObject Excel.Application
$Excel.visible = $false
$Excel.DisplayAlerts = $False # Disable comfirmation prompts
$workbook = $excel.Workbooks.Open($ExcelFile)
$data = $workbook.Worksheets['Sheet1'].UsedRange.Rows.Columns[1].Value2
Doing this in Excel can be done, but takes a bit more work.
If this is your Excel file:
$ExcelFile = "D:\Test\Test.xlsx"
$searchValue = '#'
$excel = New-Object -ComObject Excel.Application
$Excel.Visible = $false
$Excel.DisplayAlerts = $False # Disable comfirmation prompts
$workbook = $excel.Workbooks.Open($ExcelFile)
$worksheet = $workbook.Worksheets.Item(1)
# get the number of rows in the sheet
$rowMax = $worksheet.UsedRange.Rows.Count
# loop through the rows to test if the value in column 1 equals whatever is in $searchValue
# and capture the results in variable $result
$result = for ($row = 1; $row -le $rowMax; $row++) {
$val = $worksheet.Cells.Item($row, 1).Value2
if ($val -eq $searchValue) {
# output an object with both values from columns A and B
[PsCustomObject]#{A = $val; B = $worksheet.Cells.Item($row, 2).Value2}
}
}
# when done, quit Excel and remove the used COM objects from memory (important)
$excel.Quit()
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($worksheet)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
Now you can process the objects in $result. For demo just output:
$result
A B
- -
# 1
# 2
# 7
Of course, it would be far easier if you save your Excel file as CSV..
$searchValue = '#'
$result = Import-Csv -Path 'D:\Test\Test.csv' -UseCulture | Where-Object { $_.A -eq $searchValue }
$result
When exporting an Excel file to Csv, Excel won't always use the comma as delimiter character. That depends on your machine's local settings. This is the reason I added switch -UseCulture to the Import-Csv cmdlet which will make sure it uses the same delimiter character your locally installed Excel uses for its output.
I need to split and save an excel file based on the values of the first column via a powershell script. Here is how the excel file is build up (app 30.000 rows)
´´´Column1 # Column2 # Column3´´´
´´´AA # data # data # data´´´
´´´AA # data # data # data´´´
´´´AB # data # data # data´´´
´´´AC # data # data # data´´´
´´´AC # data # data # data´´´
The result should be multiple files with filenames AA.xlxs, AB.xlxs, AC.xlxs and of course the according rows data.
What I have so far is the following code:
$objexcel = New-Object -ComObject Excel.Application
$wb = $objexcel.WorkBooks.Open("C:\Test.xlsx")
$objexcel.Visible = $true
$objexcel.DisplayAlerts = $False
$ws = $wb.Worksheets.Item(1)
$doc = $ws.Range("A:A")
foreach ($doc in $docs) {
$newfile,$objexcel = $objexcel.where({$doc -eq $doc})
$newfile | Export-Excel "C:\$doc.xlxs"
}
It just opens the file, but nothing happens.
It would be great if some coder could have a look at the code or provide a working one.
Thanks in advance.
Following is a working code that will iterate through unique elements in column one and make a copy of it in a new spreadsheet and save it.
Function Create-Excel-Spreadsheet {
Param($NameOfSpreadsheet)
# open excel
$excel = New-Object -ComObject excel.application
$excel.visible = $true
# add a worksheet
$workbook = $excel.Workbooks.Add()
$xl_wksht= $workbook.Worksheets.Item(1)
$xl_wksht.Name = $NameOfSpreadsheet
return $workbook
}
$objexcel = New-Object -ComObject Excel.Application
$wb = $objexcel.WorkBooks.Open("C:\Temp\Test.xlsx") # Changing path for test.xlsx file.
$objexcel.Visible = $true
$objexcel.DisplayAlerts = $False
$ws = $wb.Worksheets.Item(1)
$usedRange = $ws.UsedRange
$usedRange.AutoFilter()
$totalRows = $usedRange.Rows.Count
$rangeForUnique = $usedRange.Offset(1, 0).Resize($UsedRange.Rows.Count-1)
[string[]]$UniqueListOfRowValues = $rangeForUnique.Columns.Item(1).Value2 | sort -Unique
for ($i = 0; $i -lt $UniqueListOfRowValues.Count; $i++) {
$newRange = $usedRange.AutoFilter(1, $UniqueListOfRowValues[$i])
$workbook = Create-Excel-Spreadsheet $UniqueListOfRowValues[$i]
$wksheet = $workbook.Worksheets.Item(1)
$range = $ws.UsedRange.Cells
$range.Copy()
$wksheet.Paste($wksheet.Range("A1"))
$workbook.SaveAs("C:\temp\" + $UniqueListOfRowValues[$i], $xlFixedFormat)
$workbook.Close()
}
Reason nothing is happening is because you are iterating over $docs which does not contain any elements. It is currently null.
When you make a reference to look up the data, you are using $objexcel, but thats your excel application.. not the worksheet that you want to iterate over. Use $as for accessing the worksheet.
You need to iterate over Cells of your $ws and take the data when cells.Item(x, 0) and create a new file based on that with values in other two columns.
Link to example on SO -> Create and Update excel file
$filepath = "C:\Users\Desktop\New folder\Tangent.xlsx"
$sheetname = "sheet"
$objExcel = New-Object -ComObject Excel.Application
$objExcel.Visible = $false
$WorkBook = $objExcel.Workbooks.Open($filepath)
$WorkBook.sheets | Select-Object -Property Name
$WorkSheet = $WorkBook.Sheets.Item($sheetname)
$myObj = [PSCustomObject][ordered]#{
john = $WorkSheet.Range("B1").Text
Rebel = $WorkSheet.Range("B2").Text
MArk = $WorkSheet.Range("B3").Text
Susan = $WorkSheet.Range("B4").Text
Patty = $WorkSheet.Range("B5").Text
}
I have hardcoded all the names into the code which is a weird way of doing it. I want it to read from the Excel directing using command. Can anyone help me please?
Create an empty hashtable and fill it as you iterate over the rows in your Excel sheet, then create the object.
$ht = #{}
$i = 1
while ($WorkSheet.Cells.Item($i, 1).Text) {
$ht[$WorkSheet.Cells.Item($i, 1).Text] = $WorkSheet.Cells.Item($i, 2).Text
$i++
}
$obj = [PSCustomObject]$ht
Untested, as I don't have Excel at hand here.
$if = '\\portal2010.brand.com\sites\HC\2017 count.xlsx'
$excel = New-Object -Com Excel.Application
$Workbook = $Excel.Workbooks.Open($if)
$page = 'HC'
$ws = $Workbook.worksheets | where-object {$_.Name -eq $page}
$range = $ws.Range("V2:AF")
$rows = $range.Rows.Count
$hcTableCopy = $ws.Range("V2:AF$rows").Copy()
$hcTablePaste = $hcTableCopy.PasteSpecial($default, $default, $default, $default, 9, $default, $default)
$SendTo = "e.mail#brand.com"
$SMTPServer = "smtp.brand.com"
$EmailFrom = "e.mail2#brand.com"
$EmailSubject = "Weekly Email"
$Image2 = '\\portal2010.brand.com\sites\HC\HC Dashboards\2017 HC_files\2017 count_25311_image002.png'
$Image4 = '\\portal2010.brand.com\sites\HC\HC Dashboards\2017 HC_files\2017 count_25311_image004.png'
$Image6 = '\\portal2010.brand.com\sites\HC\HC Dashboards\2017 HC_files\2017 count_25311_image006.png'
$Image8 = '\\portal2010.brand.com\sites\HC\HC Dashboards\2017 HC_files\2017 count_25311_image008.png'
$Message = new-object Net.Mail.MailMessage
Add-PSSnapin Microsoft.Exchange.Management.Powershell.Admin -erroraction silentlyContinue
$att2 = new-object Net.Mail.Attachment($Image2)
$att2.ContentId = "att2"
$att4 = new-object Net.Mail.Attachment($Image4)
$att4.ContentId = "att4"
$att6 = new-object Net.Mail.Attachment($Image6)
$att6.ContentId = "att6"
$att8 = new-object Net.Mail.Attachment($Image8)
$att8.ContentId = "att8"
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$body = '
<img src="cid:att2" /><br/><br/>
<img src="cid:att4" /><br/><br/>
<img src="cid:att6" /><br/><br/>
<img src="cid:att8" /><br/><br/>
'
$Message.From = $EmailFrom
$Message.To.Add($SendTo)
$Message.Cc.Add($CCTo1)
$Message.Cc.Add($CCTo2)
$Message.Cc.Add($CCTo3)
$Message.Cc.Add($CCTo4)
$Message.Subject = $EmailSubject
$Message.Body = $body + $hcTablePaste
$Message.IsBodyHTML = $true
$Message.Attachments.Add($att2)
$Message.Attachments.Add($att4)
$Message.Attachments.Add($att6)
$Message.Attachments.Add($att8)
$smtp.Send($Message)
$excel.DisplayAlerts = $False
Start-Sleep -s 5
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
The above code is what I have tried so far. I get an error that says it can't open the file from sharepoint at this point and I am not sure the table paste portion of this is going to work right.
I tried modifying the following, but I really don't think I should have to check out and make editable the file just to copy the table range to an email.
foreach ($file in $excelfiles)
{
$workbookpath = $file.fullname
if ($excel.workbooks.canCheckOut($workbookpath)) {
# open the worksheet and check it out
$excelworkbook = $excel.workbooks.Open($workbookpath)
$excelworkbook = $excel.workbooks.CheckOut($workbookpath)
# Don't ask cuz I don't know (yet). You have to open it again.
$excelworkbook = $excel.workbooks.Open($workbookpath)
# Refresh all the pivot tables with the new data.
$excelworkbook.RefreshAll()
# Save and Check it in
$excelworkbook.Save()
$excelworkbook.CheckInWithVersion()
}
}
$excel.quit()
So any help to get the table pasting part would be great. The email send with images just fine other than that.
I have a script that places everything nicely into a spread sheet. The problem is, I need it to export as a csv file instead. All the foreach loops are completely baffling me here as far as where to put the export csv functions in the script. If someone could could school me on how to get the fields into a csv file, It would be greatly appreciated.
$date = 0
$date = get-date -format "yyyy-MMM-dd-hhmm"
$date
#New Excel Application
$Excel = New-Object -Com Excel.Application
$Excel.visible = $False
# Create 1 worksheets
$Excel = $Excel.Workbooks.Add()
# Assign each worksheet to a variable and
# name the worksheet.
$Sheet1 = $Excel.Worksheets.Item(1)
$Sheet1.Name = "HH_SERVERS"
#Create Heading for General Sheet
$Sheet1.Cells.Item(1, 1) = "Machine_Name"
$Sheet1.Cells.Item(1, 2) = "OS"
$Sheet1.Cells.Item(1, 3) = "Software"
$Sheet1.Cells.Item(1, 4) = "Vendor"
$Sheet1.Cells.Item(1, 5) = "Version"
$colSheets = ($Sheet1)
foreach ($colorItem in $colSheets)
{
$intRow = 2
$intRowDisk = 2
$intRowSoft = 2
$intRowNet = 2
$WorkBook = $colorItem.UsedRange
$WorkBook.Interior.ColorIndex = 20
$WorkBook.Font.ColorIndex = 11
$WorkBook.Font.Bold = $True
}
#Auto Fit all sheets in the Workbook
foreach ($colorItem in $colSheets)
{
$WorkBook = $colorItem.UsedRange
$WorkBook.EntireColumn.AutoFit()
clear
}
$Servers = get-content "c:\temp\HH_Servers.txt"
foreach ($Server in $Servers)
{
$GenItems2 = gwmi Win32_OperatingSystem -Comp $Server
$Software = gwmi Win32_Product -Comp $Server
# Populate General Sheet(1) with information
foreach ($objItem in $GenItems2)
{
$Sheet1.Cells.Item($intRow, 2) = $objItem.Caption
}
#Populate Software Sheet
foreach ($objItem in $Software)
{
$Sheet1.Cells.Item($intRowSoft, 1) = $Server
$Sheet1.Cells.Item($intRowSoft, 3) = $objItem.Name
$Sheet1.Cells.Item($intRowSoft, 4) = $objItem.Vendor
$Sheet1.Cells.Item($intRowSoft, 5) = $objItem.Version
$intRowSoft = $intRowSoft + 1
}
}
$outputfile = "c:\temp\" + $date.toString() + "-HH_Server_Software"
$Excel.SaveAs($outputfile)
$Excel.Close()
Write-Host "*******************************" -ForegroundColor Green
Write-Host "The Report has been completed." -ForeGroundColor Green
Write-Host "*******************************" -ForegroundColor Green
# ========================================================================
# END of Script
# ==================
You can't save an entire workbook as CSV. You need to save the individual worksheet instead. The file format value for CSV is 6 (don't remember where I found that out though):
$xlCSV = 6
$outputfile = "c:\temp\" + $date.toString() + "-HH_Server_Software.csv"
$Sheet1.SaveAs($outputfile, $xlCSV)
(Tested on Windows 7 with Excel 2013.)
Thanks to #Matt for a comment with a link to the XLFileFormat Enumerations.