A key feature of the app server is the ability to install it on a remote machine via the admin website. You fill in a form, hit submit, and the server will be created on a remote machine.
- Installer credentials: the user that will run the install
- Service credentials: the user the service will run as
- Remote machine name: where to install to
- Drive letter: which drive to install it to. (So far, you can’t select the folder, just the drive. The folder is determined by convention.)
- Serve Type: optional. Select how the server should configure itself.
- The name of the application server. (There can be multiple per machine, but the name must be unique across the network.)
The hub runs in IIS, which is why we need 2 sets of credentials. IIS isn’t an admin user and doesn’t (or shouldn’t) have enough access to the other machines to install software.
The steps of the install are:
- Deploy the files to the remote machine
- Grant LogonAsService right to the service user on the remote machine
- Create the service on the remote machine
- Start the service on the remote machine
When I created the installer, I was still in feature demo mode, and just wanted to get it to work. I thought the quickest way to do that would be to generate a batch file and execute it. Process.Start allows you to pass credentials, so I thought i would use that to execute the batch file as the installer user.
It was a long time ago. I don’t remember the details of all that went wrong, but it turned into a disaster. I ended up with 2 batch files: one to run on the hub, and another to run on the remote machine. I had to use PSEXEC to run those batch files, one locally, one remotely.
After much shenanigans, it worked, but it wasn’t the quick fix I was looking for, and also it was really ugly. But, it worked, which is all that I was after at the time.
The reason I went through that is because I didn’t want to deal with writing all of the impersonation code that would allow me to execute C# code as a different user.
Finally, late last week, I circled back on that and settled in to deal with the impersonation. I properly nourished myself and made mental preparations. Then, all of a sudden, I realized it’s 2014, and we have at our disposal a little thing called NUGET. I quickly found and added the SIMPLE IMPERSONATION library, and there we go… I had impersonation.
And it works great. I haven’t had to play with anything. I’m fully confident that I could write it myself, but I would’ve tripped along the way. It’s nice to have it just done and work. So thanks for that.
Now, the installer is much simpler. I deleted a lot of code.
- The file deploy is done via C# code. There is a configurable list of files. It iterates the list and copies the files.
- Grant LogonAsService is accomplished by executing NTRIGHTS.EXE from code. NTRIGHTS.exe can run against a remote machine.
- CREATE and START the service are accomplished by executing SC from code. SC also works against remote machines.
I’m using PROCESS.START three times. As it evolves, it would be nice to eliminate that and do it all in code, but it’s MUCH better now. I’m able to collect the error output and standard output and save it to the response object.
I haven’t done this yet, but now that it’s back in code, I can use the bus to publish messages as the install proceeds. Those messages will be received by the installer page, which will display the messages to let you know what’s happening as it happens. (Today, it just sits there until the install is complete. It’s boring.)
Finally, this change offered another huge benefit: It’s so much faster. PSEXEC took a long time to run, and I ran it multiple times. It would take 20 or 30 seconds to install. Now, it takes less than 300 milliseconds. That sounds ridiculous, so I confirmed it a few times.
So far, it’s only working as a unit test. You can kick it off from the page, but you won’t get any feedback just yet.
I have to make one more change. Previously, you would install the app server, then set the server type as a second step. I recently changed that so you that you can, optionally, specify the server type on the setup page. But, I cheated and had the controller set the server type before calling the installer. It’s a hack. The installer needs to be self contained and do it all itself.
Here I use my super awesome REALLY pattern. The code that calls this method does error handling and impersonation. Impersonation is only done if demanded by the installer request. That method is pretty ugly, so I’m not including it in order to protect my self esteem.
This method is called three times: NTRIGHTS, SC CREATE, SC START.
The installer response was previously specific to the batch file implementation, and was marked as a todo. This one is more generic. You add a series of messages to the response, and the message indicates “OK”, or “PROBLEM”, and any user details.
This information will ultimately be published and, possibly, recorded.
Currently, I’m handing uninstalls manually as needed. But, that will have to be coded, too, at some point.