I searched and put together a PowerShell script to check if a windows service (SNARE) is running or not in a list of servers. At the moment, the script prints "Snare is running" if it doesn't error and "Not installed/Powered off" if it meets an error. What I am also looking for is if the script doesn't end up in error, can I somehow take the output of Status (example below) and print "Snare is stopped"?
Status Name DisplayName
------ ---- -----------
Stopped SNARE SNARE
#Powershell
$serverList = gc Final.txt
$collection = $()
foreach ($server in $serverList) {
$status = #{
"ServerName" = $server
"TimeStamp" = (Get-Date -f s)
}
if (Get-Service -Name SNARE -ComputerName $server -EA SilentlyContinue) {
$status["Results"] = "Snare is running"
} else {
$status["Results"] = "Not installed/Powered off"
}
New-Object -TypeName PSObject -Property $status -OutVariable serverStatus
}
Assign the output from Get-Service to a variable, and grab the Status property from that:
if (($snare = Get-Service -Name SNARE -ComputerName $server -EA SilentlyContinue))
{
$status["Results"] = "Snare is running"
$status["Status"] = $snare.Status
}
else
{
$status["Results"] = "Not installed/Powered off"
$status["Status"] = "Unknown"
}
Related
I am trying to write this script to restart computers only if they are Offline. The script for getting user infomration works but I cannot get the variable values for the restart portion at the bottom of the script. Does anyone have a suggestion? I am somewhat new to Powershell, but writing code. Example of my script follows:
Function Get-LoggedOnUser
{
Param
(
$ComputerName = $env:COMPUTERNAME,
$Credential
)
Function Test-RemoteRegistry
{
Param
(
[Parameter(Mandatory = $False)]
[switch]$Enable
,
[Parameter(Mandatory = $False)]
[switch]$Disable
,
[Parameter(ValueFromPipeline=$True)]
[String[]]$ComputerName = $env:COMPUTERNAME
)
Begin
{
$PipelineInput = (-not $PSBOUNDPARAMETERS.ContainsKey("ComputerName")) -and (-not $ComputerName)
Function Test ($Computer)
{
Try
{
[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) | Out-Null
#20ms faster than Get-Service per computer! Not sure how to handle/check things like the firewall though...
#If we hit here without error Remote Reg is enabled.
If ($Disable)
{
Try
{
Get-Service -Name RemoteRegistry -ComputerName $Computer | Set-Service -Status Stopped -ErrorAction Stop
Return $False
#If we hit here without error Remote Reg is now disabled.
}
Catch
{
Return $True
#If we hit here, we couldn't stop remote registry.
}
}
Else
{
Return $True
}
}
Catch
{
If ($Enable)
{
Try
{
Get-Service -Name RemoteRegistry -ComputerName $Computer | Set-Service -Status Running -ErrorAction Stop
Return $True
#If we hit here without error Remote Reg is now enabled.
}
Catch
{
Return $False
#If we hit here, we couldn't start remote registry.
}
}
Else
{
Return $False
#If we hit here remote registry is disabled.
}
}
}
}
Process
{
If ($PipelineInput)
{
Test $_
}
Else
{
$ComputerName | ForEach-Object {
Test $_
}
}
}
}
Foreach ($Computer in $Computername)
{
$Online = $False
$User = $False
$Locked = $False
If (Test-Connection $Computer -Count 2 -Quiet)
{
$Online = $True
If ($Credential)
{
$User = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer -Credential $Credential | Select-Object -ExpandProperty UserName -ErrorAction Stop
}
Else
{
$User = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer | Select-Object -ExpandProperty UserName -ErrorAction Stop
}
If (Test-RemoteRegistry -Enable -ComputerName $Computer)
{
If ((Get-Process logonui -ComputerName $Computer -ErrorAction SilentlyContinue) -and ($user))
{
$Locked = $True
}
}
}
$Output = New-Object PSObject
$Output | Add-Member noteproperty ComputerName $Computer
$Output | Add-Member noteproperty Online $Online
$Output | Add-Member noteproperty Username $User
$Output | Add-Member noteproperty Locked $Locked
$Output
}
}
Get-LoggedOnUser
If (($Online) -eq $False)
{Shutdown /r t 0 /m \\$Computername}
ELSE
{Write-host 'HELLO $Online $Computername'}
I just want this for a single user as I am using PDQ Inventory to roll out the script. The variables at the end of the script are $null?
Variables defined in a child scope - in which functions run by default - are never seen by the calling scope. See the conceptual about_Scopes help topic
It's best for functions to communicate values to the caller via their output ("return value"), which you're function is already doing: it outputs objects whose properties contain the values of interest.
Therefore:
Get-LoggedOnUser |
ForEach-Object { # Loop over all output objects
# Refer to the object at hand via the automatic $_ variable.
# Note the use of "..." (expandable strings) so as to support
# expansion (string interpolation).
if (-not $_.Online) { Shutdown /r t 0 /m "\\$($_.ComputerName)" }
else { "HELLO $($_.Online) $($_.ComputerName)" }
}
My conditions are :
if NeedReboot key is exist then result will be Pending reboot
if NeedReboot key is not exist then result will be NO Pending reboot
My desired output :
Computername,Rebootrequired
Host01,Pending reboot
Host02,NO Pending reboot
Script :
$allComputers = Get-Content '.\path\to\computers.txt'
$domainCred = Get-Credential -UserName "domain01\admin01" -Message "Please enter the DOMAIN password"
$Results = foreach($computer in $allComputers) {
$cred = $domainCred
try {
$VmTools = Invoke-Command -ComputerName $computer -Credential $cred -ScriptBlock {
Test-Path -Path "HKLM:\SOFTWARE\VMware, Inc.\NeedReboot\"
} -ErrorAction Stop
[PsCustomObject]#{ ComputerName = $computer; RebootRequired = ([int]$VmTools -eq "True") }
}
}
$Results | Format-Table -AutoSize
$Results | Export-Csv -Path 'c:\temp\vmtools.csv' -NoTypeInformation -UseCulture
Try removing the final backslash after the registry path.
Then, Test-Path just returns $true or $false, so converting that to [int] and comparing to the string "True" makes no sense..
Try
[PsCustomObject]#{
ComputerName = $computer
RebootRequired = if($VmTools) {"Pending Reboot") } else {"NO Pending Reboot"}
}
The code is also missing a catch{..} block that should accompany the try {..} block. Something like:
catch {
[PsCustomObject]#{
ComputerName = $computer
RebootRequired = "Error reading registry"
}
}
Good Day!
I work with multiple clients and one of my clients have their own toll that displays live server ping stats and this is very helpful during patching activity because of followed reboots etc. I want to design a tool like this or any other tool or means where I can see live ping stats of servers in my Domain. Can I do it using Power Shell by any chance? Is there a way that I could refresh my Power Shell page or the output HTML page which updates the ping stats of respective servers please! I have no clue about programming languages but I want to do this as my pet project and I cannot ask my client about all these details. Help is very much appreciated!
Thanks guys, I have tried below two codes:
param(
[int]$waitseconds = 3
)
while($true) {
$servers = Get-Content .\servers.txt
$collection = $()
foreach ($server in $servers)
{
$status = #{ "ServerName" = $server; "TimeStamp" = (Get-Date -f s) }
if (Test-Connection $server -Count 1 -ea 0 -Quiet)
{
$status["Results"] = "Up"
}
else
{
$status["Results"] = "Down"
}
New-Object -TypeName PSObject -Property $status -OutVariable serverStatus
$collection += $serverStatus
}
$collection | out-gridview; sleep $waitseconds
}
This gives me output in a grid view and it also successfully refreshes the output but the new refreshed output is being presented in a new grid view window. I cannot afford opening numerous windows. So I have tried below code:
param(
[int]$waitseconds = 3
)
while($true) {
$servers = Get-Content .\servers.txt
$collection = $()
foreach ($server in $servers)
{
$status = #{ "ServerName" = $server; "TimeStamp" = (Get-Date -f s) }
if (Test-Connection $server -Count 1 -ea 0 -Quiet)
{
$status["Results"] = "Up"
}
else
{
$status["Results"] = "Down"
}
New-Object -TypeName PSObject -Property $status -OutVariable serverStatus
$collection += $serverStatus
}
Start-Process -FilePath powershell.exe -ArgumentList "-WindowStyle -NoNewWindow -Command &{ $collection | out-gridview; sleep $waitseconds; exit }" -Wait
}
This code is not at all giving gridview output, it simply displays ping stats in shell itself but able to refresh.
I have tried below code too with -NoNewWWindow at the end and this gives output one time and then closes:
param(
[int]$waitseconds = 3
)
while($true) {
$servers = Get-Content .\servers.txt
$collection = $()
foreach ($server in $servers)
{
$status = #{ "ServerName" = $server; "TimeStamp" = (Get-Date -f s) }
if (Test-Connection $server -Count 1 -ea 0 -Quiet)
{
$status["Results"] = "Up"
}
else
{
$status["Results"] = "Down"
}
New-Object -TypeName PSObject -Property $status -OutVariable serverStatus
$collection += $serverStatus
}
Start-Process -FilePath powershell.exe -ArgumentList "-WindowStyle -Command &{ $collection | out-gridview; sleep $waitseconds; exit }" -Wait -NoNewWindow
}
Help me with any modifications to my 1st code so that the grid view does not open for every refresh please!
Have you tried something like this:
$collection | out-gridview
sleep $waitseconds
(Get-Process -id $PID).CloseMainWindow()
This is the script I have, I'm trying to have it read a list of services from the text but it instead combines them together. For example, I have two services(RTCRGS and TabletInputService), it will read it as
" RTCRGS TabletInputService " instead of one service at a time.
$Serverstxt = "C:\Scripts\Services\Server.txt"
$Servicestxt = "C:\Scripts\Services\Services.txt"
$ServiceList = get-content "$Servicestxt"
$ServerList = get-content "$Serverstxt"
#Initialize variables:
[string]$WaitForIt = ""
[string]$Verb = ""
[string]$Result = "FAILED"
#
foreach($Server in $ServerList){
foreach($Service in $ServiceList){
$svc = (get-service -computername $Server -name $Service)
Write-host "$Service on $Sever is $($svc.status)"
Switch ($svc.status) {
'Stopped' {
Write-host "Starting $Service..."
$Verb = "start"
$WaitForIt = 'Running'
$svc.Start()}
'Running' {
Write-host "Stopping $Service..."
$Verb = "stop"
$WaitForIt = 'Stopped'
$svc.Stop()}
Default {
Write-host "$Service is $($svc.status). Taking no action."}
}
if ($WaitForIt -ne "") {
Try {
# For some reason, we cannot use -ErrorAction after the next statement:
$svc.WaitForStatus($WaitForIt,'00:02:00')
} Catch {
Write-host "After waiting for 2 minutes, $Service failed to $Verb."
}
$svc = (get-service -computername $Server -name $Service)
if ($svc.status -eq $WaitForIt) {$Result = 'SUCCESS'}
Write-host "$Result`: $Service on $Server is $($svc.status)"
}
The foreach is correct, but you are using the entire list, instead of each element throughout your loop.
$SvrNames and $SvcNames are the contents of the files.
$SvrName and $SvcName are the current loop variables.
So you'd use
$svc = (get-service -computername $SvrName -name $SvcName)
and so on, inside the loop.
To make things a bit clearer, rename the variables
$Serverstxt = "C:\Scripts\Services\Server.txt"
$Servicestxt = "C:\Scripts\Services\Services.txt"
$ServiceList = get-content "$Servicestxt"
$ServerList = get-content "$Serverstxt"
foreach($Server in $ServerList){
foreach($Service in $ServiceList){
# . . .
$svc = (get-service -computername $Server -name $Service)
# . . .
}
}
I'm looking for help with my script again :)
I have a script that will query list of servers to find if specific service is installed. This works fine. However, I know that there are some servers in my list that I don't have access to, or there are different credentials. How do I make this visible in output? Because I only get output that service is not installed, which is not true, I just don't have correct credentials.
$name = "BESClient"
$servers = Get-content C:\list.txt
function Confirm-WindowsServiceExists($name)
{
if (Get-Service -Name $name -Computername $server -ErrorAction Continue)
{
Write-Host "$name Exists on $server"
return $true
}
Write-Host "$name does not exist on $server"
return $false
}
ForEach ($server in $servers) {Confirm-WindowsServiceExists($name)}
Also, I'd like to have output formatted into the one line, e.g.:
Server1 Service running
Server2 Service not installed
Server3 no access
etc...
Thanks a lot for any help.
Here's an option which just displays the content of the error on failure:
function Confirm-WindowsServiceExists($name)
{
if (Get-Service -Name $name -Computername $server -ErrorAction SilentlyContinue -ErrorVariable WindowsServiceExistsError)
{
Write-Host "$name Exists on $server"
return $true
}
if ($WindowsServiceExistsError)
{
Write-Host "$server" $WindowsServiceExistsError[0].exception.message
}
return $false
}
As for the second part of the question #arco444 has described the correct approach.
Here's a WMI solution. Any errors you get from attempting to connect to remote computers will be caught with the try/catch blocks. The result of each operation will be stored to a custom object and added to the array that holds the results of all the operations.
$result = #()
$name = "BESClient"
$servers = Get-Content C:\list.txt
$cred = Get-Credential
foreach($server in $servers) {
Try {
$s = gwmi win32_service -computername $server -credential $cred -ErrorAction Stop | ? { $_.name -eq $name }
$o = New-Object PSObject -Property #{ server=$server; status=$s.state }
$result += ,$o
}
Catch {
$o = New-Object PSObject -Property #{ server=$server; status=$_.Exception.message }
$result += ,$o
}
}
$result | Format-Table -AutoSize
You should end up with something like this:
server state
------ -----
s1 running
s4 stopped
s2 The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)