Being able to online a new server and have it automatically install all the required software, setup all the configs just by its hostname is a beautiful thing.
They sit on top of the package manager. Ansible is more about doing commands en masse, Puppet is more about ensuring a consistent state en masse.
What happens if you want to ensure that your server farm is all running the same version of nginx? What if you want to ensure the configuration files are all in a consistent state?
You can script it yourself if you really want to, but it's a solved problem at this point. Puppet's mission in life is to notice when a server has deviated from your specified configuration, report it, and haul the box (kicking and screaming if necessary) back into compliance.
Manual scripting doesn't scale beyond 100 servers or so. You don't have enough hours in the day.
Its "playbooks" are by default meant to be idempotent -- you can run them over and over ensuring that a system is in a consistent state.
What happens when the configuration of nginx needs to be slightly different on each server?
What happens when the configuration of nginx needs to change?
What happens when you need to install a custom version of nginx that's not in your OS repository?
What happens when you need more than one instance of nginx running on the server?
rdist was a good method in the old days which scaled a little better than one-off, but we're in the pull vs push world now.
If you have a really large deployment, you can set up your own repository, or a mirror of existing repositories. If that is still not enough, you're Facebook.
By the time you're done putting all your apt-gets and your config files and whatnot in some shell script to automate it all away, you've reinvented a poor clone of puppet/ansible/chef.
apt-get is fine, it's just that at some point you need an automation tool to trigger it. A lot of chef recipes rely on the underlying package manager, that's fine.
Simple solutions regularly fail when you add zeroes.