EventID 4740 report using powershell and suppress email delivery if empty - powershell

I have a script that gets all the information from the security log and has the event ID 4740. It then creates an html report from that and emails it. What I want to do is to exit the script if there is no data returned from $event.
# Created by Brad Tostenson 1/13/17
# This script will gather all the events with event ID 4740 (Account Locked Out)
# creates a report in HTML and emails it to the System Admins as the body
# of the email.
# Varaible the sets the reports temporary location
$LockedOut= "c:\temp\LockedOut.html"
# Setup date
$Date = Get-Date
# Sets up the report
$HTML=#"
<title>Account locked out Report</title>
<!--mce:0-->
"#
# Setup variables for the information to go under the headers in the report
$Account_Name = #{n='Account Name';e={$_.ReplacementStrings[-1]}}
$Account_domain = #{n='Account Domain';e={$_.ReplacementStrings[-2]}}
$Caller_Computer_Name = #{n='Caller Computer Name';e={$_.ReplacementStrings[-1]}}
# Pulls the information from the log
$event = Get-EventLog -LogName Security -InstanceId 4740 -after $date.AddHours(-24) |
Select TimeGenerated,ReplacementStrings,"Account Name","Account Domain","Caller Computer Name" |
% {
New-Object PSObject -Property #{
"Account Name" = $_.ReplacementStrings[-7]
"Account Domain" = $_.ReplacementStrings[5]
"Caller Computer Name" = $_.ReplacementStrings[1]
Date = $_.TimeGenerated
}
}
$event | ConvertTo-Html -Property "Account Name","Account Domain","Caller Computer Name",Date -head $HTML -body "<H2> The Following User Accounts Were Locked In Active Directory</H2>"|
Out-File $LockedOut -Append
# Takes the report and adds the information to the body of the email and sends it to the System Admins
$MailBody= Get-Content $LockedOut
$MailSubject= "Account Lock Report"
$SmtpClient = New-Object system.net.mail.smtpClient
$SmtpClient.host = "smtp.ourdomain.com"
$MailMessage = New-Object system.net.mail.mailmessage
$MailMessage.from = "LockedOut#ourdomain.com"
$MailMessage.To.add("email#ourdomain.com")
$MailMessage.Subject = $MailSubject
$MailMessage.IsBodyHtml = 1
$MailMessage.Body = $MailBody
$SmtpClient.Send($MailMessage)
del c:\temp\LockedOut.html

I don't see why $event wouldn't be null if Get-EventLog returns no data, and null has a Boolean value of false. That means you can just do this:
if (!$event) { exit }
Or, if it makes more sense, you can do this:
if ($event) {
$event | ConvertTo-Html -Property "Account Name","Account Domain","Caller Computer Name",Date -head $HTML -body "<H2> The Following User Accounts Were Locked In Active Directory</H2>"|
Out-File $LockedOut -Append
# Takes the report and adds the information to the body of the email and sends it to the System Admins
$MailBody= Get-Content $LockedOut
$MailSubject= "Account Lock Report"
$SmtpClient = New-Object system.net.mail.smtpClient
$SmtpClient.host = "smtp.ourdomain.com"
$MailMessage = New-Object system.net.mail.mailmessage
$MailMessage.from = "LockedOut#ourdomain.com"
$MailMessage.To.add("email#ourdomain.com")
$MailMessage.Subject = $MailSubject
$MailMessage.IsBodyHtml = 1
$MailMessage.Body = $MailBody
$SmtpClient.Send($MailMessage)
del c:\temp\LockedOut.html
}
Also, you might want to look at using the Send-MailMessage cmdlet. It's much simpler that what you're doing.
You also don't technically need the $LockedOut file at all, but I'm guessing you're using that in case the network is down or the email fails for some reason.

Related

How to send emails based on my data. csv file

I do a monthly report on inactive accounts and would like to send an email to the managers who are responsible for the service accounts to notify them that the accounts have not been used for more than 30 days based on my data. csv file. When I run my script it only shows one user in the email for each entry in the csv file.
$Users =
#"
Manager;inactiveADAccount
manager_1#domain.com;test_1;test_2;test_3
manager_2#domain.com.ca;test_4
"# | ConvertFrom-Csv -Delimiter ";"
ForEach($Manager in $Users) {
$bodytxt = #"
Hello,
Please verify users below
$($Manager.inactiveADAccount)
"#
$Message = New-Object System.Net.Mail.MailMessage
$Message.From = New-Object System.Net.Mail.MailAddress 'me#domain.com'
$To = $Manager.Manager
$Object = "Inactive Accounts"
$body = $bodytxt
$SMTPServer = 'mail.domain.com'
Send-MailMessage -From $Message.From -To $To -Subject $Object -Body $body -BodyAsHtml -SmtpServer $SMTPServer -Priority High -Encoding UTF8
}
Your CSV file is not constructed correctly.
See the results of your CSV File:
Manager inactiveADAccount
------- -----------------
manager_1#domain.com test_1
manager_2#domain.com.ca test_4
As you can see test_2 and test_3 users are omitted from the csv...
Use one row for each user and manager, this should be:
$Users =
#"
Manager;inactiveADAccount
manager_1#domain.com;test_1
manager_1#domain.com;test_2
manager_1#domain.com;test_3
manager_2#domain.com.ca;test_4
"# | ConvertFrom-Csv -Delimiter ";"
Which now includes all the users:
Manager inactiveADAccount
------- -----------------
manager_1#domain.com test_1
manager_1#domain.com test_2
manager_1#domain.com test_3
manager_2#domain.com.ca test_4
Then Group The results and send the mail:
foreach ($Manager in $Users | group Manager) {
$bodytxt = #"
Hello,
Please verify users below
$($Manager.Group.inactiveADAccount | Out-String)
"#
[...]

Use found Data from Windows ID and put it into an email through Powershell

I'm trying to create a Powershell script that gets the specific part of a text file, read it and then put it into the contents of an email and sends it. This is what I currently have:
$logs = (Get-EventLog system | where {$_.InstanceId -eq 7001 -and
$_.TimeWritten -gt (Get-Date).Adddays(-1)}).TimeWritten | Out-String
#to file
$logs | Out-File ".\Results.txt"
#create COM object named Outlook
$Outlook = New-Object -ComObject Outlook.Application
#create Outlook MailItem named Mail using CreateItem() method
$Mail = $Outlook.CreateItem(0)
#add properties as desired
$Mail.To = "SomeMailAddress.com"
$Mail.Subject = "Time"
$Mail.Body = $logs
#send message
$Mail.Send()
#quit and cleanup
$Outlook.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Outlook) | Out-Null
I'm able to create the text file, output the data, I believe the Get-Content is getting that specific portion of time but I'm not sure how to use Set-Content and put that into the email. Any suggestions/help would be appreciated
The simplest way to send email through PowerShell is by using Send-MailMessge.
Below is how you would send using the Outlook ComOjbect.
Note: If you have to use the outlook comobject, make sure you run PowerShell and outlook the same way with the same account.
Example:
$logs = (Get-EventLog system | where {$_.InstanceId -eq 7001 -and $_.TimeWritten -gt (Get-Date).Adddays(-1)}).TimeWritten | Out-String
#create COM object named Outlook
$Outlook = New-Object -ComObject Outlook.Application
#create Outlook MailItem named Mail using CreateItem() method
$Mail = $Outlook.CreateItem(0)
#add properties as desired
$Mail.To = "jrider#yourDomain.com"
$Mail.Subject = "Time"
$Mail.Body = $logs
#send message
$Mail.Send()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Outlook) | Out-Null

Sorting, automatically adding an attachment to an email, and forwarding an email

What I am hoping to do is once a new email hits a folder containing a specific subject. That email is then forwarded to another inbox and a specific file is automatically attached. The code I have been able to cobble together so far is this. Is a .Net method the appropriate method to achieve my goal or would using Send-MailMessge to a hastable be better method? I am new to PowerShell code I am able to get both methods to work. But was wondering A. which method is preferred. B. is there a better/more efficient way?
#####Define Variables########
$fromaddress = "donotreply#fromemail.com"
$toaddress = "blah#toemail.com"
$bccaddress = "blah#bcc.com"
$CCaddress = "blah#cc.com"
$Subject = "ACtion Required"
$body = get-content .\content.htm
$attachment = "C:\sendemail\test.txt"
$smtpserver = "smtp.labtest.com"
##############################
$message = new-object System.Net.Mail.MailMessage
$message.From = $fromaddress
$message.To.Add($toaddress)
$message.CC.Add($CCaddress)
$message.Bcc.Add($bccaddress)
$message.IsBodyHtml = $True
$message.Subject = $Subject
$attach = new-object Net.Mail.Attachment($attachment)
$message.Attachments.Add($attach)
$message.body = $body
$smtp = new-object Net.Mail.SmtpClient($smtpserver)
$smtp.Send($message)
(Example of the hashtable method
$emailHashSplat = #{ To = $toAddress
From = $fromAddress
Subject = $emailSubject
Body = $emailBody SMTPServer = $smtpServer BodyAsHtml =
$true Attachments = "C:\sendemail\test.txt" # Attachments =)
Stick with Powershell commands whenever possible, .NET might be faster in some cases but it is a best practice to use only Powershell commands when possible.
Also make sure your code is easy to read and understand by others, using hastables with splatting wil help with this, see the following example: Github Gist Link (Click Me!)
Copy of the code, in case of link failure.
### Script Global Settings
#Declare SMTP Connection Settings
$SMTPConnection = #{
#Use Office365, Gmail, Other or OnPremise SMTP Relay FQDN
SmtpServer = 'outlook.office365.com'
#OnPrem SMTP Relay usually uses port 25 without SSL
#Other Public SMTP Relays usually use SSL with a specific port such as 587 or 443
Port = 587
UseSsl = $true
#Option A: Query for Credential at run time.
Credential = Get-Credential -Message 'Enter SMTP Login' -UserName "emailaddress#domain.tld"
<#
#Option B: Hardcoded Credential based on a SecureString
Credential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList #(
#The SMTP User Emailaddress
"emailaddress#domain.tld"
#The Password as SecureString encoded by the user that wil run this script!
#To create a SecureString Use the folowing Command: Read-Host "Enter Password" -AsSecureString | ConvertFrom-SecureString
"Enter the SecureString here as a single line" | ConvertTo-SecureString
)
#>
}
### Script Variables
#Declare Mailmessages.
$MailMessageA = #{
From = "emailaddress#domain.tld"
To = #(
"emailaddress#domain.tld"
)
#Cc = #(
# "emailaddress#domain.tld"
#)
#Bcc = #(
# "emailaddress#domain.tld"
#)
Subject = 'Mailmessage from script'
#Priority = 'Normal' #Normal by default, options: High, Low, Normal
#Attachments = #(
#'FilePath'
#)
#InlineAttachments = #{
#'CIDA'='FilePath'
#} #For more information about inline attachments in mailmessages see: https://gallery.technet.microsoft.com/scriptcenter/Send-MailMessage-3a920a6d
BodyAsHtml = $true
Body = "Something Unexpected Occured as no Content has been Provided for this Mail Message!" #Default Message
}
### Script Start
#Retrieve Powershell Version Information and store it as HTML with Special CSS Class
$PSVersionTable_HTLM = ($PSVersionTable.Values | ConvertTo-Html -Fragment) -replace '<table>', '<table class="table">'
#Retrieve CSS Stylesheet
$CSS = Invoke-WebRequest "https://raw.githubusercontent.com/advancedrei/BootstrapForEmail/master/Stylesheet/bootstrap-email.min.css" | Select-Object -ExpandProperty Content
#Build HTML Mail Message and Apply it to the MailMessage HashTable
$MailMessageA.Body = ConvertTo-Html -Title $MailMessageA.Subject -Head "<style>$($CSS)</style>" -Body "
<p>
Hello World,
</p>
<p>
If your recieved this message then this script works.</br>
</br>
<div class='alert alert-info' role='alert'>
Powershell version
</div>
$($PSVersionTable_HTLM)
</P>
" | Out-String
#Send MailMessage
#This example uses the HashTable's with a technique called Splatting to match/bind the Key's in the HashTable with the Parameters of the command.
#Use the # Symbol instead of $ to invoke Splatting, Splatting improves readability and allows for better management and reuse of variables
Send-MailMessage #SMTPConnection #MailMessageA

Send Email alert on low Disk or RAM

I tried several Scripts to email the status of HDD or RAM but its not working,
1st Time using PowerShell.
Windows Server 2012 R2
Script would get triggered by the event (when the Memory is low) and send the email with the details.
to get the Disk Stats i used
Get-EventLog -LogName System | Where-Object {$_.EventID -eq 2013}
How can i add this event to the email and make it appear in the message, i tried giving it a name like
$event Get-EventLog -LogName System | Where-Object {$_.EventID -eq 2013}
but i don't know how to add it to the message body its not like java or
$message.body = $body + $event
to send email this script works,
$SMTPServer = "smtp.gmail.com"
$SMTPPort = "587"
$Username = "username#gmail.com"
$Password = "zxc"
$to = "help#x.com"
$cc = "help#x.ae"
$subject = "Low Disk Space"
$body = "The Server Disk is Low on memory"
$message = New-Object System.Net.Mail.MailMessage
$message.Subject = $subject
$message.Body = $body
$message.To.add($to)
$message.Cc.add($cc)
$message.From = $username
$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSSL = $true
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.Send($message)
Write-Host "Mail Sent"
I read that the email alerts were discontinued by MS but people still have ways of doing it, unfortunately I didn't get it to work.
Something to help you get started on this:
# We first need to know which command to use
Get-Command '*mail*'
# We use the splatting technique to provide the parameters
$Params = #{
SmtpServer = 'smtp.gmail.com'
Port = '587'
From = $username
To = 'help#x.com'
Cc = 'help#x.ae'
Subject = 'Low Disk Space'
Body = 'The Server Disk is Low on memory.'
}
# Get-Help explains what this CmdLet does
Get-Help Send-MailMessage
# Get-Help can also give you examples on how to use the CmdLet
Get-Help Send-MailMessage -Examples
# Retrieve only events of the last 24 hours and select the first one
$Today = Get-Date
$Past = $Today.AddDays(-1)
$Event = Get-EventLog -LogName System -After $Past | Where-Object {$_.EventID -eq 6013} | Select-Object -First 1
# Add the event to the mail body
$Params.Body += ' ' + $Event.Message
# Send the mail
Send-MailMessage #Params
This script can then be added to the Task-Scheduler to run once a day.
SCHTASKS /Create /RU "SYSTEM" /SC DAILY /ST 17:30 /TN DailyHDDMemReport /TR "powershell -NoProfile -NoLogo -ExecutionPolicy Unrestricted -File 'C:\Temp\file.ps1'" /F
This will create a daily task that runs a powershell script specified at 5:30 as SYSTEM in conjunction with DarkLite1's answer.
After many writing may scripts and working on it, due to increased requirements script after script, the feature requirement made me in the end use the free option of the following tool.
Has more than needed
https://www.manageengine.com/network-monitoring/
Regards

Powershell script for Soon-to-expire AD users

So basically, what I have here is a script that will scan a CSV that it imports, and for every entry in the spreadsheet, except for people in the RANDOM.DOMAIN, it will find the managers email address and send an automated email to the manager telling them user XYZ is about to expire soon, and they need to do something about it.
If the managers email is unavailable for some reason, then it defaults to sending the email to me.
This script works well.
The problem I am running into is, I want to make it so only one email is sent to each manager, despite multiple users (or entries) from the spreadsheet, list them as the manager.
I.e. if Joe Bloggs has a manager Aaron T and Jane Doe has the manager Aaron T, then Aaron T will get two emails, one email for each user.
MY QUESTION:
Is there an easy way to only get it to send one email per manager, even if that manager has multiple users reporting to them that are about to expire?
$datai = Import-Csv "Soon-to-expire User Accounts22.csv" | select 'Display Name',Manager,'Domain Name','Account Expiry Time'
Connect-QADService -Service another.DC | Out-Null
$expiringUsers = #{}
foreach ($i in $datai) {
$dn = $i.'Display Name'
$dn1 = $i.'Domain Name'
$man = $i.'Manager'
$aet = $i.'Account Expiry Time'
$subject = "Account about to expire: $dn"
$getmail = get-qaduser "$man" -LdapFilter '(mail=*)' | select mail
$emailAD = $getmail.mail
if ($man -eq "-" -or $man -like 'CN=*' -or $getmail -eq $null -or $man -eq "") {
$man = "Aaron T"
$getmail = get-qaduser "$man" -LdapFilter '(mail=*)' | select mail
$emailAD = $getmail.mail
}
if ($expiringUsers.Contains($emailAD)) {
$expiringUsers[$emailAD]["dn"] += $dn += "`n"
$expiringUsers[$emailAD]["aet"] += $aet += "`n"
$expiringUsers[$emailAD]["man"] += $man += "`n"
} else {
$expiringUsers[$emailAD] = #{
#"dn1" = $dn1
#"aet" = $aet
#"man" = $man
# "dn" = #( $dn )
}
}
}
$expiringUsers | fc #as suggested
foreach ($emailAD in $expiringUsers.Keys) {
$dn = $expiringUsers[$emailAD]["dn"]
$dn1 = $expiringUsers[$emailAD]["dn1"]
$man = $expiringUsers[$emailAD]["man"]
$aet = $expiringUsers[$emailAD]["aet"]
$subject = "Account/s About to Expire!"
$content = #"
Hi,
$dn `n
$dn1 `n
$man `n
$aet `n
$emailAD `n
Technology Services
"#
Send-MailMessage -from "aaron#website.com" `
-To $emailAD `
-Subject $subject `
-Body $content `
-Priority high `
-smtpServer "relay.server"
#using this as a test instead of sending mass emais all the time
Write-Host $content
}
UPDATED with the new script as requested.... still having issues.
Is there an easy way to only get it to send one email per manager, even if that manager has multiple users reporting to them that are about to expire?
For this you need to defer e-mail processing. Collect the users in a hashtable, e.g. by manager e-mail address:
...
$expiringUsers = #{}
foreach ($i in $datai) {
If ($i.'Domain Name' -notmatch "RANDOM.DOMAIN") {
...
if ($expiringUsers.Contains($emailAD)) {
$expiringUsers[$emailAD]["dn"] += $dn
} else {
$expiringUsers[$emailAD] = #{
"dn1" = $dn1
"aet" = $aet
"man" = $man
"dn" = #( $dn )
}
}
}
}
and move the actual e-mail processing outside the loop:
foreach ($emailAD in $expiringUsers.Keys) {
$dn1 = $expiringUsers[$emailAD]["dn1"]
$man = $expiringUsers[$emailAD]["man"]
$aet = $expiringUsers[$emailAD]["aet"]
$subject = "Account about to expire: $($expiringUsers[$emailAD]["dn"])"
$content = #"
Hi,
...
Technology Services
"#
Send-MailMessage -from "Test Script - Powershell <email#test.com>" `
-To "$emailAD" `
-Subject $subject `
-Body $content `
-Priority high `
-smtpServer servername
Write-Host "Mail Sent to $man"
}
Note that for simplicity reasons the above code only records the expiry date of the first user. If you want the expiry date of each user recorded separately, you'll have to take additonal steps, e.g.
$expiringUsers[$emailAD]["expiry"] += #{
"name" = $dn;
"date" = $aet;
}
instead of
$expiringUsers[$emailAD]["dn"] += $dn
So I finally decided to revisit this script, after many, many months.
I'm get a little better at PowerShell and while I'm sure this isn't the most effective way to do it, this is something that works for me.
I've also changed the input method; it pulls the information directly from AD, instead of using a CSV file that used to be generated from an application called 'AD Manager Plus' (Hate it).
Remember, using Quest CMDlets here because we don't have a 2008 environment. (so using Get-QADUser instead of Get-ADuser)
FYI, I have only posted the code here which sorts out the data into separate tables - you can decide how you want to utilize those results. For our environment, I have it build an nice HTML table and body, then send it to the appropriate manager to deal with.
#user data input
$data = get-qaduser -SizeLimit 0 -includedproperties accountexpires | where {$_.AccountExpires -ne $null -and $_.AccountExpires -le ((Get-Date).AddDays(45)) }
#get a list of managers, unique.
$uniqueMan = $data | select Manager -Unique
#foreach manager from $uniqueman
Foreach ($manager in $uniqueman) {
#create the array variable / clear it out for the next manager.
$myarray = #()
#foreach User found in in $data query
Foreach ($user in $data) {
#Search the $user's query for people with the same manager as that of the $uniqueman list.
If ($user.Manager -eq $manager.Manager) {
#do what with the result.
#add the person to an array
$myarray += New-Object psobject -Property #{
Name = $user.'Name'
UserName = $user.'SAMAccountName'
AccountExpires = $user.'AccountExpires'
Manager = $user.Manager
}
}
#for testing, to output the results to an HTML file.
#$myarray | ConvertTo-Html | Out-File ("C:\test\" + $manager.Manager + ".html")
}
}