Use Powershell to run Psexec command - batch-file

First post so please don't hurt me. I've searched around but can't seem to find a way to do what I want. I've made a script that copies a folder I have to numerous computers at \$computer\c$. In these folders is a batch file that runs an .exe. What I want to do is have Powershell pull from the same computers.txt that I used to copy the folder and then use psexec to run the batch file. I could do this all manually but scripting it seems to be a problem, here's what I thought would work but apparently not.
$computers = gc "C:\scripts\computers.txt"
foreach ($computer in $computers) {
if (test-Connection -Cn $computer -quiet) {
cd C:\pstools
psexec \\%computer cmd
C:\Folder\install.bat"
} else {
"$computer is not online"
}
}

Ok, let's take it from the top then.
$computers = gc "C:\scripts\computers.txt"
That loads the contents of the "computers.txt" file into the variable $computers. Simple enough, no issues there.
Next we have a ForEach loop. It splits up the contents of $computers and processes each line (presumably the name of a computer) as $computer against all the code within the curly braces.
foreach ($computer in $computers) {
That loop starts up with a standard If-Then statement. If (condition) then {do stuff}. In this case it is testing to see if the $computer is available on the network. If it is, then it attempts to run PSExec on it. If it isn't online it runs the Else clause, we'll get to that in a second.
if (test-Connection -Cn $computer -quiet) {
Then it changes directory. Kind of pointless, but ok, whatever. You could have just called it explicitly, such as C:\PSTools\PSExec.exe <arguments> and saved a line, but there's really no harm done.
cd C:\pstools
Then you are calling PSExec, though there's a little syntax error here. It should be $computer and not %computer. Also, it should just have the command you want to execute, not cmd and the command on a second line. You may have better results if you use the Call operator (&) to make powershell realize that it's trying to execute something and not run a cmdlet or function or what not.
& psexec \\$computer C:\Folder\install.bat
After that is the Else clause that says if the computer isn't online to write the string "$computer is not online" followed by closing braces for the Else clause and the ForEach loop.
} else {
"$computer is not online"
}
}
Edit: Ok, your finished script should look something like this (enclosed target in quotes in case there are spaces in the path):
$computers = gc "C:\scripts\computers.txt"
foreach ($computer in $computers) {
if (test-Connection -Cn $computer -quiet) {
& C:\pstools\psexec.exe \\$computer "C:\folder\install.bat"
} else {
"$computer is not online"
}
}

I realize this question is from 2014, and this answer will not directly address the question the user asked. But for people who come across this question these days, I want to throw out there that you don't need to use PSExec if you're using PowerShell*. Since you're already in PowerShell, just use Invoke-Command.
Syntax would be
Invoke-Command -ComputerName $Computer -ScriptBlock { C:\Folder\install.bat }
It's really that easy.
*Requires PowerShell remoting to be enabled on the target server. << Some people are using PSExec to enable PSRemoting by running winrm quickconfig... So it's still a valid question and the two things "PSExec" and "PSRemoting" are different.

Give this a try. You had a % where you wanted a $. Also the cmd.exe call is unnecessary.
$computers = gc "C:\scripts\computers.txt"
foreach ($computer in $computers) {
if (test-Connection -Cn $computer -quiet) {
cd C:\pstools
psexec \\$computer "C:\Folder\install.bat"
} else {
"$computer is not online"
}
}

use & (call) operator , &psexec \$computer "C:\Folder\install.bat"

Related

Exchange Powershell Script consumes all system resources on local pc

I'm back!
Anyway, I'm running an exchange script to find emails that contain a specific list of keywords for a specific set of users, defined as users and terms in the script below, and it works. However after about an hour or so of running, it's consuming obnoxious amounts of memory, 12 - 14 GB. and running very slowly.
It does flux between 3 GB and 14 GB, so I don't know if this is simply normal, expected behavior or if its' something wrong with my script. I am aware that I'm using a sorta(?) depreciated commandlet in the search-mailbox function, but I'm only searching about 300 users and 21 terms, so I don't think I need to use the new-mailboxsearch commandlet.
Script for Reference
$users = Get-Content x:\xxx\xxx\users.txt
$terms = Get-Content x:\xxx\xxx\Terms.txt
ForEach ($term in $Terms) {
ForEach ($line in $users) {
$Results = Search-Mailbox -Identity $line -SearchQuery $term -TargetMailbox SearchResults2 -TargetFolder $term -LogLevel Full | Select Identity,TargetFolder,ResultItemsCount
Add-Content -Path x:\xxx\xxx\outputfile.txt -Value "$($term);$($line);$($Results.TargetFolder);$($Results.ResultItemsCount)"
}
}
Anyway, any help is, as always, greatly appreciated.
Thanks!
Does foreach-object fair better?
$terms | ForEach { $term = $_
$users | ForEach { $line = $_
..
}
}
The problem wasn’t with the script itself, it was the environment we were running it in.
For some reason running the script inside of the integrated scripting environment Powershell ISE, was causing the script to suck up crazy amounts of memory, eventually halting the system. By simply launching it outside of the ISE we were able to get the script to behave normally:
Thanks to everyone who replied!

no error or result when trying to use PowerShell execute a .bat file on a remote PC

I have read many examples of how to run a .bat file on a remote PC and the one I need is based on the PowerShell invoke-command:
invoke-command -computername RemotePC -scriptblock {start-process c:\things\Thing.bat}
When I run that line of PowerShell it does not run the .bat file on the remote pc and returns no error. Any help please.
I have found that this works:
invoke-command -computername RemotePC -scriptblock {& "c:\things\Thing.bat"}
Start-Process isn't intended to be used to start scripts but instead to start processes, e.g. real exectuables.
You have some alternatives though (one you allready found):
Using the Dot sourcing {. "C:\things\thing.bat"}
Using the Call operator {& "C:\things\thing.bat"}
Differences between both operations (ref.: Source-Powershell-SS64):
Dot sourcing runs a function or script within the current scope.
unlike the call operator (&) which will run a function or script, but
it is not added to the current scope.

Nested foreach loops to install printers

I have a need to install multiple printers to multiple servers and was wanting to script this so that it would only need to be configured once.
I am using PowerShell to query 2 CSV files; 1 to get a list of computers to install printers too and the other to list the Name, IP, Driver, Location of the needed printers. I am using 2 foreach loops to accomplish this and a break to try to get it to loop correctly.
Currently with the break :outer where it is the first PC gets all printers installed. If I move it to inside the foreach ($printer in $printers) loop it will only install the first printer to all computers.
Thank you for any assistance that anyone can provide.
$computerfile = Import-Csv C:\computerlist.csv
$printers = Import-Csv C:\printers2.csv
foreach ($computer in $computerfile) {
"computer: $computer"
:outer
foreach ($printer in $printers) {
Add-PrinterPort -ComputerName $computer.Name -Name $printer.IP -PrinterHostAddress $printer.IP
Add-Printer -ComputerName $computer.Name -Name $printer.Name -DriverName $printer.Driver -PortName $printer.IP -ShareName $printer.Name -Location $printer.Location
Set-printer -ComputerName $computer.Name -Name $printer.Name -Shared $false -Published $true
}
break :outer
}
Please remove the break :outer from your code.
What is happening is that the first loop starts running, and enters the second loop only once! because of the break, and jumps to the next computer.

How to add an element back into an array

First off, I am new to Powershell, so please excuse any poor coding here.
I'm trying to write a script that queues commands. We have a piece of equipment that can only handle 32 commands at one time. What I want to do is build a command file that has many more than 32 commands and then it automatically queues any command after 32 until another free slot opens up. Everything seems to be working except when I go into my 'else' statement. I attempt to add the current $_ back into the array so it doesn't get lost and gets reprocessed, but this does not seem to be working.
I build my array from a text file:
$commands = #(Get-Content C:\scripts\temp\commands.txt)
When I try to add the current $_ value back into the array during the 'else' pause statement, it never adds back in, so that one particular command never enters back into the array. So if my script has to pause 15 times, 15 commands will never get ran as they just get processed by the 'else' statement and get thrown out of my 'if/else' loop.
Here's my if/else loop:
$commands | ForEach-Object {
If (Test-Path C:\scripts\temp\active_migrations.txt){
Remove-Item C:\scripts\temp\active_migrations.txt
}
CMD.EXE /C "ssh $user#$cluster -pw $PlainPassword lsmigrate" > C:\scripts\temp\active_migrations.txt
$count = Get-Content C:\scripts\temp\active_migrations.txt
$number_of_migrations = $count.Length / 6
IF ($number_of_migrations -lt 32)
{
Write-Host "Migrations are currently at $number_of_migrations. Proceeding with next vdisk migration."
Write-Host "Issuing command $_"
Write-Host "There are still $migrations_left migrations to execute of $total_migrations total."
Write-Host ""
CMD.EXE /C "ssh $user#$cluster -pw $PlainPassword $_"
SLEEP 2
$migrations_left--
}
ELSE
{
Write-Host "Migrations are currently at $number_of_migrations. Sleeping for 1 minute"
$commands = #($commands + $_)
Write-Host "There are still $migrations_left migrations to execute of $total_migrations total."
SLEEP 60
}}
}
Any help is much appreciated.
Thank you Paul. I ended up dumping things during the else statement to a temp array and it works! Appreciate the assistance.
function commands_temp
{
if (Test-Path C:\scripts\temp\commands_temp.txt)
{
$commands=#()
$commands=#(Get-Content C:\scripts\temp\commands_temp.txt)
Remove-Item C:\scripts\temp\commands_temp.txt
Proceed
}
Else
{
Write-Host "All migrations are either complete or successfully executed. Check the GUI or run lsmigrate CLI command for full status."

powershell condition not processing all values in an array

I am newb in powershell but keen to put it into good use. I am working on a script which should do the following:
Check for the existence of a specific folder in a specific location (mapped drive)
If the folder exists, then return a listing
If the folder does not exist, then create it.
Ideally, I would like to improve it in terms of check-if exists-remove-item (subdir); check-if not exists-create
This is to facilitate the automation of an archiving process for a specific piece of software. What I have right now sort of works but I cannot figure out how to make it do exactly what I want.
Here is the code:
$X = #("Server1", "Server2", "Server3")
$ChkFile = "f:\archive\$server\AABackup"
$myFolder = "f:\archive\$server"
$DirExists = Test-Path $ChkFile
Foreach ($server in $x){
IF ($DirExists -eq $True) {
Remove-Item $ChkFile -recurse
import-Module "AppAssurePowerShellModule"
start-archive -protectedserver $server -startdate "18/03/2013 5:30 PM" -path "f:\archive\$server"
}
Elseif ($DirExists -ne $True) {
New-Item -path $myFolder -itemType "directory"
import-Module "AppAssurePowerShellModule"
start-archive -protectedserver $server -startdate "18/03/2013 5:30 PM" -path "f:\archive\$server"
}
}
Yes I know it is rough... It's my first attempt though so I could definitely benefit from the more experienced scripters here.
Thanks in advance.
You're setting $ChkFile, $myFolder and $DirExists before the loop, which means that $server doesn't have a value yet, and -- since variables are evaluated immediately -- these variables will contain garbage.
You need to move those three statements inside the foreach loop.
You also don't need to compare -eq $true; this would be simpler:
if ($dirExists) {
# ...
}
else {
# ...
}
Oh, and you only need to import the module once -- do it at the top of the script.
Also, in terms of style: PowerShell keywords should generally be in lowercase: foreach, if, elseif; be consistent when invoking cmdlets (you have a mixture of lower-case and Upper-Case and lower-Case. Note that these don't make any real difference, but using a consistent style makes the code easier to read for someone else coming to it. I'm basing those rules on what I've seen on TechNet, PoshCode, and here, by the way -- they're definitely subjective.
And, personally, I use $lowerCase for local variables, and $UpperCase for parameters (because it makes the auto-generated help text look nicer).
Give this a shot.
$specificPath = Z:\MYDir\MySubDir
if(!(Test-Path $SpecificPath))
{
Mkdir $SpecificPath
}
Else
{
Get-ChildItem $specificPath
}
Explanation:
This checks for the existence of the path contained in $SpecificPath using Test-Path, which will return a Boolean value.
Since I used the (!()) syntax in my IF statement, it will try to evaluate the statement to false, IF the path DOES NOT EXIST, it will run the first block of code.
MkDir is an alias for New-ItemProperty, if you pass just a path to Mkdir it will make a directory, similar to the windows MkDir command.
If the statement contained in the IF statement does not evaluate to false, the ELSE block will run, and execute a get-childitem on the $specificpath variable.

Resources