I am using PowerShell 5. I am using Expand-Archive to unzip files containing several thousand files. All works well most of the time but occasionally we get a bad filename in a zip. In my test-case the pdf file within the zip file has a colon ':' in the name, which causes the failure.
This is a scheduled task that should run silently, only reporting back if there is an error. On error, it should complete, but send an error warning. Optimally I would like to know both the archive name, and on which file within the archive it failed so I can add it to the report. Does anyone know how to do this?
I tried the -WarningVariable switch described here: PowerShell Pscx Expand-Archive method failure detection but had no content in the variable.
My current code sends a report, but the report contains only the name of the archive. I see no way to obtain the name of the file within the archive that causes the failure:
$file_list | ForEach-Object {
try{
Expand-Archive -Path $_.FullName -DestinationPath $input_temp -force
}
catch {
Send-MailMessage -from "email#address" -to "email#address" -subject $subject -body "File ($need_to_know_on_which_file_the_extract_failed) failed to extract from $($_.FullName)" -SmtpServer smtp.server
}
}
I also tried:
$shell = new-object -com shell.application
$zip = $shell.NameSpace($zipFile)
$files = $zip.items().count
if ($files -gt 0) {
foreach($item in $zip.items()){
if ($item.name -like "*.pdf") {
Write-Verbose "File: $($item.name)"
try {
$shell.Namespace($destination).copyhere($item,0x14)
}
catch {
Send-MailMessage -from "email#address" -to "email#address" -subject $subject -body "File ($need_to_know_on_which_file_the_extract_failed) failed to extract from $($_.FullName)" -SmtpServer smtp.server
}
}
}
}
This lets me get the name of the problem file within the archive if I run it manually, since it outputs just before trying the copyhere, but the try/catch doesn't work because on error it pops up an application screen which stops the script and no report is sent.
Related
I'm trying to get a script to work but I'm running into some issues. We have a folder that gets ZIP files sent to it via FTP and a service on the server moves them to another directory for processing. The issue is sometimes the service will be running but not doing anything so the monitoring doesn't sent a alert. I am working on a PowerShell script to check the folder for .zip files older than 30 minutes and if found send a email to that team to check out why the files haven't been moved. Below is what I have written so far, any help would be greatly appreciated. Thanks.
$Path = "c:\test"
$SMTPServer = "mail.server.com"
$From = "Monitor Prod <mon.server.com>"
$To = "people#server.com"
$Subject = "Alert: Potential issue with service in PROD"
$SMTPMessage = #{
To = $To
From = $From
Subject = "$Subject"
Smtpserver = $SMTPServer
}
$File = Get-ChildItem -path $path -Filter ".txt" | Where-Object {$_.LastWriteTime -lt (Get-Date).AddMinutes(-30)}
If ($File)
{ $SMTPBody = "Batches older than 30 minutes located in c:\test while service is actively running. Service may require a restart."
$File | ForEach { $SMTPBody += "$($_.FullName)`n" }
Send-MailMessage #SMTPMessage -Body $SMTPBody
}
I need to find new files in a folder which are over 10 MB in size, and then send a mail with the name of the files.
The tricky part: Mail should be send when there is a new file arrives in the folder, so I have to always keep track and differentiate between the old file and new file.
Problem: I have written the following code and am not able to build the logic for mail. How can I identify the new file comes and trigger the mail?
$namearray = #()
$n = gci 'C:\Users\RF\local\ReuseLibrary\FamilySaveDirectory' | % {get-item $.FullName| ? { $.length -gt 10mb }}
foreach($a in $n) {
$namearray += $a.name
}
$namearray
Send-MailMessage -To *#gmail.com -From '****#*.com' -Subject "Add User for $namearray NX License" -Body "Script execute $namearray successfully.." -SmtpServer 'mail.****.de'
As already pointed out in the comments, you can use the FileSystemWatcher object to monitor your filesystem. It got a little more tricky than just using the object, because you also need to be able to get the files which are already in the folder, not only the newly created ones.
Should the script really trigger the mail every time it has a new item in the $namearray? Maybe you'll get a flood of e-mails then.
It's better to use a function instead of a script to do your task because you have more flexibility, so you can check for only newly created files, and also for all files inside your folder.
Please try the function. I couldn't test it very well, because I don't have a folder where 10 MB large files get created all the time.
Usage:
To get all files (the wildcard * in the filter is important!):
Get-Files -filter '*.txt' -folder 'C:\yourfolder' -AllFiles
To monitor only new created files:
Get-Files -filter '*.txt' -folder 'C:\yourfolder' -NewFiles
Function:
Please edit the Send-MailMessage part so that you will receive the mail on your account.
function Get-Files() {
param(
[string]$filter,
[string]$folder,
[Parameter(ParameterSetName='AllFiles')]
[switch]$AllFiles,
[Parameter(ParameterSetName='NewFiles')]
[switch]$NewFiles
)
# Preparing the Name array
[string[]]$namearray = #()
if(!$NewFiles.IsPresent) {
# Getting all files which are already inside the
# folder and more than 10 MB
$files = gci $folder | % { get-item $_.FullName |
? { $_.length -gt 10mb -and $_.Extension -like $filter} } |
% { $namearray += $_.FullName }
# Send E-Mail
$secondnamearray = $namearray | out-string
Send-MailMessage -To '*#gmail.com' -From '****#*.com' -Subject "Add User for NameArray NX License" -Body $secondnamearray -SmtpServer 'mail.****.de'
$namearray = ""
}
# Monitoring of the Files
$monitoring = New-Object System.IO.FileSystemWatcher
$monitoring.Filter = $filter
$monitoring.path = $folder
$monitoring.EnableRaisingEvents = $true
$event = Register-ObjectEvent -InputObject $monitoring -EventName Created -Action {
# Checking for filesize
$x = (get-item $eventArgs.FullPath).length / 10mb
if ($x -ge 1) {
Write-Host "New Item found: $($eventArgs.FullPath). Sending E-Mail!"
$namearray += $eventArgs.FullPath
$newnamearray = $namearray | out-string
Send-MailMessage -To '*#gmail.com' -From '****#*.com' -Subject "Add User for NameArray NX License" -Body $newnamearray -SmtpServer 'mail.****.de'
$namearray = ""
}
}
}
I have a basic PowerShell (v4) script running on Server 2012. I decided to have Task Scheduler task it. Task scheduler instance is running as domain and machine administrator, Runs whether the user is logged on or not and runs with Highest Privileges. It's configured for Windows Server 2012 R2.
It starts a program of "Powershell" and this is my argument: "-ExecutionPolicy Bypass -file F:\AdMgmt\scripts\newcheckandcopy.ps1"
The problem: It executes some of the script just fine - however, I have an "If/Else" statement that it ignores. I've tried this with two scripts now and the Task scheduler always executes fine, but doesn't handle the If/Else stuff - it just skips over it and runs everything else.
This is the text of the script (it runs perfectly from the powershell console when logged in as the same account that runs the task):
$path = 'Q:\'
$stats = 0
$msg = ''
$days = 1
$hours = 0
$mins = 0
$logtime = Get-Date -uFormat "%y%m%d%H%M"
$files = #(Get-ChildItem -Recurse -Path $path -Include '*.*' | ?{ $_.LastWriteTime -lt (Get-Date).AddDays(-$days).AddHours(-$hours).AddMinutes(-$minutes) -and $_.psIsContainer -eq $false})
if ($files -ne $null) {
$f_names = [System.String]::Join('|',$files)
$msg = 'Message: ' + $f_names
$stats = $files.Count
Send-MailMessage -to "domain#test.com" -from "domain#test.com" -subject "FileMaker Files older than 1 Day" -body $msg -smtpserver "smtp-relay.test.com"
} else {
$msg = 'Message: 0 files exceed defined age'
Send-MailMessage -to "domain#test.com" -from "domain#test.com" -subject "FileMaker Files OK" -body $msg -smtpserver "smtp-relay.test.com"
}
Copy-Item Q:\* F:\admgmt\ -recurse
Add-Content f:\admgmt\logs\checkandcopy.txt $logtime
Write-Host $msg
Write-Host "Statistic: $stats"
I'm guessing your issue is you are running the script outside of user context. the Q:\ drive would not be available to it. PowerShell supports UNC paths so you might be able to substitute the actual path that Q:\ points to.
Quick question;
I want to make a log of bad stuff happening to the servers, but that's not the question.
The log should be deleted upon send, and if there is no log at the time the script is being run it should send a different message, without an attachment.
Here's the script so far:
<#
Set-ExecutionPolicy unrestricted -Force
iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))
cinst BetterCredentials
#>
Import-Module BetterCredentials
$Mailcred = Get-Credential -user user#gmail.com -pass 12345password
Try{
$File = C:/path/errorfile.log
}
Catch [System.Management.Automation.PSArgumentException]
{
"invalid object"
}
Catch [system.exception]
{
"caught a system exception"
}
Finally
{
If ($File -eq ){
then (send-mailmessage -ErrorAction SilentlyContinue -from "Server <Server#company.com>" -to "IT Dept. <Itdept#company.com>" -subject "Log of the day." -body "Good Morning,
Fortuneatly, nothing bad to report today!" -priority High -dno onSuccess, onFailure -smtpServer smtp.company.com -credential ($Mailcred) -usessl)
else (send-mailmessage -ErrorAction SilentlyContinue -from "Servers <Server#company.com>" -to "IT Dept. <ITDept#company.com>" -subject "Log of the day." -body "Good Morning,
Here is your daily report." -Attachments $File -priority High -dno onSuccess, onFailure -smtpServer smtp.company.com -credential ($Mailcred) -usessl)
}
}
What AM I doing wrong here?
Some parts of your code do not make any sense. Let's review those.
$File = C:/path/errorfile.log
...
If ($File -eq ){
The first part is about assignment. In order to find out wether errorfile.log exists, use the Test-Path cmdlet like so,
$File = test-path 'C:/path/errorfile.log'
Always use quotes around file name. If you got spaces within path, unquoted path doesn't work.
The second part is the actual test.
If ($File){
... # file was on disk, send alert and delete it
} else {
... # no file found, send ok
}
I'm using the Send-MailMessage cmdlet to send a copy of a log file. However, I'd like to add a line to the log file with the status of the Send.
Code:
$To = "toaddress#email.com"
$Subject = "Test Email Attachment"
$From = "fromaddress#email.com"
$Body = "Please find log file attached"
$SMTPServer = "smtp.server.com"
$LogFile = "Testlog.log"
Add-Content -Path $LogFile -Value "Trying to email log..."
Try
{
send-mailmessage -to $To -subject $Subject -From $From -body $Body -smtpserver $SMTPServer -attachments $LogFile -ErrorAction Stop
Add-Content -Path $LogFile -Value "Successfully emailed log to: $To"
}
Catch [system.exception]
{
Add-Content -Path $LogFile -Value "Error emailing log to: $To"
}
Finally {}
Error:
PS E:\> . .\TestMail.ps1
Add-Content : The process cannot access the file 'E:\Testlog.log' because it is being used by another process.
At E:\TestMail.ps1:16 char:14
+ Add-Content <<<< -Path $LogFile -Value "Error emailing log to: $To"
+ CategoryInfo : WriteError: (E:\Testlog.log:String) [Add-Content], IOException
+ FullyQualifiedErrorId : GetContentWriterIOError,Microsoft.PowerShell.Commands.AddContentCommand
This works fine if the email succeeds, but if it fails (say the smtp server is unavailable), and erroraction is set to "Stop", then the file lock on the log file doesn't get released. If erroraction is not set to "Stop", then the log file gets released, but the Catch block doesn't get triggered.
I found a reference to the "dispose" method if you're rolling your own mail function, but I'm not sure how it could be used with the Send-MailMessage cmdlet: http://petermorrissey.blogspot.com.au/2013/01/sending-email-messages-with-powershell.html
Is there a command I should run to release the lock, or is this a bug, or am I going about this the wrong way?
Seems to be a bug: https://connect.microsoft.com/PowerShell/feedback/details/785417/out-file-cannot-access-logfile-references-in-attachments-of-failed-send-mailmessage-call
Email a copy of the file or create your own email function using PowerShell 1 methods (One example: how to send an email with attachement using powershell v1?)