Set Environment per Runspace - vb.net

I am currently working on an application that pulls tasks from a SQL table and exeutes PowerShell scripts. I want to run a SetUP.ps1 in the beginning to set variables like working dir and so on, and a tearDown.ps1 in the end just for clean up.
Currently I am using:
Using myRunSpace = RunspaceFactory.CreateRunspace
myRunSpace.Open()
Using ps = PowerShell.Create
ps.Runspace = myRunSpace
ps.AddScript("Set-ExecutionPolicy -Scope Process RemoteSigned")
ps.Invoke()
End Using
Using ps = PowerShell.Create
ps.Runspace = myRunSpace
ps.AddScript(Application.StartupPath & "\SetUp.ps1")
ps.Invoke()
End Using
Using ps = PowerShell.Create
ps.Runspace = myRunSpace
ps.AddScript(task.Script)
ReturnValue = PSSerializer.Serialize(ps.Invoke())
End Using
Using ps = PowerShell.Create
ps.Runspace = myRunSpace
ps.AddScript(Application.StartupPath & "\TearDown.ps1")
ps.Invoke()
End Using
End Using
In Setup.ps1 I a currently doing:
$env:test = Get-Random
When I now run the code above multithreaded with the snippet as code:
Start-Sleep 10; $env:test
Then all runs give me the same value. the $env:test is the same in each run. Is there a way how I can limit the scope of the $env:test to simply this one runspace?

I think this is what you want to accomplish: Have one thread write the random value to an environment variable, and the other thread reading it?
Remove-Item c:\test.out -force
Remove-Item Env:\test
$testfile = "c:\test.out"
$myRunSpace = [RunspaceFactory]::CreateRunspace()
$myRunSpace.Open()
$ps1 = [PowerShell]::Create()
$ps1.Runspace = $myRunSpace
$ps1.AddScript("`$env:test = Get-Random")
$ps1.Invoke()
$ps2 = [PowerShell]::Create()
$ps2.Runspace = $myRunSpace
$ps2.AddScript("Start-Sleep -Seconds 1")
$ps2.AddScript("`$env:test | Out-File $testfile")
$ps2.Invoke()
start-sleep 2
"variable set to: $env:test"
"file reads: " + (Get-Content $testfile)

Related

How to run a PowerShell script file using VB.Net

I have a desktop application. I am checking if the required text file exists. If it does not exist then I want to run a PowerShell script which will create the required text file.
Here's the code I am trying:
Dim userInfoPath As String = "C:\temp\UserInfo.txt"
If (IO.File.Exists(userInfoPath) = True) Then
MsgBox("The file exists !")
Else
Dim result As String
result = MsgBox("UserInfo.txt file does not exist. Click 'Yes' to create the required file.", vbYesNo)
If (result = vbYes) Then
Process.Start("powershell", "-File C:\Desktop\PowershellScript.ps1")
End If
End If
The PowerShell file placed at C:\Desktop\PowershellScript.ps1 contains the script to create a new file and add text to it:
mkdir C:\temp\
New-Item C:\temp\UserInfo.txt
Set-Content C:\temp\UserInfo.txt 'blah blah blah blah blah'
When I run the top most code and click on 'Yes' button, I get an error saying object reference not set to an instance of an object.
Error message link
What I am doing wrong?
And is there another way to run a PowerShell script file?
I'm using objShell.Run
Set wshArguments = WScript.Arguments
Set objShell = CreateObject("WScript.Shell")
sArg = wshArguments(0)
If wshArguments.Count > 1 Then
sArg = sArg & " " & wshArguments(1)
End If
sps1= "C:\Desktop\PowershellScript.ps1"
objShell.Run("powershell.exe -noprofile -noexit -ExecutionPolicy Bypass "+sps1+" ‘"+sArg+"’")

"Permission Denied" error while trying to run Powershell script from Word Macros

I need to run a powershell script with a parameter from a word macros. For now even the attempts to simply run a script from VBA are not sucessful. I do the following:
Sub Powershell_Run()
sCmd = "powershell -file ""C:\Users\i351063\Desktop\Scripts\NewAttempt.ps1"""
Set xShell = CreateObject("Wscript.Shell")
Set xShellExec = xShell.Run(sCmd)
rReturn = xShellExec.Run("C:\Users\i351063\Desktop\Scripts\NewAttempt.ps1")
End Sub
The execution of this code returns an error: "Run-time error '70': Permission denied" on the line Set xShellExec = xShell.Run(sCmd). What do I do wrong? How to fix the error?
Thanks a lot in advance!
UPD: Powershell code (I want to pass the filename as a parameter from VBA to PS, for now it's initialized right in the code)
Param([string]$filename)
$filename = "HT.docm"
Add-Type -AssemblyName "Microsoft.Office.Interop.Word"
$word = [Runtime.Interopservices.Marshal]::GetActiveObject('Word.Application')
$wshell = New-Object -ComObject Wscript.Shell
$doc = $word.Documents | Where-Object {$_.Name -eq "$filename"}
If (!$doc)
{
$form.Close()
[void] $wshell.Popup("Failed to find an opened report",0,"Report not found",0x1)
$word.ScreenUpdating = $true
Break
}
Else {...}

find browser page title with multiple instances open, multiple tabs

I have more than one instance of Firefox open, multiple tabs in each, and Chrome also running. I wish to be able to find which tab of which browser is running Pandora, for example.
I am working primarily in VBScript, but can tackle PowerShell and might have access to VB.net.
With VBScript you need a third party component to get access to that kind of information. Otherwise your only option is to shell out and parse the output of the tasklist command.
searchTerm = "Pandora"
Set sh = CreateObject("WScript.Shell")
Set ps = sh.Exec("tasklist /v /fo csv")
data = ps.StdOut.ReadAll
searchTerm = "Pandora"
Set re = New RegExp
re.Pattern = "^""(?:chrome|firefox|iexplore|opera)\.exe"""
re.IgnoreCase = True
For Each line In Split(data, vbNewLine)
If re.Test(line) Then
fields = Split(Mid(line, 2, Len(line)-2), """,""")
If InStr(fields(UBound(fields)), searchTerm) > 0 Then
pid = CInt(fields(1))
End If
End If
Next
sh.AppActivate pid
In PowerShell it's a lot easier to do this kind of thing, since Get-Process already provides you with the window titles.
$searchTerm = 'Pandora'
Get-Process | Where-Object { $_.MainWindowTitle -like "*$searchTerm*" }

VBS reboot and wait on same server

I would like to run the following script as part of my guest customization so when I deploy a VM from a template, the windows volumes have their correct drive letters. The script below works but requires a reboot, following the reboot I would like to call my SQL setup command which currently is just one line in a .cmd file. Could anyone help me to add a wait command to the drive letters script and then call the cmd after a reboot?
Also is there an easier way with PowerShell?
CMD File;
cd c:
C:\Setup\SQL2008R2_SP2\Setup.exe /CONFIGURATIONFILE=C:\Setup\SQL2008R2.ini /INDICATEPROGRESS
Change Drive Letters (Original source http://imallvirtual.com/?p=482)
' Script that changes drive letters
' Note: Do NOT use it on SYSTEM or BOOT partition drive letters !!!
set objShell = CreateObject("WScript.Shell")
' objShell.Run("regedit /s C:\Setup\MsgBox.reg")
sComputer = "."
Const HKLM = &H80000002
' from/to
If ChangeDrvLetter("D:", "T:") Then
End If
If ChangeDrvLetter("F:", "X:") Then
End If
If ChangeDrvLetter("G:", "D:") Then
End If
Function ChangeDrvLetter(sSourceDrive, sTargetDrive)
bOK = True ' Init value
Set oReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" _
& sComputer & "\root\default:StdRegProv")
sKeyPath = "SYSTEM\MountedDevices"
sSrc = "\DosDevices\" & UCase(sSourceDrive)
iRC = oReg.GetBinaryValue(HKLM, sKeyPath, sSrc, sValue)
If iRC = 0 Then
sTrg = "\DosDevices\" & UCase(sTargetDrive)
iRC = oReg.SetBinaryValue(HKLM, sKeyPath, sTrg, sValue)
If iRC = 0 Then
oReg.DeleteValue HKLM, sKeyPath, sSrc
Else
bOK = False
End If
Else
bOK = False
End If
ChangeDrvLetter = bOK
End Function
Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run "C:\WINDOWS\system32\shutdown.exe -r -t 0"
To run after reboot, you can sure have the script set a one time task in task-scheduler to run the one line command. (the task can even be set to delete himself after running)

Running powershell scripts from within a .NET windows app

I'm needing to run scripts from within a vb.net windows app.
I've got the scripts running in the background fine;
Using MyRunSpace As Runspace = RunspaceFactory.CreateRunspace()
MyRunSpace.Open()
Using MyPipeline As Pipeline = MyRunSpace.CreatePipeline()
MyPipeline.Commands.AddScript("import-module -name " & moduleName &
vbCrLf &
"(get-module -name " & moduleName & ").version")
Dim results = MyPipeline.Invoke()
'Do something with the results
End Using
MyRunSpace.Close()
End Using
However, i now need to be able to have the powershell run (not in the background) eg. When prompts occur;
Set-ExecutionPolicy unrestricted
I'm currently looking into the Microsoft.PowerShell.ConsoleHost namespace to see if i can use something like;
Dim config = RunspaceConfiguration.Create
ConsoleShell.Start(config, "Windows PowerShell", "", New String() {""})
Can anyone advise me please???
EDIT: I've fudged it a bit with this;
Public Function RunPowershellViaShell(ByVal scriptText As String) As Integer
Dim execProcess As New System.Diagnostics.Process
Dim psScriptTextArg = "-NoExit -Command ""& get-module -list"""
'Dim psScriptTextArg = "-NoExit -Command ""& set-executionPolicy unrestricted"""
'Dim psScriptTextArg = ""-NoExit -Command """ & scriptText & """"
execProcess.StartInfo.WorkingDirectory = Environment.SystemDirectory & "\WindowsPowershell\v1.0\"
execProcess.StartInfo.FileName = "powershell.exe"
execProcess.StartInfo.Arguments = psScriptTextArg
execProcess.StartInfo.UseShellExecute = True
Return execProcess.Start
End Function
But there's gotta be a better way??
There is a distinction between the PowerShell engine and its host. What you're wanting is to run the engine within your application but then fire up a separate host (which also is hosting the PowerShell engine) to handle prompts. You might want to look into modifying your application to act as a host itself. You could then react to prompts (read-host) and pop dialog boxes or whatever. Take a look at this relevant PowerShell namespace. Also check out this blog post on creating a simple PSHost.