Save an open Excel sheet using Powershell - powershell

I am completely newbie to Powershell. Need your help in saving an opened excel sheet using Powershell.
Script goes something like this
$xlPasteValues = -4163
$xlCellTypeLastCell = 11
$xl = new-object -comobject excel.application
$xl.Visible = $True
$xl.DisplayAlerts = $False
$wb = $xl.Workbooks.Add()
$i = 1
$collection = Get-ChildItem C:\Test\* -include *.csv # Change the location of your CSV files here.
$length = 4
foreach ($item in $collection) {
$wb1 = $xl.Workbooks.Open("$item")
$array = $item.ToString()
$delim = "\"
$SheetName = $array.split($delim)
$s = $SheetName[2]
$sn = $s.split(".")
$nsn = $sn[0]
$ws1 = $wb1.worksheets | where {$_.name -eq $nsn}
Write-Host $item $nsn
$used = $ws1.usedRange
$used.Select()
$used.copy()
$wb.Activate()
$ws = $wb.Sheets.Add()
$ws2 = $wb.worksheets | where {$_.name -eq "sheet$i"}
[void]$ws2.Range("A1").PasteSpecial(-4163)
$ws2.name = $nsn
$i++
$wb1.Close()
}
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$xlFixedFormat =[Microsoft.Office.Interop.Excel.XlFileFormat]::xlWorkbookDefault
$Excel = New-Object -comobject Excel.Application
$Excel.Visible = $true

Your question was rather vague so I'm assuming that you want to know how to open and save an Excel document through Powershell.
Open your Excel Document using New-Object
$a = New-Object -COM "Excel.Application"
$a.Visible = $true
$b = $a.Workbooks.Open("C:\PATH\TO\YOUR\EXCEL\sheet.xlsx")
Save and close your document
$b.Save()
$b.Close()

Check out my PowerShell Excel Module on Github. You can also grab it from the PowerShell Gallery.
Then try:
$xlFileName="c:\temp\test.xlsx"
dir *.csv |
ForEach {
$sheetName=$_.Name.Split('.')[0]
Import-Csv $_.FullName |
Export-Excel $xlFileName -WorkSheetname $sheetName
}
Invoke-Item $xlFileName

Related

I want to display a popup message of date contains in excel file

I am having a script file to compare the my system login username and samaccountname. If the system login username and samaccountname is matched then my output is display popup message of my system login username. As of now in message box only displaying the login username. Additionally i want display the date which is matching samaccountname row date.
the popup message & excel file data is attached as image file
$FilePath = 'd:\Alluserreport.xlsx'
$xl = New-Object -ComObject Excel.Application
$xl.Visible = $true
$wb = $xl.Workbooks.Open($filepath)
# get data from column 2
$data = $wb.Worksheets['Alluserreport'].UsedRange.Rows.Columns[2].Value2
# cleanup
$wb.close()
$xl.Quit()
While([System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb) -ge 0){}
while([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl) -ge 0){}
Remove-Variable xl,wb # this is optional
If ($data -eq $env:USERNAME )
{
Add-Type -AssemblyName PresentationCore,PresentationFramework
$msgBody = "$env:USERNAME"
$msgTitle = "Test"
$msgButton = 'OK'
$msgImage = 'Asterisk'
$Result = [System.Windows.MessageBox]::Show($msgBody,$msgTitle,$msgButton,$msgImage)
}
Else {
Write-Host "Not found"
}
Message box output
Excel data input
You can read the rows and columns of interest and create an array of objects from it storing both the SamAccountName and the corresponding LastLogonDate like below:
$FilePath = 'd:\Alluserreport.xlsx'
$xl = New-Object -ComObject Excel.Application
$xl.Visible = $false
$wb = $xl.Workbooks.Open($filepath)
# get data from columns 2 and 3
$sheet = $wb.Worksheets['Alluserreport']
$rowMax = $sheet.UsedRange.Rows.Count
$data = for ($row = 2; $row -le $rowMax; $row++) {
[PsCustomObject] #{
SamAccountName = $sheet.Cells.Item($row, 2).Value2
LastLogonDate = [datetime]::FromOADate($sheet.Cells.Item($row, 3).Value2) # convert to DateTime object
}
}
# cleanup
$wb.close()
$xl.Quit()
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($sheet)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
# filter for a specific username in the data
$user = $data | Where-Object { $_.SamAccountName -eq $env:USERNAME }
if ($user) {
$msgBody = "User: {0}`r`nLastLogon: {1}" -f $user.SamAccountName, $user.LastLogonDate
$msgTitle = "Test"
$msgButton = 'OK'
$msgImage = 'Asterisk'
$Result = [System.Windows.MessageBox]::Show($msgBody,$msgTitle,$msgButton,$msgImage)
}
else {
Write-Host "Not found"
}
Result:

Downloading .xlsx attachment from Outlook of Specific Date using Powershell

I have the below script. This $Tests shows the list of .xlsx attachment of specific date but is not able to download and throws an error. Please find the below script.
Add-type -assembly "Microsoft.Office.Interop.Outlook"
$olDefaultFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = New-Object -comobject Outlook.Application
$mapi = $outlook.GetNameSpace(“MAPI”)
$inbox = $mapi.GetDefaultFolder(6)
$FilePath= "c:\temp\Test\"
$subfolder = $inbox.Folders | Where-Object {$_.Name -eq “Test”}
$mail=$subfolder.Items |Select-Object -Property "ReceivedTime",#{name="Attachments";expression={$_.Attachments|%{$_.DisplayName}}} | Where-Object{$_.attachments -match ".xlsx" -and ($_.receivedtime -match "9/15/2020")} | Select-Object "attachments"
$Test = $mail.attachments
foreach ($out in $test) {$_.attachments|foreach {
Write-Host $_.filename
$Filename = $_.filename
If ($out.Contains("xlsx")) {
$_.saveasfile((Join-Path $FilePath "$out")) }}}
I am able to filter the .xlsx Attachments with Specific Date. But after this, I don't know how to save/download them.
Working with com objects can be rather frustrating in powershell. I recommend you get extremely familiar with Get-Member. You really have to interrogate each object. I've simplified your script as well as tested thoroughly. It will download each matching attachment (name) from each match email (received date)
Add-type -assembly "Microsoft.Office.Interop.Outlook"
$olDefaultFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = New-Object -comobject Outlook.Application
$mapi = $outlook.GetNameSpace(“MAPI”)
$inbox = $mapi.GetDefaultFolder(6)
$FilePath= "c:\temp\Test\"
$subfolder.Items | Where-Object {$_.receivedtime -match "9/20/2020" -and $($_.attachments).filename -match '.xlsx'} | foreach {
$filename = $($_.attachments | where filename -match '.xlsx').filename
foreach($file in $filename)
{
Write-Host Downloading $file to $filepath -ForegroundColor green
$outpath = join-path $filepath $file
$($_.attachments).saveasfile($outpath)
}
}
You may use this for more of an "in-line" approach.
Add-type -assembly "Microsoft.Office.Interop.Outlook"
$olDefaultFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = New-Object -comobject Outlook.Application
$mapi = $outlook.GetNameSpace(“MAPI”)
$inbox = $mapi.GetDefaultFolder(6)
$FilePath= "c:\temp\Test\"
$subfolder.Items | Where-Object {$_.receivedtime -match "9/20/2020" -and $($_.attachments).filename -match '.xlsx'} | foreach {
foreach($attachment in $($_.attachments | where filename -match '.xlsx'))
{
Write-Host Downloading $attachment.filename to $filepath -ForegroundColor green
$attachment.SaveAsFile((join-path $FilePath $attachment.filename))
}
}

Export table object to CSV

We extracted the required tables from word doc, but can someone help... that how can I export this table object $LETable to CSV, or export values which we have fetched below in CSV in table format.
$objWord = New-Object -Com Word.Application
$filename = 'D:\Files\Scan1.doc'
$objDocument = $objWord.Documents.Open($filename)
$LETable = $objDocument.Tables.Item(4)
$LETableCols = $LETable.Columns.Count
$LETableRows = $LETable.Rows.Count
$obj = New-Object -TypeName PSCustomObject
Write-Output "Starting to write... "
for($r=1; $r -le $LETableRows; $r++) {
for($c=1; $c -le $LETableCols; $c++) {
#Write-Host $r "x" $c
$content = $LETable.Cell($r,$c).Range.Text
Write-Host $content
}
}
$objDocument.Close()
$objWord.Quit()
# Stop Winword Process
$rc = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objWord)
Not quite ready a solution but I have no more time ATM.
The script outputs all fields enclosed in double quotes and delimited with commas per row and stores in a variable $RawCSV which then is passed to ConvertFrom-Csv
I had trouble with a cr/lf and char 7 in the cell values which I replace with nothing
## Q:\Test\2018\07\17\SO_51385204.ps1
$CsvName = '.\Test.csv'
$filename = (Get-Item ".\Test-text.docx").FullName
$tableNum = 4
$delimiter = ','
$objWord = New-Object -Com Word.Application
$objWord.Visible = $true # $false
$objDocument = $objWord.Documents.Open($filename)
$LETable = $objDocument.Tables.Item($tableNum)
$LETableCols = $LETable.Columns.Count
$LETableRows = $LETable.Rows.Count
Write-Output "Starting to write... "
# "Table rows:{0} cols:{1}" -f $LETableRows,$LETableCols
$RawCSV = for($r=1; $r -le $LETableRows; $r++) {
$content= #()
for($c=1; $c -le $LETableCols; $c++) {
#Write-Host ("R:{0},C:{1}" -f $r,$c)
$content += ("`"{0}`"" -f $LETable.Cell($r,$c).Range.Text -replace "(`r|`n|`t)|$([char]7)?")
}
$Content -join $delimiter
}
$Csv = $RawCSV | ConvertFrom-Csv
$objDocument.Close()
$objWord.Quit()
# Stop Winword Process
$rc = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objWord)
$Csv
$Csv | Export-Csv $CsvName -NoTypeInformation

replace text with a textbox input using variable

In my script I have a textbox- the user inserts text in it and than I want to change the text in a file (which the script creates earlier) to what the user inserted in the textbox.
The problem: it does deletes the part I wanted to be changed in the file- but it doesn`t write the text of the user instead. I also tried to locate the variable in the if loop- and it did changed the text like i wanted, but when I run the script again it wrote the old text in the disabled textbox.
my script is kinda long so I wont post all of it, but here are the importent parts. Thanks for the help!
#This creates a checkbox called dsp.z
$objDspCheckbox = New-Object System.Windows.Forms.Checkbox
$objDspCheckbox.Location = New-Object System.Drawing.Size(20,40)
$objDspCheckbox.Size = New-Object System.Drawing.Size(150,20)
$objDspCheckbox.Text = "dsp.z"
$objDspCheckbox.TabIndex = 0
$objForm.Controls.Add($objDspCheckbox)
#This creates the TextBox1 and put it on disable
$objTextBox1 = New-Object System.Windows.Forms.TextBox
$objTextBox1.Location = New-Object System.Drawing.Size(450,40)
$objTextBox1.Size = New-Object System.Drawing.Size(140,150)
$objTextBox1.TabIndex = 3
$objTextBox1.text = $text1
$objTextBox1.Enabled = $false
$objForm.Controls.Add($objTextBox1)
#This creates a checkbox for textbox1
$objDsp2Checkbox = New-Object System.Windows.Forms.Checkbox
$objDsp2Checkbox.Location = New-Object System.Drawing.Size(430,40)
$objDsp2Checkbox.Size = New-Object System.Drawing.Size(150,20)
$objDsp2Checkbox.TabIndex = 0
$objForm.Controls.Add($objDsp2Checkbox)
#Enables the textbox when user check the box:
#textbox1
$objDsp2Checkbox_OnClick = {
if ($objDsp2Checkbox.Checked -eq $true)
{
$objTextBox1.Enabled = $true
}
elseif ($objDsp2Checkbox.Checked -eq $false)
{
$objTextBox1.Enabled = $false
}
}
$objDsp2Checkbox.Add_Click($objDsp2Checkbox_OnClick)
#variables
$text1=$objTextBox1.Text
#This creates the ok and cancle buttons:
#ok Button
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(220,155)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click(
{
if (($objDspCheckbox.Checked -eq $true) -and ($objDsp2Checkbox.Checked -eq $true))
{
New-Item $path -itemtype file -name Dsp.json -value "old" ;((Get-Content "c:\users\$env:USERNAME\documents\Json\dsp.json") -replace 'old', $text1 | out-file "c:\users\$env:USERNAME\documents\Json\dsp.json") ;$objForm.close()
}
Try to Change This Line (specifly the $text1) to $objTextBox1.Text :
New-Item $path -itemtype file -name Dsp.json -value "old" ;
((Get-Content "c:\users\$env:USERNAME\documents\Json\dsp.json") -replace 'old', $text1 |
Out-file "c:\users\$env:USERNAME\documents\Json\dsp.json") ;$objForm.close()
To:
New-Item $path -itemtype file -name Dsp.json -value "old" ;
((Get-Content "c:\users\$env:USERNAME\documents\Json\dsp.json") -replace 'old', $objTextBox1.Text |
Out-file "c:\users\$env:USERNAME\documents\Json\dsp.json") ;$objForm.close()
I'm not sure if it's the case but if you just need to save the textbox text to file there's an easier approach :
$objTextBox1.Text | Out-file "c:\users\$env:USERNAME\documents\Json\dsp.json")

Powershell script to append the resultset from multiple source to a csv

I need help with this script. Basically I am connecting to multiple databases to get a dataset I need. And after capturing the dataset from all different databases, I need to write output it to a CSV file. Below is the code I basically came up with. The script is creating an output CSV file but it doesn't append all the data I captured. It only writes the last dataset captured. How do I go around this?
Need help to fix it.
$DB = Get-Content c:\ps\DBLIST.txt
foreach($Data in $DB)
{
Write-Host $Data
$strDB = $Data+".local"
$con = New-Object System.Data.SqlClient.SqlConnection
$con.ConnectionString = "Server=$strDB;Database=db;User ID=user;Password=password"
$con.open()
$qry = "select a, b, c, d from table1"
$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.CommandText = $qry
$cmd.Connection = $con
$da = New-Object System.Data.SqlClient.SqlDataAdapter
$da.SelectCommand = $cmd
$ds = New-Object System.Data.Dataset
$da.Fill($ds)
$dr = $cmd.ExecuteReader()
Write-Host
$outFile = "C:\ps\OUTPUT.csv"
If ($dr.HasRows)
{
write-Host a b c d
While ($dr.Read())
{
Write-Host $dr["a"] $dr["b"] $dr["c"] $dr["d"]
}
}
Else
{
Write-Host There are no records found. Try again.
}
$ds.Tables[0] | export-csv $outFile -NoTypeInfo -Force -Append
#$ds.Tables[0] | Export-Csv -Delimiter ','$outFile -Encoding "unicode"
Write-Host
$dr.Close()
$con.Close()
}
Instead of a ForEach loop, you can make use of the Powershell Pipeline via Foreach-Object. If you send the list of database servers down the pipeline, and then simply output the results inside the loop, Powershell will send all the results into the pipeline, effectively combining them. You can then take the final result and write it to the CSV file.
Note that I had to move the $outfile variable above the loop (where it belongs anyway). I also had to explicitly label the -Path parameter when I tested it in Powershell v2.0:
$outFile = "C:\TEMP\OUTPUT.csv"
$DB = Get-Content c:\ps\DBLIST.txt
$DB | Foreach-Object {
$Data = $_
$strDB = $Data+".local"
$con = New-Object System.Data.SqlClient.SqlConnection
$con.ConnectionString = "Server=$strDB;Database=db;User ID=user;Password=password"
$con.open()
$qry = "select a, b, c, d from table1"
$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.CommandText = $qry
$cmd.Connection = $con
$da = New-Object System.Data.SqlClient.SqlDataAdapter
$da.SelectCommand = $cmd
$ds = New-Object System.Data.Dataset
$da.Fill($ds) | Out-Null # prevent the number of records from going down the pipeline
$dr = $cmd.ExecuteReader()
# This is the magic right here -- it simply outputs the
# contents of $ds.Tables[0] to the pipeline
$ds.Tables[0]
$dr.Close()
$con.Close()
} | Select-Object a,b,c,d | Export-Csv -Path $outFile -Delimiter ',' -Encoding "unicode"
This should work
$SomeObject | export-csv $outFile -NoTypeInfo -Append
Edit for PowerShell v2 where -Append doesn't exist:
Collect all info in one object. Write this object to a file once done.
# before you start the loop:
$a = #()
# in your loop:
$a += $ds.Tables[0]
# after the loop:
$a | Export-Csv $outFile -NoTypeInformation