Email Notification when file added to folder - windows

I know very little about scripting, but I think what I want is possible. I would simply like to get an email notification when a file is added to a particular folder. I don't have any specific software, so I guess it would have to be a batch file or maybe VBS?

You can do this, in a batch-file with powershell enabled:
#echo off
setlocal EnableDelayedExpansion
set "emailUserName=dummyEmail#gmail.com"
set "emailPassword=dummyPassword"
set "target=yourEmail#email.com"
set "subject=File Changed"
FOR %%G IN (*) DO attrib -A "%%G"
:loop
set "body="
FOR %%G IN (*) DO (
attrib "%%G" | findstr /B /L A 1>nul
if !errorlevel! equ 0 (
echo "%%G"
set "body=!body!^<br ^/^>%%G"
attrib -A "%%G"
)
) 2>nul
if not "%body%"=="" echo sending email
if not "%body%"=="" set "body=The following files have been changed:!body!"
if not "%body%"=="" powershell.exe -command "Send-MailMessage -From '!emailUserName!' -to '!target!' -Subject '!subject!' -Body '!body!' -BodyAsHtml -SmtpServer 'smtp.gmail.com' -port '587' -UseSsl -Credential (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ('!emailUserName!', (ConvertTo-SecureString -String '!emailPassword!' -AsPlainText -Force)))"
goto :loop
For this to work, you need to create a dummy gmail acount that will send the email. This supports HTML tags in the body, as shown in the example.
Note that this doesn't work on deletion of files, only changes and new files.

Do you have information about your email server? What version of Windows and Powershell do you have?
This short Powershell script works in my environment:
$folder = "D:\"
$mailserver = "your.mailserver.your.company"
$recipient = "your.email#your.company"
$fsw = New-Object System.IO.FileSystemWatcher $folder -Property #{
IncludeSubdirectories = $true
NotifyFilter = [IO.NotifyFilters]'FileName'
}
$created = Register-ObjectEvent $fsw -EventName Created -Action {
$item = Get-Item $eventArgs.FullPath
$s = New-Object System.Security.SecureString
$anon = New-Object System.Management.Automation.PSCredential ("NT AUTHORITY\ANONYMOUS LOGON", $s)
Send-MailMessage -To $recipient `
-From "alerts#your.company" `
-Subject “File Creation Event” `
-Body "A file was created: $($eventArgs.FullPath)" `
-SmtpServer $mailserver `
-Credential $anon
}
Stop the alerts with this:
Unregister-Event -SourceIdentifier Created -Force

I built my solution based on what #xXhRQ8sD2L7Z contributed. But I was getting multiple emails as I tested, a new one for every time I ran the register script. The Unregister line was not working:
Unregister-Event -SourceIdentifier Created -Force
So I found this: Unregister a registered filewatcher event does not work
Get-EventSubscriber -SourceIdentifier "filechanged" | Unregister-Event
but it still wasn't working, so I ran Get-EventSubscriber by itself and got a list of the Subscriptions for the Event and found that, in my case, the -SourceIdentifier was a GUID. With that understanding, I grabbed each individual GUID and proceeded to unregister them. (I could've looped through them, but I will only use this once, probably. Plus, I had only created the subscription 6 times before I realized what was happening)
This eliminated all of the extra emails i received every time I tested.
Get-EventSubscriber -SourceIdentifier "<very long GUID>" | Unregister-Event
Then I wanted to send to multiple recipients, so i found this: Powershell send-mailmessage - email to multiple recipients
$recipients = "<User1#domain.com>", "<User2#domain.com>"
You can also use email distribution groups.

Related

How to break up a long string in multiple lines in a windows batch file while still allow cmd.exe to parse it as a single argument?

I have a Windows batch file for cmd.exe. It has a single line of code to invoke the PowerShell Send-MailMessage cmdlet and exit back to cmd.exe. Currently the line reads like:
PowerShell -Command "Send-MailMessage -To one.person#somewhere.com, another.person#sameplace.com -Subject 'A rather lengthy text' -From info#my.com -SmtpServer hostname.subdomain.mycompany.com -UseSsl -Credential (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'UserName',('SecretePassword1234'|ConvertTo-SecureString -AsPlainText -Force)) -Body 'A static text.'"
It works great. But I find it difficult to edit it in NotePad or any text editor because the line is way too long. What I want is to use the CMD.EXE's caret ^ character to break up the long string within the double quotes into multiple lines. However, the PowerShell.EXE command requires that:
PowerShell -Command <string>
The above syntax requires that CMD.EXE parse everything after -Command to be a SINGLE string, aka a single argument. I've tried the following that didn't work:
PowerShell -Command "Semd-MailMessage Blab Blab " ^
"More Blah Blah"
If I break up the line in my batch file as above, CMD.EXE will parse the first part of the string as one argument, and the string on the next line as another argument. Wrong. So what should I do?
I would put your command into a powershell script and then call that from cmd using the following command (or something similar):
powershell.exe -noprofile -executionpolicy bypass -file "path to my file.ps1"
then your editing can all happen inside the powershell ide and you can split your command up as required; either by using variables and/or by using the ` (back tick) character. Your Powershell script could then look like the following with is a lot easier to manage; plus you get the benefit of intellisense as you edit the script:
Send-MailMessage `
-To one.person#somewhere.com, another.person#sameplace.com `
-Subject 'A rather lengthy text' `
-From info#my.com `
-SmtpServer hostname.subdomain.mycompany.com `
-UseSsl `
-Credential (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'UserName', ('SecretePassword1234'|ConvertTo-SecureString -AsPlainText -Force)) `
-Body 'A static text.'
I can agree as you've indicated that this doesn't work:
PowerShell -Command "Semd-MailMessage Blab Blab " ^
"More Blah Blah"
…and also agree that this doesn't either:
#Echo Off
PowerShell -Command "Get-WmiObject -Class Win32_Product "^
"| Select-Object -Property Name | Sort-Object Name"
Pause
However this does:
#Echo Off
PowerShell -Command "Get-WmiObject -Class Win32_Product "^
"| Select-Object -Property Name | Sort-Object Name"
Pause
…note the very small but seemingly very important single space at the beginning of the continuation line!
I think this is the simplest and clearest way to solve this problem because this method does not require additional files nor additional options, just plain PowerShell code:
PowerShell ^
Send-MailMessage ^
-To one.person#somewhere.com, another.person#sameplace.com ^
-Subject 'A rather lengthy text' ^
-From info#my.com ^
-SmtpServer hostname.subdomain.mycompany.com ^
-UseSsl ^
-Credential (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'UserName',('SecretePassword1234'^|ConvertTo-SecureString -AsPlainText -Force)) ^
-Body 'A static text.'
Just be sure to properly escape special Batch characters, like ^|.
You either set variables in a batch file or you can do something completely hokey like this.
#echo off
setlocal enabledelayedexpansion
set "pscommand="
FOR %%G IN (
"Send-MailMessage -To one.person#somewhere.com, "
"another.person#sameplace.com "
"-Subject 'A rather lengthy text' "
"-From info#my.com "
"-SmtpServer hostname.subdomain.mycompany.com "
"-UseSsl -Credential "
"(New-Object -TypeName System.Management.Automation.PSCredential "
"-ArgumentList 'UserName',('SecretePassword1234'|ConvertTo-SecureString -AsPlainText -Force)) "
"-Body 'A static text.'"
) DO (
set "pscommand=!pscommand!%%~G"
)
PowerShell -Command "%pscommand%"

How to map Network Printers with PowerShell and CSV

I want to deploy our Network Printers that are shared from a Print-Server to Windows 10 PCs, on per-machine basis.
Currently we do this with a Kix-Script and ini file, but I want to move this to PowerShell and deploy it as a Startup/Login Script with Group Policy. The deployment must be with PowerShell not purely GPO, with a script we are more flexible to deploy to singular machines.
I've written a PS Script and using a CSV File containing the PCs and Printers to map, but it seams completely wrong. Is there a better way to deploy the printers?
Here are my CSV, 'True' is to set Printer as Default:
#TYPE Selected.System.Management.ManagementObject.Data.DataRow
Name
PC0001
\\SV0002\PR0001, True
\\SV0002\PR00002
Name
PC0002
\\SV0002\PR0001, True
\\SV0002\PR00002​
and the PS-Script:
Get–WMIObject Win32_Printer | where{$_.Network -eq ‘true‘} | foreach{$_.delete()}
$Printers=IMPORT-CSV \\server\$env:username\printers.csv
FOREACH ($Printer in $Printers) {
Invoke-Expression 'rundll32 printui.dll PrintUIEntry /in /q /n $($Printer.Name)'
}​
I edited the csv File, and it looks like this now:
Client;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;Default
PC0001;\\SV0001\PR0001;\\SV0001\PR0002;;;;;;;;;;;;;;pr_01
PC0002;\\SV0001\PR0001;\\SV0001\PR0002;\\SV0001\PR0003;;;;;;;;;;;;;pr_03
We did that with Excel, so it's easier to edit, and save it as csv.
Also where is located, we changed it to \Server\Netlogon\Subfolder\Printers.csv so that also the the Variable is changed to:
$Printers=IMPORT-CSV \\server\Netlogon\Subfolder\printers.csv
But now I think the whole script is wrong?
Using a CSV like this:
name,printers,defaultprinter
PC0001,\\SV0001\PR0001;\\SV0001\PR0002,PR0002
PC0002,\\SV0001\PR0001;\\SV0001\PR0003,PR0003
PC0003,\\SV0001\PR0001;\\SV0001\PR0004,PR0004
The code would be:
$csv = "\\server\Netlogon\Subfolder\printers.csv"
$Computers = Import-Csv $csv
foreach ($Computer in $Computers){
If ($Computer.name -eq $env:computername) {
$Printers = ($Computer.printers).split(";")
foreach ($Printer in $Printers) {Add-Printer $Printer -ErrorAction SilentlyContinue}
(New-Object -ComObject WScript.Network).SetDefaultPrinter("$($Computer.defaultprinter)")
}
}
The way we do (did) it here at work was by invoking some VBScript from within the PowerShell script.
Print server and Printer are obtained via AD cmdlets.
$net = New-Object -Com WScript.Network
$net.AddWindowsPrinterConnection("\\" + $PRINT_SERVER + "\" + $PRINTER)
Starting from Windows 8 :
# Add the printer
Add-Printer -ConnectionName ("\\" + $printServer + "\" + $printerName) -Name $printerName
# Get the printer
$printer = Get-WmiObject -Query "Select * From Win32_Printer Where ShareName = '$printerName'"
# Set printer as default
$printer.SetDefaultPrinter()
I solved the Problem with the Script of James C., many thanks to him, it was a big help!.
The only wrong Thing was that between Add-Printer and $Printer, it had to be -ConnectionName. After that Little Edit in the script, everything was fine.
So we made a GP_Printers, where we putted under Computer Configuration/Windows Settings/Scripts/Startup this Script as printermapping.ps1
Also we putted into Shutdown a PowerShell Script where all Printer Connection are deleted.
Here are all the scripts.
CSV:
name,printers,defaultprinter
PC0001,\\SV0001\PR0001;\\SV0001\PR0002,PR0002
PC0002,\\SV0001\PR0001;\\SV0001\PR0003,PR0003
PC0003,\\SV0001\PR0001;\\SV0001\PR0004,PR0004
Printer Mappings with PowerShell depending on CSV:
$csv = "\\server\Netlogon\Subfolder\printers.csv"
$Computers = Import-Csv $csv
foreach ($Computer in $Computers){
If ($Computer.name -eq $env:computername) {
$Printers = ($Computer.printers).split(";")
foreach ($Printer in $Printers) {Add-Printer-ConnectionName $Printer -ErrorAction SilentlyContinue}
(New-Object -ComObject WScript.Network).SetDefaultPrinter("$($Computer.defaultprinter)")
}
}
And the Printer Disconnection:
Get-WmiObject -Class Win32_Printer | where{$_.Network -eq ‘true‘}| foreach{$_.delete()}
I hope this could be helpfoul for others.
Again many thanks to James C.
WBZ-ITS
I've made some correction and improvements to the script, and found also some Problem that Comes if you use it on a GPO, the changes are following:
CSV:
name,printers,defaultprinter
PC0001,\\SV0001\PR0001;\\SV0001\PR0002,PR0002
PC0002,\\SV0001\PR0001;\\SV0001\PR0003,PR0003
PC0003,\\SV0001\PR0001;\\SV0001\PR0004,PR0004
The Connection Script:
$csv = "\\server\Netlogon\Subfolder\printers.csv"
$Computers = Import-Csv $csv
foreach ($Computer in $Computers){
If ($Computer.name -eq $env:computername) {
$Printers = ($Computer.printers).split(";")
foreach ($Printer in $Printers) {Add-Printer-ConnectionName $Printer -ErrorAction SilentlyContinue}
(New-Object -ComObject WScript.Network).SetDefaultPrinter("$($Computer.defaultprinter)")
}
}
And also a disconnect Script when logging off:
#$a = Get-WMIObject -query "Select * From Win32_Printer Where Name = 'Microsoft Print to PDF'"
#$a.SetDefaultPrinter()
$TargetPrinter = "Microsoft Print to PDF"
$ErrorActionPreference = “SilentlyContinue”
$LocalPrinter = GWMI -class Win32_Printer | Where {$_.Name -eq $TargetPrinter}
$LocalPrinter.SetDefaultPrinter()
$ErrorActionPreference = “Stop”
Get-WmiObject -Class Win32_Printer | where{$_.Network -eq ‘true‘}| foreach{$_.delete()}
To disconnect the default printer must be changed, otherwise it won't be disconnected.
After all Script was made, we putted them in a GPO under User Configuration\Policies\Windows Settings\Scripts and there on Logon and Logoff.
You may have some troubles that the GPOs won't run, so here some usefull troubleshooting guides that i found:
The Scripts aren't working as Machine Policies under Startup and Shutdown, they have to be in the User Configuration as mentioned above.
Also you have to configure the Policie that deley the Script of 5 minutes. These are under Computer Configuration\Administrative Templates\System\Group Policy\Configure Logon Script Delay aktivate them and set the delay to 0 minutes, otherwise any Script will be deleyed to 5 minutes after logon.
Also a problem could be, if you are running the GPO on Windows 8/10 System, and you made them on a WIndows 7 PC. Create GPOs allways on the Server 2008/R2 or 2012R2 for this kind of system.
It could be helpfoul also if you configure the Logon/Logoff GPO as follows: As Scriptname "powershell.exe" (without quotes) and as Script Parameters -F "\SERVER\FREIGABE\meinskript.ps1" (with quotes.
I hope this could help someone else.
Thanks to who hleped me.
WBZ-ITS

How to send email when SPECIFIC scheduled task fails to run

I have a exe file that is executed every day by the Task Scheduler on my Windows 2008. If that script should fail to start, or if the script fails during execution, I would like to get an email notification.
There are many examples of getting Task Schedular to send an email based on an event log entry. However, I only want to be notified if MY particular scheduled task fails, not get a notification for all tasks that fails with an EventID 203/103/201. How can I do that without any custom software?
Create a new Task that runs this PowerShell Script.
$ScheduledTaskName = "Taskname"
$Result = (schtasks /query /FO LIST /V /TN $ScheduledTaskName | findstr "Result")
$Result = $Result.substring(12)
$Code = $Result.trim()
If ($Code -gt 0) {
$User = "admin#company.com"
$Pass = ConvertTo-SecureString -String "myPassword" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential $User, $Pass
$From = "Alert Scheduled Task <task#servername>"
$To = "Admin <admin#company.com>"
$Subject = "Scheduled task 'Taskname' failed on SRV-001"
$Body = "Error code: $Code"
$SMTPServer = "smtp.company.com"
$SMTPPort = "25"
Send-MailMessage -From $From -to $To -Subject $Subject `
-Body $Body -SmtpServer $SMTPServer -port $SMTPPort -UseSsl `
-Credential $Cred
}
I just wanted to add to this post just in case someone has a similar challenge on later server o/s. There is now a PowerShell cmdlet to get Scheduled Task information.
$ScheduledTaskName = 'Taskname'
(Get-ScheduledTaskInfo -TaskName $ScheduledTaskName).LastTaskResult

Running a batch file (with blanks) from powershell script

I need to call a batch file from powershell script. The batch file name will be decided using the parameters to PS file from user. I have this code but not working as expected. Could someone poing me my mistake? Everything seems fine but I am getting issues with the actual batch file calling (one of the last 4 statements)
param(
[string]$parts
)
$sharedDrive = "\\server\share"
$cred = get-credential "DOMAIN\"
$username = $cred.UserName
$password = $cred.GetNetworkCredential().Password
$net = New-Object -com WScript.Network
$net.mapnetworkdrive("", $sharedDrive, "true", $username, $password)
$BatchFilePath = $sharedDrive + "\Public\Upgrade\Application Folder"
IF ($parts -eq "P1") {
$selectedBatchFile = "`"" + $BatchFilePath + "\P1 Upgrade File.bat" + "`""
} ELSEIF ($parts -eq "P2") {
$selectedBatchFile = "`"" + $BatchFilePath + "\P1P2 Upgrade File.bat" + "`""
} ELSE {
Write-Host "Invalid Part specified. Choose one from: P1, P2"
}
$command1 = "/k $selectedBatchFile $username $password"
## I tried all the below but NONE worked
#& cmd "`"$command1`""
#& cmd "$command1"
#Start-Process "cmd.exe" -ArgumentList "$command1"
#Start-Process "cmd.exe" -ArgumentList "`"$command1`""
Try this
Invoke-Expression "cmd /k `"$selectedBatchFile`" $username $password"
NOTE: I do not normally suggest using Invoke-Expression if it executes code from text that a user has input. For instance, think about what happens if you use Read-Host to ask the user for their username and they type in ; Remove-Item C:\ -Recurse -Force -ErrorAction 0;. Yeah, that might be a bad day for you.
On V3/V4 you could also use --% but it requires storing your info in env vars which you might not want to do with a password:
$env:file = $selectedBatchFile
$env:un = $username
$env:pw = $password
cmd.exe /c --% "%file%" %un% %pw%
See this post for more details on --%.
Is there a reason you want to start it with cmd.exe /k?
start-process -filepath $selectedbatchfile -argumentlist $username,$password

How to get PowerShell to keep a command window open?

When I run a program on PowerShell it opens a new window and before I can see the output, the window closes. How do I make it so PowerShell keeps this window open?
Try doing:
start-process your.exe -NoNewWindow
Add a -Wait too if needed.
The OP seemed satisfied with the answer, but it doesn't keep the new window open after executing the program, which is what he seemed to be asking (and the answer I was looking for). So, after some more research, I came up with:
Start-Process cmd "/c `"your.exe & pause `""
I was solving a similar problem few weeks ago. If you don't want to use & (& '.\program.exe') then you can use start process and read the output by start process (where you read the output explicitly).
Just put this as separate PS1 file - for example (or to macro):
param (
$name,
$params
)
$process = New-Object System.Diagnostics.Process
$proInfo = New-Object System.Diagnostics.ProcessStartInfo
$proInfo.CreateNoWindow = $true
$proInfo.RedirectStandardOutput = $true
$proInfo.RedirectStandardError = $true
$proInfo.UseShellExecute = $false
$proInfo.FileName = $name
$proInfo.Arguments = $params
$process.StartInfo = $proInfo
#Register an Action for Error Output Data Received Event
Register-ObjectEvent -InputObject $process -EventName ErrorDataReceived -action {
foreach ($s in $EventArgs.data) { Write-Host $s -ForegroundColor Red }
} | Out-Null
#Register an Action for Standard Output Data Received Event
Register-ObjectEvent -InputObject $process -EventName OutputDataReceived -action {
foreach ($s in $EventArgs.data) { Write-Host $s -ForegroundColor Blue }
} | Out-Null
$process.Start() | Out-Null
$process.BeginOutputReadLine()
$process.BeginErrorReadLine()
$process.WaitForExit()
And then call it like:
.\startprocess.ps1 "c:\program.exe" "params"
You can also easily redirect output or implement some kind of timeout in case your application can freeze...
If the program is a batch file (.cmd or .bat extension) being launched with cmd /c foo.cmd command, simply change it to cmd /k foo.cmd and the program executes, but the prompt stays open.
If the program is not a batch file, wrap it in a batch file and add the pause command at the end of it. To wrap the program in a batch file, simply place the command in a text file and give it the .cmd extension. Then execute that instead of the exe.
With Startprocess and in the $arguments scriptblock, you can put a Read-Host
$arguments = {
"Get-process"
"Hello"
Read-Host "Wait for a key to be pressed"
}
Start-Process powershell -Verb runAs -ArgumentList $arguments
pwsh -noe -c "echo 1"

Resources