Powershell Script to Monitor Folder Space - powershell

I have written a script which monitors specific folder space monitoring and it alerts me once it crosses the threshold. Issue what i am facing is with multiple servers in place i am getting multiple emails so is it possible that we can consolidate the results in one mail having all servers which having issue. Below is script:-
$servers = Get-Content C:\server.txt
$folder = "D$\store\"
$smtpserver = "XXXXXX"
#(
foreach ($Server in $Servers)
{
$folderSize = ( Get-ChildItem -path \\$server\$folder -Recurse -Force | Measure-Object -Property Length -Sum ).Sum
if ($folderSize -gt '60Gb') {
Write-output "Folder size exceeded 60 GB on server:-$Server. Current size on $server is $folderSize. Please review and take further action." | Out-file -FilePath "C:\reslt.txt"
Send-MailMessage -to abc#abc.com -from xx#xx.com -Subject "Limit Alert" -SmtpServer $smtpserver -Attachments "C:\reslt.txt"
}
else { Write-output "Folder size is within 60GB on $server." }})

You should post what you've tried. That being said, here's a start, written in notepad, so excuse any slight issues. You should probably just take results and instead of iterating over them, make them into a table, set the email to be HTML, and put the result of the table converted into an HTML snippet into the body of the message.
$servers = Get-Content C:\server.txt
$folder = "D$\store\"
$smtpserver = "XXXXXX"
$results = #()
foreach ($Server in $Servers) {
$folderSize = ( Get-ChildItem -path \\$server\$folder -Recurse -Force | Measure-Object -Property Length -Sum ).Sum
if ($folderSize -gt '60Gb') {
Write-Output "Folder size exceeded limit on $server"
$results += New-Object -TypeName PSObject -Property #{
ServerName = $Server
FolderSize = $folderSize
}
}
else { Write-output "Folder size is within 60GB on $server." }
}
if ($results.Count -gt 0){
$bodyString = "Servers over limit:"
#I'd get fancy here and take $results, format it as a table, and then convert to HTML and put the snippet in the body...
$results | % {$bodyString += "The server "+$_.ServerName+"is over the limit and is currently at "+$_.FolderSize+"GB`r`n"}
Send-MailMessage -to abc#abc.com -from xx#xx.com -Subject "Limit Alert" -SmtpServer $smtpserver -Body $bodyString
}
else {
Send-MailMessage -to abc#abc.com -from xx#xx.com -Subject "Limit Alert - no results" -SmtpServer $smtpserver -Body "No results were found for servers over the limit"
}

Related

Powershell get process script not working correctly

I want this script to show me WinWord processes which are running currently and to send an email alert out. Currently the 1st script works if there are WinWord processes running and lists them in an email BUT if they are no WinWord processes running the script fails advising parameter is empty or null, cannot find process.
The second script works if there are no processes and doesn’t throw the null or empty parameter if there are no WinWord processes running but only shows true or false by email. Not the list of processes running. Also would like to add some write host in the script for the email output to say. Below are the number of WinWord’s Processes running.
I am a newbie to PowerShell. I am learning as I go. I also used -erroraction silentlycontinue for the first script but when outputting to email it still fails with empty or null parameter.
Below are the scripts.
Doesn't work if process is not running, cannot accept argument which is null or empty. If process is running works perfect.
$winwords = (get-process -ComputerName Dummyserver -Name Winword |where-object { $_.Count -gt 0})| Out-String
Send-MailMessage -To "Dummyemail.co.uk" -From " Dummyemail.co.uk" -Subject "WinWord Check" -SmtpServer " Dummy.priv" -Body $winwords
Works if argument is null or empty but an email alert is out which says false or true BUT doesn't display the amount of processes running like the first script.
$processname = "Winword"
$instanceisrunning = (get-process -ComputerName Dummyserver| where-object { $_.Name -eq $processname }).count -gt 0 | Out-String
Send-MailMessage -To "Dummyemail co.uk" -From "Dummyemail.co.uk" -Subject "WinWord Check" -SmtpServer " Dummyemail.priv" -Body $isrunning
You have the right idea using where-object to limit the processes to just the name you want. The logic you are using for count greater than zero will return a boolean ($true or $false), which is why you are seeing that output.
If you want to always send a message when this is run, you can do this:
$instances = (get-process -ComputerName Dummyserver | where-object { $_.Name -eq $processname })
if($instances.count -gt 0)
{
$message = $instances | Out-String
}
else
{
$message = "No instances running"
}
Send-MailMessage -To "Dummyemail co.uk" -From "Dummyemail.co.uk" -Subject "WinWord Check" -SmtpServer " Dummyemail.priv" -Body $message
If you only want a message sent when instances are running:
$instances = (get-process -ComputerName Dummyserver | where-object { $_.Name -eq $processname })
if($instances.count -gt 0)
{
Send-MailMessage -To "Dummyemail co.uk" -From "Dummyemail.co.uk" -Subject "WinWord Check" -SmtpServer " Dummyemail.priv" -Body ($instances | Out-String)
}
Edit: Adding code to work of multiple computers with one email
$computers = #("dummyserver","otherserver","nextserver")
$processName = "WinWord"
$message = "$processName is running on the following:"
foreach ($computer in $computers)
{
$message += "Hostname: $computer"
$instances = (Get-Process -ComputerName $computers | Where-Object { $_.Name -eq $processName })
if($instances.count -gt 0)
{
$message += ($instances | Out-String)
}
else
{
$message += "No instances running"
}
}
Send-MailMessage -To "Dummyemail co.uk" -From "Dummyemail.co.uk" -Subject "WinWord Check" -SmtpServer " Dummyemail.priv" -Body $message
you can use ErrorAction as silentlyContinue which will make your script run even if there is an error "NO Winword instances".
$winwords = (get-process -ComputerName Dummyserver -Name Winword -ErrorAction
SilentlyContinue |where-object { $_.Count -gt 0})| Out-String
if($winwords -eq ""){
$winwords = "No Instances"
}
Send-MailMessage -To "Dummyemail.co.uk" -From " Dummyemail.co.uk" -Subject "WinWord
Check" -SmtpServer " Dummy.priv" -Body $winwords

Sending foreach output via email

I have a script that is checking mirror status of databases. Output in Powershell is fine, but when I try to send it via mail, I'm getting "Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData" instead of data itself. I've tried to change it to Out-String but then I'm getting all results in one line. How this could be done to have formated output the same way as it is formated directly in PowerShell?
# rozszerzenie do obslugi
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null;
$mail_from = "xxx";
$mail_to = "xxx";
$mail_subject = "Status mirrorowanych baz";
$mail_encoding = "UTF8";
$mail_smtp = "xxx";
# lista serwerow
$list = #("SERVER01V",
"SERVER02V"
);
$output = foreach($server in $list)
{
$srv = New-Object "Microsoft.SqlServer.Management.Smo.Server" $server;
# pokaz tylko mirrorowane
$databases = $srv.Databases | Where-Object {$_.IsMirroringEnabled -eq $true};
Write-Output "<br>==================================<br>";
Write-Output $server;
Write-Output "<br>==================================<br>";
$databases | Select-Object -Property Name, MirroringStatus | Format-Table -AutoSize | Out-String;
Write-Output "<br>";
}
$mail_body = $output;
Send-MailMessage -To $mail_to -From $mail_from -Subject $mail_subject -SmtpServer $mail_smtp -Encoding $mail_encoding -Body $mail_body -BodyAsHtml
You're currently sending a HTML mail. As such line breaks won't matter. If you want line breaks in your mail you either need to use text format or replace line breaks with a <br /> or something similar. It's probably going to be wise to have manually add <br /> in your look to have a consistent pattern and replace.
Try gather all data into an array, then use ConvertTo-HTML cmdlet, and the 'BodyAsHTML' switch in Send-MailMessage
$DatabaseArray=#()
ForEach ($server in $list) {
$srv = New-Object "Microsoft.SqlServer.Management.Smo.Server" $server
$DatabaseArray += $srv.Databases | Where-Object {$_.IsMirroringEnabled -eq $true} | Select-Object Name,MirroringStatus
}
$HTMLBody = $DatabaseArray | ConvertTo-HTML
Send-MailMessage -subject x -body $HTMLBody -BodyAsHTML

Powershell Event Log Reporting

I'm working on a PS script to collect on certain Event IDs and spit them back every hour or so. I'm doing this because I want to spit it out to email for ease of reading.
My problem right now is that the script will run up to a certain point and hang. If I remove the date filters, it runs well. As soon as I re-add them (either through variables or hard-coded) it does it's hanging thing again. I need the date filters, however, because otherwise I'm getting everything when I'm looking for hourly chunks.
Looking for advice as I'd like to use variables for the one-offs or modifications! I'd also like to use spinoffs for other side reporting as well.
$filedate = (get-date).ToString("MM_dd_yy-hh_mm")
$filename = "bad_logins_DC01 " + $filedate + "_log.txt"
$after = (get-date).addhours(-1)
$before = (get-date)
$eventlog = Get-EventLog -ComputerName DC01 -LogName Security -After $after -Before $before
$eventlog | ?{$_.EventID -eq 4771 -or $_.EventID -eq 4776 } | Select #{Name="Event ID";Expression={$_.InstanceID}},#{Name="UserName";Expression={$_.ReplacementStrings[0]}},#{Name="Failure Code";Expression={$_.ReplacementStrings[4]}},#{Name="Host";Expression={$_.ReplacementStrings[6]}},#{Name="Port";Expression={$_.ReplacementStrings[7]}}, #{Name="Time";Expression={$_.TimeGenerated}} | ft | Out-File $filename -append -noclobber
$to = itsupport#ourdomain.com
$from = itsupport#ourdomain.com
$subject = $filename
$smtp = exchange.ourdomain.com
$contents = cat $filename
$body = $contents
Send-MailMessage -SmtpServer $smtp -To $to -From $from -Subject $subject -Body $body -Attachments $filename
Got it!
Big thanks to TheMadTechnicial for the assist on Get-WinEvent instead of Get-EventLog
$query_sec = #"
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">*`
[System[Provider[#Name='Microsoft-Windows-Security-Auditing'] and `
TimeCreated[timediff(#SystemTime) <= 7200000] and `
(EventID=4771 or EventID=4776)]]</Select>
</Query>
</QueryList>
"#
$time = (Get-Date).ToString("dd-MM_hh-mm")
$to = "itsupport#mydomain.com"
$from = "me#mydomain.com"
$smtpServer = "mxserver.mydomain.com"
Try
{
$domain_controller = "DC01","C02","DC03"
ForEach ($dc in $domain_controller)
{
$Events = Get-WinEvent -ComputerName $dc -FilterXml $query_sec -ErrorAction Stop
ForEach ($event in $Events)
{
$eventXML = [xml]$Event.ToXml()
For ($i=0; $i -lt $eventXML.Event.EventData.Data.Count; $i++)
{
Add-Member -InputObject $Event -MemberType NoteProperty -Force `
-Name $eventXML.Event.EventData.Data[$i].Name `
-Value $eventXML.Event.EventData.Data[$i].'#text'
}
}
$filename = "C:\temp\"+$dc+"_failed_logins_"+$time+".txt"
$Events | Select-Object #{Name="UID";Expression={$_.TargetUserName}},`
ServiceName,`
#{Name="Error";Expression={$_.Status}},`
#{Name="IP";Expression={$_.IpAddress}},`
#{Name="Port";Expression={$_.IpPort}},`
#{Name="Event ID";Expression={$_.ID}},`
#{Name="Source";Expression={$_.MachineName}}`
| FT `
| Out-File $filename
$subject = "Bad Logins on " + $dc + " at " + $time
$body = Get-Content -Path $filename -Raw
Send-MailMessage -To $to -Subject $subject -From $from -SmtpServer $smtpServer -Body $body
}
}
Catch [Exception]
{
Write-Output " "
Write-Output "$dc has no relevant event logs!"
}

Powershell search for longfile path names and send the owner of a file a mail to do something about it

I am trying to set up a PowerShell script which reads out a UNC path on Windows and searches for longfiles/paths. After it has found the file it needs to send a mail to the owner of the file.
I have found already a script and tweaked it a bit but it doesn't seems to work yet. The following script can find the long file path now but the mail is not working properly.
$limit = 90
$testpath = "C:\test"
$resultpath = "c:\test"
$admins = "moh#test.com"
$from = "moh#test.com"
$smtpserver = "smtp.office365.com"
Get-ChildItem -Path $testpath -Recurse | ?{$_.fullname.length -gt $limit} |
Select-Object fullname,
#{n="owner";e={
$_.GetAccessControl().GetOwner('System.Security.Principal.NTAccount')}},
#{n="namelength"; e={$_.fullname.length}} |
%{
Out-File -FilePath "$resultpath\Longfiles of $($_.owner -replace "\\","-").txt" -Append -InputObject "$($_.namelength) - $($_.fullname)"
}
Get-ChildItem $resultpath -Filter "longfiles of *" | % {
if($_.name -match "Longfiles\sof\s(.+)\.txt"){
$user = $matches[1] -replace "-","\"
$ntacc = New-Object System.Security.Principal.NTAccount($user)
$sid = $ntacc.Translate([System.Security.Principal.SecurityIdentifier])
$aduser = [ADSI]"LDAP://<SID=$sid>"
$email = $aduser.Properties.mail
if($email) {Send-MailMessage -Attachments $_.fullname -Body "Please change the filenames of the files listed in the attached file to shorter!"
-From $from -SmtpServer $smtpserver -Subject "System notice" -To
$email -cc $admins
}
else {
Send-MailMessage -Attachments $_.fullname -Body "email coudn't be sent to owner" `
-From $from -SmtpServer $smtpserver -Subject "System notice" -To $admins
}
}
else {Write-Host "Some error with file $_"}
}
EDIT: This is what i see, after running the script... it is asking me to fill in the fields, while the fields are already filled in in the script such as (From: moh#test.com to moh#test.com)
[]
You issue was because of line breaks in the middle of a command. In some lines, you had a backtick character which escapes the end of the line. But as you found these are really easy to break and that's why it's best practice to use splatting on commands with many parameters.
I also changed your Select-Object calculated properties into a more readable [pscustomobject] since they are hard to format in a readable way, but this does require PS3+.
$limit = 90
$testpath = "C:\test"
$resultpath = "c:\test"
$admins = "moh#test.com"
$from = "moh#test.com"
$smtpserver = "smtp.office365.com"
Get-ChildItem -Path $testpath -Recurse |
Where-Object {$_.fullname.length -gt $limit} |
ForEach-Object {
[PSCustomObject]#{
'fullname' = $_.fullname
'owner' = $_.GetAccessControl().GetOwner('System.Security.Principal.NTAccount')
'namelength' = $_.fullname.length
}
} |
ForEach-Object {
Out-File -FilePath "$resultpath\Longfiles of $($_.owner -replace "\\","-").txt" -Append -InputObject "$($_.namelength) - $($_.fullname)"
}
Get-ChildItem $resultpath -Filter "longfiles of *" | ForEach-Object {
if ($_.name -match "Longfiles\sof\s(.+)\.txt") {
$user = $matches[1] -replace "-", "\"
$ntacc = New-Object System.Security.Principal.NTAccount($user)
$sid = $ntacc.Translate([System.Security.Principal.SecurityIdentifier])
$aduser = [ADSI]"LDAP://<SID=$sid>"
$email = $aduser.Properties.mail
if ($email) {
$mailparams = #{
'Attachments' = $_.fullname
'Body' = "Please change the filenames of the files listed in the attached file to shorter!"
'From' = $from
'SmtpServer' = $smtpserver
'Subject' = "System notice"
'To' = $email
'cc' = $admins
}
Send-MailMessage #mailparams
} else {
$mailparams = #{
'Attachments' = $_.fullname
'Body' = "email coudn't be sent to owner"
'From' = $from
'SmtpServer' = $smtpserver
'Subject' = "System notice"
'To' = $admins
}
Send-MailMessage #mailparams
}
} else {
Write-Host "Some error with file $_"
}
}
Remove the line breaks or escape them with a backtick: `. Your script must actually look like this:
if ($email) {
Send-MailMessage -Attachments $_.fullname -Body "Please change the filenames of the files listed in the attached file to shorter!"
-From $from -SmtpServer $smtpserver -Subject "System notice" -To
$email -cc $admins
}
else {
Send-MailMessage -Attachments $_.fullname -Body "email coudn't be sent to owner" `
-From $from -SmtpServer $smtpserver -Subject "System notice" -To $admins
}
And Powershell doesn't know that the line beginning with -From is part of Send-MailMessage.

Get PrimaryStatus of DCIM_PhysicalDiskView and Put it in a variable in Powershell

I am trying to use the primaryStatus from the DCIM_PhysicalDiskView and compare it to 3 (degraded hard disk). If there is a match, an email will be sent to notify the admin. Here is the code:
$computerNames = Get-Content -Path C:\scripts\nameTest.txt
foreach ($computer in $computerNames) {
write-host "$computer"
$value = "3"
$smtpserver = "mailserver.xxxx.com" # your mail server here
$smtpFrom = "xx#xxxx.com" # your from address, mail server will most likely allow any
$smtpTo = "xx#xxxx.com" #your email address here. can also be an array and will send to all
$MessageSubject = "Testing Failed Disk in $computer" #whatever you want the subject to be.
gwmi -Namespace root\dcim\sysman -computername $computer -Class DCIM_PhysicalDiskView | ForEach {$name = $_.Name; $primaryStatus = $_.PimaryStatus}
if( $primaryStatus -contains $value )
{
Send-MailMessage -SmtpServer $smtpserver -from $smtpFrom -to $smtpto -subject $messageSubject
Write-Host "error message sent"
}
}
My problem is that the command is not piping to the foreach. The $name and $primaryStatus are staying null, therefore not going through the if statement.
Any help on this will be greatly appreciated. Thanks!
The below is your code with the braces changed for the foreach loop. Note that you will still need to have the closing brace from your opening foreach-object.
gwmi -Namespace root\dcim\sysman -computername $computer -Class DCIM_PhysicalDiskView | ForEach-Object {
$name = $_.Name
$primaryStatus = $_.PrimaryStatus
if( $primaryStatus -eq $value )
{
Send-MailMessage -SmtpServer $smtpserver -from $smtpFrom -to $smtpto -subject $messageSubject
Write-Host "error message sent"
}
}
Fixed the typo for $_.PrimaryStatus as well. Is $primaryStatus an array? -Contains only works with arrays. I think you wanted -eq? I dont have access to that namespace so i cannot test my theory. Also, I think that you could change this a little with a Where-Object.
gwmi -Namespace root\dcim\sysman -computername $computer -Class DCIM_PhysicalDiskView |
Where-Object { $primaryStatus -eq $value } | ForEach-Object{
Send-MailMessage -SmtpServer $smtpserver -from $smtpFrom -to $smtpto -subject $messageSubject
Write-Host "error message sent"
}
disclaimer: I do not have that namespace so I cannot test this but the logic looks sounds so it should work.