PowerShell Add-Content with Get-Service - windows

$Service = Get-Service -ComputerName SYNESIGNSVR -Name SynAutoImportService
$ServiceDName = $Service.DisplayName
$ServiceStat = $Service.Status
Add-Content "c:\temp\logtemp - Services Status.log" "$(Get-Date -Format
"yyyy-MM-dd HH:mm:ss") - $ServiceDName service on server SYNESIGNSVR current
status: $ServiceStat"
I am writing a PS script that will create a log file with several different services and their current status from a few different windows servers. The above code works and returns:
2017-04-19 12:01:17 - Synergy Document AutoImport service on server SYNESIGNSVR current status: Running
This seems like sloppy code, the way I've pulled the Get-Service and made the Status and DisplayName pull in.
I'm looking for ways I could make this code cleaner and or more efficient.

What about:
$Computer = 'SYNESIGNSVR'
$Service = Get-Service -ComputerName $Computer -Name SynAutoImportService
$LogLine = "{0} - {1} service on server {2} current status: {3}" -f [DateTime]::Now,
$Service.DisplayName,
$Computer,
$Service.Status
Add-Content -Path "C:\temp\logtemp - Services Status.log" -Value $LogLine

Related

Powershell Remove-WmiObject: Specified argument was out of the range of valid values. Parameter name: path

$binaryPath = "aValidPath"
$user = "aUser"
$passwrd = "aPassword"
$serviceName = "aService"
if (Get-Service $serviceName -ErrorAction SilentlyContinue)
{
# using WMI to remove Windows service because PowerShell does not have CmdLet for this
$serviceToRemove = Get-WmiObject -Class Win32_Service -Filter "name='$serviceName'"
$serviceToRemove | Stop-Service
Remove-WmiObject $serviceToRemove
}
$secured = ConvertTo-SecureString $passwrd -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ($user, $secured)
New-Service -name $serviceName -binaryPathName $binaryPath -displayName $serviceName -startupType Automatic -credential $creds
Everytime I try to execute this code I get:
Remove-WmiObject: Specified argument was out of the range of valid values.
Parameter name: path
And I can't figure out why.
If you hover over "$serviceToRemove" you get:
\\*redacted*\root\cimv2:Win32_Service.Name="*redacted*"
PSComputerName:"*redacted*"
RunspaceId:*redacted*
PSShowComputerName:$false
the $binaryPath is absolute accurate, as the "New-Service" portion uses it just fine. Remove won't remove the service though cause of that error. And $serviceToRemove.delete() doesn't exist.
Every single example website I can find says to use this:
Remove-WmiObject $serviceToRemove
or
$serviceToRemove.delete()
the way I fixed this script was by using this syntax instead
Remove-WmiObject -path $serviceToRemove
I recently updated everything to the latest Powershell versions and am using the latest "Visual Studio Code" for this. Note: I got the same error messages using the oldschool ISE.

Restarting service remotely as non-admin?

We host (on Server 2019) a few server apps that someone else knows and configures the internal settings through a web interface or client application. Occasionally, the manager of the application needs the service to be stopped or restarted because it hung or they made a setting change that requires a restart. I'd like to give them a script to do that on their own time rather than wait for me. These users are not able to log into the server.
As a sysadmin from a workstation, these kinds of PoSh lines work:
invoke-command -ComputerName $server -ScriptBlock { stop-Service 'XYZservice' }
Get-Service -ComputerName $Server -Name $Service | start-service
I've given the users "start/stop" permission on the services, and they (and not other users) can get the status of the service with:
Get-Service -ComputerName $Server -Name $Service
However, if my unprivileged user tries to actually start/stop the service, we get:
invoke-command -ComputerName $server -ScriptBlock { stop-Service 'XYZservice' }
[server.domain.edu] Connecting to remote server server.domain.edu failed with the following error
message : Access is denied. For more information, see the about_Remote_Troubleshooting Help topic.
And:
Get-Service -ComputerName $Server -Name $Service | start-service
start-service : Service 'XYZservice' cannot be started due to the following error: Cannot open XYZservice service on
computer 'server.domain.edu'.
Likewise:
(Get-WmiObject Win32_Service -filter "name='XYZservice'" -ComputerName $Server).StopService()
Get-WmiObject : Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
Is there some other PowerShell trick get this to work? What use is the 'start/stop' privilege if it only allows viewing the status?
Thanks.
#Lee_Dailey directed me to the most excellent solution. Following these excellent instructions with a little more info from here I was able to do exactly what I needed.
Specifically, I included this line in the .psrc file:
VisibleCmdlets = #{ Name = 'Start-Service'; Parameters = #{ Name = 'Name'; ValidateSet = 'XYZservice' }},
#{ Name = 'Restart-Service'; Parameters = #{ Name = 'Name'; ValidateSet = 'XYZservice' }},
#{ Name = 'Stop-Service'; Parameters = #{ Name = 'Name'; ValidateSet = 'XYZservice' }}
and then my unprivileged user could do this:
$sesh = new-pssession -ComputerName $Server -ConfigurationName $ConfigName
$cmdString = "restart-service $service"
$scriptBlock = [Scriptblock]::Create($cmdString)
invoke-command -session $sesh -ScriptBlock $scriptblock
remove-pssession $sesh
and nothing else (except the related start/stop commands) on the server.
This JEA ability should be more widely documented.

Powershell Automation for Services

I have 100+ servers for which I have to check services. On almost 5 servers we have identical services. Each boxes contains min of 3 services.
I am importing content from the file I saved in a location. For Server name I am good. For Service I have saved like service_starting_name* in column under the file like below
AA*
BB*
CC*
Below is the code. Is this good idea for automation as per below code ?
$ServerName = Get-Content "Absolutepath"
$Service = Get-Content "Absolutepath"
foreach ($Server in $ServerName) {
write-host $($server)
Get-Service -ComputerName $Server $Service
}
Also , How can we do a better display like, without printing the service name ?
Suppose, In Server X , 5 services , so if all services are running just print all good on that server.
I tried using if conditions but as there are many services , it is printing multiple times because for for each loop .
Please suggest.
You can play around with various things..
For testing I use localhost, when you try:
Get-Service -ComputerName localhost -DisplayName *sophos*
You get:
(Get-Service -ComputerName localhost -DisplayName *sophos*).count
result 10
(Get-Service -ComputerName localhost -DisplayName *sophos*).Status -contains "stopped"
result True
So if you use:
if (!((Get-Service -ComputerName localhost -DisplayName *sophos*).Status -contains stopped)) { Write-Host "Localhost: All ok" }
Or:
if (!(Get-Service -ComputerName $server -DisplayName $Service | select status | where {$_.Status -like "Stopped"})) { Write-Host "$($Server): All OK" }
You can use various checks for services "stopping" or whatever..
The above are just to give you ideas!
Hope it helps.

powershell: Changing start type of services on a windows 7 machine

Ive wrote a script to get the services of a machine and i want to mirror the StartMode to another machine. I cant think of how to achieve the latter: setting the services on the remote machine. Heres a script ive written so far:
#List of Issue of services
$NamesOfIssueServices = "Browser", "Dhcp", "Dnscache", "dwmrcs", "iphlpsvc", "LanmanServer", "LanmanWorkstation", "MMCSS", "MpsSvc", "Netlogon", "Netman", "netprofm", "NlaSvc", "nsi", "p2pimsvc","PNRPsvc","PolicyAgent", "SessionEnv", "stisvc", "W32Time", "WinHttpAutoProxySvc", "WinRM"
#get all services
$W32Services = Get-WmiObject Win32_Service
#filter wanted services
$IssueServices = $W32Services | Where-Object {$NamesOfIssueServices -contains $_.name}
#display wanted services
$IssueServices | Sort-Object name | ft Name, DisplayName, State, StartMode, StartName
If you're using PowerShell v2.0 you can easily do that by using the Set-Service cmdlet:
foreach ($service in $issueServices) {
$startMode = $service.StartMode
if ($service.StartMode -eq "Auto") {
$startMode = "Automatic"
}
Set-Service -ComputerName TheRemoteMachine -Name $service.Name -StartupType $startMode
}
The conditional statement is due to an incompatibility between the value "Auto" obtained from the StartMode property and the argument "Automatic" expected by the -StartupType parameter.

Powershell script to change service account

Does anyone have a Powershell script to change the credentials used by a Windows service?
Bit easier - use WMI.
$service = gwmi win32_service -computer [computername] -filter "name='whatever'"
$service.change($null,$null,$null,$null,$null,$null,$null,"P#ssw0rd")
Change the service name appropriately in the filter; set the remote computer name appropriately.
I wrote a function for PowerShell that changes the username, password, and restarts a service on a remote computer (you can use localhost if you want to change the local server). I've used this for monthly service account password resets on hundreds of servers.
You can find a copy of the original at http://www.send4help.net/change-remote-windows-service-credentials-password-powershel-495
It also waits until the service is fully stopped to try to start it again, unlike one of the other answers.
Function Set-ServiceAcctCreds([string]$strCompName,[string]$strServiceName,[string]$newAcct,[string]$newPass){
$filter = 'Name=' + "'" + $strServiceName + "'" + ''
$service = Get-WMIObject -ComputerName $strCompName -namespace "root\cimv2" -class Win32_Service -Filter $filter
$service.Change($null,$null,$null,$null,$null,$null,$newAcct,$newPass)
$service.StopService()
while ($service.Started){
sleep 2
$service = Get-WMIObject -ComputerName $strCompName -namespace "root\cimv2" -class Win32_Service -Filter $filter
}
$service.StartService()
}
The PowerShell 6 version of Set-Service now has the -Credential parameter.
Here is an example:
$creds = Get-Credential
Set-Service -DisplayName "Remote Registry" -Credential $creds
At this point, it is only available via download via GitHub.
Enjoy!
I created a text file "changeserviceaccount.ps1" containing the following script:
$account="domain\user"
$password="passsword"
$service="name='servicename'"
$svc=gwmi win32_service -filter $service
$svc.StopService()
$svc.change($null,$null,$null,$null,$null,$null,$account,$password,$null,$null,$null)
$svc.StartService()
I used this as part of by post-build command line during the development of a windows service:
Visual Studio: Project properties\Build Events
Pre-build event command line:
"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\installutil.exe" myservice.exe /u
Post-build event command line:
"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\installutil.exe" myservice.exe
powershell -command - < c:\psscripts\changeserviceaccount.ps1
A slight variation on the other scripts here, is below. This one will set credentials for any/all services running under a given login account. It will only attempt to restart the service if it was already running, so that we don't accidentally start a service that was stopped for a reason. The script has to be run from and elevated shell (if the script starts telling you about ReturnValue = 2, you're probably running it un-elevated). Some usage examples are:
all services running as the currently logged in user, on the local host:
.\set-servicecredentials.ps1 -password p#ssw0rd
all services running as user: somedomain\someuser on host somehost.somedomain:
.\set-servicecredentials.ps1 somehost.somedomain somedomain\someuser p#ssw0rd
Set-ServiceCredentials.ps1:
param (
[alias('computer', 'c')]
[string] $computerName = $env:COMPUTERNAME,
[alias('username', 'u')]
[string] $serviceUsername = "$env:USERDOMAIN\$env:USERNAME",
[alias('password', 'p')]
[parameter(mandatory=$true)]
[string] $servicePassword
)
Invoke-Command -ComputerName $computerName -Script {
param(
[string] $computerName,
[string] $serviceUsername,
[string] $servicePassword
)
Get-WmiObject -ComputerName $computerName -Namespace root\cimv2 -Class Win32_Service | Where-Object { $_.StartName -eq $serviceUsername } | ForEach-Object {
Write-Host ("Setting credentials for service: {0} (username: {1}), on host: {2}." -f $_.Name, $serviceUsername, $computerName)
$change = $_.Change($null, $null, $null, $null, $null, $null, $serviceUsername, $servicePassword).ReturnValue
if ($change -eq 0) {
Write-Host ("Service Change() request accepted.")
if ($_.Started) {
$serviceName = $_.Name
Write-Host ("Restarting service: {0}, on host: {1}, to implement credential change." -f $serviceName, $computerName)
$stop = ($_.StopService()).ReturnValue
if ($stop -eq 0) {
Write-Host -NoNewline ("StopService() request accepted. Awaiting 'stopped' status.")
while ((Get-WmiObject -ComputerName $computerName -Namespace root\cimv2 -Class Win32_Service -Filter "Name='$serviceName'").Started) {
Start-Sleep -s 2
Write-Host -NoNewline "."
}
Write-Host "."
$start = $_.StartService().ReturnValue
if ($start -eq 0) {
Write-Host ("StartService() request accepted.")
} else {
Write-Host ("Failed to start service. ReturnValue was '{0}'. See: http://msdn.microsoft.com/en-us/library/aa393660(v=vs.85).aspx" -f $start) -ForegroundColor "red"
}
} else {
Write-Host ("Failed to stop service. ReturnValue was '{0}'. See: http://msdn.microsoft.com/en-us/library/aa393673(v=vs.85).aspx" -f $stop) -ForegroundColor "red"
}
}
} else {
Write-Host ("Failed to change service credentials. ReturnValue was '{0}'. See: http://msdn.microsoft.com/en-us/library/aa384901(v=vs.85).aspx" -f $change) -ForegroundColor "red"
}
}
} -Credential "$env:USERDOMAIN\$env:USERNAME" -ArgumentList $computerName, $serviceUsername, $servicePassword
Considering that whithin this class:
$class=[WMICLASS]'\\.\root\Microsoft\SqlServer\ComputerManagement:SqlService'
there's a method named setserviceaccount(), may be this script will do what you want:
# Copyright Buck Woody, 2007
# All scripts provided AS-IS. No functionality is guaranteed in any way.
# Change Service Account name and password using PowerShell and WMI
$class = Get-WmiObject -computername "SQLVM03-QF59YPW" -namespace
root\Microsoft\SqlServer\ComputerManagement -class SqlService
#This remmed out part shows the services - I'll just go after number 6 (SQL
#Server Agent in my case):
# foreach ($classname in $class) {write-host $classname.DisplayName}
# $class[6].DisplayName
stop-service -displayName $class[6].DisplayName
# Note: I recommend you make these parameters, so that you don't store
# passwords. At your own risk here!
$class[6].SetServiceAccount("account", "password")
start-service -displayName $class[6].DisplayName
Just making #alastairs's comment more visible: the 6th parameter must be $false instead of $null when you use domain accounts:
$service = Get-WMIObject -class Win32_Service -filter "name='serviceName'"
$service.change($null, $null, $null, $null, $null, $false, "DOMAIN\account", "mypassword")
Without that it was working for 4/5 of the services I tried to change, but some refused to be changed (error 21).
$svc = Get-WmiObject win32_service -filter "name='serviceName'"
the position of username and password can change so try this line to find the right place$svc.GetMethodParameters("change")
$svc.change($null,$null,$null,$null,$null,$null,$null,$null,$null,"admin-username","admin-password")
What I cannot find in the default PS stack, I find it implemented in Carbon:
http://get-carbon.org/help/Install-Service.html
http://get-carbon.org/help/Carbon_Service.html (Carbon 2.0 only)
The given answers do the job.
Although, there is another important detail; in order to change the credentials and run the service successfully, you first have to grant that user account permissions to 'Log on as a Service'.
To grant that privilege to a user, use the Powershell script provided here by just providing the username of the account and then run the other commands to update the credentials for a service as mentioned in the other answers, i.e.,
$svc=gwmi win32_service -filter 'Service Name'
$svc.change($null,$null,$null,$null,$null,$null,'.\username','password',$null,$null,$null)
Sc config example. First allowing modify access to a certain target folder, then using the locked down "local service" account. I would use set-service -credential, if I had PS 6 or above everywhere.
icacls c:\users\myuser\appdata\roaming\fahclient /grant "local service:(OI)(CI)(M)"
sc config "FAHClient" obj="NT AUTHORITY\LocalService"

Resources