I have a bunch of csv files from which I am writing data into a particular worksheet of an existing excel file. I have the below code and it works while looping through the CSV files and writing data into the existing worksheet
$CSVs ="rpt.test1",
"rpt.test2"
foreach ($csv in $CSVs)
{
$csv_name = $csv
echo "n - - - $sav_name - - -n"
foreach ($source in $Sources)
{
$src = $source
$inputCSV = "C:\Users\xxxx\Desktop\$src.$csv_name.csv"
$Path = "C:\Users\xxxx\Desktop\$csv_name.xlsx"
### Create a new Excel Workbook with one empty sheet
#$excel = New-Object -ComObject excel.application
#$workbook = $excel.Workbooks.Add(1)
#$worksheet = $workbook.worksheets.Item(1)
# Open the Excel document and pull in the 'Play' worksheet
$excel = New-Object -Com Excel.Application
$Workbook = $Excel.Workbooks.Open($Path)
$page = 'data'
$worksheet = $Workbook.worksheets | where-object {$_.Name -eq $page}
# Delete the current contents of the page
$worksheet.Cells.Clear() | Out-Null
### Build the QueryTables.Add command
### QueryTables does the same as when clicking "Data ยป From Text" in Excel
$TxtConnector = ("TEXT;" + $inputCSV)
$Connector = $worksheet.QueryTables.add($TxtConnector,$worksheet.Range("A1"))
$query = $worksheet.QueryTables.item($Connector.name)
### Set the delimiter (, or ;) according to your regional settings
$query.TextFileOtherDelimiter = $Excel.Application.International(5)
### Set the format to delimited and text for every column
$query.TextFileParseType = 1
$query.TextFileColumnDataTypes = ,2 * $worksheet.Cells.Columns.Count
$query.AdjustColumnWidth = 1
### Execute & delete the import query
$query.Refresh()
$query.Delete()
$Workbook.SaveAs($Path,51)
$excel.Quit()
}
Since it is an existing excel workbook, it throws a pop-up every time a file is being over-written. Have more than 15 CSV's and clicking Yes everytime is annoying
I have tried
$excel.DisplayAlerts = FALSE
and I have tried
$excel.CheckCompatibility = $False
and pretty much anything available on the internet. I am still learning powershell and at my wits end trying to stop this. Any help would be very much appreciated
Use display alerts statement before the SaveAs call:
$excel.DisplayAlerts = $false;
$excel.ActiveWorkbook.SaveAs($xlsFile);
$excel.DisplayAlerts = $false worked for me.
Related
I am absolute beginner in coding, My question is how can i retrieve complete details of Digital Certificate of files containing in multiple sub folders(export to csv). With little help from google, i found below powershell code which suffice this for a single file.
get-childitem C:\Windows\notepad.exe | Get-AuthenticodeSignature |Format-List
I am having 300+ files (exe/DLLS) in multiple sub folders.
What i what
Fetch the details for all the files including files in sub folders.
Export the details to csv. attached image file
Column headers > SignerCertificate:Subject,SignerCertificate: Issuer,SignerCertificate: Serial Number,SignerCertificate: Not Before,SignerCertificate: Not After,SignerCertificate:Thumbprint,TimeStamperCertificate:Subject,TimeStamperCertificate: Issuer,TimeStamperCertificate: Serial Number,TimeStamperCertificate: Not Before,TimeStamperCertificate: Not After,TimeStamperCertificate:Thumbprint,Status,StatusMessage,Path
If you know the paths to all of the files you could add them all to a .csv, then run a script with a ForEach loop containing all of them.
Example of csv formatting:
Then you could try the following script:
EDIT: Removed the System.Array as per Santiago.
# Make an array containing all FilePaths by importing the .csv file to a variable
$Files = (Import-Csv C:\Path\To\Import.csv).FilePath
# Run the script for each $File in the $Files array
$AllFilesExport = ForEach($File in $Files) {
# Get the information you want about the file
$FileInfo = Get-ChildItem $File | Get-AuthenticodeSignature
# Specify the Column name (left) and what data it should have in it (right)
[pscustomobject]#{
'SignerCertificate:Subject' = $FileInfo.SignerCertificate.Subject
'SignerCertificate: Issuer' = $FileInfo.SignerCertificate.Issuer
'SignerCertificate: Serial Number' = $FileInfo.SignerCertificate.SerialNumber
'SignerCertificate: Not Before' = $FileInfo.SignerCertificate.NotBefore
'SignerCertificate: Not After' = $FileInfo.SignerCertificate.NotAfter
'SignerCertificate: Thumbprint' = $FileInfo.SignerCertificate.Thumbprint
'TimeStamperCertificate: Subject' = $FileInfo.TimeStamperCertificate.Subject
'TimeStamperCertificate: Issuer' = $FileInfo.TimeStamperCertificate.Issuer
'TimeStamperCertificate: Serial Number' = $FileInfo.TimeStamperCertificate.SerialNumber
'TimeStamperCertificate: Not Before' = $FileInfo.TimeStamperCertificate.NotBefore
'TimeStamperCertificate: Not After' = $FileInfo.TimeStamperCertificate.NotAfter
'TimeStamperCertificate: Thumbprint' = $FileInfo.TimeStamperCertificate.Thumbprint
'Status' = $FileInfo.Status
'StatusMessage' = $FileInfo.StatusMessage
'Path' = $FileInfo.Path
}
}
# Export all of the file information from the AllFileExport array into an export .csv file
$AllFilesExport | Export-Csv -NoTypeInformation C:\Path\To\Export.csv
I have a powershell script which calls for a progress bar in a form showing execution of some batch files. Which class or method should be used from windows forms namespace to show the name of running batch script using powershell in a form class.
In the code below, in installationScriptsHome folder there are bunch of batch and vbs files, while those scripts are being called i want to show the name of the running script over the progress bar to show which script is running or may be some customised name\message with each script.
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | Out-Null
$ScriptsHome = Get-Item 'c:\installationScriptsHome\*'
# Init Form
$Form = New-Object System.Windows.Forms.Form
$Form.width = 1000
$Form.height = 200
$Form.Text = "**OSP Installation in Progress**"
# Init ProgressBar
$ProgressBar = New-Object System.Windows.Forms.ProgressBar
$ProgressBar.Maximum = $ScriptsHome.Count
$ProgressBar.Minimum = 0
$ProgressBar.Location = new-object System.Drawing.Size(10,70)
$ProgressBar.size = new-object System.Drawing.Size(967,10)
$Form.Controls.Add($ProgressBar)
# Add_Shown action
$ShownFormAction = {
$Form.Activate()
foreach ($b in $ScriptsHome) {
$ProgressBar.Increment(1)
Start-Process $b.FullName -Wait -WindowStyle Hidden
}
$Form.Dispose()
}
$Form.Add_Shown($ShownFormAction)
# Show Form
$Form.ShowDialog()
Thanks in advance.
Use the Label class (MSDN page).
$Label = New-Object System.Windows.Forms.Label
$Label.Location = New-Object System.Drawing.Point(140,20)
$Label.Size = New-Object System.Drawing.Size(280,20)
# put this in the foreach ($b in $ScriptsHome) { loop
$Label.Text = "$($b.Name)"
Useful link: MSDN System.Windows.Forms NameSpace
I found a script to download windows updates that I've been tweaking to fit my needs. It seems to work fine except I can't figure out how to remove the optional updates before downloading. I've found that the "Critical", "Important", and "Moderate" updates will have a MsrcSeverity value of one of those 3 words, where optional will be blank. How do I remove the updates with no msrcseverity value from the list before downloading??
Here's the whole code...
$global:scriptpath = $MyInvocation.MyCommand.Path
$global:dir = Split-Path $scriptpath
$global:logfile = "$dir\updatelog.txt"
write-host " Searching for updates..."
$session = New-Object -ComObject Microsoft.Update.Session
$searcher = $session.CreateUpdateSearcher()
$result = $searcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0")
if ($result.Updates.Count -eq 0) {
Write-Host "No updates to install"
}
else {
$result.Updates | Select Title
$result.Title >> $logfile
}
$downloads = New-Object -ComObject Microsoft.Update.UpdateColl
foreach ($update in $result){
$downloads.Add($update)
}
$count = $result.Updates.Count
write-host ""
write-host "There are $($count) updates available."
write-host ""
read-host "Press Enter to download\install updates"
$downloader = $session.CreateUpdateDownLoader()
$downloader.Updates = $downloads
$downloader.Download()
$installs = New-Object -ComObject Microsoft.Update.UpdateColl
foreach ($update in $result.Updates){
if ($update.IsDownloaded){
$installs.Add($update)
}
}
$installer = $session.CreateUpdateInstaller()
$installer.Updates = $installs
$installresult = $installer.Install()
$installresult
I have the "read-host" in there right now to stop it from downloading until I get this figured out. I've tried putting an extra pipe in $result.updates | Select Title | where {$result.Updates.MsrcSeverity -ne $null}, I've also tried that with just $result.MsrcSeverity and no go. I've tried the "where" pipe in a couple different places. I've also tried making an If statement in a couple places that says if the MsrcSeverity doesn't equal null then add it to the list. I've also tried adding onto the $searcher.Search( line with an and MsrcSeverity = 'Important'") just to test and that didn't do anything.
So far it still lists all the updates whether or not there's something in the MsrcSeverity column. Am I looking in the wrong place? It's the only thing I can see that tells the difference between an Important update and an Optional.
Thanks.
Search criterias are documented at IUpdateSearcher::Search method
The BrowseOnly=0 unfortunately doesn't exclude Optional updates as seen in Windows Update program. But AutoSelectOnWebSites=1 does.
"BrowseOnly=1" finds updates that are considered optional.
"BrowseOnly=0" finds updates that are not considered optional.
"AutoSelectOnWebSites=1" finds updates that are flagged to be automatically selected by Windows Update.
"AutoSelectOnWebSites=0" finds updates that are not flagged for Automatic Updates.
$session1 = New-Object -ComObject Microsoft.Update.Session -ErrorAction silentlycontinue
$searcher = $session1.CreateUpdateSearcher()
#Do not search for optional updates and exclude hidden
$result = $searcher.Search("IsInstalled=0 AND AutoSelectOnWebSites=1 AND IsHidden=0")
Thanks for all the help, everyone. I got so many helpful suggestions I didn't know where to begin...
I got it figured out, thanks.
I'm in the process of writing a PowerShell script to help in the process of setting up new PC's for my work. This will hopefully be used by more than just me so I'm trying to think of everything.
I have offline installers (java, flash, reader, etc) saved on our FTP server that the script downloads if a local copy hasn't already been saved in the Apps directory that gets created. Periodically the files on the FTP server will get updated as new versions of the programs are released. I want the script to have an option of checking for newer versions of the installers in case someone likes to carry around the local copies and forgets to check the server every now and then. It also will need to work in Windows 7 without any need to import additional modules unless there's an easy way to do that on multiple PC's at a time. I know about the import command, but the experiences I've had needed me to copy the module files into multiple places on the PC before it'd work.
Right now I haven't had much luck finding any solutions. I've found code that checks for modified dates on local files, or files on a local server, but nothing that deals with FTP other than uploading\downloading files.
Here's the last thing I tried. I tried a combination of what I found for local files with FTP. Didn't work too well.
I'm new to PowerShell, but I've been pretty good at piecing this whole thing together so far. However, this idea is becoming troublesome.
Thank you for the help.
$ftpsite = "ftp://ftpsite.com/folder/"
$firefox = (Get-Item $dir\Apps\install_firefox.exe).LastWriteTime.toString("MM/dd/yyyy")
if ($firefoxftp = (Get-ChildItem $ftpsite/install_firefox.exe | Where{$_.LastWriteTime -gt $firefox})) {
$File = "$dir\Apps\install_firefox.exe"
$ftp = "ftp://ftpsite.com/folder/install_firefox.exe"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
$webclient.DownloadFile($uri, $File)
}
UPDATE:
Here's what I have after Martin's help. It kind of works. It downloads the file from FTP, but it's not comparing the remote and local correctly. The remote file returns 20150709140505 and the local file returns 07/09/2015 2:05:05 PM. How do I format one to look like the other before the comparison, and is "-gt" the correct comparison to use?
Thanks!
function update {
$ftprequest = [System.Net.FtpWebRequest]::Create("ftp://ftpsite.com/Script_Apps/install_firefox.exe")
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::GetDateTimestamp
$response = $ftprequest.GetResponse().StatusDescription
$tokens = $response.Split(" ")
$code = $tokens[0]
$localfile = (Get-Item "$dir\Apps\install_firefox.exe").LastWriteTimeUtc
if ($tokens -gt $localfile) {
write-host "Updating Firefox Installer..."
$File = "$dir\Apps\install_firefox.exe"
$ftp = "ftp://ftpsite.com/Script_Apps/install_firefox.exe"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
$webclient.DownloadFile($uri, $File)
"Updated Firefox" >> $global:logfile
mainmenu
}
else {
Write-Host "Local Copy is Newer."
sleep 3
mainmenu
}
}
UPDATE 2:
Seems to be working! Here's the code. Thanks for the help!
function update {
$ftprequest = [System.Net.FtpWebRequest]::Create("ftp://ftpserver.com/Script_Apps/install_firefox.exe")
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::GetDateTimestamp
$response = $ftprequest.GetResponse().StatusDescription
$tokens = $response.Split(" ")
$code = $tokens[0]
$localtime = (Get-Item "$dir\Apps\install_firefox.exe").LastWriteTimeUtc
if ($code -eq 213) {
$tokens = $tokens[1]
$localtime = "{0:yyyymmddHHmmss}" -f [datetime]$localtime
}
if ($tokens -gt $localtime) {
write-host "Updating Firefox Installer..."
$File = "$dir\Apps\install_firefox.exe"
$ftp = "ftp://ftpserver.com/Script_Apps/install_firefox.exe"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
$webclient.DownloadFile($uri, $File)
"Updated Firefox" >> $global:logfile
mainmenu
}
else {
Write-Host "Local Copy is Newer."
sleep 3
mainmenu
}
}
You cannot use the WebClient class to check remote file timestamp.
You can use the FtpWebRequest class with its GetDateTimestamp FTP "method" and parse the UTC timestamp string it returns. The format is specified by RFC 3659 to be YYYYMMDDHHMMSS[.sss].
That would work only if the FTP server supports MDTM command that the method uses under the cover (most servers do, but not all).
$url = "ftp://ftpsite.com/folder/install_firefox.exe"
$ftprequest = [System.Net.FtpWebRequest]::Create($url)
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::GetDateTimestamp
$response = $ftprequest.GetResponse().StatusDescription
$tokens = $response.Split(" ")
$code = $tokens[0]
if ($code -eq 213)
{
Write-Host "Timestamp is" $tokens[1]
}
else
{
Write-Host "Error" $response
}
It would output something like:
Timestamp is 20150709065036
Now you parse it, and compare against a UTC timestamp of a local file:
(Get-Item "install_firefox.exe").LastWriteTimeUtc
Or save yourself some time and use an FTP library/tool that can do this for you.
For example with WinSCP .NET assembly, you can synchronize whole remote folder with installers with a local copy with one call to the Session.SynchronizeDirectories. Or your can limit the synchronization to a single file only.
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.Protocol = [WinSCP.Protocol]::Ftp
$sessionOptions.HostName = "ftpsite.com"
$session = New-Object WinSCP.Session
# Connect
$session.Open($sessionOptions)
$transferOptions = New-Object WinSCP.TransferOptions
# Synchronize only this one file.
# If you remove the file mask, all files in the folder are synchronized:
$transferOptions.FileMask = "install_firefox.exe"
$session.SynchronizeDirectories(
[WinSCP.SynchronizationMode]::Local, "$dir\Apps", "/folder",
$False, $False, [WinSCP.SynchronizationCriteria]::Time,
$transferOptions).Check()
To use the assembly, just extract a contents of .NET assembly package to your script folder. No other installation is needed.
The assembly supports not only the MDTM, but also other alternative methods to retrieve the timestamp.
See also a related Powershell example that shows both the above code and other techniques.
(I'm the author of WinSCP)
I am using powershell to run a sql query and export to a csv file. Process works great, but it is dropping a leading 0 in one of my columns. Field type in SQL Server is a varchar (not an option to change it unfortunately), and here is my syntax. Is it possible to continue to use my powershell export process and keep the leading zero?
$GoodSyntax = "Select * From tableunknown"
$extractFile = "C:\Test.csv"
Execute-SQLquery
if (Execute-SQLquery $GoodSyntax)
Function Execute-SQLquery {
param ($GoodSyntax)
$server = "Server01"
$database = "database01"
$connectionTemplate = "Data Source={0};Integrated Security=SSPI;Initial Catalog={1};"
$connectionString = [string]::Format($connectionTemplate, $server, $database)
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$command = New-Object System.Data.SqlClient.SqlCommand
$command.CommandText = $QueryString
$command.Connection = $connection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $command
$DataSet = New-Object System.Data.DataSet
$rowCount = $SqlAdapter.Fill($DataSet)
$DataSet.Tables[0] | Export-Csv $extractFile -NoTypeInformation
$connection.Close()
EDIT ---
Upon further examination, and opening my CSV file in a raw editor like notepad++ the leading zeros are their! They are just dropped when you attempt to view the file in Excel. So Excel is the culprit here.
I have never seen sql drop any data from varchar. How are you testing this? Maybe the problem is not in the sql but in whatever you use to look at the data.
I know that Excel does remove leading 0's because it tries to be clever and convert chars to numbers but sql does not.
Try viewing the result of $DataSet.GetXml() which will show the raw data more clearly.
$connection.Close()
$DataSet.GetXml()
Update: Excel strips the leading 0's and is confusing you. The sql data is correct.
How to specify formatting when opening a csv file in excel
http://www.upenn.edu/computing/da/bo/webi/qna/iv_csvLeadingZeros.html
Normally I create an xml/html file from the data. Excel will open it and supports various attributes with the data that control formatting, but its messy.
You should be able to do something like:
$a = "1000"
$a = $a.substring($a.length - 3, 37)
Ref. - https://technet.microsoft.com/en-us/library/ee176945.aspx