BITS Uploads
Autor: Felipe Simon | Publicado: 2012-08-07T06:51:00-07:00

I'll start writing in English here because these posts can be useful to people anywhere in the world who are facing the same challenges my team and I are.

In summary, I currently work for Brazil's national aviation agency (ANAC) and my job is to create a reliable and secure publishing system that comprehends all the agency's home made applications, including the legacy ones. This means versioning, tagging, building, deploying and occasionally performing a rollback in ASP, VB6, PHP, .NET and Java on Windows and Linux servers.

Where does BITS enter the story?

BITS (Background Intelligent Transfer Service) is a Microsoft file transfer tool which can work over several different protocols and deal with issues such as bandwidth restriction, prioritization and interruption/resumption. BITS can be easily invoked via powershell and &nbsp;easily enabled in IIS as an upload server.

For internal policy reasons, builds have to be transfered to the application servers using HTTP, which makes it convenient for us to use BITS on the Windows Servers.

Activating BITS on IIS was a very simple process in Windows 2008. The first step was to install BITS.

1. On the Server Manager (run "CompMgmtLauncher"), click on the "Features" node
2. Click "Add Feature"
3. Select BITS
4. Follow the Installation Wizard

The next step was to set up a virtual directory on IIS that accepts BITS uploads.

1. Right-Click your website and select "Add Virtual Directory"
2. Give it an alias name and point it to the physical path you'd like the uploads to be saved on.
3. Click the new virtual directory
4. Scroll down the window until you see the "BITS Uploads" item and double-click it
5. Select "Allow clients to upload files". In our case, the option "Use default settings from parent" was enough to do the job.

The best approach is to create a website to deal with the uploads separate from the applications website, so that there aren't configuration clashes in the future. For instance, the BITS website can have some bandwidth and permissions restricitions in a way that it wouldn't be possible to run the applications, if the BITS website will serve only for deploy purposes. The BITS website can also run in a different port, to keep things organized, so the next step was to create a new Binding.

1. Right-click the website and choose "Edit Bindings"
2. Choose "Add"
3. Point the binding to your desired protocol, host name and port. In our case, merely choosing "https", "All Unassigned" IP addresses and the port was enough.

Job done! The powershell call to the BITS client is very straight-forward:

1. Import-Module BitsTransfer
2. Start-BitsTransfer -TransferType Upload -Source &lt;filename&gt; -Destination &lt;websiteurl&gt;

And that was it. The file was there.

HOWEVER.... as one knows, things are never that simple in the world of computing

It worked fine for my user account, but when BITS was called from the web application, it would result in the following error:


Start-BitsTransfer : The operation being requested was not performed because the user has not logged on to the network. The specified service does not exist.
(Exception from HRESULT: 0x800704DD)

MSDN tells us that:
"BITS transfers files only when the job's owner is logged on to the computer (the user must have logged on interactively). BITS does not support the&nbsp;RunAs&nbsp;command. Nor does BITS support terminal services logon on Windows&nbsp;2000.&nbsp;If you do not require a user's context for your application, consider writing a service running as LocalSystem, LocalService, or NetworkService instead. These system accounts are always logged on."

There are several workarounds.

1. runas /user:"nt authority\LocalService &lt;command&gt;" (that didn't work in our server)
2. sc create newservice -binPath="powershell -command `"Start-BitsServer &lt;params&gt;`""; sc start newservice; sc delete newservice (that didn't work either)
3. Scheduling a task running as the Local Service account that uploads files from a queue.

In Windows 2008, creating a task in Task Scheduler is quite simple.&nbsp;

1. Click Start Menu/Programs/Accessories/System Tools/Task Scheduler
2. On the General tab click Change User or Group
3. Make sure "Select this object type" contains "Built-in secutiry principal" and "From this location" contains the computer name.
4. In the "Enter the object name to select" type "LOCAL SERVICE" and click OK
5. Select "Run with highest privileges"
6. On the Triggers tab, configure when you would like the task to be triggered (in our case was Daily, Repeating the task every 1 minute for 24 hours.
7. On the Actions tab, choose "Action": "Start a Program", "Program / Script": "powershell", "Add arguments": "-noprofile -NonInteractive -executionpolicy unrestricted -file &lt;scriptpath&gt;"
8. On the Settings tab, make sure to set "If the task fails, restart every": "1 minute"

The disavantage of this approach is that from the moment the build is ready until the moment the build is copied to the application server, it can take up to a minute, which can &nbsp;take some developers to a nervous breakdown, but for us a complete publishing task was taking up to 8 minutes, so that wasn't a big issue.

Sources:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa362783(v=vs.85).aspx