Error on Get Service from multiple servers and sending to email

Hi all,

I have a script which does the above in 4 of our environments but fails in DEV. Same OS, same PS version, same .Net - BUT get an error ERROR:

“Send-MailMessage : Cannot validate argument on parameter ‘Body’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.”

Import-module ActiveDirectory
$body=@"
$($ComputerList = Get-ADComputer -filter 'name -like "vw*"' | foreach { $_.Name}
$ComputerList | where {$computerlist -notcontains (Get-Service -displayname "ApplicationService" -computername $_).MachineName } | out-string)
"@
Send-MailMessage -From PSADMIN@email.com -To PlatformsInbox@email.com-Subject "DEV Servers without App" -Body $body -SmtpServer ExchangeServer

I’m not very proficient with Powershell but I managed to combine several examples to get this working in most of our environments. Just not one. Any ideas why?

Not going to lie, I’ve never seen a here-string (@" … "@, or @’ … '@ [never mind the semi-colons. I’m not sure why the forum software is adding those]), quite that like one. I’ve seen variables expanded in a here-string, but not the setting of variables, and running of commands inside a here-string. I suppose it “works,” but for me I’d rather run my code outside of a here-string, so I can be a bit more certain about what I’m placing in to it. That said, I don’t know that you even need a here-string. They’re typically used when you want to something to look exactly as you’ve typed it, such as when you might intentionally leave blank lines between rows of text.

Anyway, the error indicates that value you’re trying to provide to the -Body parameter (the contents of $body) is empty. I’ve written a bit of a more structured example. Perhaps it’ll be helpful. Please try it out and ask questions. It’s set up to email the names of computers that don’t have the “ApplicationService.” If the idea was to send an email when the service was found, then move $Body = $Computer from the catch portion of the try-catch and into the try portion.

$ComputerList = (Get-ADComputer -Filter 'name -like "vw*"').Name

If ($ComputerList) {
    $Service = 'ApplicationService'
    Foreach ($Computer in $ComputerList) {
        $Body = $null
        try {
            Get-Service -ComputerName $Computer -DisplayName $Service -ErrorAction Stop | Out-Null
        } catch {
            Write-Verbose -Message "Cannot locate $Service on $Computer."
            $Body = $Computer
        }

        If ($Body) {
            $Params = @{
                From = 'PSADMIN@email.com'
                To = 'PlatformsInbox@email.com'
                Subject = 'DEV Servers without App'
                Body = $Body
                SmtpServer = 'ExchangeServer'
            }
            Send-MailMessage @Params
        }
    }
} Else {
    Write-Warning -Message 'No matching computers.'
}

brilliant! It kind of returns some issues I was having originally, IE. with the various possible output states:

  1. PC is unavailable on the network
  2. PC is available but service status is unavailable (caused by firewall or GP or unsupported)
  3. PC is available but Service is down
  4. PC is available and Service is up and running

I want to receive available servers where the service is not installed.
Fortunately, I can work around all this without a problem, The only final question I have is:

Is there any way I can make it so that all results go to one email rather than individual emails? This is how it works with my script in the other environments…

Thanks again!

If you take a look at the code, you’ll see that the $Param hash table, Send-MailMessage, and the containing If statement all happen inside the Foreach construct. Knowing that, it makes perfect sense why you’re getting multiple emails – this code happens during each iteration of the loop.

To correct this you’ll first need to move that code out of the Foreach. Additionally, we’ll remove the $Body = $null assignment as we won’t want to ever clear this variable’s contents, and instead continually add to this variable to using the += assignment operator. Here’s a modified example. Let me, or anyone else paying attention to this thread, know if you have any questions. There’s a lot of great PowerShell concepts to learn from in these few lines.

$ComputerList = (Get-ADComputer -Filter 'name -like "vw*"').Name

If ($ComputerList) {
    $Service = 'ApplicationService'
    Foreach ($Computer in $ComputerList) {
        try {
            Get-Service -ComputerName $Computer -DisplayName $Service -ErrorAction Stop | Out-Null
        } catch {
            Write-Verbose -Message "Cannot locate $Service on $Computer."
            $Body += "$Computer `r`n"
        }
    }

    If ($Body) {
        $Params = @{
            From = 'PSADMIN@email.com'
            To = 'PlatformsInbox@email.com'
            Subject = 'DEV Servers without App'
            Body = $Body
            SmtpServer = 'ExchangeServer'
        }
        Send-MailMessage @Params
    }
} Else {
    Write-Warning -Message 'No matching computers.'
}

Perfect! Thankyou very much and good day to you!

Does anybody know why this would be returning the same data every time it is run? We have since installed the service on the listed servers but the Tasked script returns the same servers every week.