How do you Stop / Start Services on 140+ servers quickly? - powershell

We have over 140+ servers and when we need to perform an upgrade we need to stop 10 different services on each server before we can perform an upgrade. What is the fastest way to turn off all the services we require on all the servers ?
Thanks

I was just searching for something similar and if this help you out man, that's great!
$Servers = Get-Content c:\serverlist.txt
Invoke-Command -ComputerName $Servers -ScriptBlock {
if ((Get-Service -Name BITS).Status -e "Running") {
Stop-Service -Name SERVICENAME
}
}
This would need a file name serverlist.txt with the names of the servers on it listed only one on each line and it being located in C:\, you can also name whatever you want and place it wherever, just change the path.

Related

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

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.

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..."

net use * /delete /y doesn't resolve error "New-PSDrive : Multiple connections to a server ...."

This New-PSDrive command
New-PSDrive -Name Z -PSProvider FileSystem -Root \\$j\share -Credential $credentials -ErrorAction Stop
Causes the error
New-PSDrive : Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the
server or shared resource and try again
I have tried disconnecting all drives first, then creating new drive,
net use * /delete /y
New-PSDrive -Name Z -PSProvider FileSystem -Root \\$j\share -Credential $credentials -ErrorAction Stop
I get the output
You have these remote connections:
\\TSCLIENT\C
\\TSCLIENT\D
\\TSCLIENT\E
\\TSCLIENT\I
\\TSCLIENT\J
\\TSCLIENT\K
\\TSCLIENT\L
\\TSCLIENT\R
\\TSCLIENT\T
Continuing will cancel the connections.
The command completed successfully.
New-PSDrive : Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the
server or shared resource and try again
I also tried removing the Z drive first
Remove-PSDrive -name Z
New-PSDrive -Name Z -PSProvider FileSystem -Root \\$j\share -Credential $credentials -ErrorAction Stop
And get error
Remove-PSDrive : Cannot find drive. A drive with the name 'Z' does not exist.
...
New-PSDrive : Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the
server or shared resource and try again
How to fix?
UPDATE
I even rebooted the machine and changed the drive name, but I still get the same error of "New-PSDrive: Multiple connections ......"
UPDATE 2
I have also tried using IP address instead of computer name, but that doesn't work either, http://support.microsoft.com/kb/938120
I found workaround to this problem that seem to always work. You need to change the computer name, but since this will also stop working eventually just as with server name and IP, you need the option to specify arbitrary number of new computer names resolving to the same computer.
The obvious choice is hosts file. You can add any number of aliases to the IP to it. Afterwards, use the alias that isn't already blocked.
==== EDIT ===
Here is the handy function:
<# Use unique hostname for this script by altering hosts table to handle situation with multiple different scripts
using this share with different creds which leads to following Windows error:
Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed.
Disconnect all previous connections to the server or shared resource and try again
#require -RunAsAdministrator
#require -module PsHosts
https://stackoverflow.com/a/29059100/82660
#>
function Set-UncHostnameAlias($UncPath) {
$remote_hostname = $UncPath -split '\\' | ? {$_} | select -First 1
$remote_alias = (Split-Path -Leaf $MyInvocation.ScriptName) -replace '.ps1$'
$hostEntry = Get-HostEntry $remote_alias* -ea 0 | ? { $_.Comment -eq $remote_hostname } | select -First 1
if (!$hostEntry) {
$remote_alias += (Get-HostEntry $remote_alias*).Count + 1
Write-Verbose "Adding alias $remote_alias => $remote_hostname"
$remote_ip = Test-Connection -ComputerName $remote_hostname -Count 1 | % IPV4Address | % IPAddressToString
Add-HostEntry -Name $remote_alias -Address $remote_ip -Force -Comment $remote_hostname | Out-Null
} else {
$remote_alias = $hostEntry.Name
Write-Verbose "Using $remote_hostname alias: $remote_alias"
}
$UncPath.Replace("\\$remote_hostname", "\\$remote_alias")
}
Do this on the start of the script:
$PathAlias = Set-UncHostnameAlias $Path
and used aliased path afterwards with the New-PSDrive. This works always, even if some other scripts on the same system use different credentials for the same server.
I was having the same issue with local scripts and found this to be a simple solution. The Get-CimInstance returns all of the mapped network connections, then just pass that to the net use /delete /y command.
$shareDrives = Get-CimInstance -ClassName Win32_NetworkConnection
if ($shareDrives -ne $null)
{
foreach ($shareDrive in $shareDrives)
{
Write-Host "`nRemoving mapped drive $($shareDrive.LocalName)"
net use $shareDrive.LocalName /delete /y
}
}
else
{
Write-Host "`nNo mapped drives to remove!"
}
Using the FQND worked for me..
How to find out FQDN??
ping -a -n 1
Pinging [This is the FQND!!!!] [192.168.0.1] with 32 bytes of
Reply from 192.168.0.1: bytes=32 time=1ms TTL=128
You need to use the FQDN instead of the NetBios name.
My script needs to be client computer independent since other members of my team might run it. This works for me. Not sure if the "Write-Host" is needed but it also doesn't get in the way. Also, there is some sort of error that doesn't affect using the drive again if it already exists.
if (Get-PSDrive DLL_NEW_TEMP -ErrorAction SilentlyContinue){Write-Host "DLL_NEW_TEMP Drive exists"}
else{
New-PSDrive -Name DLL_NEW_TEMP -PSProvider FileSystem -Root \\WTDHSxxxL32\d$\ServerDLLDev\New_DLL_temp_location -Credential $credential
}
if (Get-PSDrive DLL_WORKING -ErrorAction SilentlyContinue){Write-Host "DLL_WORKING Drive exists"}
else{
New-PSDrive -Name DLL_WORKING -PSProvider FileSystem -Root \\WTDHSxxx32\d$\ServerDLLDev -Credential $credential
}
The below information I found on here
This error message means that you already have a connection to that UNC path, whether it's defined on your computer or not. Windows only allows you to connect to a particular UNC path with one username, regardless of the number of connections to that UNC. If you use the same username for all connections to a UNC from your computer then you shouldn't run into this error.
My issue and its solution: I was connected to the shared drive and it was open in my session with my credentials and at the same I've tried to run New-PSDrive with other credential. After closing my session with the shared drive the command worked like a pro.
If you have to use a different username to connect, then one workaround is to connect to the UNC using an IP address or other alias so that it looks like it's a different path. This is also the workaround recommended by Microsoft:

Second hop authentication in vmware vco workflow

I'm writing a flow that suppose to copy files from the Netapp storage to one of my VMs, but i have a problem regarding Second Hop authentication.
I found a way to enable a second hop functionality using powershell -CredSSP, but that option does not exist in vco powershell plugin
Are any other way to do that? Or some way to enable cressp in the plugin..
Thanks =)
One workaround that I've used is to deceive the second hop by not using the second hop. First I copy the files that I want each computer to run to the local computer before remoting and running the command. You can accomplish this by using Windows administrative shares. By default, Windows shares their local drives (\\ComputerName\c$ or \\ComputerName\e$). So my script sorta went like this:
$Computers = Get-Content Computerlist.txt
$File = \\Server1\applications$\file.exe
foreach($Computer in $Computers){
copy $file "\\$Computer\c$"
invoke-command -ComputerName $Computer -ScriptBlock {& 'C:\file.exe'}
del "\\$Computer\c$\file.exe"
}

Powershell Remoting Speeding up a Foreach Loop Hosted Exchange

I have a CSV of email adddresses and Departments that I need to set on Live#edu. The command I currently have looks something like this:
Import-CSV departments.csv | ForEach-Object { Set-User $_.EmailAddress $_.Department }`
The problem is, this operation takes FOREVER.
My first thought is that it would be great to have the ForEach-Object command actually be forwarded over to the remote machine, so that it will only need to create the one pipeline between the two machines, but when I go into the PSSession, there doesn't seem to be any foreach-object available. For reference, How I Import the PSSession is:
Import-PSSession(New-PSSession -ConfigurationName Microsoft.Exchange `
-ConnectionUri 'https://ps.outlook.com/powershell' `
-Credential (Get-Credential) `
-Authentication Basic -AllowRedirection)
Is there a better way that I can import the session to allow ForEach-Object to be remote, or to import an aliased version of the remote foreach-object, perhaps as ForEach-Object-Remote, or perhaps does anybody have something better to suggest to streamline this process?
UPDATE:
A Couple Things I've tried:
Using the -AsJob switch on the implicitly remoted command.
Import-CSV departments.csv | ForEach-Object { Set-User $_.EmailAddress $_.Department -AsJob }
This, unfortunately, doesn't work because there are throttling limits in place that don't allow the additional connections. Worse than that, I don't even know that anything went wrong until I check the results, and find that very few of them actually got changed.
Importing the ForEach-Object under a different name.
Turns out that adding a prefix is easy as putting -Prefix RS in the Import-PSSession Command to have things like the ForEach-Object from the Remote Session become ForEach-RSObject in the local session. Unfortunately, this won't work for me, because the server I'm connecting to does not does not have the Microsoft.Powershell ConfigurationName available to me.
UPDATE 2: The Set-User cmdlet seems to be Microsoft provided for Live#edu administration. Its purpose is to set User attributes. It is not a script or cmdlet that I am able to debug. It doesn't take pipeline input, unfortunately, so that would not be able to fix the issue.
As Far as I can tell, the problem is that it has to construct and tear down a pipeline to the remote machine every time this command runs, rather than being able to reuse it. The remote ForEach idea would have allowed me to offload that loop to avoid having to create all those remote pipelines, while the -asJob would have allowed them to all run in parallel. However, it also caused errors to fail silently, and only a few of the records actually get properly updated.
I suspect at this point that I will not be able to speed up this command, but will have to do whatever I can to limit the amount of data that needs to be changed in a particular run by keeping better track of what I have done before (keeping differential snapshots). Makes the job a bit harder.
EDIT: Start-Automate left a very useful help, unfortunately, neither of them work. It is my feeling at this point that I won't find a way to speed this up until my provider gives access to more powershell cmdlets, or the exchange cmdlets are modified to allow multiple pipelines, neither of which I expect to happen any time soon. I am marking his answer as correct, despite the ultimate result that nothing helps significantly. Thanks, Start-Automate.
You can speed up your script and also avoid trying to make two connections to the server by the use of the foreach statement, instead of Foreach-Object.
$departments = #(import-csv .\departments.csv)
foreach ($user in $departments) {
Set-User $user.EmailAddress $user.Department
}
If you need to batch, you could use the for statement, moving forward in each batch
for ($i =0; $i -lt $departments.Count; $i+=3) {
$jobs = #()
$jobs+= Invoke-Command { Set-User $departments[$i].EmailAddress $departments[$i].Department } -AsJob
$jobs+= Invoke-Command { Set-User $departments[$i + 1].EmailAddress $departments[$i + 1].Department } -AsJob
$jobs+= Invoke-Command { Set-User $departments[$i + 2].EmailAddress $departments[$i + 2].Department } -AsJob
$jobs | Wait-job | Receive-job
}
Hope this helps