In PowerShell, how to I send mail and enumerate in email body list of discovered errors - powershell

I have the following code snippet from my PowerShell script that...
Loops through a list of servers
Does a Select-String -notmatch on the error log at each server
Flags the server if the error log is bad, and gives the OK if the error log if fine
What I'd like to also do is send an email report that enumerates each of the discovered bad error logs in a list, and also list all the servers whose error logs are OK. Something like this in the email body:
The following servers have bad error logs:
Server3
Server6
Server14
The following servers are OK:
Server1
Server2
Server5
Here is my code snippet:
$Servers = Get-Content $ServerLst
ForEach ($Server in $Servers)
{
$ErrorLog = Get-ChildItem -Path \\$Server\$LOG_PATH -Include Error.log -Recurse | Select-String -notmatch $SEARCH_STR
If ($ErrorLog)
{
Write-Host "Bad Error Log found at $Server!"
}
Else
{
Write-Host "Error log is OK."
}
}
I'm guessing I would need a Send-Mail function where I would pass in the server names with bad error logs, etc. However, I'm not quite sure how to approach this.
Any great ideas will be greatly appreciated. Thanks!

If you are using Powershell V1 use this function from the Powershell Cookbook to send mail. In Powershell V2 you can send mail using Send-MailMessage.
$Servers = Get-Content $ServerLst
$Bad = "The following servers have bad error logs:`n`n"
$OK = "`nThe following servers are OK:`n`n"
ForEach ($Server in $Servers)
{
$ErrorLog = Get-ChildItem -Path \\$Server\$LOG_PATH -Include Error.log -Recurse | Select-String -notmatch $SEARCH_STR
If ($ErrorLog)
{
$Bad += "`t - $Server`n"
}
Else
{
$OK += "`t - $Server`n"
}
}
Send-MailMessage -Body "$Bad $OK" -Subject "Bad Logs" -SmtpServer $servername -To $to -From $from
Remark: The smtpserver parameter is called smtphost in the Powershell cookbook function.

You will need to make the function on your own, but here is some pseudo code:
Function SendMail
{
Param(...your params here)
...send the mail...
}
<...
Your code to check all your servers
You need to save your errors or issues to an array or hashtable.
I'll assume you use a 2-field array called $ErrArray
...>
# Now at the end you build a string for the body of the email to incorporate your errors
$StrBody = "Bad Error Log Report`n`n"
$ErrArray | ForEach-Object {$StrBody = $Strbody + "`n$($_[0]) server had an issue: $($_[1])`n"}
SendMail $EmailTo $EmailSubject $StrBody
So the breakdown:
Make a mail function
Save the results of your analysis to an array or hashtable
Iterate through your results object and append each result record to the string for your email
Call the email function

Related

DNS name from IP address

I need to create a list of IP addresses and DNS names. I
am trying to get DNS names from IP addresses. I have tried two ways:
try/catch but it ends afterwards.
Without and it just outputs DNS names that I can't relate to the IP addresses.
Here's what I have so far:
#try {
Get-Content C:\Users\pintose\Documents\IP_Address.txt | ForEach-Object
{([system.net.dns]::GetHostByAddress($_)).hostname >> C:\Users\user\Documents\hostname.txt}
# }
# catch {
if ($_.Exception.Message -like "*The requested name is valid*") {
Write-Output "UNREACHABLE" | Out-File C:\Users\user\Documents\hostname.txt }
# }
Try this solution:
$outFile = "C:\Users\user\Documents\hostname.txt"
Get-Content C:\Users\pintose\Documents\IP_Address.txt | ForEach-Object {
$hash = #{ IPAddress = $_
hostname = "n/a"
}
$hash.hostname = ([system.net.dns]::GetHostByAddress($_)).hostname
$object = New-Object psobject -Property $hash
Export-CSV -InputObject $object -Path $outFile -Append -NoTypeInformation
}
We create a objects, that have the IPaddress in it and a hostname n/a if it cannot be resolved. Then, the object gets exported into the file. You'll get something like:
192.0.0.1; Server1
This uses a workflow so it can do parallel foreach
Workflow Get-DNSNames([string[]]$IPAddresses){
foreach -parallel ($IP in $IPAddresses){
try{
#{$IP = $(([system.net.dns]::GetHostByAddress($IP)).hostname)}
}catch{
#{$IP = "N/A"}
}
}
}
$List = Get-DNSNames -IPAddresses $(Get-Content "C:\IPAddresses.txt").Split("[\r\n]")
$List | Out-File "C:\IPAddresses_Complete.txt"
You might want to try the other solutions offered here, but here are some things you might want to think about.
First, I'd recommend not putting the try{}catch{} around the whole of the first command. If you are looping through data and just one of them causes an exception, you risk not completing the task. Put the try{}catch{} around just the "risky" line of code:
Get-Content C:\Users\pintose\Documents\IP_Address.txt | Foreach-Object {
try {
([system.net.dns]::GetHostByAddress($_)).hostname >> C:\Users\user\Documents\hostname.txt
}
catch {
if ($_.Exception.Message -like "*The requested name is valid*") {
Write-Output "UNREACHABLE" | Out-File C:\Users\user\Documents\hostname.txt
}
}
}
When you catch the exception, you only write to the text file in the case that "the requested name is valid" (do you mean invalid?). You never write anything to the file otherwise. Thus, going back to your original code:
IF there is an exception caused by ANY of the IP addresses
AND the exception is NOT "the requested name is valid" (which I think might be a typo?)
THEN no error gets written to the file and the script ends without necessarily completing all the IP addresses.
Other things:
You use two methods to write to the file: >> and Out-File. Probably better to use the PowerShell cmdlet but with the -Append switch to ensure you append to the end of the file:
([system.net.dns]::GetHostByAddress($_)).hostname | Out-File C:\Users\user\Documents\hostname.txt -Append
Write-Output "UNREACHABLE" | Out-File C:\Users\user\Documents\hostname.txt -Append
#restless1987 has suggested a way to ensure you write both the IP address and the hostname (if determined) to the output file. I'd have a look at that to work out what is going on.
My final tip would be to be wary of reading in from .txt files with Get-Content. They often have trailing (blank) lines and you might want to try to ignore such blanks. Probably not a big issue in this case as it will just mean a failed DNS attempt, but I have seen such things wreak havoc on every mailbox in a (very) large company when used with other commands.
Another way...
$ip_list = Get-Content ./IP_Address.txt
foreach ($ip in $ip_list) {
try {
([system.net.dns]::GetHostByAddress($ip)).hostname |
Out-File -FilePath ./hostname.txt -Append
}
catch {
if ($_.Exception.Message -like "*The requested name is valid*") {
Write-Output "UNREACHABLE" | Out-File -FilePath './hostname.txt' -Append }
}
}
There are many tools that can accomplish this, but if you need a quick and dirty solution that you can run just about anywhere this will get the job done.
Using the eternally useful ps tools such as psloggedon /accepteula \\computername or ip address you can get who is currently logged in to check if this is the correct machine. For example:
c:\pstools>psloggedon /accepteula \\10.0.0.10
loggedon v1.33 - See who's logged on
Copyright ⌐ 2000-2006 Mark Russinovich
Sysinternals - www.sysinternals.com
Users logged on locally:
Error: could not retrieve logon time
NT AUTHORITY\LOCAL SERVICE
Error: could not retrieve logon time
NT AUTHORITY\NETWORK SERVICE
1/12/2015 8:06:51 AM DOMAIN\user
Error: could not retrieve logon time
NT AUTHORITY\SYSTEM
Users logged on via resource shares:
1/17/2015 2:26:43 PM DOMAIN\user
Now that you have confirmed that this IP address is the correct one for this user. We just need to lookup the IP address using nslookup.
C:\>nslookup 10.0.0.10
Server: server.domain.com
Address: 10.10.0.1
Name: Workstation07.server.domain.com
Address: 10.0.0.10
Now we know that the computer name for that computer is Workstation07.

In EWS/Powershell How Do I Remove Email Addresses From $EmailMessage.ToRecicpients?

Here's what is happening. I have this handy powershell script I've written up to connect to EWS using Impersonation, read plain-text files containing email messages, and send them one at a time in a loop. At the end there is a little Inbox and Sent Items folder clean up. (Kudos to my friends at stackoverflow, as many of the issues I already encountered in writing this have been solved by looking up the answers right here.)
Everything works great except ToRecipients.Add, which works a little _too_well_.
$EmailMessage.ToRecipients.Add($DName)
When the loop iterates to read the next file, the $EmailMessage.ToRecipients array retains it's information after executing
$EmailMessage.SendAndSaveCopy($SentItems.Id).
I had somehow expected a SendAndSaveCopy to discard all information in $EmailMessage, but it does not (Send, Save, and then Discard, right?).
So when sending one file, there is no issue whatsoever. But when sending multiple files, each subsequent file just adds its email addresses on to the end of the $EmailMessage.ToRecipients array. So the second file is sent to the first file's recipients AND the second file's recipients. The third file goes to first, second, and third file recipients. The more files, the bigger the mess.
I've tried various things at the end of the loop, before it iterates, to clear out the ToRecipients, but have not had any luck. Powershell rejects any direct assignment of any kind. I expected there to be a ToRecipients.Clear or .Empty or .Remove or something like that, but I haven't found anything that works. These approaches fail:
Remove-Variable $EmailMessage
Remove-Variable $EmailMessage.ToRecipients
Remove-Variable $EmailMessage.ToRecipients[x] #rejects all counter values
Clear-Variable $EmailMessage
Clear-Variable $EmailMessage.ToRecipients
Clear-Variable $EmailMessage.ToRecipients[x] #rejects all counter values
$EmailMessage.ToRecipients = NULL
$EmailMessage.ToRecipients = ""
I can loop through and read every value, get a count of how many ToRecipients there are, print them out, and all sorts of other nifty things, but it seems to be read-only and I haven't found any way to clear them out before iterating.
I have thought of a couple "band-aid" approaches, which I haven't tried yet:
1. Put it in a function with local variables, and $EmailMessage will poof on the function exit (with any luck).
2. Just process one file, and at the end of the script right before exiting, check to see if any files remain to process, and call the script again using Invoke-Expression.
Really, this seems like overkill, and a work around to the original problem rather than a direct fix. I should just be able to clear out the email addresses before the loop iterates, right?
Here is the full code:
<#
Script Overview
1. Opens EWS services for shared mailbox using an appid
2. Reads files from c:\outbox\ containing email to send, one per file
3. Sends the mail message in each file found
4. Renames each file found
5. Moves sent items into c:\sentemail\
6. Deletes any emails in the Inbox or Sent Items folders that are older than 14 days ($purgebeforedate))
#>
#Variables
## Define UPN of the Account that has impersonation rights
$AccountWithImpersonationRights = "myappid"
$appidpasswd = "mysupersecretpassword"
##Define the SMTP Address of the mailbox to impersonate
$MailboxToImpersonate = "autoemail#mysite.mydomain"
##Define CAS URL (Client Access Server) - can be found in system registry if necessary
$CASURL = "https://my.site.and.domain/ews/exchange.asmx"
$ewsApiDownload = "http://www.microsoft.com/en-us/download/details.aspx?id=35371"
## Define Exchange web services DLL path (requires 2.0 for our environment)
$dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"
##Define folders for incoming and archived email files
$GetFolder = "c:\outbox\" #folder where input email message files are in plaintext
$SaveFolder = "c:\sentemail\" #folder to move sent email files to after processed
$PurgeBeforeDate = (Get-Date (Get-Date).AddDays(-14) -format G) #format m/d/yyyy hh:mm:ss ap- ::Date is 14 days ago [AddDays(-14)]
##Define location of error log on the local windows server where this script runs
$errorLog = "c:\sentemail\MyAutoSentEmail.log"
# Post a note in the error log that the script has started
$date = Get-Date -format G #Default date and time output used in log file.
Add-Content $errorLog $date": Email Service script started."
Add-Content $errorLog $date": Old sent email delete cutoff - $PurgeBeforeDate"
## Load Exchange web services DLL
Import-Module $dllpath
#Exit script if importing EWS API fails.
if ($? -eq $false)
{
$date = Get-Date -format s #Default date and time output used in log file. (was G before)
Add-Content $errorLog $date": Faied to load EWS, ensure it is installed:"$ewsApiDownload
Add-Content $errorLog $date": EWS API expected location:" $dllpath
Add-Content $errorLog $date": Failed to import the EWS API. Script terminated."
exit
}
## Set Exchange Version (our site requires Exchange2010_SP2)
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2
## Create Exchange Service Object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
#Get valid Credentials using UPN for the ID that is used to impersonate mailbox
$service.Credentials = New-Object System.Net.NetworkCredential($AccountWithImpersonationRights, $appidpasswd);
## Set the URL of the CAS (Client Access Server)
$service.Url = New-Object Uri($CASURL)
##Login to Mailbox with Impersonation
#Write-Host 'Using ' $AccountWithImpersonationRights ' to Impersonate ' $MailboxToImpersonate
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxToImpersonate );
#Connect to the Inbox and display basic statistics
$SentFolder = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SentItems,$ImpersonatedMailboxName)
$SentItems = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$SentFolder)
if ($? -eq $false) #Exit script if binding to folder fails.
{
Add-Content $errorLog $date": Failed to bind to the specified mailbox folder. Script terminated."
exit
}
#For info only, can uncomment to get a list of how many items are in the Inbox and how many are unread
#Write-Host 'Total Item count for Inbox:' $Inbox.TotalCount
#Write-Host 'Total Items Unread:' $Inbox.UnreadCount
#get the filenames in the outgoing-email directory into $files so they may be read one at a time
$files = Get-ChildItem $GetFolder
if ($files.Count -gt 0)
{ #if there are files to process
#Create new email message to send out
$EmailMessage = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service
$EmailMessage.Body = New-Object Microsoft.Exchange.WebServices.Data.MessageBody
$EmailMessage.Body.BodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::HTML
$EmailMessage.From = $MailboxToImpersonate
for ($i=0; $i -lt $files.Count; $i++) { #start for $i loop for each filename found
$emailfile = Get-Content $files[$i].FullName #read file into $emailfile
$To = "empty" #init $To to empty
$Subject = "empty" #init $Subject to empty
$Body = "empty" #init $Body to empty
$Bodytext = "" #init $Bodytext - place to accumulate body lines before adding to email message, gets html return on end of each line
$Bodyhtml = "" #init $Bodyhtml - body lines are placed here in parallel, no html carriage return added
foreach ($Data in $emailfile) { #put each line of the file into $Data and process it
if ( ($Data.StartsWith("to:")) -and ($Subject -eq "empty") )
{ #add recipients to the email message, all to: lines must be before the subject: line
$To = "found" #mark $to line(s) as having been found
Write-Host "TO: line found - " $Data
$Data = $Data.Substring(3) #get all chars after subject:
if ($Data.Contains(";"))
{ #if the email address passed in contains multiples separated by semicolons, split and add them
$DataNames = $Data -split ';' #split $Data into an array of semicolon separated substrings
foreach ($DName in $DataNames) { #validate each semicolon separated email address and add it
$EmailMessage.ToRecipients.Add($DName)
Write-Host $date": TO: address added: "$DName
} #end foreach $DName in $DataNames
} else { #else if no semicolon, just add what you have
$EmailMessage.ToRecipients.Add($Data)
Write-Host $date": TO: address added: "$DName
} #end if email address passed doesn't contain a semicolon
} #end add recipients to $To
if ( ($Data.StartsWith("subject:")) -and ($Body -eq "empty") )
{ #add subject line to the email message, must come before any body: line(s)
$Data = $Data.Substring(8) #get all chars after subject:
$EmailMessage.Subject = $Data
$Subject = "found" #mark $subject line as having been found
Write-Host "SUBJECT: line found - " $Data
} #end add recipients to $To
if ($Data.StartsWith("body:"))
{ #first line of body found
$Data = $Data.Substring(5) #get all chars after subject:
$Bodytext = $Data
$Bodyhtml = $Data
$Body = "found" #mark $body line as having been found
Write-Host "BODY: line found - " $Data
} #end add recipients to $To
elseif ($Body -eq "found")
{ #accumulate the remaining lines into the $bodytext string with '<br />' as carriage returns between lines
$Bodytext = -join($Bodytext, '<br />', $Data); #text lines get an html carriage return
$Bodyhtml = -join($Bodyhtml, $Data); #html lines don't get an html carriage return added, shouldnt need it
Write-Host "body data found - " $Data
} #end accumulate the remaining lines into the $bodytext string with '<br />' as carriage returns between lines
} #end foreach - done processing this text file
$CurrentDateAndTime = $(get-date -f yyyy-MM-dd-HH-mm-ss) #date format to add to the front of the filename when moving/renaming
$OldFileName = $files[$i].FullName
$NewFileName = [io.path]::GetFileName($OldFileName) #just the filename without the path for rename later
Write-Host "OldFileName - " $OldFileName
if ( ($To -eq "found") -and ($Subject -eq "found") -and ($Body -eq "found") )
{ #if all parts found, add body text to the new email message and send it off
if ( ( $Bodyhtml -Match "<" ) -and ( $Bodyhtml -Match ">" ) ) {
$EmailMessage.Body.Text = $Bodyhtml } else {
$EmailMessage.Body.Text = $Bodytext
} #end if html set body.text to html, otherwise set to bodytext with '<br />' crlf's added
$EmailMessage.SendAndSaveCopy($SentItems.Id)
Add-Content $files[$i].FullName "EMAIL-SENT-AT: $CurrentDateAndTime"
Add-Content $errorLog $date": Email File Sent Successfully:"$OldFileName
$NewFileName = -join($CurrentDateAndTime, "-$i-", $NewFileName)
} Else { #add error messages to output file, and select a name that includes NOTSENT so it is obvious
Add-Content $errorLog $date": NOTSENT-ERROR in file: "$OldFileName
if ( $To -eq "empty") {
Add-Content $files[$i].FullName "NOTSENT-ERROR: NO TO: LINE FOUND"
Add-Content $errorLog $date": NOTSENT-ERROR: NO TO: LINE FOUND"
}
if ( $Subject -eq "empty") {
Add-Content $files[$i].FullName "NOTSENT-ERROR: NO SUBJECT: LINE FOUND"
Add-Content $errorLog $date": NOTSENT-ERROR: NO SUBJECT: LINE FOUND"
}
if ( $Body -eq "empty") {
Add-Content $files[$i].FullName "NOTSENT-ERROR: NO BODY: LINE FOUND"
Add-Content $errorLog $date": NOTSENT-ERROR: NO BODY: LINE FOUND"
}
$EmailMessage.Delete #delete new composition if it is not set up properly
$NewFileName = -join($CurrentDateAndTime, "-$i-NOTSENT-", $NewFileName) #note in filename if email was not sent
} #end send-or-not-to-send if
Write-Host "NewFileName - " $NewFileName
#rename and move the file with the date and time it was sent (or tried to be sent)
$NewFileName = -join($SaveFolder, $NewFileName)
Move-Item $OldFileName $NewFileName
$EmailMessage.ToRecipients.Clear() ##BenH - clears ToRecipients before next file is processed so they don't accumulate
} #end for $i loop for each filename found
} #end if there are files to process
if ($? -eq $false)
{
Add-Content error.log $date": error sending message. Script terminated."
exit
}
#Delete items from the Sent Items folder that are too old
#Get the ID of the folder to move to, by searching up from the mailbox root folder
$RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service, [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
$fvFolderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(1)
$fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
$SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,"Sent Items")
$findFolderResults = $RootFolder.FindFolders($SfSearchFilter,$fvFolderView) #search from mailbox root folder for a folder named Sent Items
if ($? -eq $false)
{
#Write-Host 'Unable to locate destination folder:' $findFolderResults
Add-Content error.log $date": Unable to locate the Sent Items folder for cleanup. Script terminated."
exit
}
$sentitemsfolder = $findFolderResults.Folders[0] #save reference to the Sent Items folder in $sentitemsfolder
$SfSearchFilterI = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,"Inbox")
$findFolderResults = $RootFolder.FindFolders($SfSearchFilterI,$fvFolderView) #search from mailbox root folder for a folder named Inbox
if ($? -eq $false)
{
#Write-Host 'Unable to locate destination folder:' $findFolderResults
Add-Content error.log $date": Unable to locate the Inbox folder for cleanup. Script terminated."
exit
}
$inboxfolder = $findFolderResults.Folders[0] #save reference to the Inbox folder in $inboxfolder
#search through the Sent Items and Inbox folders for items older than $purgebeforedate and soft delete them
$puItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(500, 0, [Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
$puItemView.PropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly, [Microsoft.Exchange.WebServices.Data.ItemSchema]::Subject, [Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived)
$puItemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Shallow
#Sent Items scan for old items to delete
$puItems = $null #init FindItems results to null before executing find
do
{ #start do
$puItems = $service.FindItems($sentitemsfolder.Id,$puItemView) #find all items in Sent Items
if ($puItems.Items.Count -gt 0)
{ #if Sent Items folder not empty, inspect all items found
foreach($Item in $puItems.Items)
{
if ($item.datetimereceived -le $PurgeBeforeDate) #compare email date to purge cutoff date
{
#Write-Host "Delete Item with date time received" $item.DateTimeReceived
[void]$item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::SoftDelete)
#Note: exchange email internal date is in format of : 5/31/2016 8:02:19 AM (get-date -format G)
} #end compare email date to purge cutoff date
} #end foreach $Item in $puItems.items
} #end if Sent Items folder not empty
$puItemView.Offset += $puItems.Items.Count
} #end do
while($puItems.MoreAvailable -eq $true)
#Inbox scan for old items to delete
$puItems = $null #init FindItems results to null before executing find
do
{ #start do
$puItems = $service.FindItems($inboxfolder.Id,$puItemView) #find all items in Sent Items
if ($puItems.Items.Count -gt 0)
{ #if Sent Items folder not empty, inspect all items found
foreach($Item in $puItems.Items)
{
if ($item.datetimereceived -le $PurgeBeforeDate) #compare email date to purge cutoff date
{
#Write-Host "Delete Item with date time received" $item.DateTimeReceived
[void]$item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::SoftDelete)
#Note: exchange email internal date is in format of : 5/31/2016 8:02:19 AM (get-date -format G)
} #end compare email date to purge cutoff date
} #end foreach $Item in $puItems.items
} #end if Sent Items folder not empty
$puItemView.Offset += $puItems.Items.Count
} #end do
while($puItems.MoreAvailable -eq $true)
#script done
Add-Content $errorLog $date": Email Service script finished."
exit
##############################################################################
And here are some sample email input files, where lines start with to:, subject:, and the first line of the message body starts with body:
Sample File 1:
to:sam.lemon#lemonade.org
subject:ews mail test
body:testing line 1
testing line 2
testing line 3
testing line 4
done testing
Sample File 2:
to:bill.hickock#wild.west.org
subject:ews mail test
body:testing line 1
testing line 2
testing line 3
testing line 4
done testing
With these two sample files, the first one would be sent to sam.lemon#lemonade.org as expected. But the second one would go to both sam.lemon#lemonade.org and bill.hickock#wild.west.org, which it should not, since the second file is only for bill.hickock#wild.west.org.
Thanks in advance!
Zistrosk
#BenH - Here's what I coded and what came out in white on the Powershell ISE console. No errors or warnings, just these info messages, and the email addressees were not cleared or removed, same symptoms of the problem persisted. Does my coding look right?
$EmailMessage.ToRecipients.Clear
MemberType : Method
OverloadDefinitions : {void Clear()}
TypeNameOfValue : System.Management.Automation.PSMethod
Value : void Clear()
Name : Clear
IsInstance : True
$EmailMessage.ToRecipients.Remove
MemberType : Method
OverloadDefinitions : {bool
Remove(Microsoft.Exchange.WebServices.Data.EmailAddress
emailAddress)}
TypeNameOfValue : System.Management.Automation.PSMethod
Value : bool
Remove(Microsoft.Exchange.WebServices.Data.EmailAddress
emailAddress)
Name : Remove
IsInstance : True
Note: added the following line above, thanks to #BenH, which fixes the issue and clears the ToRecipients array before the loop iterates and the next file is read, preventing an unwanted accumulation of TO addresses:
$EmailMessage.ToRecipients.Clear() ##BenH - clears ToRecipients before next file is processed so they don't accumulate

Error Message from Powershell Script mail to EmailAddress

I have constructed an script for our Dataloader in Salesforce:
**# The Function for the Check Error File**
function CheckErrorFile {
Param ([string]$perrorfilepath)
$csvlines = (Get-Content $perrorfilepath | Measure-Object -line).Lines
# header is always 1 row so we look for > 1 rows
if ($csvlines -gt 1)
{
$errorCount = $csvLines - 1
$errorMessage = [string]::Format("** errors ** {0} failed rows in {1}", $errorCount, $perrorfilepath)
# this will print red error message but not stop execution
write-error $errorMessage
}
}
**# set up locations/variables for data loader**
$dataloader = "C:\Program Files (x86)\salesforce.com\Data Loader\bin\process.bat"
$dlconfig = "U:\Projekte\Salesforce\Dataloader-Test-Infor-Files\Example-Migration-SF-Test\DataLoaderConfig"
set-location "C:\Program Files (x86)\salesforce.com\Data Loader\bin"
$dlLogFolder = "U:\Projekte\Salesforce\Dataloader-Test-Infor-Files\Example-Migration-SF-Test\Logs\"
**# execute the data loader**
& $dataloader $dlconfig testsf-delete
**# check for errors in the error.csv file**
CheckErrorFile ("{0}error_testsf.csv" -f $dlLogFolder)
So far so good it think
But what i want to is that the Error Code which comes Back from CheckErrorFile Command should sent to an Email Address.
So i had think about 2 Considerations:
when if error sent to mail
pack the error output in a variable and sent to mail adress
But i'm not so good at Powershell that i know how i have to integrate this in my script.
I found a site with an send trigger when a eventlog has changed but I'm not sure if i can use this for my purposes and how i have to integrate this:
Powershell Email when Eventlog is Changed
May someone could help
thnks

perform nslookup from PowerShell

I'm writing a powershell to exact the ip from a server name, which need me to embed the nslookup code into my powershell
how can I do the intergrating work?
Can any body help me?
Add-PSSnapin Microsoft.SharePoint.PowerShell
$web = Get-SPWeb -Identity “http://nycs00058260/sites/usitp“
$server_status = "PROD"
$list=$web.Lists[”DNS_Status”]
$items = $list.items
Foreach($item in $items){
$item_name = $item["Server_name"] #need to get the ip by this name
/*nslook up*/
$item_name.update()
}
If you install the PSCX module, it comes with a cmdlet Resolve-Host which handles name lookups.
Absent that, this one-liner will do the job
[System.Net.Dns]::GetHostAddresses("www.msn.com")
You can also pass in an IP address - but the results will be different.
See also http://blogs.msdn.com/b/powershell/archive/2006/06/26/647318.aspx & http://powershell.com/cs/media/p/210.aspx
PowerShell 3.0 on Windows 8 and higher comes with a Resolve-DnsName cmdlet that will get this information:
(Resolve-DnsName $server_name)[0].IpAddress
Simply use :
Resolve-DnsName monServer | ? { # make selection here } | % { $_.IPAdress } | select-object -first 1
#Here is a far better method for nslookup
# HOWTO ensure an nslookup results no errors but still gives the original names and column separations
$day = Get-Date -Format yyyyMMdd #<-- HOWTO set the day variable for today
$ErrorActionPreference = ‘SilentlyContinue’ #<-- HOWTO turn off error messages
$WarningActionPreference = 'SilentlyContinue' #<-- HOWTO turn off warning messages
$servers = Get-Content .\input\servers.txt
Foreach ($server in $servers){
$result = Resolve-DnsName $server -Server $env:LOGONSERVER.Remove(0,2) -Type ALL #<-- NSLOOKUP using your logon server
write-host ($server+","+$result.Name+","+$result.IPAddress) #<-- HOWTO Write two variables separated by a comma
}
$ErrorActionPreference = ‘SilentlyContinue’ #HOWTO turn on error messages
$WarningActionPreference = 'SilentlyContinue' #HOWTO turn on warning messages

Expressions are only allowed as the first element of a pipeline

I'm new at writing in powershell but this is what I'm trying to accomplish.
I want to compare the dates of the two excel files to determine if one is newer than the other.
I want to convert a file from csv to xls on a computer that doesn't have excel. Only if the statement above is true, the initial xls file was copied already.
I want to copy the newly converted xls file to another location
If the file is already open it will fail to copy so I want to send out an email alert on success or failure of this operation.
Here is the script that I'm having issues with. The error is "Expressions are only allowed as the first element of a pipeline." I know it's to do with the email operation but I'm at a loss as to how to write this out manually with all those variables included. There are probably more errors but I'm not seeing them now. Thanks for any help, I appreciate it!
$CSV = "C:filename.csv"
$LocalXLS = "C:\filename.xls"
$RemoteXLS = "D:\filename.xls"
$LocalDate = (Get-Item $LocalXLS).LASTWRITETIME
$RemoteDate = (Get-Item $RemoteXLS).LASTWRITETIME
$convert = "D:\CSV Converter\csvcnv.exe"
if ($LocalDate -eq $RemoteDate) {break}
else {
& $convert $CSV $LocalXLS
$FromAddress = "email#address.com"
$ToAddress = "email#address.com"
$MessageSubject = "vague subject"
$SendingServer = "mail.mail.com"
$SMTPMessage = New-Object System.Net.Mail.MailMessage $FromAddress, $ToAddress, $MessageSubject, $MessageBody
$SMTPClient = New-Object System.Net.Mail.SMTPClient $SendingServer
$SendEmailSuccess = $MessageBody = "The copy completed successfully!" | New-Object System.Net.Mail.SMTPClient mail.mail.com $SMTPMessage
$RenamedXLS = {$_.BaseName+(Get-Date -f yyyy-MM-dd)+$_.Extension}
Rename-Item -path $RemoteXLS -newname $RenamedXLS -force -erroraction silentlycontinue
If (!$error)
{ $SendEmailSuccess | copy-item $LocalXLS -destination $RemoteXLS -force }
Else
{$MessageBody = "The copy failed, please make sure the file is closed." | $SMTPClient.Send($SMTPMessage)}
}
You get this error when you are trying to execute an independent block of code from within a pipeline chain.
Just as a different example, imagine this code using jQuery:
$("div").not(".main").console.log(this)
Each dot (.) will chain the array into the next function. In the above function this breaks with console because it's not meant to have any values piped in. If we want to break from our chaining to execute some code (perhaps on objects in the chain - we can do so with each like this:
$("div").not(".main").each(function() {console.log(this)})
The solution is powershell is identical. If you want to run a script against each item in your chain individually, you can use ForEach-Object or it's alias (%).
Imagine you have the following function in Powershell:
$settings | ?{$_.Key -eq 'Environment' } | $_.Value = "Prod"
The last line cannot be executed because it is a script, but we can fix that with ForEach like this:
$settings | ?{$_.Key -eq 'Environment' } | %{ $_.Value = "Prod" }
This error basically happens when you use an expression on the receiving side of the pipeline when it cannot receive the objects from the pipeline.
You would get the error if you do something like this:
$a="test" | $a
or even this:
"test" | $a
I don't know why are trying to pipe everywhere. I would recommend you to learn basics about Powershell pipelining. You are approaching it wrong. Also, I think you can refer to the link below to see how to send mail, should be straight forward without the complications that you have added with the pipes : http://www.searchmarked.com/windows/how-to-send-an-email-using-a-windows-powershell-script.php