I was working on a project recently where I had to deploy a settings file to the root of my web applications folder (where the web.config file resides). If you've ever had to do something like this before then you know that you cannot do this declaratively using the WSP's Solution schema. The Solution schema is really quite limiting as to where you can actually deploy files - as a result your only option is to create a custom Feature that runs some code when executed (because we certainly don't want to go the xcopy route).
To do this we're going to create a custom Feature which contains all the files that we need to copy and then we'll provision a one-time timer job to copy the file to the target location on each server.
Here's our Feature.xml file:
<?xml version="1.0" encoding="utf-8"?>
Description="This Feature deploys a settings file to a the web application root."
ReceiverAssembly="MyCustomFeature, Version=18.104.22.168, Culture=neutral, PublicKeyToken=39b13c54ceef5193"
<ElementFile Location="Files\settings.config" />
As you can see we are including a "settings.config" file which is located in a folder called "Files" directly under the Feature folder. You could easily have any number of files here by simply adding additional ElementFile elements. Also note that we are linking a feature receiver to the Feature which will execute upon activation and deactivation.
Here's our feature receiver class:
Notice that within the FeatureActivated method I'm getting a reference to the SPWebApplication object and passing that to a CopySettingsJob class which is our timer job that will do all the work. On the FeatureDeactivating event you can see similar code but I'm passing in false instead of true to the Submit method. The Boolean value indicates whether we are activating or deactivating our Feature. I'm also passing in the path to the Feature folder in the 12 hive as that is where our source files are located.
Lets look at the timer job class now:
As you can see the code simply stores the Feature folder as a property and then sets a one-time schedule. When the code runs it copies the source file to the target. Because we're using an SPJobLockType value of "None" in the constructor the code will execute on every server (set it to "Job" if you want it to run on just the server in which the Feature was actually activated).
Of course the code above isn't very generic as it hard codes the settings.config file which isn't very reusable but I wanted to keep this sample nice and simple. A better approach would be to require an either an XML file to be stored in the Feature folder and then read by the timer job or have the SubmitJob method take in parameters that describe what files to move and where to move them.
One key thing to remember is that this code will run once on each server for every web application on which the Feature has been activated. If you need a Farm scoped Feature because perhaps you are copying the noise words file for instance then you'll want to change the constructor of the timer job to take in an SPService object and change the FeatureActivated method as shown below:
Hopefully this simple example helps you to solve your file deployment challenges.