Verifique se existe um serviço do Windows e exclua no PowerShell

148

No momento, estou escrevendo um script de implantação que instala vários serviços do Windows.

Os nomes dos serviços são versionados, portanto, desejo excluir a versão anterior do serviço do Windows como parte das instalações do novo serviço.

Como posso fazer isso da melhor maneira no PowerShell?

Adrian Russell
fonte

Respostas:

235

Você pode usar o WMI ou outras ferramentas para isso, pois não há Remove-Servicecmdlet até o Powershell 6.0 ( consulte o documento Remover serviço)

Por exemplo:

$service = Get-WmiObject -Class Win32_Service -Filter "Name='servicename'"
$service.delete()

Ou com a sc.exeferramenta:

sc.exe delete ServiceName

Por fim, se você tiver acesso ao PowerShell 6.0:

Remove-Service -Name ServiceName
Ravikanth
fonte
2
Você também pode portar a parte relevante deste exemplo para o PowerShell (use a classe TransactedInstaller): eggheadcafe.com/articles/20060104.asp No entanto, o método do ravikanth é provavelmente mais simples.
johnl
7
Versões mais recentes do PS têm o Remove-WmiObject e cuidado com as falhas silenciosas do $ service.delete () - adicionaram outra resposta com exemplos formatados.
Straff
1
Resumindo, a versão mais atualizada é executar o Powershell como administrador e executar o seguinte: $service = Get-WmiObject -Class Win32_Service -Filter "Name='servicename'" $service | Remove-WmiObject
BamboOS
Para obter informações de todos, a resposta de straff diz "Cuidado com silenciosa falhar por $service.delete()"
sirdank
A partir do Windows PowerShell 3.0, o cmdlet Get-WmiObject foi substituído pelo Get-CimInstance. Então, hoje em dia você pode fazer isso:Stop-Service 'servicename'; Get-CimInstance -ClassName Win32_Service -Filter "Name='servicename'" | Remove-CimInstance
Rosberg Linhares
122

Não há mal nenhum em usar a ferramenta certa para o trabalho, acho que estou executando (da Powershell)

sc.exe \\server delete "MyService" 

o método mais confiável que não possui muitas dependências.

dcx
fonte
55
A parte .exe é muito importante, já que sc, por si só, é um alias para Set-Content.
Tom Robinson
@ tjrobinson Obrigado por isso, eu tinha omitido o .exeaté ver seu comentário. Agora está funcionando para mim.
gwin003
Isso só é útil se você tiver direitos para o computador remoto. Se não (como na maioria dos ambientes seguros) isso não vai funcionar e você precisa de algo que suportes passando credenciais
DaveStephens
O nome do servidor ( \\server) é simplesmente omitido se o serviço for local.
Jpmc26
1
este é melhor porque é mais facilmente programável com %e$_
Chaim Eliyah
84

Se você deseja apenas verificar a existência do serviço:

if (Get-Service "My Service" -ErrorAction SilentlyContinue)
{
    "service exists"
}
Dmitry Fedorkov
fonte
21

Usei a solução "-ErrorAction SilentlyContinue", mas depois deparei com o problema de deixar um ErrorRecord para trás. Então, aqui está outra solução para verificar se o Serviço existe usando "Get-Service".

# Determines if a Service exists with a name as defined in $ServiceName.
# Returns a boolean $True or $False.
Function ServiceExists([string] $ServiceName) {
    [bool] $Return = $False
    # If you use just "Get-Service $ServiceName", it will return an error if 
    # the service didn't exist.  Trick Get-Service to return an array of 
    # Services, but only if the name exactly matches the $ServiceName.  
    # This way you can test if the array is emply.
    if ( Get-Service "$ServiceName*" -Include $ServiceName ) {
        $Return = $True
    }
    Return $Return
}

[bool] $thisServiceExists = ServiceExists "A Service Name"
$thisServiceExists 

Mas o ravikanth tem a melhor solução, pois o Get-WmiObject não gera um erro se o Serviço não existir. Então, decidi usar:

Function ServiceExists([string] $ServiceName) {
    [bool] $Return = $False
    if ( Get-WmiObject -Class Win32_Service -Filter "Name='$ServiceName'" ) {
        $Return = $True
    }
    Return $Return
}

Então, para oferecer uma solução mais completa:

# Deletes a Service with a name as defined in $ServiceName.
# Returns a boolean $True or $False.  $True if the Service didn't exist or was 
# successfully deleted after execution.
Function DeleteService([string] $ServiceName) {
    [bool] $Return = $False
    $Service = Get-WmiObject -Class Win32_Service -Filter "Name='$ServiceName'" 
    if ( $Service ) {
        $Service.Delete()
        if ( -Not ( ServiceExists $ServiceName ) ) {
            $Return = $True
        }
    } else {
        $Return = $True
    }
    Return $Return
}
Kent
fonte
7
Decidi fazer uma comparação de velocidade entre Get-WmiObject -Class Win32_Service -Filter "Name='$serviceName'"e Get-Service $serviceName -ErrorAction Ignore(que oculta completamente o erro se o serviço não existir) para garantir a integridade. Eu esperava que o Get-WmiObject fosse mais rápido porque não gera um erro. Eu estava muito errado. Executando cada um em um loop 100 vezes, o Get-Service levou 0,16 segundos, enquanto o Get-WmiObject levou 9,66 segundos. Portanto, o Get-Service é 60x mais rápido que o Get-WmiObject.
Simon Tewsi 27/03
13

Versões mais recentes do PS têm o Remove-WmiObject. Cuidado com falhas silenciosas para $ service.delete () ...

PS D:\> $s3=Get-WmiObject -Class Win32_Service -Filter "Name='TSATSvrSvc03'"

PS D:\> $s3.delete()
...
ReturnValue      : 2
...
PS D:\> $?
True
PS D:\> $LASTEXITCODE
0
PS D:\> $result=$s3.delete()

PS D:\> $result.ReturnValue
2

PS D:\> Remove-WmiObject -InputObject $s3
Remove-WmiObject : Access denied 
At line:1 char:1
+ Remove-WmiObject -InputObject $s3
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Remove-WmiObject], ManagementException
    + FullyQualifiedErrorId : RemoveWMIManagementException,Microsoft.PowerShell.Commands.RemoveWmiObject

PS D:\> 

Para minha situação, eu precisava estar executando 'Como administrador'

Straff
fonte
7

Para excluir vários serviços no Powershell 5.0, pois o serviço de remoção não existe nesta versão

Execute o comando abaixo

Get-Service -Displayname "*ServiceName*" | ForEach-object{ cmd /c  sc delete $_.Name}
Stuart Smith
fonte
5

Combinando as respostas de Dmitri e dcx, fiz o seguinte:

function Confirm-WindowsServiceExists($name)
{   
    if (Get-Service $name -ErrorAction SilentlyContinue)
    {
        return $true
    }
    return $false
}

function Remove-WindowsServiceIfItExists($name)
{   
    $exists = Confirm-WindowsServiceExists $name
    if ($exists)
    {    
        sc.exe \\server delete $name
    }       
}
Richard
fonte
4

Pode-se usar Where-Object

if ((Get-Service | Where-Object {$_.Name -eq $serviceName}).length -eq 1) { "Service Exists" }

ShaneH
fonte
3

Para verificar se MySuperServiceVersion1existe um serviço do Windows nomeado , mesmo quando você não tiver certeza do nome exato, você pode empregar um curinga, usando uma substring como:

 if (Get-Service -Name "*SuperService*" -ErrorAction SilentlyContinue)
{
    # do something
}
Ifedi Okonkwo
fonte
3

Para um único PC:

if (Get-Service "service_name" -ErrorAction 'SilentlyContinue'){(Get-WmiObject -Class Win32_Service -filter "Name='service_name'").delete()}

else{write-host "No service found."}

Macro para lista de PCs:

$name = "service_name"

$list = get-content list.txt

foreach ($server in $list) {

if (Get-Service "service_name" -computername $server -ErrorAction 'SilentlyContinue'){
(Get-WmiObject -Class Win32_Service -filter "Name='service_name'" -ComputerName $server).delete()}

else{write-host "No service $name found on $server."}

}
Ivan Temchenko
fonte
3

O PowerShell Core ( v6 + ) agora tem um Remove-Servicecmdlet .

Eu não sei sobre os planos para voltar porta-lo para o Windows PowerShell, onde é não disponível a partir de v5.1.

Exemplo:

# PowerShell *Core* only (v6+)
Remove-Service someservice

Observe que a chamada falha se o serviço não existir; portanto, para removê-lo apenas se ele existir atualmente, você pode:

# PowerShell *Core* only (v6+)
$name = 'someservice'
if (Get-Service $name -ErrorAction Ignore) {
  Remove-Service $name
}
mklement0
fonte
3
  • Para versões do PowerShell anteriores à v6, você pode fazer o seguinte:

    Stop-Service 'YourServiceName'; Get-CimInstance -ClassName Win32_Service -Filter "Name='YourServiceName'" | Remove-CimInstance
  • Para v6 +, você pode usar o cmdlet Remove-Service .

Observe que, a partir do Windows PowerShell 3.0, o cmdlet Get-WmiObject foi substituído pelo Get-CimInstance.

Rosberg Linhares
fonte
2

Adaptado para obter uma lista de entrada de servidores, especificar um nome de host e fornecer uma saída útil

            $name = "<ServiceName>"
            $servers = Get-content servers.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
            }

            function Remove-WindowsServiceIfItExists($name)
            {   
                $exists = Confirm-WindowsServiceExists $name
                if ($exists)
                {    
                    Write-host "Removing Service $name from $server"
                    sc.exe \\$server delete $name
                }       
            }

            ForEach ($server in $servers) {Remove-WindowsServiceIfItExists($name)}
Gerald
fonte