c# : Create windows scheduled task as a different user - c#

I'm creating Windows Scheduled Tasks dynamically from c# using the build-in TaskService and TaskDefinition libraries.
But for some of them, we need to create then to run as a different user (Local Service or Network Service). As the tasks are created and removed dynamically we cannot edit all of them manually to change the user. We need to do it via code.
Is is possible?
I've tried the following settings:
TaskDefinition.Principal.Id = "NETWORK SERVICE";
TaskDefinition.Principal.LogonType = TaskLogonType.ServiceAccount;
but this gives me the very descript error when creating the task:
System.Runtime.InteropServices.COMException: '(52,4):Task:'
Without those 2 lines, it works but creates them as the logged in user.

I've played around with the Task Scheduler stuff a bit and have replicated your problem. I believe I’ve found some things out, maybe they can help.
1. Firstly if your making Tasks in the debugger using Services Accounts, you'll want to ensure your Visual Studio or other IDE is launched as administrator to ensure you have the correct privileges to do this task.
2. I'm not sure if you do this later in your code but to make the task save and run as NETWORK SERVICE, I had to Identify Network Service as NT AUTHORITY\\NETWORKSERVICE in both the principle and on the RegisterTaskDefinition method:
TaskService tService = new TaskService();
TaskDefinition tDefinition = tService.NewTask();
tDefinition.Principal.Id = "NT AUTHORITY\\NETWORKSERVICE";
tDefinition.Principal.LogonType = TaskLogonType.ServiceAccount;
tDefinition.RegistrationInfo.Description = "Testing";
tDefinition.Triggers.Add(new DailyTrigger {DaysInterval = 2});
tDefinition.Actions.Add(new ExecAction("notepad.exe"));
tService.RootFolder.RegisterTaskDefinition(#"Test", tDefinition, TaskCreation.CreateOrUpdate,
"NT AUTHORITY\\NETWORKSERVICE", null,
TaskLogonType.ServiceAccount);
I used the above code to make a test Task that got successfully added to my scheduler as Network Service as shown below:
I'm guessing that one or both of the above points may have stopped the task from being added, hope that helps

Related

SSIS Package Created and Executed via Code Requires Service to be Restarted after Multiple Runs

we have existing C# code to dynamically create SSIS packages that reads from different files and into a SQL Server 2016 database. It does what we need it to, but stumbled into an issue that we remain unable to resolve: being unable to keep running the .Execute command without having to restart our custom Windows service.
Right now the limit is at 2 files. If we run our code that calls the Execute command the third time, it will get stuck up on post validate based from what we're logging via the Component and Run Event handlers, and we couldn't proceed until we restart the Windows service and run again. We cannot just always restart the service because there could be other processes that can be disrupted if we go with that approach.
Things we've tried so far:
Extract the dtsx package that the code creates and attempt to run it using dtexec or BIDS locally. No errors / warnings raised, and we can keep re-running the same package over and over without fail.
Run the same code locally. Same result as #1.
Run a SQL trace with the help of our DBA. Confirmed that we have no queries locking the destination table and the database itself. We did observe some SPIDs being retained after the third run but all are in a sleeping state.
Modify our RunEventHandler and ComponentEventHandler to log which step is the process in. Also tried enabling logging via Event Viewer. No errors, really just getting stuck at post-validate as mentioned earlier come the third run.
Explicitly call the SSIS dispose methods, even tried explicitly disposing the connection managers themselves.
Play around the DelayValidations and ValidateExternalMetadata properties.
Any chance others have encountered this before and were able figure out what's causing this?
To expound on the latest comment, we've found that the issue stems from the fact that we're calling a separate AppDomain to take care of running the job and consequently executing the package. The way this separate AppDomain is created is via CreateInstanceAndUnwrap,using the following code:
private AppDomain CreateAppDomain(string applicationName)
{
var uid = Guid.NewGuid();
var fi = new FileInfo(Assembly.GetExecutingAssembly().Location);
var info = new AppDomainSetup
{
ConfigurationFile = #"D:\Deploy\App\web.config",
ApplicationBase = #"D:\Deploy\App",
ApplicationName = applicationName,
CachePath = fi.Directory.Parent.FullName + #"\Temp",
ShadowCopyDirectories = #"D:\Deploy\App\bin",
PrivateBinPath = "bin",
ShadowCopyFiles = "true"
};
var domain = AppDomain.CreateDomain(uid.ToString(), null, info);
return domain;
}
How we're able to test out this theory is by calling AppDomain.Unload(domain) against that created domain, and while we get an DomainUnloadException, this does prevent the job from freezing after being run twice.
We still haven't determined exactly what's within the domain that's getting locked up and preventing us to run the job more than twice, and guidance to learn more about that will be helpful. In the meantime, we're using this workaround of unloading the app domain for now.

Coded UI Application Under Test

Just started implementing a coded ui test automation solution but keep running into an issue when starting the application.
The application seems to start just fine but no matter what I always get an exception stating:
Microsoft.VisualStudio.TestTools.UITest.Extension.FailedToLaunchApplicationException: "The application cannot be started. This could be due to one of the following reasons:
1) Another instance of the application is already running and only one instance can be running at a time.
2) The application started another process and has now stopped. You may need to launch the process directly.
3) You do not have sufficient privileges for this application."
The application is a little strange as it currently is setup to run off of a setup.exe so the user always has the latest version.
Am I missing something in my code (sample below)? Or does the application need to be better set up before I start writing the automation tests. EXE is located in a network location.
ApplicationUnderTest aut = ApplicationUnderTest.Launch(#"\\test.com\\applicationdir\\testenv\\application\\setup.exe");
WpfEdit userName = new WpfEdit(aut);
userName.SearchProperties.Add(WpfEdit.PropertyNames.AutomationId, "PART_UserName");
userName.Text = "TEST";
Currently using a workaround where I start the app via Process and then pass it to the application under test FromProcess(). Seemed to fix the issue.
Probably not the best solution and have to use a Thread.Sleep() but it works for now.
Example:
var process = Process.Start(#"pathToApplication");
Thread.Sleep(2000);
process = Process.GetProcessesByName("process.Name")[0];
ApplicationUnderTest aut = ApplicationUnderTest.FromProcess(process);

Programmatically grant local user rights to start a service with .Net

I want to implement an update service in my application (so users don't need admin rights to update my app once the service is installed, much like Google or Mozilla do their updates), and I think I found a good way to do this with WCF.
I have a WCFServiceLibrary-Project which contains the ServiceContract and the core functionality (download/install updates) and a Windows Service-Project which implements the WCFServiceLibrary as a Windows Service.
Additionally, there is a Visual Studio Installer-Project which installs the service and my application, which should be able to start/stop/communicate with the service using NamedPipes.
The service is configured to start manually with the LocalSystem-Account.
Now when the service is installed, I can start/stop it using services.msc (probably elevated), but not when I try it with net start Servicename (Error 5: Access denied) or with my application, which tells me that the local users probably don't have the permission to start/stop the service.
I need the service to run with higher permissions in order to perform the installation of updates, so I would like to give local users permission to start my service either during the first installation of the service or when the service starts for the first time (since I can trigger that also during installation).
However, how would I accomplish this with VB.NET (or C#)? I found some examples using API-Calls of advapi32.dll, but it didn't looks like the permission can be changed with this.
So, long story short, heres a summary of what I'm looking for:
Grant Group "Local Users" permission to Start my Service, best approach either during installation (Maybe with Custom Actions in Visual Studio Installer Project? Or in the ServiceInstaller-Class in the Windows Service-Project?) or the first time the service starts (OnStart-Event in Windows Service Project)
The service must not run with local user rights, since it would miss elevated privileges then which would be necessary to install updates.
I can't assign permissions through GPO/Local Policy since the users are not within our company, but all around the world. For the same reason I cannot assume that they can get an admin to elevate them everytime an update comes out.
I would like to avoid commandline calls if possible (as in assign permissions through command line, since those are most likely os-dependent)
Another solution would be to configure the Service as Automatic and Start it after install, but I don't like the idea that my service runs all the time since its only needed when my main application starts up.
Its most likely not a file permission issue. EVERYONE, SYSTEM and SERVICE got FULL ACCESS to the services folder and files in it.
There are already different similar questions here, but none did give a clear answer to this problem. One user probably did it using the WiX-Installer, but I would like to keep the Visual Studio Installer Project since it's pretty straight forward and easy to use.
After a bit more of googling and trying to find a "clean" solution, I've given up and using now Process.Start to execute sc.exe and set new Permissions after Installation.
Here's my ServiceInstaller-Class, for anyone curious:
[VB.NET]
Imports System.ComponentModel
Imports System.Configuration.Install
Imports System.ServiceProcess
<RunInstaller(True)>
Public Class SvcInstaller
Inherits Installer
Dim svcprocinst As ServiceProcessInstaller
Dim svcinst As ServiceInstaller
Public Sub New()
svcprocinst = New ServiceProcessInstaller
svcprocinst.Account = ServiceAccount.LocalSystem
svcinst = New ServiceInstaller
svcinst.ServiceName = "KrahMickeySvc"
svcinst.DisplayName = "Mickey-Service"
svcinst.Description = "This Service is used by KRAH Mickey for application updates and maintenance"
Installers.Add(svcprocinst)
Installers.Add(svcinst)
End Sub
Private Sub SvcInstaller_AfterInstall(sender As Object, e As InstallEventArgs) Handles Me.AfterInstall
'Set new permissions acc. to Security Descriptor Definition Language (SDDL)
'Source: https://blogs.msmvps.com/erikr/2007/09/26/set-permissions-on-a-specific-service-windows/
'Keeping the source DACL and just adding RP,WP and DT (Start/Stop/PauseContinue) to IU (Interactive User)
Dim DACLString As String = "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRCRPWPDT;;;IU)(A;;CCLCSWLOCRRC;;;SU)"
process.Start("sc.exe", $"sdset {svcinst.ServiceName} ""{DACLString}""")
End Sub
End Class

Is it possible to use Windows 7 Task scheduler in own application

I'm developing add-on based application. Every add-on use scheduler. Loaded add-on schedule task. My application run only one instance. Sometimes application closed, sometimes running. Therefore i need to use Windows 7 Task scheduler
How to use task scheduler on own application?
I need create new task from application
I need that when task finish, Send message to my application or invoke any function of my application
Is it possible?
How to do it?
Check out this project at CodeProject.
A New Task Scheduler Class Library for .NET
If you want to interact with the Windows 7 Scheduled Tasks system from your code to create, manage, or delete a task that is no problem. (I cover this in the Windows 7 course I wrote for Pluralsight.) Add a COM reference to TaskScheduler and you then do this sort of thing:
ITaskService scheduler = new TaskSchedulerClass();
scheduler.Connect(null, null, null, null);
ITaskFolder rootFolder = scheduler.GetFolder("\\");
ITaskFolder folder = rootFolder.GetFolders(0).Cast<ITaskFolder>().FirstOrDefault(
f => f.Name == "Windows7Course");
if (folder == null)
{
folder = rootFolder.CreateFolder("Windows7Course", null);
}
ITaskDefinition task = scheduler.NewTask(0);
IExecAction action = (IExecAction)task.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC);
action.Path = typeof(SayHello.Form1).Assembly.Location;
action.WorkingDirectory = Path.GetDirectoryName(typeof(SayHello.Form1).Assembly.Location);
ISessionStateChangeTrigger trigger = (ISessionStateChangeTrigger)task.Triggers.Create(
_TASK_TRIGGER_TYPE2.TASK_TRIGGER_SESSION_STATE_CHANGE);
trigger.StateChange = _TASK_SESSION_STATE_CHANGE_TYPE.TASK_SESSION_UNLOCK;
task.Settings.DisallowStartIfOnBatteries = true;
task.RegistrationInfo.Author = "Kate Gregory";
task.RegistrationInfo.Description = "Launches a greeting.";
IRegisteredTask ticket = folder.RegisterTaskDefinition(
"GreetReturningUser",
task,
(int)_TASK_CREATION.TASK_CREATE_OR_UPDATE,
null,
null,
_TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN,
null);
This particular sample runs an exe that is in the same solution (another project). You would have to adjust the Action.Path, Action.WorkingDirectory etc to meet your needs.
Thanks for the excellent example Kate, I rue that in my wanderings I didn't come across your code first.
I've had a merry dance with registering scheduled tasks via c# code and unsurprisingly have had to tweak my schedule creation code as I encounter roadblocks on one environment that I hadn't encountered on any previously. I hope I've arrived at the most robust code and thought it might also be of benefit to others if I share some things you might try if encountering environmental issues:
Put the path to the executable in double quotes
Do not put the working folder in quotes
Once the task is created, if you make subsequent changes, call the ask.RegisterChanges(); - it may be worth setting the task.Definition.Principal.RunLevel = TaskRunLevel.Highest;
I typically default to a System account to run the task, but in some network environments I've encountered issues which I was unable to fathom but was able to resolve by providing a user account for the task to run under.
all the best
Matt

How a LocalSystem Service can run an application as a user [C#]?

On my local machine I am running an administrative c# service as LocalSystem called Serv.exe which performs various tasks. One of the tasks it needs to perform is to launch an application under the USER account (currently logged on), not as admin - as this violates security.
The obvious solution would be simple impersonation when launching the application - however I run into a small problem whereas I am not priviledged to the user account credentials (Username & Password) and therefore am unable to impersonate in the conventional way.
So, using a C# service running as LocalSystem when logged on to a User account - is there anyway I can launch an application as that User?
From the comments:
what happens is that the Application itself asks the Service to do a job and then terminates. when the job is funished the application must restart itself - I thought the best way would be to have the service restart it when it was done ...
Any help would be greatly appreciated.
Thanks,
You can use Windows Scheduler to start your app as a user.
Take a look at this wrapper - http://www.firatatagun.com/c-windows-task-scheduler-wrapper-classes-c-sharp/2010/04/22/
and then you can simply create a scheduled task to run your software immediately, afterward you can delete this redundant task after 2 seconds.
Sample code:
using (TaskService ts = new TaskService())
{
// Create a new task
const string taskName = "RunMyProcessNowAsUser";
Task t = ts.AddTask(taskName,
new TimeTrigger() { StartBoundary = DateTime.Now, Enabled = false },
new ExecAction("YourProcess.exe");
t.Run();
// delete the task after 2 seconds.
new Action(() =>
{
Thread.Sleep(2000);
using (TaskService ts2 = new TaskService())
{
ts2.RootFolder.DeleteTask(taskName);
}
}).BeginInvoke(null, null);
}
Instead of breaching security this way you can make the application wait and then restart itself. See this SO question and this one.

Categories

Resources