I am trying to write a script that checks folders to see if a backup has run for that particular installation.I have a number of folders and in these folders are the backup .zip files, the names of the files are the same for all folders so for example folder A has the file "Backup.zip" folder B will also have "Backup.zip" but from a different installation. At the moment I got it working fine but it's not looking very good and for example if a new folder gets created and a backup will be placed here I need to copy paste a whole block of text in the script and change the names. I'm looking for a more automated check that if I add a folder it will check that aswell.
Also the point of the checks is , if the file didn't change (or is not present at all) in the last 24 hours I should be notified by e-mail, which I also have working right now.
This is the code I have right now
if (Test-Path "<Path to file>")
{
if ( ((get-date) - (ls <Path to file>).LastWriteTime).days -lt 1)
{
$success++
}
else
{
Send-MailMessage -From $MailFrom -To $MailTo -Subject "!!Backup failed!!" -Body "!!Backup failed!!" -Port $SMTPPort -Credential $EmailCredential -UseSsl
$failed++
}
}
else
{
Send-MailMessage -From $MailFrom -To $MailTo -Subject "!!Backup not found!!" -Body "!!Backup not found!!" -Port $SMTPPort -Credential $EmailCredential -UseSsl
$failed++
}
The variables $success and $failed is just used for a general email that gets sent after it has ran all the checks to see how many ran successfull and how many failed.
But this code above is what I have to copy everytime a new folder for backups is created, so I manually have to edit the Path to file aswell. Can somebody help me or push me in the right direction to make it an automated thing? I'm extremely new to Powershell and I don't really know how to get it going. I do know there is a thing called 'foreach' but I have no clue how to implement it here.
Thanks in advance!
EDIT: I managed to get it working partly, for some reason about 8 folders can't get checked, I get this error on those folders. 37 other folders get checked fine, what can be the issue here? All the folders have the same properties.
Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.IO.FileInfo".
At line:25 char:6
+ if ( ([System.Io.fileinfo] $i).LastWriteTime.Date -ge [datetime]::Tod ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : ConvertToFinalInvalidCastException
the code it will be like that
#if there is more than one parent path mentioned all of them in array like $parents = "path1","path2", ..... and loop on them with the same foreach
$Parentpath = <Parent path which contain the backups>
foreach ($folder in $parentpath){
#check it is a folder not a file
if (($Folder.GetType().name) -eq "DirectoryInfo"){
$path = Convert-Path $folder.PSPath
}
if (Test-Path "$path")
{
if ( ((get-date) - (ls if (Test-Path "$path") ).LastWriteTime).days -lt 1)
{
$success++
}
else
{
Send-MailMessage -From $MailFrom -To $MailTo -Subject "!!Backup failed!!" -Body "!!Backup failed!!" -Port $SMTPPort -Credential $EmailCredential -UseSsl
$failed++
}
}
else
{
Send-MailMessage -From $MailFrom -To $MailTo -Subject "!!Backup not found!!" -Body "!!Backup not found!!" -Port $SMTPPort -Credential $EmailCredential -UseSsl
$failed++
}
}
$Parentpath = "\\x.x.x.x\FTPbackups\3CX\"
foreach ($i in $parentpath)
{
if ( ([System.Io.fileinfo] $i).LastWriteTime.Date -ge [datetime]::Today )
{
Write-Host "-=File Found for '$i'"
$success++
}
else
{
Write-Host "-=File found but not modified in the last 24 hours"
Send-MailMessage -From $MailFrom -To $MailTo -Subject "!!Backup van niet gelukt!!" -Body "!!Backup van niet gelukt!!" -Port $SMTPPort -Credential $EmailCredential -UseSsl
$failed++
}
}
I don't quite understand how it works, do I need to make an array with every path of every folder?
Related
I have a web application and I have one worker process set up. I need to ensure that IIS restarts when the value is exceeded. I am attaching a script below that works for me, but only when I have only one application pool. I am also attaching an error that appears on a server where there are multiple application pools.
$process_dump32 = get-process w3wp
$process_dump32_output = [Math]::Round($process_dump32.privatememorysize64, 2)
Write-Output $process_dump32_output
#Email Settings
$EmailBody = 'IIS has more than 800'
$subject_mail = "IIS has more than 800 - $Date"
$MailFrom = 'email#example.com'
$MailTo = 'email2#example.com'
$smtpServer = '10.0.0.1'
$smtpPort = '587'
$secpasswd1 = ConvertTo-SecureString "Paassword" -AsPlainText -Force
$mycreds1 = New-Object System.Management.Automation.PSCredential ("notification", $secpasswd1)
$encoding = [System.Text.Encoding]::UTF8
If ($process_dump32_output -lt 800000000)
{
#Do Nothing if RAM is less then 800MB
}
Else
{
#Stop/Start
Stop-Service W3SVC
Start-Sleep -seconds 5
Start-Service W3SVC
Send-MailMessage -from $MailFrom -to $MailTo -subject $subject_mail -body $EmailBody -Encoding $encoding -smtpServer $smtpServer -Port $smtpPort -credential $mycreds1
}
ERROR
Cannot find an overload for "Round" and the argument count: "2".
At I:\MONITOR\check-memory-exception-iis.ps1:7 char:1
+ $process_dump32_output = [Math]::Round($process_dump32.privatememorys ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
Thanks for help
Gild
Can someone please help me figure this part out.
#Mail Configuration
$smtpUser = "email#domain.com"
$smtppass = ConvertTo-SecureString "PasswordAsPlainText" -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential -ArgumentList ($smtpuser, $smtppass)
$ToAddress = "toaddress#domain.com"
$FromAddress = "send#domain.com"
$SMTPSERVER = "smtp.office365.com"
$SMTPPORT = "587"
$MailParam = #{
To = $ToAddress
From = $FromAddress
Subject = $subject
Body = $Mail
SMTPServer = $SMTPServer
Port = $SMTPPORT
Credential = $pscred
}
#Send Email
$GetChildItem = Get-ChildItem -Path C:\Temp
if ($GetChildItem -ne $Null)
{
Write-Host "Backup Success"
$subject = "$env:COMPUTERNAME SQL Backup Success"
$Mail = "SQL Backup Succeded on the server, please see the attached report for more details"
$attachment = $reportfile
Send-MailMessage #MailParam -BodyAsHtml -usessl
#Exit
}
if ($GetChildItem -eq $Null)
{
Write-Host "Backup Failed"
$subject = "$env:COMPUTERNAME SQL Backup Failed"
$Mail = "SQL Backup Failed on the server, please see the attached report for more details"
$attachment = $reportfile, $debuglog
Send-MailMessage #MailParam -usessl -BodyAsHtml
#Exit
}
Now I don't think anything is wrong with the code above, I am no expert coder but I can pretty much copy and paste :) and take stuff from there to get it working for me.
The issue with the above code is that when run it, it doesn't work for the 1st time but when you run it again it works fine. The error you get the first time is
Send-MailMessage : Cannot validate argument on parameter 'Body'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At C:\Users\Aasim\Desktop\Untitled1.ps1:29 char:26
+ Send-MailMessage #MailParam -BodyAsHtml -usessl
+ ~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Send-MailMessage], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.SendMailMessage
I am not sure what the issue is and why it doesn't work only the first time but works every other subsequent time until you close and reopen the script.
Basically I am creating a SQL backup script that backs up databases to our Network Share and then emails me whether it was successful or not. so far the rest of the script works just fine.
In your If and Else blocks, instead of creating a variable $subject, or $Mail, update the hashtable, $MailParam. Also, you seem to be missing the Attachment variable in your Send-MailMessage
if ($GetChildItem -eq $Null)
{
Write-Host "Backup Failed"
$MailParam.Subject = "$env:COMPUTERNAME SQL Backup Failed"
$MailParam.Body = "SQL Backup Failed on the server, please see the attached report for more details"
$attachment = $reportfile, $debuglog
Send-MailMessage #MailParam -usessl -BodyAsHtml -Attachments $attachment
#Exit
}
I'm creating a PS script to automate email blast to 17k users. Our exchange security baseline is set to only accept 60 requests per minute. Because I'm looping through the email list (CSV) line by line (sleep 1 sec), it took hours for my script to complete. What I'm trying to achieve now is to send the email to 100 users per request. I'm figuring out how to store the emails in an array of 100 & send the mail before going for the next 100. Any suggestion?
$recipients = Get-Content "mailinglist.csv"
foreach($rcpt in $recipients)
{
Write-Host "Attempt sending email to $rcpt ..."
Send-MailMessage -ErrorAction SilentlyContinue -ErrorVariable SendError -From $From -to $rcpt -Subject $Subject -SmtpServer $SMTPServer -port $SMTPPort -UseSsl -Credential $Cred -BodyAsHtml ($Body -f $Subject, $Date, $Venue, $Description, $Image)
$ErrorMessage = $SendError.exception.message
If($ErrorMessage)
{
Write-Host "Failure - $ErrorMessage" -ForegroundColor Red
Start-Sleep -Seconds 60
Send-MailMessage -ErrorAction SilentlyContinue -ErrorVariable SendError -From $From -to $rcpt -Subject $Subject -SmtpServer $SMTPServer -port $SMTPPort -UseSsl -Credential $Cred -BodyAsHtml ($Body -f $Subject, $Date, $Venue, $Description, $Image)
}
ElseIf($SendError.exception.message -eq $null)
{
Write-Host "Email has been sent to $rcpt" -ForegroundColor Green
Start-Sleep -Seconds 1
$n++
}
}
Write-Host "Total sent = $n"
You could use a traditional for loop and access your array elements by index.
$recipients = Get-Content "mailinglist.csv"
$To = <SomeValidEmailAddress>
$LastIndex = $recipients.GetUpperBound(0)
for ($i = 0; $i -le $LastIndex; $i+=100) {
$upperRange = [Math]::Min(($i+99),$LastIndex)
$Params = #{
ErrorAction = 'SilentlyContinue'
ErrorVariable = 'SendError'
Bcc = $recipients[$i..$upperRange]
To = $To
From = $From
Subject = $Subject
SmtpServer = $SMTPServer
Port = $SMTPPort
Credential $Cred
Body = $Body -f $Subject, $Date, $Venue, $Description, $Image
BodyAsHTML = $true
UseSsl = $true
}
"Attempt sending email to $($recipients[$i..$upperRange]) ..." # You may want to alter this to be more readable
Send-MailMessage #Params
# Other code
}
Explanation:
I've opted to use Splatting here for readability and manageability with the $Params hash table. It is entirely optional.
The -bcc parameter of Send-MailMessage supports a string array (string[]). Using this over the -To parameter will preserve privacy of the recipients. You can then easily send an email to multiple recipients provided you pass it an array. However, -To is required for Send-Mailmessage to work. It is recommended to make the email address passed into -To something that can be spammed or has a way of handling these types of emails. I have set up the $To variable for you to provide that email address. If privacy is of no concern whatsoever, -Bcc can just be replaced with -To.
Since $recipients is an array, you can access its elements by index, which supports the range operator ... $recipients[0..99] would be the first 100 items in the list.
$LastIndex stores the last index of the list, which is the value returned by the Array.GetUpperBound(Int32) method with dimension 0. Since the array is one-dimensional, 0 is the only dimension.
$upperRange is the beginning index ($i) plus 99. Should $upperRange ever be larger than $LastIndex, it will be set to $LastIndex. Depending on your PowerShell version, the $i+99 and $LastIndex comparison may not be necessary. Accessing an upperbound range beyond the size of the array, will just return all of the remaining elements of the array without throwing an error. This is likely just for completeness.
I have a PowerShell script that generates emails from a list of contacts imported from a CSV file:
# Get Credential
$Credential = & "C:\Powershell Scripts\Windows\Get-CredentialFromWindowsCredentialManager.ps1" ms.outlook.15:my.email#domain.com
# Get Contacts
$contacts = Import-Csv -Path "C:\Powershell Scripts\Email\Contacts.csv"
# Compose Email for each contact
foreach( $contact in $contacts )
{
Write-Output "Creating email for: $($contact.FirstName) $($contact.LastName)"
$To = "$($contact.FirstName) $($contact.LastName) <$($contact.Email)>"
$From = "My Email <my.email#domain.com>"
$Subject = "$($contact.FirstName), I have a suggestion for you!"
$Body = "<html></html>"
$SMTPServer = "smtp.office365.com"
$Port = 587
Send-MailMessage -To $To -From $From -Subject $Subject -SmtpServer $SMTPServer -Credential $Credential -UseSsl -Body $Body -BodyAsHtml -Port $Port
# Due to the Message Send rate limit (30 per minute) I added this to slow the rate down
Start-Sleep -Seconds 10
}
Every 10 minutes I get the following SMTP Exception:
Send-MailMessage : Service not available, closing transmission channel. The
server response was: 4.4.1 Connection timed out. Total session duration:
00:10:08.3716645
At C:\Powershell Scripts\Email\SendEmail.ps1:17 char:2
+ Send-MailMessage -To $To -From $From -Subject $Subject -SmtpServer $SMTPServer ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.Mail.SmtpClient:SmtpClient) [Send-MailMessage], SmtpException
+ FullyQualifiedErrorId : SmtpException,Microsoft.PowerShell.Commands.SendMailMessage
Are there any settings that I can modify or changes in code that will prevent this?
Don't take this personally, this is just a general rant, but you've got a script that performs an action against a resource that is out of your control. As such, you can't just expect an SMTP connection to succeed and wonder what you should do to prevent it from failing. Deep breath. The answer is to consider edge cases and actually cater for them in your code. Granted, you've got a sleep in there to try to not fall foul of rate limiting, but that's not a robust solution. In this instance, a simple exception handler around the Send-MailMessage call would suffice. You could include a number of retries and small sleep delays.
A "maximum number of acceptable failures" threshold could be used to break out of the retry loop and cause some kind of inward alerting, Etc.
Long story short, don't just throw spaghetti at the wall with your eyes closed.
</rant>
An example, but not necessarily the tidiest solution:
[Int32] $maxAttempts = 5;
[Int32] $failureDelay = 2;
[Int32] $interMessageDelay = 10;
[Int32] $numAttempts = 0;
[Boolean] $messageSent = $false;
:CONTACT foreach ( $contact in $contacts ) {
$numAttempts = 1;
$messageSent = $false;
while ( ($numAttempts -le $maxAttempts) -and (! $messageSent) ) {
try {
Write-Host -Object ( 'Sending message, attempt #{0} of #{1}...' -f $numAttempts, $maxAttempts );
Send-MailMessage <blah>;
Write-Host -Object "Ok.`n";
$messageSent = $true;
} #try
catch [System.Exception] {
# ^^^^^ This exception type needs changing, but I don't know the full
# type of your failure.
Write-Host -Object 'Failed.';
if ( $numAttempts -ge $maxAttempts ) {
Write-Host -Object "ERROR : Maximum attempts reached - aborting.`n";
continue CONTACT;
} else {
Write-Host -Object ( 'Sleeping for {0} second(s)...' -f $failureDelay );
Start-Sleep -Seconds $failureDelay;
$numAttempts++;
} #else-if
} #catch
} #while
Write-Host -Object ( 'Sleeping for {0} second(s)...' -f $interMessageDelay );
Start-Sleep -Seconds $interMessageDelay;
} #foreach
I am using robocopy to backup one hard drive to another below is the code that I am using:
robocopy "\\server_location\XXXX" "\\local_location\xxxx" /copyall /TEE /S /r:1 /ndl /xc /xo /xn /log+:"C:\desktop\log.TXT"
START mailto:myemail.job.com?subject=Ovl%%2Reportˆ&body=Test,%%0D%%0A%%0D%%0AHere%%20is%%20is%%the%%20ovl%%20report.%%0D%%0A%%0D%%0ABye
I need to be able to do a couple of things:
email the text file (powershell?) automatically through outlook.
When comparing files if the time stamp is different, append the destination file name, and copy over the file from the source.
So here is a script I've used for a lot of my robocopy jobs. I'm not sure if it fits your "send email through outlook" as it's just doing an SMTP, it's not actually hooking in to Outlook to do it. The script is a Powershell 2.0 script, and remember only Server 2008R2 and Windows Vista+ have the /MT option, which is bad to use with the /TEE option. Script is at the bottom of the post.
For the second point you have, I think you might want two different scripts, one that specifically ignores timestamps, and then another that would include the timestamp difference files, which if ran directly after the first should be about right. This would be with the /XO switch which is "/XO : eXclude Older - if destination file exists and is the same date or newer than the source - don’t bother to overwrite it."
Other options that might help you in scripting that out are:
/XC | /XN : eXclude Changed | Newer files
/XL : eXclude "Lonely" files and dirs (present in source but not destination) This will prevent any new files being added to the destination.
/XX : eXclude "eXtra" files and dirs (present in destination but not source) This will prevent any deletions from the destination. (this is the default)
#*=============================================
#* Base variables
#*=============================================
$SourceFolder = "C:\SourceFolder"
$DestinationFolder = "C:\DestinationFolder"
$Logfile = "C:\Robocopy.log"
$Subject = "Robocopy Results: Copy Purpose - Location to Location"
$SMTPServer = "smtp.server.com"
$Sender = "Server <email#address.com>"
$Recipients = "User1 <email#address.com>"
$Admin = "Admin <email#address.com>"
$SendEmail = $True
$IncludeAdmin = $True
$AsAttachment = $False
#*=============================================
#* GMAIL variables
#*=============================================
#$SMTPServer = "smtp.gmail.com"
#$cred = New-Object System.Net.NetworkCredential("username", "password");
# Add "-UseSsl -Credential $cred" to the Send-MailMessage
#*=============================================
#* SCRIPT BODY
#*=============================================
# Change robocopy options as needed. ( http://ss64.com/nt/robocopy.html )
Robocopy $SourceFolder $DestinationFolder /MIR /R:2 /W:5 /LOG:$Logfile /NP /NDL
# The following attempts to get the error code for Robocopy
# and use this as extra infromation and email determination.
# NO OTHER CODE BETWEEN THE SWITCH AND THE ROBOCOPY COMMAND
Switch ($LASTEXITCODE)
{
16
{
$exit_code = "16"
$exit_reason = "***FATAL ERROR***"
#$IncludeAdmin = $False
#$SendEmail = $False
}
8
{
$exit_code = "8"
$exit_reason = "**FAILED COPIES**"
#$IncludeAdmin = $False
#$SendEmail = $False
}
4
{
$exit_code = "4"
$exit_reason = "*MISMATCHES*"
$IncludeAdmin = $False
#$SendEmail = $False
}
2
{
$exit_code = "2"
$exit_reason = "EXTRA FILES"
$IncludeAdmin = $False
#$SendEmail = $False
}
1
{
$exit_code = "1"
$exit_reason = "Copy Successful"
$IncludeAdmin = $False
#$SendEmail = $False
}
0
{
$exit_code = "0"
$exit_reason = "No Change"
$SendEmail = $False
$IncludeAdmin = $False
}
default
{
$exit_code = "Unknown ($LASTEXITCODE)"
$exit_reason = "Unknown Reason"
#$SendEmail = $False
$IncludeAdmin = $False
}
}
# Modify the subject with Exit Reason and Exit Code
$Subject += " : " + $exit_reason + " EC: " + $exit_code
# Test log file size to determine if it should be emailed
# or just a status email
If ((Get-ChildItem $Logfile).Length -lt 25mb)
{
If ($IncludeAdmin)
{
If ($AsAttachment)
{
Send-MailMessage -From $Sender -To $Recipients -Cc $Admin -Subject $Subject -Body "Robocopy results are attached." -Attachment $Logfile -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
} Else {
Send-MailMessage -From $Sender -To $Recipients -Cc $Admin -Subject $Subject -Body (Get-Content $LogFile | Out-String) -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
}
} Else {
If ($AsAttachment)
{
Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body "Robocopy results are attached." -Attachment $Logfile -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
} Else {
Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body (Get-Content $LogFile | Out-String) -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
}
}
} Else {
# Creat the email body from the beginning and end of the $Logfile
$Body = "Logfile was too large to send." + (Get-Content $LogFile -TotalCount 15 | Out-String) + (Get-Content $LogFile | Select-Object -Last 13 | Out-String)
# Include Admin if log file was too large to email
Send-MailMessage -From $Sender -To $Recipients -Cc $Admin -Subject $Subject -Body $Body -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
#Exclude Admin if log file was too large to email
#Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body $Body -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
}
#*=============================================
#* END OF SCRIPT: Copy-RobocopyAndEmail.ps1
#*=============================================