Search code examples
powershelluser-input

How can I implement Get-Credential with a timeout


In some of my scripts I have a fall-back approach for getting a PSCredential object via the Get-Credential cmdlet. This is very useful when running those scripts interactively, esp. during testing etc.

If & when I run these scripts via a task scheduler, I'd like to ensure that they don't get stuck waiting for interactive input and simply fail if they don't have the necessary credentials. Note that this should never happen if the task is set to run under the correct application account.

Does anyone know of an approach that would allow me to prompt for input with a timeout (and presumably a NULL credential object) so the script doesn't get stuck?

Happy to consider a more general case with Read-Host instead of Get-Credential.


Solution

  • I use something similar in some of my scripts based around a timeout on read-host, code here :

    Function Read-HostTimeout {
    #  Description:  Mimics the built-in "read-host" cmdlet but adds an expiration timer for
    #  receiving the input.  Does not support -assecurestring
    
    # Set parameters.  Keeping the prompt mandatory
    # just like the original
    param(
        [Parameter(Mandatory=$true,Position=1)]
        [string]$prompt,
    
        [Parameter(Mandatory=$false,Position=2)]
        [int]$delayInSeconds=5,
    
        [Parameter(Mandatory=$false,Position=3)]
        [string]$defaultValue = 'n'
    )
    
    # Do the math to convert the delay given into milliseconds
    # and divide by the sleep value so that the correct delay
    # timer value can be set
    $sleep = 250
    $delay = ($delayInSeconds*1000)/$sleep
    $count = 0
    $charArray = New-Object System.Collections.ArrayList
    Write-host -nonewline "$($prompt):  "
    
    # While loop waits for the first key to be pressed for input and
    # then exits.  If the timer expires it returns null
    While ( (!$host.ui.rawui.KeyAvailable) -and ($count -lt $delay) ){
        start-sleep -m $sleep
        $count++
        If ($count -eq $delay) { "`n"; return $defaultValue}
    }
    
    # Retrieve the key pressed, add it to the char array that is storing
    # all keys pressed and then write it to the same line as the prompt
    $key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp").Character
    $charArray.Add($key) | out-null
    
    # Comment this out if you want "no echo" of what the user types
    Write-host -nonewline $key
    
    # This block is where the script keeps reading for a key.  Every time
    # a key is pressed, it checks if it's a carriage return.  If so, it exits the
    # loop and returns the string.  If not it stores the key pressed and
    # then checks if it's a backspace and does the necessary cursor 
    # moving and blanking out of the backspaced character, then resumes 
    # writing. 
    $key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
    While ($key.virtualKeyCode -ne 13) {
        If ($key.virtualKeycode -eq 8) {
            $charArray.Add($key.Character) | out-null
            Write-host -nonewline $key.Character
            $cursor = $host.ui.rawui.get_cursorPosition()
            write-host -nonewline " "
            $host.ui.rawui.set_cursorPosition($cursor)
            $key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
        }
        Else {
            $charArray.Add($key.Character) | out-null
            Write-host -nonewline $key.Character
            $key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
        }
    }
    Write-Host ""
    $finalString = -join $charArray
    return $finalString
    }