remote Multiple invocations using Powershell - powershell

I appreciate you taking the time to read this.
My issue is as follows: I'm trying to create a program that uses powershell to do the following:
Take a table generated outside of powershell
Loop calls to a powershell script with the parameters from the table
The powershell script calls a special type of .cmd file and then runs commands on it that are located in a different shared location.
Now my problem is with the 3rd point.
I'm currently using the following to call my script (and the arguements are just hard coded to get it working, they'll be generated by the calls from step 2 later on):
powershell.exe -ExecutionPolicy Bypass -Command {invoke-command -file \\sharedlocation\test5.ps1 -computername server1121 -argumentlist 7058,Jason}
The inside of test5.ps1 is currently:
param(
[Parameter(Mandatory=$true)]
[string] $Var1,
[Parameter(Mandatory=$true)]
[string] $Var2
)
$CommandsPath = "\\sharedlocation\testcommands.cmd"
$path = "C:\"+$Var1+"\TOOLS\"+$Var2+"launchtool.cmd"
$scriptPath = [scriptblock]::Create($path)
$out | invoke-command {PARAM($MyArg) $scriptPath } -ArgumentList $CommandsPath
I've also tried using
$CommandsPath = "\\sharedlocation\testcommands.cmd"
$path = "C:\"+$Var1+"\TOOLS\"+$Var2+"\launchtool.cmd & " + $CommandsPath
$scriptPath = [scriptblock]::Create($path)
$out | invoke-command {$scriptPath }
I've also tried to call with hardcoded testcommands instead of them being in a file.
Now my problem is in both cases, it DOES run launchtool.cmd, but it doesn't pass the testcommands.cmd file.
However when on the machine i run
C:\7058\TOOLS\Jason\launchtool.cmd & \\sharedlocation\testcommands.cmd
It works fine.
Any ideas what I'm doing wrong?

Try, invoke-expression "cmd.exe /c C:\7058\TOOLS\Jason\launchtool.cmd & \sharedlocation\testcommands.cmd"
cmd.exe /c is my best way to ensure consistency between cmd and powershell

Is the UNC Path accessible from powershell? Copy the testcommands.cmd to a local path and try if it works!
$CommandsPath = "\\sharedlocation\testcommands.cmd"
if(Test-Path $CommandsPath)
{
$path = "C:\"+$Var1+"\TOOLS\"+$Var2+"\launchtool.cmd & " + $CommandsPath
$scriptPath = [scriptblock]::Create($path)
$out | invoke-command {$scriptPath }
}

Related

How to get back result as returned object type when executing script as PowerShell.exe -File

If I have a script (script.ps1) like below:
$dir = Get-Item "C:\Windows"
$dir
and if I call it like:
$myDir = "script.ps1"
I get back the $dir object in $myDir and if I use:
$myDir.Name
I get:
Windows
I have a scenario where I need to call scripts using
$myDir = Powershell.exe -File "script.ps1"
With this, I end up getting the output in the form of a string array object and not in the form of the script-returned object as the command is apparently executed in a different session. Due to this, I can't call any property. E.g: $myDir.Name returns nothing
My question is, how do I get the returned object type back as the result in the above case. i.e, even when I execute
$myDir = Powershell.exe -File "script.ps1"
$myDir.Name should return Windows. Is it even possible?
Thanks for any help.
You can use the -OutputFormat parameter of powershell.exe.
#Write the command to file
"get-item c:\windows\" | out-file C:\temp\test.ps1
$justastring = powershell.exe -file c:\temp\test.ps1
#this is a string array
$justastring | get-member
$notastring = powershell.exe -outputformat xml -file c:\temp\test.ps1
#this is a Deserialized.System.IO.DirectoryInfo
$notastring | get-member
This output is also able to be saved as a file and brought back in later. See Import-CliXml and Export-CliXml. Note that because they are serialized objects they are no longer "live". Some of the methods you are used to seeing will not be there, property values will no longer update either. Its essentially a snapshot of the command output at the instant that the command ran.
You could try like this :
script.ps1 (returns value of $dir):
$dir = Get-Item "C:\Windows"
return $dir
script2.ps1 (returns name of $dir) :
$myDir = .\script.ps1
$myDir.Name
$myDir.Name returns : Windows

Setting variables for batch files in Powershell

I have a batch file named bar.cmd with a single line: ECHO %Foo%.
How can I set Foo in a Powershell script so that when I call & .\bar.cmd, it will print Bar?
To set an environment variable in PowerShell:
Set-Item Env:foo "bar"
or
$env:foo = "bar"
If you want to do it the other way around:
When you run cmd.exe to execute a shell script (.bat or .cmd file) in PowerShell, the variable gets set in that running instance of cmd.exe but is lost when that cmd.exe instance terminates.
Workaround: Run the cmd.exe shell script and output any environment variables it sets, then set those variables in the current PowerShell session. Below is a short PowerShell function that can do this for you:
# Invokes a Cmd.exe shell script and updates the environment.
function Invoke-CmdScript {
param(
[String] $scriptName
)
$cmdLine = """$scriptName"" $args & set"
& $Env:SystemRoot\system32\cmd.exe /c $cmdLine |
Select-String '^([^=]*)=(.*)$' | ForEach-Object {
$varName = $_.Matches[0].Groups[1].Value
$varValue = $_.Matches[0].Groups[2].Value
Set-Item Env:$varName $varValue
}
}

Sending each line from text file to remote computer?

This is my script to whitelist IP in a remote system. I want my script to read data from a text file on my local system and then foreach line I want to execute the scriptblock on the remote server.
Text file looks like this:
url1
url2
url3
Here is my code:
Invoke-Command -ComputerName $($server.text) -Credential ciqdev\riteshthakur {
param($a, $b, $c, $url)
Set-Location "C:\Windows\System32\inetsrv"
$url | foreach {
.\appcmd.exe set config "$_" -section:system.webServer/security/ipSecurity /+"[ipAddress='$($a)',allowed='$($c)',subnetMask='$($b)']" /commit:apphost
}
} -ArgumentList $ip.text, $mask.text, $allowed, (get-content "File location")
This adds provided ip to all the pages in all the websites in IIS. Please help.
EDIT: Improved efficiency by generating the command dynamically, and invoking it once.
I'd suggest using a technique similar to the following, where you read in the text file as an array of lines, and then iterate over each line, generating the commands that you want to run on the remote system.
Once you've generated the command as a string, you simply call the static [ScriptBlock]::Create() method to create a ScriptBlock object, based on the command string, and pass that into Invoke-Command.
I'd suggest you get familiar with the concept of PowerShell Splatting, which I talk about in this YouTube video: https://www.youtube.com/watch?v=CkbSFXjTLOA. It's a really powerful concept, and helps make your code easier to read. The example code below uses PowerShell Splatting (available in PowerShell 3.0 and later).
### Read the text file on the local system
$Whitelist = Get-Content -Path IPwhitelist.txt;
### Generate the stub for the remote command
$RemoteCommand = #'
param($a, $b, $c)
Set-Location -Path C:\Windows\System32\inetsrv
'#
### Add more commands to the remote command
foreach ($Line in $Whitelist) {
$RemoteCommand += '{1}.\appcmd.exe set config "{0}" -section:system.webServer/security/ipSecurity /+"[ipAddress=''$($a)'',allowed=''$($c)'',subnetMask=''$($b)'']" /commit:apphost' -f $Line, "`n";
}
### Invoke the entire remote command (once)
$Command = #{
ComputerName = $Server.Text
Credential = Get-Credential -Credential ciqdev\riteshthakur
ScriptBlock = [ScriptBlock]::Create($RemoteCommand);
ArgumentList = #($ip.text, $mask.text, $allowed)
}
Invoke-Command #Command;
Just read the file using the Get-Content cmdlet and iterate over each item using the Foreach-Object cmdlet:
Invoke-Command -ComputerName $($server.text) -Credential ciqdev\riteshthakur {
param($a, $b, $c, $urls)
Set-Location "C:\Windows\System32\inetsrv"
$urls | Foreach {
.\appcmd.exe set config $_ -section:system.webServer/security/ipSecurity /+"[ipAddress='$($a)',allowed='$($c)',subnetMask='$($b)']" /commit:apphost
}
} -ArgumentList $ip.text, $mask.text, $allowed, (Get-Content 'Path_to_your_file')

Script not running at powershell

I have this snippet of code
$actDate=Get-Date -Format 'yyyy-MM-dd'
Start-job -name "FMLE" -command { cmd.exe /c 'c:\Program Files (x86)\Adobe\Flash Media Live Encoder 3.2\FMLEcmd.exe' /p C:\tasks\testing_2\testing 2_$actDate.xml /ap username:password /ab username:password /l C:\Users\acruz\AppData\Local\Temp\temp.log }
I know for sure, that the var $actDate is not being replaced at the line, how shuld I do that?
My two questions are: how to replace the $actDate for its value and how to save the result of the job to one log
Thanks for your help
EDIT
This does not works either:
$actDate = (Get-Date -Format 'yyyy-MM-dd')
$Args = ("/p C:\tasks\testing_2\testing 2_$actDate.xml","/ap username:password", "/ab uysername:password", "/l C:\Users\acruz\AppData\Local\Temp\temp.log")
$Args
$j = Start-job -name "FMLE" -ScriptBlock { & 'c:\Program Files (x86)\Adobe\Flash Media Live Encoder 3.2\FMLEcmd.exe' #args } -ArgumentList $args
Get-Job $j.Id
Receive-Job -Job $j | Out-File 'C:\Users\acruz\AppData\Local\Temp\temp.log' -encoding ASCII -append -force
Although $Args has the right information...
For your first question, you need to include the path using double quotes. A suggestion if you can then remove the space in the testing 2
"C:\tasks\testing_2\testing2_$actDate.xml"
To log result of the job use Receive-Job cmdlet.
One more try:
Try to put all paths in double quotes and then surround everything with a single quote after the cmd.exe /c part as shown below. Try to achieve something simpler with a simple task and then try to add complexity
$job = Start-Job -name "Hel" -Command { cmd.exe /c '"C:\Program Files (x86)\Mozilla Firefox\firefox.exe" /?'}
I was able to make it work by doing it like this:
Start-job -Verbose -ScriptBlock {
$actDate = Get-Date -Format yyyy-MM-dd
cd "c:\Program Files (x86)\Adobe\Flash Media Live Encoder 3.2\"
.\FMLEcmd.exe /p "C:\site.com.mx\tasks\test_23445678\test 23445678_$actDate.xml" /ap user:password /ab user:password /l C:\site.com.mx\task.log
}
By doing it with -command it does not work, cause it does not replace the variable at all. Also, if I do it with -ArgumentList either was replacing the variable $actDate, so I though that may be by adding the whole script within the block it was work... and indeed, it did it...
So I don't know why it does not works, but this is a fix for me.

Error running PowerShell script with .bat

I'm trying to run a .bat file calling a shell .ps1 file.
I've tested my script directly in powershell and there it works.
But when I run the .bat, a error occurs saying to me something like [ The string started with: (...) does not contain the terminator " ]
My .bat file:
powershell.exe -command "& C:\Users\I\Desktop\teste.ps1"
My .ps1 file:
$scripts = 'C:\Users\I\Desktop\Teste_0.1\Teste\Teste_run.bat', 'C:\Users\I\Desktop\Teste_0.2\Teste\Teste_run.bat','C:\Users\I\Desktop\Teste_0.3\Teste\Teste_run.bat' |%{ Start-Job –scriptblock (iex "[Scriptblock] { $_ } ")}| wait-job
To call another script from powershell, I don't think you need to use -command. You should just be able to call the script directly.
powershell -nol -noe C:\Users\I\Desktop\teste.ps1
Alternately, here is another clever way to do it.
#echo off
more +4 "%~dpnx0" >> temp.ps1 && powershell -nol -noe .\temp.ps1
exit /b
$scripts = 'C:\Users\I\Desktop\Teste_0.1\Teste\Teste_run.bat', 'C:\Users\I\Desktop\Teste_0.2\Teste\Teste_run.bat','C:\Users\I\Desktop\Teste_0.3\Teste\Teste_run.bat' |%{ Start-Job –scriptblock (iex "[Scriptblock] { $_ } ")}| wait-job