Powershell Run VBScript with Parameters

I am trying to run a VBScript from Powershell with parameters. For some reason I can’t get the parameters to run. The script and parameters are being ran from a txt file.

cscript $fullpath $var

Is this the proper way to call the script?

What’s in your $var variable?

 
$<#	
fullpath = \\Server\Install_W864_W764\Webex One-Click (2.82.1.424_32b) EN\runinst.vbs,
$var = PRODUCT_CODE "" APPNAME "Webex One-Click (2.82.1.424_32b) EN" PKGVER "P00" NBACTIONS 7 Noreboot

Here is the full code for context.

$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath

$site = "\\server"

$defaultmargin = 20

$software = Import-CSV -Path "$dir\software.txt" | Sort-Object FriendlyName
$software | Measure-Object | Out-Null

$count = $software.Count

# Icon Location 
$icon = "\\Server\Scripts\ScriptAddOns\logo.ico"

# Start Form Build
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# Form Layout Variables
$fheight = $count * $defaultmargin + 125
$form = New-Object system.Windows.Forms.Form
$form.Text = "Software Install v2.0" # Form Title
$form.Height = $fheight
$form.Width = 400
$form.Icon = $icon

$varx = 145
$i = 0
ForEach ($app in $software) {
	$VariableName = "Install" + $app.ShortName
	"Creating $VariableName"
	New-Variable -Name $VariableName -Value (new-object system.windows.forms.checkbox) -Force
	$form.Controls.Add((get-variable $VariableName).Value)
	(get-variable $VariableName).value.Text = $app.FriendlyName
	$i++
	$y = $i * 20
	(get-variable $VariableName).value.Location = New-Object System.Drawing.Size(20, $y)
	(get-variable $VariableName).value.Size = New-Object System.Drawing.Size($varx, 20)
}

$pcoutposx = $varx + 40
$pcoutheight = $count * $defaultmargin
$pcoutput = New-Object System.Windows.Forms.TextBox
$pcoutput.MultiLine = $True
$pcoutput.ReadOnly = $True
$pcoutput.BackColor = "White"
$pcoutput.ScrollBars = "Vertical"
$pcoutput.Size = New-Object System.Drawing.Size(175, $pcoutheight)
$pcoutput.Location = New-Object System.Drawing.Size($pcoutposx, 20)
$pcoutput.Text = "Idle ...`r`n"
$form.Controls.Add($pcoutput)

$install_Onclick = {
	ForEach ($app in $software) {
		$name = $app.FriendlyName
		$path = $app.Path
		$var = $app.Variable
		$pathend = $path.LastIndexOf("`.")
		$typebegin = $pathend + 1
		$type = $path.Substring($typebegin)
		if ((get-variable ("Install" + $app.Shortname)).value.Checked) {
			$pcoutput.AppendText("Installing $name ...`r`n")
			if ($type -eq "bat") {
				Start-Process "$site$path" -Wait
			}
			else {
				$fullpath = "$site$path"
				cscript $fullpath $var
			}
		}
	}
	$wshell = New-Object -ComObject Wscript.Shell
	$wshell.Popup("All Selected Software Installed", 0, "Done", 0x0)
}

# Install Button begins the installation process based on checkboxes checked.
$btny = $count * $defaultmargin + 40
$installButton = New-Object System.Windows.Forms.Button
$installButton.Name = "installButton"
$installButton.Text = "Install"
$installButton.Location = New-Object System.Drawing.Size(20, $btny)
$installButton.add_Click($install_Onclick)
$form.Controls.Add($installButton)

$form.ShowDialog()

OK, that’s your problem. When you call a console app such as CScript.exe, PowerShell will put quotes around the variable that you’re passing in. (It things $var is a single argument, rather than a whole command line.) The safest fix is to make an array of individual arguments, and use splatting to send that to the program:

$var = @(
    'PRODUCT_CODE', 'SomeProductCode',
    'APPNAME', "Webex One-Click (2.82.1.424_32b) EN",
    'PKGVER', 'P00'
)

cscript $fullpath @var

Ok this is what I have done to solve based on your suggestion.

$var = $var -split " "

Works perfectly. Thank You

Spoke to soon. :frowning:

I get the following error:

Microsoft VBScript runtime error: Subscript out of range

It only happens on one line item and I have a feeling it has something to do with the Product Code

PRODUCT_CODE "{ED9A1358-C3FB-413B-9D28-1FF7DCCF6377}" APPNAME "Fun R2 (4.1.850_32b) EN" PKGVER "P00" NBACTIONS

Just as a side note…Fun R2 is not Fun to work with.

The problem with splitting on the space like that is that you have spaces inside your AppName argument, and you don’t actually want to split on those. Did you try setting up the array like I gave in the example first?

Sorry for the delay in answering. Had to work at a different site and couldn’t do any scripting. I did not try your example. The reason I did not try is because this list of software installs has to be easily updatable by other people who would not know how to correctly add the proper commas in the proper places. Your example is what led me to the split.

So I guess my question would be, how to pull this:

PRODUCT_CODE “{ED9A1358-C3FB-413B-9D28-1FF7DCCF6377}” APPNAME “Fun R2 (4.1.850_32b) EN” PKGVER “P00” NBACTIONS

And make it into a proper array like this:

$var = @(
    'PRODUCT_CODE', 'SomeProductCode',
    'APPNAME', "Webex One-Click (2.82.1.424_32b) EN",
    'PKGVER', 'P00'
)

All the variables are written by our corporate hq, so I just copy and past them.

Depends on how safe you want the code to be, how much work you want to do, and what version of PowerShell you’re using. A very quick answer here would be to use Invoke-Expression, but I literally never use that command, because it leaves you vulnerable to code injection attacks. Unless you completely trust the input, it’s a bad idea to just blindly execute it as code.

If you have PowerShell v3 or later, I’d use PowerShell’s built-in parser and AST for this, personally. That makes it very easy to validate that the code won’t do anything dangerous before you run it:

function Invoke-SafeVbScript
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string] $VbsScriptPath,

        [Parameter(Mandatory)]
        [string] $ArgumentString
    )

    $tokens = $parseErrors = $null
    $code = "cscript.exe '$VbsScriptPath' $string"

    $ast = [System.Management.Automation.Language.Parser]::ParseInput($code, [ref] $tokens, [ref] $parseErrors)

    if ($parseErrors.Count -gt 0)
    {
        throw "Script path or arguments contained $($parseErrors.Count) parse errors:`r`n`r`n$($parseErrors | Out-String)"
    }

    if ($ast.EndBlock.Statements.Count -gt 1 -or
        $ast.EndBlock.Statements[0].PipelineElements.Count -gt 1)
    {
        # Someone stuck a newline or semicolon in there in such a way that we'd wind up executing more than
        # one command (or pipe to something else.)  NOT safe.

        throw "String contained potentially malicious code injection."
    }

    $scriptBlock = $ast.GetScriptBlock()
    & $scriptBlock
}

With a function like that in your script, you’d just call:

Invoke-SafeVbScript -VbsScriptPath $fullpath -ArgumentString $var

And any funny business in the strings (bad syntax, malicious code, whatever) would cause an error instead of being executed.

Ok, so here is what I had to do and while it’s not pretty and had to have quicker solution as my timeline got moved way up on having this deployed to all of our technicians.

Here is the code:

$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath

$site = "server"

$defaultmargin = 20

$software = Import-CSV -Path "$dir\software.txt" | Sort-Object FriendlyName
$software | Measure-Object | Out-Null

$count = $software.Count

# Icon Location 
$icon = "\\server\Scripts\ScriptAddOns\logo.ico"

# Start Form Build
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# Form Layout Variables
$fheight = $count * $defaultmargin + 125
$form = New-Object system.Windows.Forms.Form
$form.Text = "Software Install v2.0" # Form Title
$form.Height = $fheight
$form.Width = 400
$form.Icon = $icon

$varx = 145
$i = 0
ForEach ($app in $software) {
	$VariableName = "Install" + $app.ShortName
	"Creating $VariableName"
	New-Variable -Name $VariableName -Value (new-object system.windows.forms.checkbox) -Force
	$form.Controls.Add((get-variable $VariableName).Value)
	(get-variable $VariableName).value.Text = $app.FriendlyName
	$i++
	$y = $i * 20
	(get-variable $VariableName).value.Location = New-Object System.Drawing.Size(20, $y)
	(get-variable $VariableName).value.Size = New-Object System.Drawing.Size($varx, 20)
}

$pcoutposx = $varx + 40
$pcoutheight = $count * $defaultmargin
$pcoutput = New-Object System.Windows.Forms.TextBox
$pcoutput.MultiLine = $True
$pcoutput.ReadOnly = $True
$pcoutput.BackColor = "White"
$pcoutput.ScrollBars = "Vertical"
$pcoutput.Size = New-Object System.Drawing.Size(175, $pcoutheight)
$pcoutput.Location = New-Object System.Drawing.Size($pcoutposx, 20)
$pcoutput.Text = "Idle ...`r`n"
$form.Controls.Add($pcoutput)

$install_Onclick = {
	ForEach ($app in $software) {
		$name = $app.FriendlyName
		$path = $app.Path
		$var = $app.Variable
		$pathend = $path.LastIndexOf("`.")
		$typebegin = $pathend + 1
		$type = $path.Substring($typebegin)
		if ((get-variable ("Install" + $app.Shortname)).value.Checked) {
			$pcoutput.AppendText("Installing $name ...`r`n")
			if ($type -eq "bat") {
				Start-Process "$site$path" -Wait
			}
			else {
				$var = $var 
				$fullpath = "$site$path"
				
				"cscript //b `"$fullpath`" $var" | Out-File "C:\Temp\script.bat" -Encoding "ascii"
				"taskkill /im ProgressBar.exe" | Out-File "C:\Temp\script.bat" -Encoding "ascii" -Append
				
				Start-Process "C:\Temp\script.bat" -wait
			}
		}
	}
	$wshell = New-Object -ComObject Wscript.Shell
	$wshell.Popup("All Selected Software Installed", 0, "Done", 0x0)
}

# Install Button begins the installation process based on checkboxes checked.
$btny = $count * $defaultmargin + 40
$installButton = New-Object System.Windows.Forms.Button
$installButton.Name = "installButton"
$installButton.Text = "Install"
$installButton.Location = New-Object System.Drawing.Size(20, $btny)
$installButton.add_Click($install_Onclick)
$form.Controls.Add($installButton)

$form.ShowDialog()