Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 628 Vote(s) - 3.49 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Powershell test for noninteractive mode

#1
I have a script that may be run manually or may be run by a scheduled task. I need to programmatically determine if I'm running in -noninteractive mode (which is set when run via scheduled task) or normal mode. I've googled around and the best I can find is to add a command line parameter, but I don't have any feasible way of doing that with the scheduled tasks nor can I reasonably expect the users to add the parameter when they run it manually.
Does noninteractive mode set some kind of variable or something I could check for in my script?

Edit:
I actually inadvertently answered my own question but I'm leaving it here for posterity.

I stuck a read-host in the script to ask the user for something and when it ran in noninteractive mode, boom, terminating error. Stuck it in a try/catch block and do stuff based on what mode I'm in.

Not the prettiest code structure, but it works. If anyone else has a better way please add it!
Reply

#2
Implement two scripts, one core.ps1 to be manually launched, and one scheduled.ps1 that launches core.ps1 with a parameter.
Reply

#3
powerShell -NonInteractive { Get-WmiObject Win32_Process -Filter "Name like '%powershell%'" | select-Object CommandLine }

powershell -Command { Get-WmiObject Win32_Process -Filter "Name like '%powershell%'" | select-Object CommandLine }

In the first case, you'll get the "-NonInteractive" param. In the latter you won't.

Reply

#4
C:\> powershell -NoProfile -NoLogo -NonInteractive -Command "[Environment]::GetCommandLineArgs()"
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
-NoProfile
-NoLogo
-NonInteractive
-Command
[Environment]::GetCommandLineArgs()
Reply

#5
Script: IsNonInteractive.ps1

function Test-IsNonInteractive()
{
#ref:

[To see links please register here]

#powershell -NoProfile -NoLogo -NonInteractive -File .\IsNonInteractive.ps1
return [bool]([Environment]::GetCommandLineArgs() -Contains '-NonInteractive')
}

Test-IsNonInteractive


Example Usage (from command prompt)

pushd c:\My\Powershell\Scripts\Directory
::run in non-interactive mode
powershell -NoProfile -NoLogo -NonInteractive -File .\IsNonInteractive.ps1
::run in interactive mode
powershell -File .\IsNonInteractive.ps1
popd


More Involved Example Powershell Script

#script options
$promptForCredentialsInInteractive = $true

#script starts here

function Test-IsNonInteractive()
{
#ref:

[To see links please register here]

#powershell -NoProfile -NoLogo -NonInteractive -File .\IsNonInteractive.ps1
return [bool]([Environment]::GetCommandLineArgs() -Contains '-NonInteractive')
}

function Get-CurrentUserCredentials()
{
return [System.Net.CredentialCache]::DefaultCredentials
}
function Get-CurrentUserName()
{
return ("{0}\{1}" -f $env:USERDOMAIN,$env:USERNAME)
}

$cred = $null
$user = Get-CurrentUserName

if (Test-IsNonInteractive)
{
$msg = 'non interactive'
$cred = Get-CurrentUserCredentials
}
else
{
$msg = 'interactive'
if ($promptForCredentialsInInteractive)
{
$cred = (get-credential -UserName $user -Message "Please enter the credentials you wish this script to use when accessing network resources")
$user = $cred.UserName
}
else
{
$cred = Get-CurrentUserCredentials
}
}

$msg = ("Running as user '{0}' in '{1}' mode" -f $user,$msg)
write-output $msg
Reply

#6
I came up with a posh port of existing and proven C# code that uses a fair bit of P/Invoke to determine all the corner cases. This code is used in my [PowerShell Build Script](

[To see links please register here]

) that coordinates several build tasks around Visual Studio projects.

# Some code can be better expressed in C#...
#
Add-Type @'
using System;
using System.Runtime.InteropServices;

public class Utils
{
[DllImport("kernel32.dll")]
private static extern uint GetFileType(IntPtr hFile);

[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(int nStdHandle);

[DllImport("kernel32.dll")]
private static extern IntPtr GetConsoleWindow();

[DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr hWnd);

public static bool IsInteractiveAndVisible
{
get
{
return Environment.UserInteractive &&
GetConsoleWindow() != IntPtr.Zero &&
IsWindowVisible(GetConsoleWindow()) &&
GetFileType(GetStdHandle(-10)) == 2 && // STD_INPUT_HANDLE is FILE_TYPE_CHAR
GetFileType(GetStdHandle(-11)) == 2 && // STD_OUTPUT_HANDLE
GetFileType(GetStdHandle(-12)) == 2; // STD_ERROR_HANDLE
}
}
}
'@

# Use the interactivity check somewhere:
if (![Utils]::IsInteractiveAndVisible)
{
return
}

Reply

#7
Testing for interactivity should probably take both the process and the user into account. Looking for the `-NonInteractive` (minimally `-noni`) powershell switch to determine process interactivity (very similar to @VertigoRay's script) can be done using a simple filter with a lightweight `-like` condition:

function Test-Interactive
{
<#
.Synopsis
Determines whether both the user and process are interactive.
#>

[CmdletBinding()] Param()
[Environment]::UserInteractive -and
!([Environment]::GetCommandLineArgs() |? {$_ -ilike '-NonI*'})
}

This avoids the overhead of WMI, process exploration, imperative clutter, double negative naming, and even a full regex.
Reply

#8
I wanted to put an updated answer here because it seems that `[Environment]::UserInteractive` doesn't behave the same between a .NET Core (container running `microsoft/nanoserver`) and .NET Full (container running `microsoft/windowsservercore`).

While `[Environment]::UserInteractive` will return `True` or `False` in 'regular' Windows, it will return `$null` in 'nanoserver'.

If you want a way to check interactive mode regardless of the value, add this check to your script:

`($null -eq [Environment]::UserInteractive -or [Environment]::UserInteractive)`

EDIT: To answer the comment of why not just check the truthiness, consider the following truth table that assumes such:

left | right | result
=======================
$null | $true | $false
$null | $false | $true (!) <--- not what you intended
Reply

#9
This will return a Boolean when the `-Noninteractive` switch is used to launch the PowerShell prompt.

[Environment]::GetCommandLineArgs().Contains('-NonInteractive')
Reply

#10
I think the question needs a more thorough evaluation.

- "interactive" means the shell is running as REPL - a continuous read-execute-print loop.

- "non-interactive" means the shell is executing a script, command, or script block and *terminates* after execution.

If PowerShell is run with any of the options `-Command`, `-EncodedCommand`, or `-File`, it is non-interactive. Unfortunately, you can also run a script *without* options (`pwsh script.ps1`), so there is no bullet-proof way of detecting whether the shell is interactive.

So are we out of luck then? No, fortunately PowerShell does automatically add options that we can test if PowerShell runs a script block or is run via ssh to execute commands (`ssh user@host command`).

```powershell
function IsInteractive {
# not including `-NonInteractive` since it apparently does nothing
# "Does not present an interactive prompt to the user" - no, it does present!
$non_interactive = '-command', '-c', '-encodedcommand', '-e', '-ec', '-file', '-f'

# alternatively `$non_interactive [-contains|-eq] $PSItem`
-not ([Environment]::GetCommandLineArgs() | Where-Object -FilterScript {$PSItem -in $non_interactive})
}

```

Now test in your PowerShell profile whether this is in interactive mode, so the profile is not run when you execute a script, command or script block (you still have to remember to run `pwsh -f script.ps1` - not `pwsh script.ps1`)
```powershell
if (-not (IsInteractive)) {
exit
}
````
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through