PowerShell to stop and disable a service. Reading the servers from a TXT file - powershell

I see some questions about this topic, but I cannot get it working
Get-Service -Name Spooler -ComputerName (Get-Content c:\tmp\scripts\Servers\iservers.txt) |
Stop-Service -PassThru | Set-Service -StartupType Disabled -whatif
The code executes for each server on the txt file, and stops de service, but not disable the service.
Any help to get it work and/or Troubleshooting???
Regards.

How to approach this kind of problem
In automation, we work up to complexity, meaning you should start simply and then add on more features until you see where it breaks.
Right now, you're trying to do a bunch of operations in one single line:
Load a list of computers and
Reach out to the computers and Stop a service and
Also while doing this, set the service to not automatically start.
There are a lot of problems you can run into, like "what happens if these PCs aren't enabled for remoting", or "what if you need a different account to handle stopping or disabling a service".
When you're trying to figure it all out in one-line, you're in for a bad and frustrating time.
How to fix it
Start simply. Start with one computer that's nearby and definitely turned on.
Begin with reading a service. Can you even get this operation to run?
Get-Service -ComputerName SomePC123 Spooler
Status Name DisplayName
------ ---- -----------
Running spooler Print Spooler
If you run into an error, then first figure out how to be able to remote into that one PC and see if the Print Spooler is running. Then, you will know what steps to deploy to all of your machines to prepare them for remoting.
Then, once you can check if a service is running, you can add on the next step, try to stop the service.
So your code would start to look like this:
$computers = get-content .\someTextFile.txt
forEach($computer in $computers){
$service = Get-Service -ComputerName $computer Spooler
"status of spooler on $computer is $($service.Status), with start type of $($service.StartType)"
#todo, set start type to Disabled...
}
Eventually, you will have migrated each step out of the one-liner and you'll know where and why any given command is failing. This is the way.

Related

Powershell Script cycles through machines but hangs if one loses network temporarily

I have a powershell script that parses a txt file which is full of machine names, then one by one, it creates a session to the system, runs a few commands, and moves to the next system. The script usually take about 10-30 seconds to run on each system depending on the case encountered in the script.
Once in a while the system that is currently being checked will lose the network connection for some various reason. When this happens the console starts writing yellow warning messages about attempting to reconnect for 4 minutes and then disconnects the session when it cannot reconnect.
Even if it establishes the connection again within the 4 minutes, it doesn't do anything after that, it's like the script just freezes. It won't move on to the next system and it doesn't stop the script, I have to manually stop it, or if i manually run the script, i can hit control+c to break out of the current loop, and it then moves on to the next machine in the list.
Is there any way to break out of the current loop if a warning is encountered so it can move on to the next machine? That would be my ideal solution. thanks!
Script is simple..
foreach($server in Get-Content .\machines.txt) {
if($server -match $regex){
invoke-command $server -ErrorAction SilentlyContinue -ScriptBlock{
command1
command2
command3
}
}
this is what happens
PS C:\temp> .\script.ps1
machine1
machine2
machine3
machine4
machine5
WARNING: The network connection to machine5 has been interrupted. Attempting to reconnect for up to 4 minutes...
WARNING: Attempting to reconnect to machine5 ...
WARNING: Attempting to reconnect to machine5 ...
WARNING: Attempting to reconnect to machine5 ...
WARNING: The network connection to machine5 has been restored.
But it never goes on to machine6
When i work remotely with multiple machines i usually start the processes on the machines in parallel. So i have less impact when single machines are timing out. I use powershell 7 ForEach-Object -Parallel Feature for this https://devblogs.microsoft.com/powershell/powershell-foreach-object-parallel-
feature/
Try something like this:
$Credential=Get-Credential
#all Necessary parameters must be in the Object i give to ForEach Object
$myHosts = #(
#Hosts i want to connect to with values i want to use in the loop
#{Name="probook";cred=$Credential;param1="one_1";param2="two_1"}
#{Name="probook";cred=$Credential;param1="one_2";param2="two_2"}
)
$var1="one"
$var2="two"
$myHosts | ForEach-Object -Parallel {
#Variables outside of this "Parallel" Loop are not available. Because this is startet as separate SubProcess
#All Values come from the Object i piped in the ForEach-Object
$myHost=$_
#This is written to your local Shell
Write-Host ("Computer: "+ $env:Computername)
Write-Host $myHost.param1
Write-Host $myHost.param2
Write-Host $myHost.cred.UserName
Invoke-Command -ComputerName $myHost.Name -Credential $myHost.cred -ArgumentList #($myHost.param1,$myHost.param2) -ScriptBlock {
#Variables outside of of this Invoke Command Script Block are not available because this is a new Remote-Shell on the remote Host
#Parameters in Ordner of -Argument List
param($param1,$param2)
#Do your things on the Remote-Host here
#This is not Visbible -> it is only written on the "remote Shell"
Write-Host $env:Computername
#Here you get Back Values from the remote Shell
$env:Computername
$param1
$param2
}
} -ThrottleLimit 5
Hmm his is indeed a Problem.
You could experiment with:
Start-Job
(https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/start-job?view=powershell-7.1)
Get-Job (https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/get-job?view=powershell-7.1)
Receive-Job (https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/receive-job?view=powershell-7.1)
So you have more control what the processes do.
You start Background Jobs with Start-Job. Start-Job deliveres Job Objects Back -> save them in a array or variables
With Get-Job you see all Jobs currently Running
with Receive-Job you get back the output from a Job so far. You can use receive-Job to get back all PSObjects a Background Job has written.
Cannot explain in Detail, but this woul be another try i would do for this job.

Using Powershell to pull services on multiple domain computers

My code currently is:
$Workstationlist=get-adcomputer -filter * -searchbase 'OU=Workstations, DC=example, DC=com' -SearchScope 2 | foreach {$_.Name}
foreach($workstation in $Workstationlist){
get-service -ComputerName $workstation -name wauaserv
}
It seems to pulling service statuses from the array that is being parsed by the foreach method, but after a few values are returned it will spit this error message in between which seems to point at the location I saved the script
Running wuauserv Windows Update
Stopped wuauserv Windows Update
Running wuauserv Windows Update
Stopped wuauserv Windows Update
get-service : Cannot find any service with service name 'wuauserv'. At
\locationIsavedthescript
The idea is that I will stop the wauaserv service on all workstations in the domain, I was then going to add lines to delete everything from the \windowsdistrobution\ folder. And then restart the service. This effectively kills any downloading pending install update. I can drop $workstation into a ls snippet easy enough but pulling services doesn't seem to work quite as clean. Not sure why it is point the get-service to the location that I saved the script.
Any ideas and explanations as to how to accomplish what I'm shooting for a bit more cleanly or at least hide the erroneous output since the command IS running against the array.
I hope this made sense to someone out there.

How to start a process on remote computer using powershell WMI?

We are upgrading our servers and need to stop our application before we perform update and then start it back again.
I was reading online about this and most of the links talk about remoting but some of the machines don't have PSRemoting enabled and therefore I need to stick to using wmi.
Would appreciate some pointers on this ?
To terminate the process I am using something like below:
$processes=Get-WmiObject -Class Win32_Process -ComputerName $Address -Filter "name='$ProcessName'"
foreach ($process in $processes)
{
$returnval = $process.terminate()
$processid = $process.handle
if($returnval.returnvalue -eq 0) {
write-host "The process $ProcessName `($processid`) terminated successfully"
}
else {
write-host "The process $ProcessName `($processid`) termination has some problems"
}
}
You don't say what OS and PS version(s) you are trying to deal with.
You are not saying what or if you are having issues with what you posted.
Even using only WMI, you still must have Windows WMI properly configured to do this as well as know Windows is not out of the boxed configured to let you what you are after without making all the proper WinRM, WMI and firewall manual configs.
It's far simpler just to enable PSRemoting via GPO.
Otherwise, you will need tp look toward maybe winrs.exe or MS SysInternals psexec.
winrs
Windows remote Management allows you to manage and execute programs remotely.
PsExec v2.2
Also back to my what OS and PowerShell version you are using. There is the
Invoke-Wmi​Method
Which can lead to stuff like this ---
Invoke-WmiMethod -ComputerName $TargetMachine -Namespace root\cimv2 -Class Win32_Process..."

Determining when machine is in good state for Powershell Remoting?

Update - the original question claimed that I was able to successfully perform an Invoke-Command and then shortly after was unable to; I thought it was due to processes going on during login after a windows upgrade.
It turns out the PC was actually starting, running a quick batch/cmd file, and then restarting. This is what was leading to being able to do PS Remoting and then suddenly not. The restart was quick enough after first boot that I didn't realize it was happening. Sorry for the bad question.
For the curious, the machine was restarting because of a remnant of the Microsoft Deployment Toolkit in-place upgrade process. The way MDT completes its task-sequence post-upgrade is problematic for many reasons, and now I've got another to count.
Old details (no longer relevant, with incorrect assumption that machine was not restarting after first successful Invoke-Command):
I'm automating various things with VMs in Hyper-V using powershell and powershell remoting. I'll start up a VM and then want to run some commands on it via powershell.
I'm struggling with determining when I can safely start running the remote commands via things like Invoke-Command. I can't start immediately as I need to let the machine start up.
Right now I poll the VM with a one second sleep between calls until the following function returns $true:
function VMIsReady {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)][object]$VM
)
$heartbeat = $vm.Heartbeat
Write-Host "vm heartbeat is $heartbeat"
if (($heartbeat -eq 'OkApplicationsHealthy') -or ($heartbeat -eq 'OkApplicationsUnknown'))
{
try
{
Invoke-Command -VMName $vm.Name -Credential $(GetVMCredentials) {$env:computername} | out-null
}
catch [System.Management.Automation.RuntimeException]
{
Write-Host 'Caught expected automation runtime exception'
return $false
}
Write-Host 'remoting ready'
return $true
}
}
This usually works well; however, after a windows upgrade has happened, there are issues. I'll get Hyper-V remoting errors of various sorts even after VMIsReady returns $true.
These errors are happening while the VM is in the process of first user login after upgrade (Windows going through "Hi;We've got some updates for your PC;This might take several minutes-Don't turn off your PC). VMIsReady returns true right as this sequence starts - I imagine I probably should be waiting until the sequence is done, but I've no idea how to know when that is.
Is there a better way of determining when the machine is in a state where I can expect remoting to work without issue? Perhaps a way to tell when a user is fully logged on?
You can use Test-WSMan.
Of run a script on the invoke that will receive a response from the server.
[bool]$Response | Out-Null
try{
$Response = Invoke-Command -ComputerName Test-Computer -ScriptBlock {return $true}
}catch{
return $false
}
if ($Response -ne $true){
return $false
}else{
return $true
}

Do I need to check instancestatus before I start or stop an azure VM?

I'm automating an Azure Virtual Machine using powershell, just starting and stopping one machine on a schedule. I've done this before, but I came across this code snippet which has an extra step, and I want to make sure I'm not missing something important:
# Shutdown VM(s)
$vmList = ('VM1', 'VM2', 'VM3')
$svcName = 'servicename'
For ( $vmCount = 0; $vmCount -lt $vmList.Count; $vmCount++) {
$vm = Get-AzureVM `
-ServiceName $svcName `
-Name $vmList[$vmCount]
if ( $vm.InstanceStatus -eq 'ReadyRole' ) {
Stop-AzureVM `
-ServiceName $vm.ServiceName `
-Name $vm.Name `
-Force
}
}
So I would have just called Stop-AzureVM... What does the check to InstanceStatus do? Does it, say, prevent the VM from shutting down if it's in the middle of installing updates? I'm thinking no, and that this is a check that's more important for other commands. But now I want to know.
I tried searching around and found it used in several unrelated code samples, but I've been unable to find an explanation.
ReadyRole is the steady state for an Azure VM. It essentially means it's not starting, stopping, provisioning, transitioning, stopped etc.
To me, I think the line $vm.InstanceStatus -eq 'ReadyRole' is just a basic check on the machine status. If you try to shutdown, or run any other command on the VM whilst it is busy doing something, your command will fail with an error anyway.
I just ran a test trying to stop a VM after I'd started it from the web management console and this is what I received:
stop-azurevm : ConflictError: Windows Azure is currently performing an operation with x-ms-requestid
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx on this deployment that requires exclusive access.
At line:1 char:1
In this case it's because the status was most likely starting
However, once the VM is stopped, issuing another stop command (whilst daft) works without any apparent problem.
PS > get-azurevm
ServiceName Name Status
----------- ---- ------
vm cloudservice StoppedDeallocated
PS > stop-azurevm -servicename cloudservice -name vm
OperationDescription OperationId OperationStatus
-------------------- ----------- ---------------
Stop-AzureVM xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Succeeded
So, in conclusion, I'd say it's a tidy bit of scripting diligence to avoid pointless / impossible operations during the script execution.