Raw string to a Powershell script through command line - python-2.7

I have powershell script which takes input from a python GUI. And the script looks like (foo.ps1):
#.\foo.ps1
[String]$UserName = $args[0]
[String]$Password = $args[1]
[String[]] $ComputerName = $args[2]
[String[]] $Users = $args[3]
Write-Output "Username is $UserName"
Write-Output "Password is $Password"
foreach ($Computer in $ComputerName) {
Write-Output "Computer is $Computer"
}
foreach ($User in $Users) {
Write-Output "Users is $User"
}
And my execution line in powershell window looks like:
PS dir>powershell -executionpolicy bypass -Command .\foo.ps1 "my_username" "my_password" "Computer1, Computer2" "User1, User2"
Problem arises when my password has special characters like '}', '{','(', ')', ',', '&', ';' need to be passed within these apostrophes. But when it contains characters like ' " `, it throws an exception: "The string is Missing the terminator: '.
How do I solve it. I use python subprocess to input that line to powershell using variables from that python script.
import subprocess
process = subprocess.Popen(['powershell.exe',"""powershell -executionpolicy bypass -File Foo.ps1 "username" "password" "computer1,computer2" "user1,user2" """], stdin=subprocess.PIPE,stdout=subprocess.PIPE)
print(process.communicate())
Expecting a simple way to address this issue.
When we enter the special characters through prompt, that is from 'read-host' line in powershell, it accepts everything without any trouble.
Is there anyway that I could automate that read-host prompt input using python subprocess ?

Try sending all the cmd arguments in a list format.
import subprocess
process = subprocess.Popen("""powershell.exe powershell -executionpolicy bypass -File Foo.ps1 "username" "password" "computer1,computer2" "user1,user2" """.split(), stdin=subprocess.PIPE,stdout=subprocess.PIPE)
print(process.communicate())

Related

Regex in Windows Batch to automate Docker run

I am trying to automate the process of sending my temporary Amazon AWS keys as environment variables to a Docker image using Windows. I have a file, credentials.txt that contains my AWS credentials (the 3 ids are always the same, but the string values change regularly). I am using Windows command prompt.
Input:
(includes 2 empty lines at end) credentials.txt:
[default]
aws_access_key_id = STR/+ing1
aws_secret_access_key = STR/+ing2
aws_session_token = STR/+ing3
Desired output:
I need to issue the following command in order to run a Docker image (substituting the strings with the actual strings):
docker run -e AWS_ACCESS_KEY_ID=STR/+ing1 -e AWS_SECRET_ACCESS_KEY=STR/+ing2 -e AWS_SESSION_TOKEN=STR/+ing3 my-aws-container
My idea is to try to use regex on credentials.txt to convert it to:
SET aws_access_key_id=STR/+ing1
SET aws_secret_access_key=STR/+ing2
SET aws_session_token=STR/+ing3
And then run:
docker run -e AWS_ACCESS_KEY_ID=%aws_access_key_id% -e AWS_SECRET_ACCESS_KEY=%aws_secret_access_key% -e AWS_SESSION_TOKEN=%aws_session_token% my-aws-container
Does anyone have any advice on how to achieve this?
You can parse your credentials.txt with a for /f loop to set the variables (effectively removing the spaces):
for /f "tokens=1,3" %%a in ('type credentials.txt ^| find "="') do set "%%a=%%b"
and then run the last code line from your question:
docker run -e AWS_ACCESS_KEY_ID=%aws_access_key_id% -e AWS_SECRET_ACCESS_KEY=%aws_secret_access_key% -e AWS_SESSION_TOKEN=%aws_session_token% my-aws-container
Note: the values should not contain spaces or commas.
I've had a go in python that seems to work. Someone else may have a better answer.
I create the python file:
docker_run.py
import re
import os
myfile = 'C:/fullpath/credentials'
with open(myfile,'r') as f:
mystr = f.read()
vals = re.findall('=[\s]*([^\n]+)',mystr)
keys = ['AWS_ACCESS_KEY_ID','AWS_SECRET_ACCESS_KEY','AWS_SESSION_TOKEN']
environment_vars = ''.join([' -e ' + k + '=' + v for k,v in zip(keys,vals)])
cmd = 'docker run'+environment_vars+' my-aws-container'
os.system(cmd)
Then from command prompt I run:
python docker_run.py
This succeeds in running docker
(note: I tried using exec() in the final line rather than os.system(), but got the error "SyntaxError: invalid syntax")

Attempt to split string on '//' in Jenkinsfile splits on '/' instead

What is the correct way to tokenize a string on double-forward-slash // in a Jenkinsfile?
The example below results in the string being tokenized on single-forward-slash / instead, which is not the desired behavior.
Jenkinsfile
An abbreviated, over-simplified example of the Jenkinsfile containing the relevant part is:
node {
// Clean workspace before doing anything
deleteDir()
try {
stage ('Clone') {
def theURL = "http://<ip-on-lan>:<port-num>/path/to/some.asset"
sh "echo 'theURL is: ${theURL}'"
def tokenizedURL = theURL.tokenize('//')
sh "echo 'tokenizedURL is: ${tokenizedURL}'"
}
} catch (err) {
currentBuild.result = 'FAILED'
throw err
}
}
The Logs:
The log output from the preceding is:
echo 'theURL is: http://<ip-on-lan>:<port-num>/path/to/some.asset'— Shell Script<1s
[ne_Branch-Name-M2X23QGNMETLDZWFK7IXVZQRCNSWYNTDFJZU54VP7DMIOD6Z4DGA] Running shell script
+ echo theURL is: http://<ip-on-lan>:<port-num>/path/to/some.asset
theURL is: http://<ip-on-lan>:<port-num>/path/to/some.asset
echo 'tokenizedURL is: [http:, <ip-on-lan>:<port-num>, path, to, some.asset]'— Shell Script<1s
[ne_Branch-Name-M2X23QGNMETLDZWFK7IXVZQRCNSWYNTDFJZU54VP7DMIOD6Z4DGA] Running shell script
+ echo tokenizedURL is: [http:, <ip-on-lan>:<port-num>, path, to, some.asset]
tokenizedURL is: [http:, <ip-on-lan>:<port-num>, path, to, some.asset]
Note that the logs show that the string is being tokeni on / instead of on //.
tokenize takes string as optional argument that may contain 1 or more characters as delimiters. It treats each character in string argument as separate delimiter so // is effectively same as /
To split on //, you may use split that supports regex:
theURL.split(/\/{2}/)
Code Demo

Powershell script to open another script as admin

I have a script that I can double click and it'll open other scripts as admin. Works with some things but not everything. For one script, it opens the next window and then immediately closes it. For another, I get this error:
At MYPATH\InstallClient.ps1:33 char:78
+ ... tall_x64.msi" -force -recurse -ErrorAction Stop #Cleans out the file ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The string is missing the terminator: ".
At MYPATH\InstallClient.ps1:27 char:31
+ ForEach ($entry in $computers){ #start of foreach loop
+ ~
Missing closing '}' in statement block or type definition.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
Below is the script to open a script as an admin:
Function Get-FileName($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $initialDirectory
$OpenFileDialog.filter = "PS1 (*.ps1)| *.ps1"
$OpenFileDialog.ShowDialog() | Out-Null
$OpenFileDialog.filename
}
$inputfile = Get-FileName "MYPATH\Scripts"
powershell.exe -noprofile -command "&{start-process powershell -ArgumentList '-NoExit -noprofile -file $inputfile' -verb RunAs}"
This is the script that it gives the previous error for while trying to open:
Function Get-FileName($initialDirectory) #Function to choose a file
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $initialDirectory
$OpenFileDialog.filter = "MSI (*.msi)| *.msi" #type of files that will be available for selection
$OpenFileDialog.ShowDialog() | Out-Null
$OpenFileDialog.filename
}
$inputfile = Get-FileName "MyPath" #Directory that is going to open to select a file from
Function Get-FileName($initialDirectory) #Function to choose a file
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $initialDirectory
$OpenFileDialog.filter = "CSV (*.csv)| *.csv" #type of files that will be available for selection
$OpenFileDialog.ShowDialog() | Out-Null
$OpenFileDialog.filename
}
$inputfile1 = Get-FileName "MyPath\ServerLists"
$computers = import-csv $inputfile1
ForEach ($entry in $computers){ #start of foreach loop
$computername = $entry.computernames #this saves the single entry under computernames for each entry in csv file
Copy-item $inputfile -container -recurse \\$computername\C$\windows\temp #this copies the msi file that we selected to the computer entry called from the csv file's temp folder
Invoke-Command -Computername $computername –ScriptBlock {Start-process -Wait "C:\windows\temp\ShadowSuiteClientInstall_x64.msi"} | out-null #This starts the msi file that we just copied and waits for the installation to be completed before moving on
If($?){ #If the last command was successful
Echo "Installed ShadowSuiteClientInstall_x64 on $computername."
Remove-Item "\\$computername\C$\windows\temp\ShadowSuiteClientInstall_x64.msi" -force -recurse -ErrorAction Stop #Cleans out the file we copied into the temp folder
}
}
Does anyone have any ideas on why this will open some things fine but give this error for this script and immediately close other scripts without running them? Does anyone have a better way to navigate through scripts and select one to open as admin?
Ok I figured this out. I loaded the script into powershell ISE and I saw that it was compiling it incorrectly. It kept turning the -Scriptblock into an ae symbol instead of the - in front of scriptblock. Weird AF IMO but ok, I fixed it in ISE, which I recommend to anyone struggling with weird compiling errors like this.

Powershell v2.0 - find start path and instance and starting an application

I am trying to write a simple script that stops and then starts a process (Application).
I can stop it fine, but can't find a way to start it again.
The string to start the process should be: "c:\AppFolder\AppName.exe" instance1
my script is:
$appName = "AppName.exe"
$filter = "name like '%"+$appName+"%'"
$result = Get-WmiObject win32_process -Filter $filter
$processid = $result.ProcessId
$command = $result.CommandLine
stop-process $processid
start $command
If I run $result | select * I see that there is an item for CommandLine which is "C:\AppFolder\AppName.exe" instance1
But If I try and do:
$command = $result.CommandLine
stop-process $processid
start $command
I get start-process : This command cannot be executed due to the error: The system cannot find the file specified
But if I manually type into a powershell window start "c:\AppFolder\AppName.exe" instance1 the Application starts fine.
Am I missing something here?
(n.b. it was suggested to me in "powershell v2 - how to get process ID" that I could use
$processid = get-process appName | select -expand id to get the processid, but when I expanded this to get all the items (probably not the correct term?)
in the object I couldn't see an option for CommandLine or similar)
I found the following (but still doesn't work)
$command = Get-WmiObject win32_process -Filter $filter | select -expandproperty CommandLine
write-host $command
This writes "c:\AppFolder\AppName.exe" instance1
start-process $command
But this then results in the following error:
Start-Process : This command cannot be executed due to the error: The system cannot find the file specified
.
At line:11 char:14
+ start-process <<<< $command
+ CategoryInfo : InvalidOperation: (:) [Start-Process], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand
However, running:
start-process "c:\AppFolder\AppName.exe" instance1
starts the application?
I think I've solved it!! (and it's quite simple really) After much googling. . .
Apparently the start-process cmdlet only accepts a file location.
In order to add an argument (in this case the instance name) I need to use the attribute -ArgumentList
So I need to get the CommandLine item, and split it up, then pass it back in two parts
e.g.
$result = Get-WmiObject win32_process -Filter $filter
$comLine = $result.CommandLine -split"( )"
$comm = $commLine[0]
$inst = $commLine[2]
start-process -FilePath $comm -ArgumentList $inst
And this works as I expected it to.

How to use psake from a batch file?

What I want is a one file I can double-click that will run the required build process using psake.
I'm new to psake and PowerShell so be gentle :-).
What I have now are 3 files:
File 1: Build.bat
PowerShell -ExecutionPolicy Unrestricted -File .\Build.ps1 %1
File 2: Build.ps1
Import-Module .\psake.psm1
Invoke-psake .\BuildTasks.ps1 $args
File 3: BuildTasks.ps1
task default -depends Verify, Joe
task Verify {
write-host "hello from Verify!"
}
task Joe {
write-host "hello from Joe"
}
Is there anyway to merge Build.ps1 and BuildTasks.ps1 into one file?
You should be able to do this with
powershell -Command "& {Import-Module .\psake.psm1; Invoke-psake .\BuildTasks.ps1 %*}"
which gets rid of the build.ps1 file.
Psake comes with a powershell script "psake.ps1" which wraps the call for you. It looks like:
import-module .\psake.psm1
invoke-psake #args
remove-module psake
So your batch script looks like
powershell {path-to-module}\psake.ps1 .\buildTasks.ps1