Currently I'm writing a GUI Powershell script to run PSR.exe so that the helpdesk can capture the user problems then email the output to the help desk with minimal input.
The problem I'm having is attaching a file that was generated using environment variables. It works if I use static file name as shown in the example, however no computer name has the same hostname and user at the same time. How do I tell to attach the file from the two variables used to create the file?
#Directory storage
$DIR = "D:\Reports"
#Max number of recent screen captures
$MAX = "100"
#Captures Screen Shots from the recording
$SC = "1"
#Turn GUI mode on or off
$STR = "0"
#Caputres the current computer name
$PCName = "$ENV:COMPUTERNAME"
#Use either the local name or domain name
#$User = "$ENV:UserDomainName"
$User = "$ENV:UserName"
#Work in progress.
$File = "D:\Reports\LabComputer-Tester01.zip"
$buttonStart_Click={
New-Item -ItemType directory -Path D:\Reports
psr.exe /start /output $DIR\$PCName-$User.zip /maxsc $MAX /sc $SC /gui $STR
}
$buttonStop_Click={
psr.exe /stop
}
$buttonEmail_Click={
#TODO: Place custom script here
$Outlook = New-Object -ComObject Outlook.Application
$Mail = $Outlook.CreateItem(0)
$Mail.To = "deaconf19#gmail.com"
$Mail.Subject = "Capture Report"
$Mail.Body = "Something in here"
$Mail.Attachments.Add($File)
$Mail.Send()
}
I changed $File = "D:\Reports\LabComputer-Tester01.zip" to $File '\$DIR\\$PCName-$User.zip' I added the single tick and another backslash to the variable
I changed $File = "D:\Reports\LabComputer-Tester01.zip" to $File '\$DIR\\$PCName-$User.zip' I added the single tick and another backslash to the variable
Related
I have created a powershell script to send csv files automatically using Task Scheduler. I feel as if I am making a silly mistake with my pathing as nothing is sending.
I have testing the script below.
if($args.Count -lt 1)
{
Write-Host "Use: SendMail.ps1 <"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Office\Microsoft Outlook 2010.lnk">"
Write-Host
Write-Host "<"C:\CSV">"
Write-Host
exit
}
$FullPath=$args[0]
#Get an Outlook application object
$o = New-Object -com Outlook.Application
$mail = $o.CreateItem(0)
#2 = High importance message
$mail.importance = 2
$mail.subject = "CSV File"
$mail.body = "Here is the CSV file."
#separate multiple recipients with a ";"
$mail.To = <---->
#$mail.CC = <OTHER RECIPIENT 1>;<OTHER RECIPIENT 2>
# Iterate over all files and only add the ones that have an .csv extension
$files = Get-ChildItem $FullPath
for ($i=0; $i -lt $files.Count; $i++) {
$outfileName = $files[$i].FullName
$outfileNameExtension = $files[$i].Extension
# if the extension is the one we want, add to attachments
if($outfileNameExtension -eq ".csv")
{
$mail.Attachments.Add($outfileName);
}
}
$mail.Send()
# give time to send the email
Start-Sleep 20
# quit Outlook
$o.Quit()
#end the script
exit
I believe that the following is incorrect:
Write-Host "Use: SendMail.ps1 <"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Office\Microsoft Outlook 2010.lnk">"
But I am unsure as to what the path should be.
The path should be something like:
C:\Program Files (x86)\Microsoft Office\root\Office16\Outlook.exe
This link has a few methods of finding the path to the executable of a running process:
http://www.softwareok.com/?seite=faq-Windows-10&faq=152
I wrote a PowerShell workflow that analyses the Excel data and triggers a mail based on the values in Excel.
I have developed a workflow which triggers mail but I'm having trouble in analysing the Excel data, where I couldn’t call the Send-Mail workflow.
Note: column “E-remainder 1” and “F-remainder 2” contains the date of the remainder mail to be sent.
Workflow test {
$worksheet = InlineScript {
$objExcel = New-Object -ComObject Excel.Application
$objExcel.Visible = $false
$WorkBook = $objExcel.Workbooks.Open(#filepath)
$worksheet = $workbook.Sheets.Item(#sheetname)
$currentdate = (Get-Date).ToString()
$rownumber = #contains rownumber from excel whose details has to be mailed
foreach ($i in $rownumbers) {
if ($worksheet.Range("B$i").Text -eq $currentdate) {
$output = [PSCustomObject][ordered]#{
ComputerName = $WorkSheet.Range("C$i").Text
Fromaddress = $WorkSheet.Range("D$i").Text
Toaddress = $WorkSheet.Range("E$i").Text
}
# I need to call a workflow which sends a mail
# (workflow which I have to trigger mail)
Send-Mail -To $output.Toaddress -From $output.Fromaddress -Name $output.ComputerName
} elseif ($worksheet.Range("F$i").Text -eq $currentdate) {
$output = [pscustomobject][ordered]#{
ComputerName = $WorkSheet.Range("C$i").Text
Fromaddress = $WorkSheet.Range("D$i").Text
Toaddress = $WorkSheet.Range("E$i").Text
}
# I need to call a workflow which sends a mail
# (Based on my knowledge I know that we couldn't call a workflow inside the inline script)
Send-Mail -To $output.Toaddress -From $output.Fromaddress -Name $output.ComputerName
}
} # foreach ends
} # Inline ends
} # workflow ends
Im getting the following error:
ERROR:The term "Send-Mail" is not recognized as the name of the cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
I am trying to create a PowerShell script that will send an email if a service goes into a stopped state. I would like to be able to read the email configuration from another file.
Email configuration file:
.\emailconfig.conf
$emailSmtpServer = "smtp.company.com"
$emailSmtpServerPort = "587"
$emailSmtpUser = "usera"
$emailSmtpPass = "passwordb"
$emailFrom = "userA#company.com"
$emailTo = "userB#company.com"
$emailcc= "userC#company.com"
And this is what I have so far in the PowerShell script:
.\emailservicecheck.ps1
$A = Get-Service "Service B"
if ($A.Status -eq "Stopped") {
Get-Content emailconfig.conf | Out-String
$emailMessage = New-Object System.Net.Mail.MailMessage($emailFrom, $emailTo)
$emailMessage.Cc.Add($emailcc)
$emailMessage.Subject = "subject"
#$emailMessage.IsBodyHtml = $true # true or false depends
$emailMessage.Body = Get-Service "Service B" | Out-String
$SMTPClient = New-Object System.Net.Mail.SmtpClient($emailSmtpServer, $emailSmtpServerPort)
$SMTPClient.EnableSsl = $False
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential($emailSmtpUser, $emailSmtpPass);
$SMTPClient.Send($emailMessage)
}
The script works if I enter the text from the email config file into the script but I cannot seem to be able to read in the data from the file on the fly and get the script to work. It errors out and says that my variables are empty.
What you are searching for, (I think) are .psd1 files. I personally prefer them (along with JSON) over the other configuration formats. The link I'm referring to also describes other well-known formats and how to use them in PowerShell.
In short, module manifests work as follows:
configuration.psd1
#{
SmtpServer = "";
MailFrom = "";
Auth = #{
User = "";
Pass = "";
};
}
Script.ps1
$mailConfig = Import-LocalizedData -BaseDirectory C:\ -FileName configuration.psd1
$emailMessage = New-Object System.Net.Mail.MailMessage( $$mailConfig.mailFrom , $mailConfig.mailTo )
As Mark already pointed out, Get-Content emailconfig.conf | Out-String will just output the content of the file, it won't define the variables in your code. For that you'd need to dot-source the file, which requires a file with the extension ".ps1".
If you want to stick with a simple config file format I'd recommend changing the file to something like this:
emailSmtpServer = smtp.company.com
emailSmtpServerPort = 587
emailSmtpUser = usera
emailSmtpPass = passwordb
emailFrom = userA#company.com
emailTo = userB#company.com
emailcc = userC#company.com
And importing it into a hashtable via ConvertFrom-StringData:
$cfg = Get-Content emailconfig.conf | Out-String | ConvertFrom-StringData
The data in the hashtable can be accessed via dot-notation ($cfg.emailFrom) as well as via the index operator ($cfg['emailFrom']), so your code would have to look somewhat like this:
$msg = New-Object Net.Mail.MailMessage($cfg.emailFrom, $cfg.emailTo)
$msg.Cc.Add($cfg.emailcc)
$msg.Subject = 'subject'
$msg.Body = Get-Service 'Service B' | Out-String
$smtp = New-Object Net.Mail.SmtpClient($cfg.emailSmtpServer, $cfg.emailSmtpServerPort)
$smtp.EnableSsl = $false
$smtp.Credentials = New-Object Net.NetworkCredential($cfg.emailSmtpUser, $cfg.emailSmtpPass)
$smtp.Send($msg)
It looks like what you're trying to do is include some script from another file. This can be done by dot sourcing, however the file needs to be saved as a .ps1 file, you can't use .conf.
You'd do it as follows (in place of your existing Get-Content) line:
. .\emailconfig.ps1
Assuming the file is kept in the current working directory of the script.
Your script wasn't working because
get-content emailconfig.conf | Out-String
Was returning the contents of that file to the output pipeline, rather than including it in the script and executing it.
I'm not sure i understood correctly what you want.
If you want to use variables from external file, you need to dot source your external script, for example, create a file named variables.ps1 and put in the same folder
In the beginning of the main script use
. .\variables.ps1
If you are after expanding variables that are in external file to ues as an email template please do as following:
$HTMLBody = get-content "yourfilepath" | Foreach-Object {$ExecutionContext.InvokeCommand.ExpandString($_)}
This will expand all variables and put it in the $HTMLBody variable
Then use:
$emailMessage.Body = (ConvertTo-Html -body $HTMLBody)
im trying to create a script that will check the contents of a log file, using windows powershell. The powershell script is checking the log file that is created from another windows application.
The script is the following:
$smtpServer = "MailServer"
$fdate = Get-Date -Format yyyyMMdd
$fname = "C:\tmp\"+$fdate+".log"
$content=Get-Content $fname -wait | where { $_ -match ": exception" }|
foreach {
$line=$_
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = "admin#xyz.com"
$msg.ReplyTo = "logs#xyz.com"
$msg.To.Add("logs#xyz.com")
$msg.subject = "Exception"
$msg.body = $line
$smtp.Send($msg)
Write-Host $line
}
The script above is not able to read the new additions of the log file. If for example i have 10 lines in the log file and i start the script now it will check only the current 10 lines. If any new lines are added from the application while the script is running, the script is not able to check the newly added lines!
Any recommendations for a proper solution to that issue? Did anyone tried to do something similar using C/C++ or java?
Thank you
Also you can use these: Multithreading with Jobs in PowerShell and PowerShell Multithreading
I am a novice when it comes to scripting so please bear with me. I am trying to create a script that will monitor a bait file that is added to all file shares on a server. When the script sees that the file was modified it will block access to the user that made the modification and send an email. The script seems to work ok other than the FileSystemWatcher. It will only monitor the last share. I have seen a similar post on here but was getting confused with the answer. Can someone please help me with the task of creating a FileSystemWatcher for each bait file? I would also like any input as to how I might improve upon the script in other ways. Your help is greatly appreciated.
$to = "joe#blow.com"
$File = "test.txt"
$FilePath = "C:\temp"
$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
## SEND MAIL FUNCTION
function sendMail($s, $to) {
$smtpServer = "mail.nowhere.com"
$smtpFrom = "alert#nowhere.com"
$smtpTo = $to
$messageSubject = $s[0]
$message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpto
$message.Subject = $messageSubject
$message.IsBodyHTML = $false
$message.Body = $s[1]
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($message)
}
## Get a list of shares and Perform tasks on each location.
$cryptopaths = Get-WmiObject -Class win32_share -filter "Type=0 AND name like '%[^$]'" | ForEach ($_.Path) {
$cryptopath = $_.Path
## Copy the bait file to the share location
Copy $FilePath\$File $cryptopath\$File -Force
##Get files hash
Try {
$Origin = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes("$FilePath\$File")))
$Copy = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes("$CryptoPath\$File")))
}
##Error on reading hash
Catch {
echo "error reading $CryptoPath\$File"
}
## If files don't match, then Send messaged and quit
if (Compare-Object $Origin $Copy){
## files don't match
$subject = "Error logged on $CryptoPath\$File by $env:username on $env:computername"
$body = "The original file does not match the witness file. Aborting monitor script."
$email =#($subject,$body)
sendMail -s $email -to "ben22#nowhere.com"
Exit
}
## CREATE WATCHER ON DIRECTORY
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $CryptoPath
$watcher.Filter = $File
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $false
$watcher.NotifyFilter = [System.IO.NotifyFilters]::LastWrite -bor [System.IO.NotifyFilters]::FileName
}
## Execute Watcher
while($TRUE){
$result = $watcher.WaitForChanged([System.IO.WatcherChangeTypes]::Changed `
-bor [System.IO.WatcherChangeTypes]::Renamed `
-bor [System.IO.WatcherChangeTypes]::Deleted `
-bor [System.IO.WatcherChangeTypes]::Created, 1000);
if ($result.TimedOut){
continue;
}
if ($result.Name -eq $File) {
### Make sure the files do not match
try {
$FileCheck = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes("$CryptoPath\$File")))
if (Compare-Object $Origin $FileCheck){
## files don't match
$body = "Witness file $FilePath\$File on $env:computername has been modified."
}
}
catch {
## file deleted
$body = "Witness file $FilePath\$File on $env:computername has been deleted"
}
finally {
## Deny owner of changed file access to shares and disconnect their open sessions. Send email alert
Get-Acl "$CryptoPath\$File" | foreach ($_.Owner) {
Get-SmbShare | Block-SmbShareAccess –AccountName $_.Owner
Close-SmbSession –ClientUserName $_.Owner
}
$subject = "EMERGENCY ON FILE SERVER -- $FilePath\$File by $env:username on $env:computername"
$email =#($subject,$body)
sendMail -s $email -to "ben22#nowhere.com"
sendMail -s $email -to "5555555555#txt.bell.ca"
Exit
}
}
}
The problem is that you create FileSystemWatcher instances in a loop (ForEach ($_.Path) {}), but you assign them to the same variable $watcher, overwriting the previous reference each time. Once outside the loop, you work with the $watcher variable, which references the last FileSystemWatcher instance you created and that's why you are receiving notifications for the last file only.
To get this working, you should use a type that allows storing multiple references -- that is, an array. Example:
$watchers = #();
...
$watcher = New-Object System.IO.FileSystemWatcher;
...
$watchers += $watcher;
Also, I would propose to use event handler/callback/delegate-based approach instead of waiting for a change using WaitForChanged(), because waiting for multiple file system watchers would call for a parallelized solution (well, ideally). Use Register-ObjectEvent to register an event handler and see this example in particular: http://gallery.technet.microsoft.com/scriptcenter/Powershell-FileSystemWatche-dfd7084b.
PowerShellPack also has a Start-FileSystemWatcher cmdlet that wraps this all up nicely, but I'm not sure about the status of PowerShellPack in general. It should be part of the Windows 7/8 Resource Kit, though.