Powershell Run VBScript with Parameters

This topic contains 9 replies, has 2 voices, and was last updated by Profile photo of Joseph Monarch Joseph Monarch 7 months, 2 weeks ago.

Viewing 10 posts - 1 through 10 (of 10 total)
  • Author
    Posts
  • #34764
    Profile photo of Joseph Monarch
    Joseph Monarch
    Participant

    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?

    #34765
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    What's in your $var variable?

    #34777
    Profile photo of Joseph Monarch
    Joseph Monarch
    Participant
     
    $< #	
    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()
    
    
    #34778
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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
    
    #34779
    Profile photo of Joseph Monarch
    Joseph Monarch
    Participant

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

    $var = $var -split " "
    

    Works perfectly. Thank You

    #34781
    Profile photo of Joseph Monarch
    Joseph Monarch
    Participant

    Spoke to soon. 🙁

    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.

    #34782
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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?

    #34995
    Profile photo of Joseph Monarch
    Joseph Monarch
    Participant

    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.

    #35005
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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.

    #35141
    Profile photo of Joseph Monarch
    Joseph Monarch
    Participant

    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()
    
    
Viewing 10 posts - 1 through 10 (of 10 total)

You must be logged in to reply to this topic.