Puppet or Chef: The configuration management dilemma
Puppet is model-driven, Ruby is procedural, and both are large, messy, open source ecosystems plagued with pitfalls
Models vs. procedures
Are there differences? Not in the most abstract sense. They both send instructions to configure or reconfigure your collection of machines. And "reconfigure" generally means installing new versions of libraries or other files.
But the world is full of factions, and whenever we get close to a unifying harmonic convergence, humans split into competing groups. Humans find some way to splinter.
In this case the fight is over language. Chef is written in Ruby and Erlang, and you'll get to write your specifications or extensions in Pure Ruby, a subset of the full and apparently impure language that is sometimes called a domain-specific langauge. This subset will give you just enough rope to hang yourself.
Puppet, on the other hand, has its very own language that bundles together all of the installation requirements into packages with curly brackets. It's a bit like JSON, but with conditional operators and object-oriented class structure added for fun. If you don't like it, Puppet started letting you write your specifications in Ruby after version 2.6.
It's probably not a good idea to spend too much time worrying about the language -- the real differences lie deeper. Puppet code is a list of dependencies that Puppet interprets. You might say, "Install A before B," and you could say this anywhere in your code. Puppet will find it and ensure that A is added first.
Chef, on the other hand, is more transparent. You better make sure the section for installing A comes before the section for installing B because Chef follows the instructions it's given.
Which is right for you? Luckily there are people writing long articles arguing for their vision. One that I liked is Steve Traugott and Lance Brown's "Why Order Matters: Turing Equivalence in Automated Systems Administration." Chef lovers like how it says that all of those dependencies turn into a crazy mess that doesn't encourage coherence. Andrew Clay Shaefer argues on his blog that most of the time we're smart enough to do the right thing with Puppet and use its powers for good not confusion -- except when it doesn't. I still can't make up my mind.
System architecture
In any case, both Puppet and Chef are pretty easy to pick up once you wade through the syntax. They both have their own client/server architecture that compiles these instructions and keeps track of what the machines should have installed. Puppet sticks its instructions in its data stores called PuppetDB and Hiera. Chef uses a mixture of Postgres, Solr, and its own internal structure to keep track of what the client machines should be doing.
These databases end up maintaining a catalog of the state of your vast empire of machines. They track the different files, libraries, executables, configurations, and other details. Then when you push the button, they send reconfiguration lists to the various machines to keep them current.
It's common, for instance, to define a few standard configurations for your enterprise and ask either of these tools to be ready to install them on a new machine. You might have a mail server or a Hadoop node. When a new machine or instance is created, Puppet or Chef will reach out with a list of files that need to be installed.