Interestingly most posts which recommend using Puppet/Chef depend on Fabric for deployment. Is a mutually exclusive or pure Puppet/Chef approach better in any way?
If you want to use fabric (or a shell script) to run puppet, go ahead. I'm just suggesting that you really want to use a deploy system with proper dependency management.
The issue I ran into with fabric is that I often got stuck in dependency hell. The following is fabric's simplest method of dependency management:
def install_foo():
install_foo_dependency()
...
Unfortunately, you don't want to do this every time you deploy because install_foo_dependency() might take a while to run. You can work around it by checking inside install_foo_dependency whether it's already there. In practice, you probably won't always do this. Puppet usually has recipes which already do this for you.
In theory, you can do things right with fabric. In practice, you have to do a lot of work to replicate what puppet (together with assorted easy to find recipes) gives you out of the box.
As someone who has worked with a fairly involved fabric deployment & provisioning process, I'm forced to agree. Fabric is great for what it is, but you lose so much by not using chef.
Eric Holscher also has an excellent blog post, http://ericholscher.com/blog/2010/nov/8/building-django-app-... ,
describing how to deploy using both fabric and chef. There are certain instances where one tool works better than the other, and in that situation that tool is used.