/build/static/layout/Breadcrumb_cap_w.png
05/13/2018 621 views

I have frankensteined a script to handle invoking the slmgr.vbs to perform a Windows Activation via user select Smart Card token.  I have left the portions credited to the original programmer but suspect that the method used for parsing arguments is not properly passing the thumbprint information.  Any insight anyone can offer to help me isolate the issue is greatly appreciated.  When executed I am seeing the following:

PS C:\WINDOWS\system32> C:\Users\Administrator\Desktop\DeployRimpacBuild\Scripts\02-TokenActivateWindows10.ps1
Activating using the following Thumbprint: 26EBD904347CCF547A82721C920ED662BA61E9DD
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

Error: option fta needs <Certificate Thumbprint> [<PIN>]
Windows Software Licensing Management Tool
Usage: slmgr.vbs...


My script code is as follows:

#PREP LAPTOP FOR RIMPAC 2018
#
#Author: WO1 Kevin Shultz
#Command: 351st CACOM
#Date: 06MAY2018
#
#Description: Preps Windows 10 AGM for RIMPAC 2018 Mission Laptops
#             Configures Policy, User Interface, User Account Creation, Windows and Office Activation

#requires -Version 2
Function Invoke-VBScript {
    <#
    .Synopsis
       Run VBScript from PowerShell
    .DESCRIPTION
       Used to invoke VBScript from PowerShell

       Will run the VBScript in a separate job using cscript.exe
    .PARAMETER Path
       Path to VBScript.
       Accepts relative or absolute path.
    .PARAMETER Argument
       Arguments to pass to VBScript
    .PARAMETER Wait
       Wait for VBScript to finish  
    .EXAMPLE
       Invoke-VBScript -Path '.\VBScript1.vbs' -Arguments '"MyFirstArgument"', '"MySecondArgument"' -Wait
       Run VBScript1.vbs using cscript and wait for the script to complete.
       Displays progressbar while waiting.
       Returns script output as single string.
    .EXAMPLE
       '.\VBScript1.vbs', '.\VBScript2.vbs' | Invoke-VBScript -Arguments '"MyArgument"'
       Starts both VBScript1.vbs and VBScript2.vbs in separate jobs simultaneously.
       Both scripts will be run using the same arguments.
       Returns job items.
    .EXAMPLE
       [PSCustomObject]@{Path='.\VBScript1.vbs';Arguments='"Script1"'},[PSCustomObject]@{Path='.\VBScript2.vbs';Arguments='"Script2"'} | Invoke-VBScript -Wait -Verbose
       Runs two scripts after each other, waiting to one to complete
       before starting next.
       Each script will run with different parameters.
       Displays progressbar while waiting.
       Returns script output in one single string per script.
    .NOTES
       Written by Simon Wåhlin
       http://blog.simonw.se
    #>
    [cmdletbinding(SupportsShouldProcess=$true,ConfirmImpact='None',PositionalBinding=$false)]
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Position=0)]
        [ValidateScript({if(Test-Path $_){$true}else{Throw "Could not find script: [$_]"}})]
        [String]
        $Path,

        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
        [Alias('Args')]
        [String[]]
        $Argument,

        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [Switch]
        $Wait
    )
    Begin
    {
        Write-Verbose -Message 'Locating cscript.exe'
        $cscriptpath = Join-Path -Path $env:SystemRoot -ChildPath 'System32\cscript.exe'
        if(-Not(Test-Path -Path $cscriptpath))
        {
            Throw 'cscript.exe not found.'
        }
        Write-Verbose -Message ('cscript.exe found in: {0}' -f $cscriptpath)
    }
    Process
    {
        Try
        {
            $ResolvedPath = Resolve-Path -Path $Path
            Write-Verbose -Message ('Processing script: {0}' -f $ResolvedPath)
            if($PSBoundParameters.ContainsKey('Argument'))
            {
                $ScriptBlock = [scriptblock]::Create(('& "{0}" "{1}" "{2}"' -f $cscriptpath, $ResolvedPath,($Argument -join '" "')))
            }
            else
            {
                $ScriptBlock = $ScriptBlock = [scriptblock]::Create(('& "{0}" "{1}"' -f $cscriptpath, $ResolvedPath))
            }
            Write-Verbose -Message 'Starting script'
            if($PSCmdlet.ShouldProcess($ResolvedPath,'Invoke script'))
            {
                $Job = Start-Job -ScriptBlock $ScriptBlock
                if($Wait)
                {
                    $Activity = 'Waiting for script to complete: {0}' -f $ResolvedPath
                    Write-Progress -Activity $Activity -Id 1
                    $i = 1
                    While($Job.State -eq 'Running')
                    {
                        $WaitTime = (Get-Date) - $Job.PSBeginTime
                        Write-Progress -Activity $Activity -Status "Waited for $($WaitTime.TotalSeconds -as [int]) seconds." -Id 1 -PercentComplete ($i%100)
                        Start-Sleep -Seconds 1
                        $i++
                    }
                    Write-Progress -Activity $Activity -Status 'Waiting' -Id 1 -Completed
                    $Result = Foreach($JobInstance in ($Job,$Job.ChildJobs))
                    {
                        if($JobInstance.Error -ne $null)
                        {
                            Throw $JobInstance.Error.Exception.Message
                        }
                        else
                        {
                            $JobInstance.Output
                        }
                    }
                    Write-Output -InputObject ($Result -join "`n")
                    Remove-Job -Job $Job -Force -ErrorAction SilentlyContinue
                }
                else
                {
                    Write-Output -InputObject $Job
                }
            }
            Write-Verbose -Message 'Finished processing script'
        }
        Catch
        {
            Throw
        }
    }
}


#Enable Bypass for overriding policies to execute VBS, etc...
Set-ExecutionPolicy Bypass


$SCSerials = certutil -scinfo -silent | Where{$_ -match 'Serial Number: (\S+)'} | ForEach {$Matches[1]}
$SelectedThumb = Get-ChildItem Cert:\CurrentUser\my | Where{$_.SerialNumber -in $SCSerials} | Select Subject,Issuer,NotBefore,NotAfter,Thumbprint | Out-GridView -Title 'Select a smartcard certificate.' -OutputMode Single |% Thumbprint
$UserCert = Get-Item Cert:\CurrentUser\My\$SelectedThumb
$CertThumbPrint = $UserCert.Thumbprint

$VBPath = 'C:\Windows\System32\slmgr.vbs'

Write-Host "Activating using the following Thumbprint: $CertThumbPrint" -ForegroundColor Green

#Run SLMGR.VBS to force token activation of Windows 10
Invoke-VBScript -Path $VBPath -Argument '"/fta $CertThumbPrint"' -Wait


I have tried passing the /fta and thumbprint as a single argument, multiple arguments, etc...  All to no avail.  Thank you in advance for your help.

0 Comments   [ + ] Show comments

Comments


All Answers

0
I still always forget how to embed code on this forum....in any case:

Try changing this:
Invoke-VBScript -Path $VBPath -Argument '"/fta $CertThumbPrint"' -Wait

To this:
Invoke-VBScript -Path $VBPath -Argument @("/fta",$CertThumbPrint) -Wait

'Argument' (not 'Arguments' as in the usage comments for the function) is an array of strings.  The VBScript engine will see /fta as being one argument, and the thumbprint as being the other.  In your example you pass them both as one argument.
Answered 05/15/2018 by: captain_planet
Black Belt