I'm ultimately moving to a Chef system to auto scale/discovery, but right now I actually control the server bootstrap process directly from the source code of the thing I need the server to be deployed on. Not sure if you're on the webstack, or if you're using ruby, but a really nice tool to use if you are is: recap (based on Capistrano)
It allows you to run scripts on any number of remote servers. It works by using a 'Capfile' to define your servers and such, and then has a few built in tasks that you can extend. One of them being the :bootstrap task. So I have a simple chain when provisioning new servers of
before 'bootstrap', 'bootstrap:dependencies'
namespace :bootstrap do
task :dependencies do
uname = capture('uname -r')
abort('ENV Not Supported') unless uname.strip == "some uname version"
sudo('apt-get -y install build-essential')
run('echo 'America/New_York' | sudo tee /etc/timezone')
That's just a sample of a few things you can do using the 'capture', 'sudo' and 'run' commands that recap provides you. Of course, this example assumes the software that you're deploying is tied to the ruby stack, but the point is, there are tools like this for all kinds of languages / frameworks.
My suggestion is simply to tie the provisioning of the server to the code you're deploying on that server, unless you want to go for a tool like chef or puppet.