1

I have a powershell script that, among other things, sometimes needs to call a third-party application (TPA) to trigger a login prompt. My issue is that TPA's password prompt includes additional warning lines that I don't need cluttering up my UX. I can (and am) passing the username to the application just fine via $TpaParams, but I'd prefer to leave the handling the user's password to the TPA itself.

The password warning comes through through via STDOUT (not STDERR) and this is the same place that the necessary prompt text comes through. If I redirect the whole thing (Out-Null or the like), then the login fails because it couldn't ask for the password.

I am hoping to be able to show the prompt (only) and strip out the warning. Failing that, I'd be just as happy to recolor the warning text (using Write-Host) to dark-gray-on-blue to make it less eye-catching (I already have a number of colors set up in my script for just this purpose).

Additional (potentially helpful?) data:

  • TpaCmd is a CMD file that contains lots of error checking plus one line of actual code: "%JAVACMD%" -jar "%JARFILE%" %*
  • I've been unable to find any help with either Java or TPA's author (IBM), either
  • I do not have the ability to change the JAR file; it has to remain intact.

Can anyone think of a way to do this on the powershell end?

Things I've tried without success:

No redirection

$TpaCmd login $TpaParams

Password not passed [...]  
Password :  

Redirect Out/Err Separately
$TpaCmd login $TpaParams 1> $TmpOut 2> $TmpErr

(null output)  

$TmpOut contains

Password not passed [...]  

$TmpErr contains

tpapp.cmd : java.lang.NullPointerException  
At D:\path\to\powershell\DoStuff.ps1:288 char:3  
+         &  ${TpaCmd} login --verbose ${TpaParams} 1> $TmpOut  
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
    + CategoryInfo          : NotSpecified: (java.lang.NullPointerException:String) [], RemoteException  
    + FullyQualifiedErrorId : NativeCommandError  
  
    at com.tpappname.ds.client.DeployCLI.main(DeployCLI.java:246)  
java.lang.NullPointerException  

Redirect Out, leave Err
$TpaCmd login $TpaParams 1> $TmpOut
$TmpOut contains

Password not passed as command line property password or set in environment, DS_PASSWORD  

NullPointerException error (above) is displayed to the screen

Redirect Err, leave Out
$TpaCmd login $TpaParams 2> $TmpErr

Password not passed [...]  
Password :  

$TmpErr is empty

Redirect Everything
$TpaCmd login $TpaParams *> $TmpAll

(null output)  

$TmpAll contains both the password warning and the NPE

Password not passed [...]
Retry with --verbose flag for more info.
tpapp.cmd : java.lang.NullPointerException  
    At D:\path\to\powershell\DoStuff.ps1:288 char:3  
    +         &  ${TpaCmd} login --verbose ${TpaParams} 1> $TmpOut  
    +         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
        + CategoryInfo          : NotSpecified: (java.lang.NullPointerException:String) [], RemoteException  
        + FullyQualifiedErrorId : NativeCommandError  
      
        at com.tpappname.ds.client.DeployCLI.main(DeployCLI.java:246)  
    java.lang.NullPointerException  
Sas
  • 51
  • 6
  • What if you pipe the output to `Select-Object -Skip 1` – Doug Maurer Sep 20 '22 at 18:42
  • @DougMaurer while that would have been a simple solution, it results in another NullPointerException error: `java.lang.NullPointerException Retry with --verbose flag for more info.` – Sas Sep 22 '22 at 18:03

3 Answers3

2

After doing a bit more research AND using the answers above as inspiration, I've decided to go with this:

$UserEncryptedPassword = Read-Host "Enter login password" -AsSecureString
& $TpaCmd login $TpaParams -password ([Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR(${UserEncryptedPassword})))

Thank you all for your inspiration and assistance!

Sas
  • 51
  • 6
  • 1
    Nice, though I suggest `[pscredential]::new('unused', $UserEncryptedPassword).GetNetworkCredential().Password` for simplicity. As an inconsequential aside: strictly speaking, it should be `PtrToStringBSTR`, not `PtrToStringAuto` (see [this answer](https://stackoverflow.com/a/60406968/45375) for details). – mklement0 Sep 22 '22 at 18:25
1

Depending on what you mean by I'd prefer to leave the handling the user's password to the TPA itself there might be more solutions.

Temporary password variable: the following might be a simple way to pass the password

$Password = Read-Host "Enter Password"
$TpaParms += " -password $Password"
$TpaCmd login $TpaParams

If you don't want the password in a variable, you might try this with no password variable:

$TpaCmd login "$TpaParms -password $(Read-Host "Enter Password")"

As i don't have your program and your $TpaParms to test my script i tried to find a documentation by the error messages and error codes, and found the syntax on that page https://www.ibm.com/docs/en/urbancode-deploy/7.0.3?topic=reference-command-format that i used for password. The -username [username] has to be in the $TpaParms to make this work.

An-dir
  • 465
  • 2
  • 13
  • 1
    Your first solution is exactly what I did not want to do; I don't want my script to be responsible for keeping the user's password secure. That being said, I like your second option but that this shows the password entry on the screen as it's being typed. I'd use the -MaskInput flag, but apparently our PS version is incredibly out of date and that flag isn't available! :( – Sas Sep 22 '22 at 18:07
0

You'll need to use a trick to get this to work, which relies on you knowing both the content of the line to ignore and the prompt string that the batch file uses to prompt for the password:

& $TpaCmd login $TpaParams |
  ForEach-Object { 
    if ($_ -match '\bPassword not passed\b') { # Line to ignore
      # Simulate the prompt that the batch file will show next, but
      # initially invisibly.
      Write-Host -NoNewLine 'Password : '
    } elseif ($_ -match '^Password : (.*)') { # The line with the prompt
      # Print the part of the line *after* the prompt string, which is
      # the *next* statement's output.
      $Matches.1 
    } else { # All other lines: pass through
      $_
    }
}
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • As software gets updated, this would bear the risk of a changed prompt no longer being masked. Thank you, anyhow! – Sas Sep 22 '22 at 18:14
  • @Sas, what the user typed would still be masked if the prompt string changes, but the prompt string itself would no longer be stripped from the output. However, I agree that this is a brittle solution, and it sounds like you found a better one. – mklement0 Sep 22 '22 at 18:21