My requirement is, the script needs to be sent an email to set of people whenever application pool recycled. Script will search for application pool event IDs from Event Viewer and will send an email if it find any events. If it's found the Event ID it will create an log file in D:\temp and send that log file to recipients.
The problem is, the script is sending an email with empty log file as attachment when no events happen. I dont want to send empty file and script should sent ONLY when event occurs. This can be resolved if we use an if condition, but not sure how to use that in my script.
Below is the script which I used to run. Currently it is sending log when events occur and not occur as well.
$date = Get-Date -Format "MM-dd-yyyy HH-mm-ss"
$starttime = (Get-Date).AddMinutes(-5)
Get-WinEvent -ComputerName computer1 -Filterhashtable #{
LogName='system';
StartTime=$StartTime;
ID=3201,5079,2262,2263,5070,5074,5075,5076,5077,5078,5080,5081,5093,5117,5186
} |
select machinename,timecreated,id,message |
Format-Table machinename,timecreated,id,message -AutoSize |
Out-String -Width 6096 |
Out-File "D:\Logs\PS\$date.txt" -NoClobber
Send-MailMessage -To user1#test.com `
-Subject "Application Pool Recycled in Computer1" `
-Body "Application Pool has Recycled in Computer1" `
-SmtpServer smtp.com `
-From Apppool_Monitoring#test.com `
-Attachments "D:\Logs\PS\$date.txt"
Check if you actually got an event before creating output and sending mail:
$evt = Get-WinEvent -ComputerName computer1 -Filterhashtable #{...}
if ($evt) {
$evt | Format-Table ... |
Out-String -Width 6096 |
Out-File "D:\Logs\PS\$date.txt" -NoClobber
Send-MailMessage ...
}
If you want the message inline instead of as an attachment put the event string in a variable and put that in the message body:
if ($evt) {
$msg = $evt | Format-Table ... | Out-String -Width 6096
Send-MailMessage ... -Body "Application Pool has Recycled in Computer1`n$msg" ...
}
read the contents of the logfile..if the log file contains data then send email otherwise do nothing.
if(Get-Content -Path "D:\Logs\PS\$date.txt")
{
#send email.......
}
Related
Send-MailMessage -From "test#xxx.com" -To "aaa#xxx.com","bbb#xxx.com","ccc#xxx.com","ddd#xxx.com" -SmtpServer "xx.xx.xx.xx" -Subject "Test email subject" -Body "Test email body"
I want to write a script using above command in loop say 100 times and run every 2 seconds and write an output to a log file which later can be used for analysis.
Issue: My severer gives intermittent issue while sending emails. I want to see logs to see how many emails go through out of 100 and how many failed and with what error message.
Running multiple times would require some sort of loop. A for loop seems most appropriate for each loop of 100, and a while loop seems appropriate for looping over each set.
Since you are running commands very quickly you will want some sort of parallelism. Jobs, Runspaces, and workflows are the three most common forms. Jobs are the easiest to work with but the least efficient.
Then the Start-Sleep command can be used for the delay between trials.
There are many ways to write out to a file, Out-File -Append is one way. Try\catch can be used to catch the errors and output those to the log as well.
$Iterations = 100
$Delay = 2
$StopTime = (Get-Date).AddMinutes(5)
$Command = {Send-MailMessage -From "test#xxx.com" -To "aaa#xxx.com","bbb#xxx.com","ccc#xxx.com","ddd#xxx.com" -SmtpServer "xx.xx.xx.xx" -Subject "Test email subject" -Body "Test email body"}
while ((Get-Date -le $StopTime)) {
try {
Remove-Job -State Completed
for ($i = 0; $i -lt $Iterations; $i++) {
Start-Job -ScriptBlock $command
}
"Jobs created at (Get-Date)" | Out-File "C:\example\log.txt" -Append
Start-Sleep $delay
}
catch {
$error[0] | Out-File "C:\example\log.txt" -Append
}
}
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 got this script from a blog and modified it to suit my environment. However I am having problems iterating through the foreach loop and getting the service status or the remote computers ($unregisteredVM).
It seems as if the Get-Service was trying to query the computer executing the script instead of the remote computers.
Also I tried running the portion of the script which grabs the status of the computer and set that variable as the -Computername parameter and it doesn't seem to take it.
$icaservicestate = Get-Service PorticaService -ComputerName $unregisteredVM | select status
What am i missing? Below is the entire code.
##Load Citrix Modules
asnp Citrix.*
#Variables for email
[string[]]$recipients = "xxxxx#xxxxxx.com"
$fromEmail = "xxxxx#xxxx.com"
$server = "itopia-us.mail.protection.outlook.com"
$date= Get-Date
##Check for VMs on and unregistered
$unregisteredVMs = Get-BrokerDesktop -RegistrationState Unregistered | Select MachineName
[string]$emailVMs = Get-BrokerDesktop -RegistrationState Unregistered | Select MachineName | ft -wrap -autosize | Out-String
IF (!$unregisteredVMs) {
##Send all clear email
[string]$emailBody = "There were no powered on desktops in an unregistered state."
send-mailmessage -from $fromEmail -to $recipients -subject " XenDesktop Daily Check - $date" -body $emailBody -priority High -smtpServer $server
}
Else {
##If powered-on and unregistered, perform a forceful restart
foreach ($unregisteredVM in $unregisteredVMs)
{
$icaservicestate = Get-Service PorticaService -ComputerName $unregisteredVM | select status
IF ($icaservicestate = Stopped) {
Set-Service -Name PorticaService -Status Running
}
Else {
sc \\$unregisteredVM start PorticaService
}
#Write-Host "Hello, I am unregistered: $unregisteredVM"
}
#Send an email report of VMs to be restarted due to being in an unregistered state
[string]$emailBody = "The following desktops were forcefully restarted due to not registering with the DDCs in a timely manner. `n $emailVMs"
send-mailmessage -from $fromEmail -to $recipients -subject " XenDesktop Daily Check - $date" -body $emailBody -priority High -smtpServer $server
}
I'm not sure if this is your entire problem but I don't think this is what you want:
IF ($icaservicestate = Stopped)
That assigns the result of executing a command named Stopped to the variable $icaservicestate. I doubt you have a command named Stopped. For equality comparison use -eq and quote Stopped:
IF ($icaservicestate -eq 'Stopped') { ... }
There are two potential reasons you may not be getting the result you want.
First: In Powershell a single equals sign is the assignment operator.
Your if statement line 24 is re-assigning the value of $icaservicestate to an error
$icaservicestate = Get-Service PorticaService -ComputerName $unregisteredVM | select status
IF ($icaservicestate = Stopped) {
Set-Service -Name PorticaService -Status Running
}
Use IF ($icaservice -eq 'stopped') {
Second: The line 23 Cmdlet Get-Service maybe failing due to line 11 the object you are passing to it
$unregisteredVMs = Get-BrokerDesktop -RegistrationState Unregistered | Select MachineName
In the above line you've generated an [array] of objects with each object having a single property called MachineName.
On line 23 you are trying to pass the objects to the ComputerName parameter of the Get-Service Cmdlet. To work around this you can use $unregisteredVM.MachineName in the Get-Service cmdlet
$icaservicestate = Get-Service PorticaService -ComputerName $unregisteredVM.MachineName | select status
You can read more about the way property values are passed in the pipeline by typing at the prompt:
help about_piplines and looking for 'ByValue' and 'ByPropertyName'
UPDATE:
if ($icaservicestate -eq 'Stopped') {
Get-Service -Name PorticaService -ComputerName $unregisteredVM.MachineName | Set-Service -Status Running
}
Previously, the line Set-Service -Name PorticaService -Status Running would be attempting to start the service on your local machine.
First we grab a reference to the PorticaService on the remote machine, then pipe that object to the Set-Service Cmdlet and set the status to running.
I think your script doesn't work because the machinename you get back from "get-brokerdesktop" is in the format domainname\vdiname. For "get-service -computername" you only need to enter the hostname.
To "isolate" the hostname I'd do:
$unregisteredVM=$unregisteredVM.machinename -replace ("domainname\," ")
$unregisteredVM=$unregsiteredVM.trim()
Now $unregisteredVM should contain only the hostname.
ok fellas, I've been reading, researching, learning and testing about powershell. Within the last 20 days, here is what I've been able to come up with.
get-mailbox -identity $_.name | select name,userprincipalname,#{label="Size";expression={$size = Get-Mailboxstatistics $_.identity;$size.TotalItemSize.Value.ToMB()}},#{label="Items";expression={$items = Get-Mailboxstatistics $_.name;$item.ItemCount}}
I stored this in a script called accountsizes.ps1. It works exactly as I expected by outputting all the email accounts with the sizes, but in order for me to get only the mailboxes over 2048MB, I have to call it like this:
PS C:\accountsizes.ps1 | where size -gt "2048" | select userprincipalname,size
And this works by return the email addresses and mailbox sizes in MBs. But now my dilemma is; how do I enumerate through the results and extract each email address and send an email to that user and myself, warning them that their mailbox is too large and they need to archive. From what I been reading and learning, I would have to use a ForEach loop and the send-mailmessage cmdlet. I cannot figure out how to use the ForEach and incorporate it with the script: Here is I go brain dead with the ForEach:
PS C:\accountsizes.ps1 | where size -gt "2048" | select userprincipalname,size | ForEach($user in userprincipalname){$_.userprincipalname}
I do not know the right way to go about doing this (so, don't ask me why I'm doing this way :)), I have no previous knowledge about scripting and coding.
Here is my email part:
$smtpserver = "domain.com"
$smtpFrom = "me#domain.com"
$smtpTo = "you#domain.com"
$messageSubject = "Warning Email Mailbox Too Large"
$body = "blah blah blah blah"
send-mailmessage -from $smtpFrom -to $smtpTo -subject $messageSubject -body $body
Thanks in advance for your helpful advice.
The foreach keyword and the ForEach-Object cmdlet are two different things.
If you use the foreach keyword, you give a name to the iteration variable, and you iterate on a collection. For example:
$collection = #("one", "two")
foreach ($item in $collection) {
Write-Host $item
}
Instead, if you pipe commands outputs, you have to use the ForEach-Object cmdlet with a script block. Inside the script block, you refer to the iteration variable with the special variable $_. Example:
$collection = #("one", "two")
$collection | ForEach-Object {
Write-Host $_
}
You can shorten ForEach-Object with %:
$collection | % {
Write-Host $_
}
So, in your case you should probably do this:
C:\accountsizes.ps1 | where size -gt "2048" | select userprincipalname,size | % { $_.userprincipalname }
Paolo provided excellent information and deserves upvotes if nothing else. As to your question of how to enumerate and send email you probable need something like:
C:\accountsizes.ps1 | where size -gt "2048" | %{
$smtpServer = "smtp.domain.com"
#Creating SMTP server object
$SMTP = new-object Net.Mail.SmtpClient($smtpServer)
#Creating a Mail object
$EMail = new-object Net.Mail.MailMessage
#Construct Email
$EMail.From = "me#domain.com"
$EMail.ReplyTo = "me#domain.com"
$EMail.To.Add($_.userprincipalname)
$EMail.subject = "Warning Email Mailbox Too Large"
$EMail.body = "blah blah blah blah"
$SMTP.Send($EMail)
}
You could get a lot more fancy, go by size and send different emails depending on how large their mailbox is, or get content from files for subject and body depending on size, but that is just going to make things complicated. You could also use Send-MailMessage, and that works just fine, I just like this way because it makes it easier to work with in my opinion than one really long line with a ton of switches. If the message and subject are going to be generic you may want to do something more like:
$smtpServer = "smtp.domain.com"
#Creating SMTP server object
$SMTP = new-object Net.Mail.SmtpClient($smtpServer)
#Creating a Mail object
$EMail = new-object Net.Mail.MailMessage
#Construct Email
$EMail.From = "me#domain.com"
$EMail.ReplyTo = "me#domain.com"
C:\accountsizes.ps1 | where size -gt "2048" | %{$EMail.BCC.Add($_.userprincipalname)}
$EMail.subject = "Warning Email Mailbox Too Large"
$EMail.body = "blah blah blah blah"
$SMTP.Send($EMail)
That would make one email and BCC everybody on it. Then you could do another email to yourself stating who warnings got sent to.
When I write the following script into the powershell command line one by one, it successfully sends the email, but when I run the script, it returns a bunch of errors. I'm guessing something syntactically needs to be changed in order to run it as a script? Any ideas?
Start-Process Outlook
$o = New-Object -com Outlook.Application
$mail = $o.CreateItem(0)
#2 = high importance email header
$mail.importance = 2
$mail.subject = “Auto Build Test“
$mail.body = “This is a test“
#for multiple email, use semi-colon ; to separate
$mail.To = “myemail#company.com"
$mail.Send()
# $o.Quit()
Param(
[parameter(Mandatory=$true)]
[alias("e")]
[string]$RecipientEmailAddress
)
if($RecipientEmailAddress -notmatch "\b[A-Za-z0-9._%+-]+#BLAHH.com")
{
Write-Output "the email address for the receipient of log reports is not a valid email address, hence will not send the report via email. They can still be accessed at " |Out-String ;
}else
{
$returnVal= New-Object PSObject ;
$returnVal |Add-Member -Name is_Success -MemberType NoteProperty -Value $null;
$returnVal |Add-Member -Name Explanation -MemberType NoteProperty -Value $null;
try{
$Attachments =Get-ChildItem -Path "C:\FOLDERWHEREYOURAATACHMENTS ARESTORED";
if($Attachments.count -eq 0)
{
$returnVal.Explanation="Error sending log report email to the user: $RecipientEmailAddress. Please check if the C:\FOLDERWHEREYOURAATACHMENTS is accessible and there are indeed log files present";
#Write-Output "Error sending log report email to the user: $RecipientEmailAddress" |Out-String ;
#Write-Output "Please check if the C:\FOLDERWHEREYOURAATACHMENTS is accessible and there are indeed log files present "|Out-String;
$returnVal.is_Success= $false;
return $returnVal;
}
$TestedAttachmentsList = new-Object System.Collections.ArrayList;
for($i=0;$i -lt $Attachments.count;$i++)
{
$TestedAttachmentsList.add($Attachments[$i].FullName);
}
Send-MailMessage -From "<FROM#BLAHH.COM>" -To "<$RecipientEmailAddress>" -SmtpServer "mail.BLAHH.com" -Attachments $TestedAttachmentsList -Subject "BLAHH SUBJECT" -Body "BLAHH BLAHH";
$returnVal.is_Success=$true;
$returnVal.Explanation="An email has been sent to the $RecipientEmailAddress containing the log of the setup and configuration."
return $returnVal ;
}Catch [System.Exception]
{
#Write-Output "Error sending log report email to the user: $RecipientEmailAddress" |Out-String ;
#Write-Output "Please check communication between your host machine and mail.BLAHH.com on port 25 is possible"|Out-String;
$returnVal.is_Success= $false;
$returnVal.Explanation="Error sending log report email to the user: $RecipientEmailAddress Please check communication between your host machine and mail.BLAHH.com on port 25 is possible";
return $returnVal ;
}
}
The syntax doesn't change between the command line and a script file. What changes is how fast the commands are executed. If you're typing them in then there is plenty of delay between each command. But if they run from a script they get presented to Outlook much more quickly.
A simple way to fix this is add Sleep 1 (or similar) before the command that fails. Without seeing your error output I would guess that you want to sleep after CreateItem and maybe before Send. But if you look at the error messages closely you'll see they identify which line of the script failed. Put Sleep before the first line that failed. Retry the script. If a new line fails, then put a delay before it as well. If the first line still fails you can try Sleep 2. You can also make the Sleep shorter. For 1/2 second: Sleep -milliseconds 500.
IF adding Sleeps fixes the problems - in other words the problem is a synchronization issue, there may be something in the Outlook object model you could use that wouldn't be as hackish as using Sleeps.
I wasn't able to repro this on my Outlook 2010 installation. However I did look up an alternate method for sending email from PS (below). Maybe this method will work.
$i=$o.Session.folders.item(2).folders.item("Outbox").items.add(0)
$i.to="me#whereiwork.com"
$i.Subject="a wittle testy"
$i.Body="some body"
$i.send()