Powershell Script to search a file and then send an email - powershell

I am new to Powershell
I am creating a file with output in it and then search that file for a specific phrase and if it contains it, get sent an email. I've been able to create the file and then filter out what I need using sls but I can't seem to figure out how to get that file sent to me via email if it contains a specific word.
Ex - If the file contains the word Offline send that file attached to an email.
These are the following commands I've run so far -
d:
set-location -Path "program files\veritas\volmgr\bin"
.\vmoprcmd >d:\test.data\mediastatus.txt
cd \
set-location -Path "test.data"
sls offline .\mediastatus.txt

So in your situation-
you have output that's generated by this vmoprcmd executable
it's redirected to a file
you want to detect whether the file/output contains the string "Offline"
if it does, trigger an email
To achieve this, you can utilize the Select-String and Send-MailMessage cmdlets:
$Output = 'D:\test.data\mediastatus.txt'
& 'D:\Program Files\veritas\volmgr\bin\vmoprcmd.exe' > $Output
if (Select-String -Pattern offline -Path $Output -Quiet) {
$MailArgs = #{
'To' = 'mailaddress#example.com'
'From' = 'mymailbot#example.com'
'Subject' = 'Device offline!'
'Attachments' = $Output
'Body' = 'Whatever you want it to be'
'SmtpServer' = 'my.smtp.server.com'
'Port' = 25
}
Send-MailMessage #MailArgs
}
Documentation:
Call operator &
Select-String
Send-MailMessage
#Splatting

Related

Save Email body to html file powershell

I am trying to convert a folder full of MSG files to HTML files. I have a scrip that gets most of the way there, but instead of displaying the text in powershell I need it to save each one as an individual html file. For some reason I can't get the save to work. I've tried various options like out-file and $body.SaveAs([ref][system.object]$name, [ref]$saveFormat)
$saveFormat = [Microsoft.Office.Interop.Outlook.olSaveAsType]::olFormatHTML
Get-ChildItem "C:\MSG\" -Filter *.msg |
ForEach-Object {
$body = ""
$outlook = New-Object -comobject outlook.application
$msg = $outlook.Session.OpenSharedItem($_.FullName)
$body = $msg | Select body | ft -AutoSize
}
Any advice on how to save this as individual files would be great.
To start with, you should not capture the output of a Format-* cmdlet in a variable. Those are designed to output to something (screen, file, etc).
Ok, that aside, you are already opening the msg files, so you just need to determine a name and then output the HTMLBody property for each file. Easiest way would be to just tack .htm to the end of the existing name.
Get-ChildItem "C:\MSG\*" -Filter *.msg |
ForEach-Object {
$body = ""
$outlook = New-Object -comobject outlook.application
$msg = $outlook.Session.OpenSharedItem($_.FullName)
$OutputFile = $_.FullName+'.htm'
$msg.HTMLBody | Set-Content $OutputFile
}

Send email if character/string detected in PowerShell

Problem:
I need a PowerShell script that checks each file in a folder for a specific character (#), then emails the filename only if the character was found.
What I have: I'm still new to loops/testing logic and not sure where it would fit in. My current script successfully checks for the bad character, but emails regardless if something was found or not.
$scr = dir \\fileshare\businessfolder\filedrop | Select-String '#'
$FromAddress = "myself#something.com"
$ToAddress = "myself#something.com"
$MessageSubject = "Bad Character Detected"
$SendingServer = "mail.something.com"
$SMTPMessage = New-Object System.Net.Mail.MailMessage $FromAddress, $ToAddress, $MessageSubject,$scr
$SMTPClient = New-Object System.Net.Mail.SMTPClient $SendingServer
$SMTPClient.Send($SMTPMessage)
This currently outputs in an email:
\fileshare\businessfolder\filedrop\AD060101.107:3:#N/A
\fileshare\businessfolder\filedrop\AD060102.107:3:#N/A
\fileshare\businessfolder\filedrop\AD060101.103:14:#N/A
The files I'm scanning through are formatted in this way:
AHMMDDNN.LLL
ADMMDDNN.LLL
The AH/AD indicates if it's the header or detail, the MMDD is month/day, and NN is if it's the first generated file for the day, second generated file, et cetera. LLL is the location number.
What I really want:
To take it a step further, I would like to delete the bad file header & detail file and email the extension to the business unit.
The process would be:
Scan for '#' in all files in the folder.
Delete the ADMMDDNN.LLL and corresponding AHMMDDNN.LLL file. (Example: AD060101.107 had a '#' in it, so delete that and the AH060101.107 file too)
Send an email stating "location LLL has been deleted, please recreate file". (Example: "Location 107 has been deleted, please recreate file")
Just iterate over all matches from the Select-String cmdlet and use a regex to create a pattern for the Remove-Item (alias rm) cmdlet like A[D|H]060101.103:
$filesToDelete = Select-String -Path '\\fileshare\businessfolder\filedrop\*' -Pattern '#'
$filesToDelete | ForEach-Object {
'location {0} has been deleted, please recreate file' -f ($_.Filename -split '\.')[-1]
$_.Path -replace '(.*\\A).(.*)', '$1[D|H]$2' | rm -Force -ea 0
}
Note: I omit the E-Mail part but added the message to the foreach.

Powershell Email specific Error via log timestamp

$log = "C:\bobloblawslawblog.log"
Get-Content $log |
? {$_.Modified -le $(Get-Date).AddHours(-4)} |
Select-String "TCP" -Context 1 |
% { #($_.Context.PreContext) + #($_.Line)}
{
$Workstation = hostname
$emailSmtpServer = "mail.server"
$emailFrom = "Error CODE 1234#$Workstation.com"
$emailTo = "my.name#my.company.com"
$emailSubject = "$Workstation Error CODE 1234"
$emailBody = "$Workstation experiencing Error Code 1234"
Send-MailMessage -To $emailTo -From $emailFrom -Subject $emailSubject -Credential $credentials -Body $emailBody -SmtpServer $emailSmtpServer
$credentials= New-Object System.Management.Automation.PSCredential ("username", $secpasswd)
}
I cannot seem to get this to grab the error from just the past 4hrs. The script grabs all the matching codes and sends them along.
The format of the timestamp in the log file is:
[2015-09-01 03:12:34,774] INFO com.server.mobilize.jte.service.listener.DesktopClientTransferListener(147) exception (Transport Protocol Seeker)- 218362: a warning or retriable exception has occurred Transfer warning: Error connecting to TCP Server-A/127.0.0.1:49221 relayed via Sever-B/External-IP:49221: Connection lost on tcp port read
I don't know what those last 3 numbers '975' indicate. It's not a pid.
What I want to do is have the script scrape a .log file - return a match from the last 4 hours and email an alert.
Ideally, I'd like the email body to include the actual error from the log.
Like I was saying in the comments you are trying access properties that don't exist in the pipeline. Get-Content returns a string array. Keep in mind that those whiles string do have special properties like linenumber there is no magical way that it interprets the date at the beginning. That is up to us.
Get-Content $log | ForEach-Object{
# Extract the date
$date = $null # Assume null
# Match the data that occurs after the bracket and before the comma. This can only be at the beginning of the line.
If($_ -match "^\[(.*?),"){
$date= [datetime]$matches[1]
}
# Build a new object as pass it along the pipe
New-Object -TypeName psobject -Property #{
Modified = $date
Line = $_
}
}
Many ways to do this but I opted for creating a new object where we give it a modified property. We use regex to extract the date from the beginning of the line and cast it to a [datetime] so that you can now perform date calculations.
Assuming the rest of your logic does what you want it to you just need to append it after the last } in my snippet above.
Something a little better
Making another object seemed like a waste of resources so I used the same logic in the Where instead. I have also added the Select-String portion of you code as well. I made some changes.
$results = Get-Content $log | Where-Object{
# Match the data that occurs after the bracket and before the comma. This can only be at the beginning of the line.
$date = $null
If($_ -match "^\[(.*?),"){$date = [datetime]$matches[1]}
$date -gt (Get-Date).AddHours(-4)
} | Select-String "tcp" -Context 1 | ForEach-Object{
"{0}`r`n{1}`r`n{2}" -f ($_.Context.Precontext -join "`r`n"), $_.line, ($_.context.postcontext -join "`r`n")
}
Now results will have just that. So you would see in there all entries that have occured in the last 4 hours that have the string "TCP" somewhere on the line. It will also include the line before and after the match. $results has the potential to be an array so you would need to account for that in your email. A simple $results -join "`r`n" would do it.

Attachments.Add wildcard with Powershell

I have a ZIP file generated with dynamic information (Report_ PC Name-Date_User). However when I go to attach the file I'm unable to use a wildcard. There is only one ZIP file in this directory so using a wildcard will not attach any other ZIP files.
#Directory storage
$DIR = "$ENV:TEMP"
#Max number of recent screen captures
$MAX = "100"
#Captures Screen Shots from the recording
$SC = "1"
#Turn GUI mode on or off
$GUI = "0"
#Caputres the current computer name
$PCName = "$ENV:COMPUTERNAME"
#Use either the local name or domain name
#$User = "$ENV:UserDomainName"
$User = "$ENV:UserName"
#Timestamp
$Date = Get-Date -UFormat %Y-%b-%d_%H%M
#Computer Information
$MAC = ipconfig /all | Select-String Physical
$IP = ipconfig /all | Select-String IPv4
$DNS = ipconfig /all | Select-String "DNS Servers"
#Needed to add space after user input information
$EMPT = "`n"
#Quick capture of the computer information
$Info = #"
$EMPT
*** COMPUTER INFORMATION ***
$PCName
$IP
$MAC
$DNS
"#
# Used to attach to the outlook program
$File = Get-ChildItem -Path $Dir -Filter "*.zip" | Select -Last 1 -ExpandProperty Fullname
$Start_Click = {
psr.exe /start /output $DIR\$Date-$PCName-$User.zip /maxsc $MAX /sc $SC /gui $GUI
}
$Stop_Click={
psr.exe /stop
}
$Email_Click = {
$Outlook = New-Object -Com Outlook.Application
$Mail = $Outlook.CreateItem(0)
$Mail.To = "deaconf19#gmail.com"
$Mail.Subject = "Capture Report from " + $PCName + " " + $User + " " + $Date
$Mail.Body = $Problem.text + $Info
$Mail.Attachments.Add($File)
$Mail.Send()
}
I no longer get an error but the file will not attach the first time around. The second time it will attach but it does the previous .zip not the most recent. I added my entire code
As per the msdn article it shows what the source needs to be which is.
The source of the attachment. This can be a file (represented by the
full file system path with a file name) or an Outlook item that
constitutes the attachment.
Which mean that it does not accept wildcards. To get around this you should instead use Get-ChildItem to return the name of your zip.
$File = Get-ChildItem -Path $Dir -Filter "*.zip" | Select -First 1 -ExpandProperty Fullname
That should return the full path to the first zip. Since Get-ChildItem returns and object we use -ExpandProperty on the Fullname so that you just return the full path, as a string, to the file. -First 1 is not truly required if you really only have the one file. On the off-chance you do including -First 1 will make sure only one file is attached.
Update from comments
I see that you are having issues with attaching a file still. My code would still stand however you might be having an issue with your .zip file or $dir. After where $file is declared I would suggest something like this:
If (! Test-Path $file){Write-Host "$file is not a valid zip file"}
If you would prefer, since I don't know if you see your console when you are running your code, you could use a popup

Outlook PowerShell script does not retrieve latest email

I have a script that reads an Outlook folder for emails, selects the most recently received email, and saves its attachment into a dir.
It is not working properly - it seems as if it only knows which email to receive if I first open Outlook before running the script - otherwise, it thinks the most recently-received email is the one that was last-received when I had Outlook open last.
Is there some way to prompt Outlook to refresh before the script prompts the scan?
My code is below:
$filepath = $args[0]
$account = $args[1]
<#
#file path
$filepath = "I:\folder"
$account = "account#host.com"
#>
#set outlook to open
$o = New-Object -comobject outlook.application
$n = $o.GetNamespace(“MAPI”)
$Account = $n.Folders | ? { $_.Name -eq $account };
$Inbox = $Account.Folders | ? { $_.Name -match 'Inbox' };
$Data = $Inbox.Folders | ? { $_.Name -match 'Data' };
$f = $Data.Folders | ? { $_.Name -match 'MyTargetFolder' };
$email = $f.Items | Sort-Object ReceivedTime -Descending | Select-Object -First 1
# Log the email we are looking for, and mention attachments if they exist.
Write-Output "Last email received at $($email.receivedtime), attached file(s) are: (if any)"
$email.attachments | %{Write-Output $_.filename}
# If the email has at least one attachment, save them to the filepath.
if ($email.attachments.count -gt 0) {
$email.attachments | %{$_.saveasfile((join-path $filepath $_.filename))}
} else {
Write-Output "Latest email at $($email.receivedtime) has no attachments!"
}
Sounds like you need a
$a.Store | ?{$_.DisplayName -eq $account} | %{If($_.IsCachedExchange){Start-Job {$n.SendAndReceive($false)}|Wait-Job}}
in there to make sure that if it is in Cached mode it will Send & Receive before checking for the email. See Edit note below.
You may also want to make sure that the user isn't in offline mode with a:
If($n.offline){write-host "You must be online before performing this script" -fore red;start-sleep 5;break}
That way if the user is in offline mode it'll display red text stating they need to be in online mode, wait 5 seconds, and then break out of the script.
Edit: Thanks to Adi Inbar it will wait for Outlook to finish its send and receive process before moving on to make sure that you have the latest email cached before moving on. Thanks Adi, I had no idea about that command, it's pretty handy!
Is this a cached (as opposed to online) Exchange store? Try to turn caching off.