Reputation: 143
I have a script that will stop and start services. However, I've had a few issues where services aren't stopping correctly which requires user intervention on the server. Before the start services portion of the script runs I'm using an if
statement to check if all the services listed have stopped and if they haven't the script will pop an alert.
Below is a snippet of the script that does this. I'm trying to list the services in the pop-up with their status and start-up type but for some reason I'm not getting the list return. Using the line that is currently commented out I'm getting the service names returned but it's in a string.
#$RunningServices = (Get-Service -ComputerName $targetServer | Where-Object {($_.Name -like '*serviceAgroup*'-or $_.Name -like '*serviceBgroup*') -and $_.Status -eq "Running"})
$RunningServices = (Get-Service -ComputerName $targetServer |
Where-Object {($_.Name -like '*serviceAgroup*' -or $_.Name -like '*serviceBgroup*') -and $_.Status -eq "Running"}) |
select DisplayName, Status, StartType |
sort DisplayName;
Format-Table;
$pop = New-Object -ComObject WScript.Shell
$pop.Popup("Log onto $targetserver and stop Running serviceAgroup and serviceBgroup.`r`n$RunningServices", 0, "Services have not stopped!", 1)
Upvotes: 0
Views: 947
Reputation: 200193
The pipeline whose output you assign to the variable $RunningServices
ends at sort DisplayName
. The output is not passed into Format-Table
. If you put a variable with an object (or an array of objects) into a string, the objects are expanded to their respective string representation.
Demonstration:
PS C:\> $s = Get-Service | Select-Object -First 3 PS C:\> $s Status Name DisplayName ------ ---- ----------- Stopped AeLookupSvc Application Experience Stopped ALG Application Layer Gateway Service Stopped AppIDSvc Application Identity PS C:\> "$s" AeLookupSvc ALG AppIDSvc PS C:\> _
For services the string representation is the value of the Name
property, as you can see above. With your property selection (without the property Name
) the result should be an empty string instead:
PS C:\> $s = Get-Service | Select-Object DisplayName,Status,StartType -First 3 PS C:\> $s DisplayName Status StartType ----------- ------ --------- Application Experience Stopped Manual Application Layer Gateway Service Stopped Manual Application Identity Stopped Manual PS C:\> "$s" PS C:\> _
To get the table output as a string you must pipe your object list not only through Format-Table
, but also through Out-String
. That's because Format-Table
doesn't generate string output, but a list of format objects. Out-String
converts these into actual string output.
PS C:\> $s1 = $s | Format-Table PS C:\> "$s1" Microsoft.PowerShell.Commands.Internal.Format.FormatStartData Microsoft.PowerShe ll.Commands.Internal.Format.GroupStartData Microsoft.PowerShell.Commands.Interna l.Format.FormatEntryData Microsoft.PowerShell.Commands.Internal.Format.FormatEnt ryData Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData Microsoft.P owerShell.Commands.Internal.Format.GroupEndData Microsoft.PowerShell.Commands.In ternal.Format.FormatEndData PS C:\> $s2 = $s | Format-Table | Out-String PS C:\> "$s2" DisplayName Status StartType ----------- ------ --------- Application Experience Stopped Manual Application Layer Gateway Service Stopped Manual Application Identity Stopped Manual
But even with that your output probably won't be what you expect. Unlike the PowerShell console GUI dialogs use a proportional font, meaning that not all characters have the same width. Because of that something that looks like a proper table when using a monospace font will most likely look rather deformed when using a proportional font. Outputting the above string in a GUI dialog like this:
PS C:\> Add-Type -Assembly 'System.Windows.Forms' PS C:\> [Windows.Forms.Messagebox]::Show($s2)
should present you with something like this:
To get an at least somewhat presentable result I'd suggest using custom format strings:
PS C:\> $s3 = $s | ForEach-Object {"{0}`t{1}`t{2}" -f $_.StartType,$_.Status,$_.DisplayName} | Out-String PS C:\> [Windows.Forms.Messagebox]::Show($s3)
That should produce somewhat more presentable output:
The above takes advantage of the fact that the values of the StartType
and Status
properties don't vary too much in length, so the columns can be aligned reasonably well by separating the values with tab characters. If you must have the service names on the left side formatting becomes a lot more complicated, because you need to insert a variable amount of tabs depending on the number and width of the characters in the names. I'm going to leave that as an exercise for the reader.
Bottom line: Change your code to something like this:
Add-Type -Assembly 'System.Windows.Forms'
$svc = Get-Service -ComputerName $targetServer | Where-Object {
($_.Name -like '*serviceAgroup*' -or $_.Name -like '*serviceBgroup*') -and
$_.Status -eq 'Running'
} | Select-Object DisplayName,Status,StartType
$str = $svc | Sort-Object DisplayName | ForEach-Object {
"{0}`t{1}`t{2}" -f $_.StartType,$_.Status,$_.DisplayName
} | Out-String
[Windows.Forms.Messagebox]::Show($str)
and it should do (more or less) what you want.
Addendum: A better option for displaying the properties of a list of objects to the user would be a gridview:
$svc = Get-Service -ComputerName $targetServer | Where-Object {
($_.Name -like '*serviceAgroup*' -or $_.Name -like '*serviceBgroup*') -and
$_.Status -eq 'Running'
} | Select-Object DisplayName,Status,StartType
$svc | Out-GridView -Title 'Services'
Upvotes: 1