Powershell Script To Reindex WSUS DB and email results - powershell

Hi I am trying to write a PowerShell script that reindexs the WSUS Database and I am stuck. So far I have a separate config file that holds the email details of the server, and I think I have got the WSUS reindex working. However I'm stuck getting the data from the sql query. I have used a TechNet article as a starting point.
The code I have so far is:
$configKey = #{}
Get-Content server.ini | ForEach-Object {
$keys = $_ -split "="
$configKey += #{$keys[0]=$keys[1]}
}
cd "c:\Program Files\Microsoft SQL Server\100\Tools\Binn\"
"Invoke-sqlcmd -I -i"c:\scripts\WSUSDBMaintenance.sql" -S "np:\\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query"
#file backup
$body += '<font size="5" face="Calibri"Windows Server Reindex Report:</font><br / ><font face="Calibri">'
$body += $Result + "<br>"
"
Invoke-Sqlcmd -Query "PRINT 'Number of indexes to rebuild: ' + cast(##ROWCOUNT as nvarchar(20)';" -Verbose
$body += "</div></font>"
$subject += $configKey.company
#Send Email
Send-MailMessage -From $configKey.from -To $configKey.to -Subject $subject -Body $Body -BodyAsHtml -Smtpserver $configKey.server

a few things jump out at me.
"Invoke-sqlcmd -I -i"c:\scripts\WSUSDBMaintenance.sql" -S "np:\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query"
should probably be
Invoke-sqlcmd -I -i "c:\scripts\WSUSDBMaintenance.sql" -S "np:\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query"
And you have an extraneous double quote here:
$body += $Result + "<br>"
"<<<<<<<<<<<<<<<<<<<<<<<<<
Also, are the tools really in a directory called Binn rather than Bin?

Related

Powershell script to email an event

I am new to powershell and I am trying to write a script that will email me whenever someone moves or deletes a file or folder from a shared drive. I already set up the task scheduler to execute the script when one of those events happen. The problem that I have is that I execute script from the shell it provides me with all the information regarding the last entry in the event logs. However, when it runs from the task scheduler it does not execute. I believe that it has to do with the body line. Any suggestions or insights will be greatly appreciated.
This is my code:
$events = Get-EventLog -Logname Security -Newest 1
$From = $env:COMPUTERNAME + "#company.com"
$To = "me#company.com"
$Subject = $env:COMPUTERNAME + " " + $env:UserName + " " + $_.eventID
$Body = $events | format-list -property * | out-string
$SMTPServer = "exch"
Send-MailMessage -From $From -to $To -Subject $Subject -Body $Body -SMTPServer $SMTPServer

Cannot Get Send-MailMessage to Send Multiple Lines

I apologize for this in advance because I see plenty of questions regarding it throughout the web but for some reason I'm still having issues.
I have a script that creates an array that has information dynamically added to it. When the script completes, I need it to email that information to me. The problem is that each line is combined to that I get 1 line. For example:
$Body = #()
$Body += "1"
$Body += "2"
$Body += "3"
$Body += "4"
Here's my send command:
Send-MailMessage -To $Recipients -From $Sender -Smtp $SMTP $Subject "Test" -Body ($Body | Out-String)
What I get in the email body is: 1234
I've tried this for loop to append `n to the beginning of each line (except the 1st) like so:
for ($i = 0; $i -lt Body.Count; $i++){
if($i -eq 0){
Write-Host $i
}else{
$Body[$i] = "`n" + $Body[$i]
Write-Host $Body[$i]
}
}
The results of that are better but I get an extra line:
1
2
3
4
Ultimately I just want this:
1
2
3
4
In the past, I've gotten the format I want by creating the variable like this:
$Body = #"
This is the email I want to send. It formats great if:
1) I want to make all the content static.
Is it possible to create the `$Body variable like this but add line by line dynamically and maintain a serpate line. (without extra lines)?
"#
What am I missing? It HAS to be something simple... Thanks for your help!
WOW so it's such a finicky thing... I found that using this would get everything to look right (as suggested by Ryan, above):
-Body ($Body -Join "`r`n")
But in the context of my actual script it wasn't working. Well, it appears that it all had to do with the way the date was going in there. Here's a reduction to show:
DOES NOT WORK
$Body = #()
$Body += "Beginning processing on: " + (Get-Date)
$Body += "No advertisements found, exiting"
ALSO DOES NOT WORK
$Body = #()
$DateTime = Get-Date
$Body += "Beginning processing on: $DateTime"
$Body += "No advertisements found, exiting"
When I removed anything that had to do with the Date, formatting was correct. Ultimately it just took a simple "." to make it work right.
WORKS - NOTE THE 3RD LINE
$Body = #()
$DateTime = Get-Date
$Body += "Beginning processing on: $DateTime." #Note the period at the end of the line.
$Body += "No advertisements found, exiting"
Presumably I could just concatenate and it would work but oh well, I'm keeping it as it. I didn't think that it would be that finicky about a period but now I know.

How can I format my output

How can I format my output to PowerShell's script
My output looks like
Hello #{SamAccountName=user1} is locked out
#{SamAccountName=user2} is locked out
My PowerShell code is
if ($users)
{
foreach($user in $users)
{
$message = $message + " " + $user + " is locked out" + "`r`n"
Write-Host $user
}
Send-MailMessage -To $to -Subject "Locked Accounts" -BodyAsHtml $message -From $from -Credential $cred -SmtpServer $server -Debug
}
Thank you
I would like my output to look like
Hello Administrator,
The following accounts are locked as of 10:31 AM on the 13th of April,
2015.
User1 User2
Thank You Automated System
As you can see in my current output, I can't figure out how to get a new line and it outputs #{SamAccountName=user1} instead of user1
Thank you
Here, try this approach instead. If you use a here-string (which is depicted like so:
$message = #"
So
this
keeps
track of spaces?
"#
You can put any amount of text inside and preserve the spacing of the message, while still having the ease of using this in your script by putting any variables inside that you might need.
As you'll see in the finished answer below, the line spacing is preserved. I'm using the $($variableName) format to allow me to pluck out one value of an object from within a bigger string. If I didn't use that format, the whole object would be listed, including all AD properties, which is not what we want.
if ($users)
{
$message = #"
Hello Administrator,
The following accounts are locked as of $((get-date).DateTime).
$($users | select -expand SamAccountName)
Thank You,
Automated System
"#
Send-MailMessage -To $to -Subject "Locked Accounts" -BodyAsHtml $message -From $from -Credential $cred -SmtpServer $server -Debug
}
The message will look like this:
Hello Administrator,
The following accounts are locked as of Monday, April 13, 2015 10:22:30 AM.
localadmin Guest Stephen RDV GRAPHICS SERVICE Jim SCVMM81221tqYYJ stephen.owen krbtgt _svc_sccm azure_adfs
Thank You,
Automated System
You need to directly access the sAMAccountName property on the $user object:
if ($users)
{
foreach($user in $users)
{
$message = $message + " " + $user.SamAccountName + " is locked out" + "`r`n"
}
Send-MailMessage -To $to -Subject "Locked Accounts" -BodyAsHtml $message -From $from -Credential $cred -SmtpServer $server -Debug
}
If you wanted to make something pretty, save the user names to an array first:
$lockedUsers = #()
foreach($user in $users)
{
$lockedUsers += ,$user.SamAccountName
}
Then you can construct your message like:
$message = #"
Hi Admin,
The following accounts are locked as of $(get-date)
$($lockedUsers -join ", ")
Thank you, Automated System
"#
I figured it out. Since the Body is HTML, I used standard HTML tags when forming the strings
<br> = new line
<strong></strong> = bold
<font color='red'></font> = red font
For the account names, I did
$message = $message + " " + $user.SamAccountName.ToLower() + " is locked out" + "<br>"

Join an array with newline in PowerShell

I have an array of names that I'm trying to join using a new line character. I have the following code
$body = $invalid_hosts -join "`r`n"
$body = "The following files in $Path were found to be invalid and renamed `n`n" + $body
Finally, I send the contents via email.
$From = "myaddress#domain.com"
$To = "myaddress#domain.com
$subject = "Invalid language files"
Send-MailMessage -SmtpServer "smtp.domain.com" -From $From -To $To -Subject $subject -Body $body
When I receive the message, the line The following files in <filepath> were found to be invalid and renamed has the expected double space, but the contents of $invalid_hosts are all on one line. I've also tried doing
$body = $invalid_hosts -join "`n"
and
$body = [string]::join("`n", $invalid_hosts)
Neither way is working. What do I need to do to make this work?
Pipe the array to the Out-String cmdlet to convert them from a collection of string objects to a single string:
PS> $body = $invalid_hosts -join "`r`n" | Out-String
It is sufficient just pipe to Out-String (see https://stackoverflow.com/a/21322311/52277)
$result = 'This', 'Is', 'a', 'cat'
$strResult = $result | Out-String
Write-Host $strResult
This
Is
a
cat
I'm unsure about how to answer everything else, but for guaranteed newlines in Powershell, use:
[Environment]::NewLine in place of your "`n"
Had to solve this today; thought I'd share my answer since the question and other answers helped me find the solution. Instead of
$body = $invalid_hosts -join "`r`n"
$body = "The following files in $Path were found to be invalid and renamed `n`n" + $body
use
$MessageStr = "The following files in " + $Path + " were found to be invalid and renamed"
$BodyArray = $MessageStr + $Invalid_hosts
$Body = $BodyArray -join "`r`n"
I went about it differently and just replaced the newline
$result -replace("`r`n"," ")
I am certainly no expert in PowerShell, but I found a much easier way to do it. Simply pipe to Write-Host like this:
$array = 'This', 'Is', 'a', 'cat'
$array | Write-Host
Output:
This
Is
a
cat
This is a slightly different use case than the OP question. It does not join the array with newlines, but it does give newlines when writing the output.

PowerShell - have trouble using the Count method

I have the following code (thanks to input from 'jon Z' and 'manojlds') that processes server lists, checks the error logs for each server against a search string, and then sends an email report listing the servers with bad error logs and good error logs.
What I would like to do now is do a count of the number of servers with bad error logs and good error logs and put that info in the email report as well.
I would have something like this in each heading of my email report:
The following n servers have bad error logs:
The following n servers are OK:
I've been trying to accomplish this by using the Count method, but have so far been unsuccessful. Is the Count method the correct approach? Is so, how (where would I apply it in my code)? If not, then what is the best approach?
Code:
$BadServerLogs = "<font style=`"font-family:verdana;font-size:9pt`"><p><b>The following servers have bad error logs:</b></p>"
$GoodServerLogs = "<font style=`"font-family:verdana;font-size:9pt`"><p><b>The following servers are OK:</b></p>"
# Use hash table to associate server list to search string array
$Groups = #{
$SERVER_LST_1=$SEARCH_STR_ARRAY_1;
$SERVER_LST_2=$SEARCH_STR_ARRAY_2;
$SERVER_LST_3=$SEARCH_STR_ARRAY_3;
$SERVER_LST_4=$SEARCH_STR_ARRAY_4;
}
$StartupErrors = #{}
$Groups.keys | %{
$key = $_
gc $key | %{
# Check StartupError.log files for errors
$StartupErrors[$_] = Get-ChildItem -Path \\$_\$LOG_PATH -Include StartupError.log -Recurse | Select-String -notmatch $Groups["$key"]
If ($StartupErrors[$_])
{
$Subject = "StartupError Logs Report: BAD ERROR LOGS!"
$BadServerLogs += "<li>$_</li>"
}
Else
{
$Subject = "StartupError Logs Report: All Logs are Fine"
$GoodServerLogs += "<li>$_</li>"
}
}
}
# Send email listing servers with bad/good StartupError log files
Send-MailMessage -Body "$BadServerLogs $GoodServerLogs" -BodyAsHtml -Subject $Subject -SmtpServer $SmtpServer -To $MailTo -From $MailFrom
Thanks in advance! -Keith
You can just create two variables:
$goodCount = 0
$badCount = 0
Then in the loop increment each depending on the case:
If ($StartupErrors[$_])
{
$Subject = "StartupError Logs Report: BAD ERROR LOGS!"
$BadServerLogs += "<li>$_</li>"
$badCount += 1
}
Else
{
$Subject = "StartupError Logs Report: All Logs are Fine"
$GoodServerLogs += "<li>$_</li>"
$goodCount += 1
}
Then create some HTML and send it:
$html = "<h1>The following $badCount servers have bad error logs:</h1>" + $BadServerLogs
$html += "<h1>The following $goodCount servers are OK:</h1>" + $GoodServerLogs
Send-MailMessage -Body $html -BodyAsHtml -Subject $Subject -SmtpServer $SmtpServer -To $MailTo -From $MailFrom