I have a deployment server app that makes changes on the target client. Part of the process requires closing another application.
I would like to have an option to present a Windows form box that allows the to acknowledge that the application needs to be shutdown.
The deployment server app uses Powershell to perform everything. All pieces of the script work fine except for showing user form. If I run the script locally on the machine (as admin or even as the system account using PSExec), everything works fine and I see the Windows form. However, running the script in the deployment server app results in the call to show the Windows form returning immediately with the response "OK".
In Powershell, the simple way of doing this is with a sequence like the following. My issue is the second line always immediately returns if I try to do this in the deployment app. Running any other way seems to work fine.
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null;
$response = [System.Windows.Forms.MessageBox]::Show("User Input Text", "Window Title", ...);
Write-Host "User selected $response"
I have also tried many other variations including the addition of "$this" as the first argument to [System.Windows.Forms.MessageBox]::Show and putting the call in a background job. Like I said, it always works outside of running as the deployment app, but the deployment app execution never shows the form because the call always returns immediately.
Services don't normally interact with logged on users. Prior to Windows Vista, services and applications ran in the same session, and a service running as the system account could be configured to interact with the first desktop session. On systems supporting multiple sessions--Citrix WinFrame on Windows NT 3.51, Windows NT 4.0 Terminal Server Edition, Windows 2000 Terminal Services, Windows XP Fast User Switching, and Windows 2003 Terminal Services--services could interact with session 0 but not other sessions without using an alternative interprocess communication mechanism. (There was a free resource kit tool for Windows NT 3.51 that provided multiple local sessions as well, similar to multiple sessions in a *nix environment, but I've long since forgotten its name.)
With the introduction of Windows Vista and Windows Server 2008, Microsoft isolated session 0 from user sessions, prohibiting services from interacting directly with user desktops; however, through the Interactive Service Detection service, it is possible for a user to switch to the service desktop to view notifications from services. Interactive services running as the system account and services running as users that create windows on desktops will be visible on the isolated session 0 desktop.
You can read more about session 0 isolation here.
All that said, you should probably look at the WTSSendMessage function. It works correctly from a service and would allow you to expand your check to support systems with multiple sessions. I.e. After enumerating the active sessions, you could send a message to each session, possibly as multiple asynchronous PowerShell jobs, and record the response from each user.
Services don't normally interact with logged on users. Prior to Windows Vista, services and applications ran in the same session, and a service running as the system account could be configured to interact with the first desktop session. On systems supporting multiple sessions--Citrix WinFrame on Windows NT 3.51, Windows NT 4.0 Terminal Server Edition, Windows 2000 Terminal Services, Windows XP Fast User Switching, and Windows 2003 Terminal Services--services could interact with session 0 but not other sessions without using an alternative interprocess communication mechanism. (There was a free resource kit tool for Windows NT 3.51 that provided multiple local sessions as well, similar to multiple sessions in a *nix environment, but I've long since forgotten its name.)
With the introduction of Windows Vista and Windows Server 2008, Microsoft isolated session 0 from user sessions, prohibiting services from interacting directly with user desktops; however, through the Interactive Service Detection service, it is possible for a user to switch to the service desktop to view notifications from services. Interactive services running as the system account and services running as users that create windows on desktops will be visible on the isolated session 0 desktop.
You can read more about session 0 isolation here.
All that said, you should probably look at the WTSSendMessage function. It works correctly from a service and would allow you to expand your check to support systems with multiple sessions. I.e. After enumerating the active sessions, you could send a message to each session, possibly as multiple asynchronous PowerShell jobs, and record the response from each user.
Also, I initially missed the way to use PSexec to properly run as the local system account as a service, which would have revealed the issue to me much sooner.
Per the PSexec docs, use psexec -s -i 0 ...
to run the process as a service.
For others looking for references:
WTSSendMessage
https://msdn.microsoft.com/en-us/library/windows/desktop/aa383842(v=vs.85).aspx
Session 0 Isolation:
http://www.coretechnologies.com/WindowsServices/FAQ.html#WhatIsSession0Isolation
Some Pinvoke examples with WTSSendMessage:
https://gallery.technet.microsoft.com/scriptcenter/Send-TSMessageBox-c2badfcc
Thank you for the detailed response. I will check this out and get back to you.
I can't post links (yet?), apparently, but pinvoke.net has a PowerShell example for enumerating sessions synchronously with WTSEnumerateSessions.
No problem. I have already had to do that in the past, so I should be able to get over that hurdle.
FYI, while I probably won't help this problem, have you seen the Splunk powershell resource kit?
Skimmed it at one point, but have not dug into it for this item. Will do today. Thanks.
While no expert in powershell, my first thought is that UAC or some other permission is somehow causing you trouble. Have you tried elevating it? Even if that's not precisely the problem, it's possible this will work for your needs anyway by creating a new process with higher permissions. Here's a Microsoft Blog on the topic of getting elevated permissions in a script.
I can poke a guy at work who does quite a bit of powershell on Monday if this question is still hanging out there. Not sure he's stumbled across a problem like this but he might have.
How are you launching the script? Inputs.conf? If so, are you using the script or powershell stanza?
Inputs.conf contains the following stanza:
[script://.\bin\install.path]
The file install.path calls the actual Powershell script with this single command
$SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -command " &'$SPLUNK_HOME\etc\apps\<MY_APP_NAME>\bin\install.ps1'"
Once again just noting that the script does run without issue (no errors or warnings). The Windows Messagebox Show() call does execute and return without error. The problem is it always returns immediately with no UI component ever being shown.
Also note this is a deployment app given to customers that use the Windows forwarder on client machines. This is not for a scripted input/add-on that I use internally on my system.
I was able to reproduce on my windows 10 laptop (running splunk free). I tried a few things as well, none of which worked. I tried making the splunk service logon with system interactively. I tried passing the ServiceNotification and DefaultDesktopOnly messagebox options, and those didn't work either.
The strange thing I'm seeing is that the messagebox method always returns the Ok button. It doesn't matter that I'm using YesNo or RetryCancel, or using button1 or button2 as default...it always returns Ok. Of course, running it manually returns the button I click.
Thank you for putting in the effort. I updated the question to reflect the response "OK". Went back and confirmed that is what I am seeing too.
I put this "Answer" as a "Comment" so that the question would remain with no actual answer, that way more folks will look at it.
If this DID answer it for you, or get you close enough, please let me know and I'll convert it to an answer so you can accept it.
The powershell script in the deployment app already runs with elevated privileges (precisely, the local system account). The script always successfully carries out every other step that requires admin privileges (such as running an MSI or editing the registry) - only the user prompt seems to misbehave.
I have tried putting a the prompt generation step as a background job in the running process, and in a separate process. The exhibited behavior is always the same.
My fear is the environment that Splunk wraps around the spawned deployment server app script has some kind of limitation that is blocking UI related things, but I am unclear on how to prove that.
Well, shucks. I'll ask Monday in case my coworkers have hit this elsewhere and solved it. For now, I'll leave this as a comment so the question remains unanswered.
(Maybe after this question has an accepted, proper answer, I'll move this to be a secondary extra answer in case it's helpful to other people who have the simpler version of the problem. But that's for later. 🙂
No prob Rich. This is probably one of those things where someone from Splunk says I am using the deployment mechanism in an unsupported/unintended way.
I appreciate the help, especially from a fellow fez.