CloudFormation is... fine until you realize it's missing that resource that you need. CF is notorious for lagging behind products in terms of support. It's incredibly painful.
Cloudformation is the best tool for aws infra-as-a-code. Nothing is even close to it. It rolls back, no leftovers on delete, fast parallel ops, full control of resource properties. Yes, some things can be better, but above is priceless. 10yrs experience here.
I used CloudFormation for a few years and ran up against a lot of its limitations. Maximum file size. Maximum resource count. Automatic rollbacks of THE WHOLE STACK on any subsystem failure like unavailable instance. It has no templating built in, so doing ten things with minor differences means copying and pasting 10x or deploying your own templating solution to generate CF. And I did this back in the ROLLBACK_FAILED days, where if you did something that it couldn't automatically undo you were stuck - no way to roll forward or backward, just abandon innplace. The button for "Continue rollback" or whatever that came out 5-10 years ago was huge.
Contrast that with Terraform: all of your points addressed, plus all of mine are non-problems. It lets you do some awesome things that are no doubt inspired by CF, but it takes them to an entirely new level.
There are a few downsides too, but nothing compared to CF. You can build too-complex things much more easily in TF, so you have to be careful not to go overboard. It also bugs me that I can't spec a high-level thing like "compute with 4 cores and 32GB RAM, repeat 10x and put behind a load balancer with DNS name foo" and use it anywhere, I have to say google_compute or azure_load_balancer or aws_dns. That was the biggest disappointment coming out of CF and hearing how awesome this thing was, and then realizing it still left me vendor locked.
> ran up against a lot of its limitations. Maximum file size. Maximum resource count. Automatic rollbacks of THE WHOLE STACK
I totally can see where this is coming from :) CFN is best used as separate templates/stacks for parts of the solution, not the whole solution rolled in a single template. Reusable is the key word here, and Parameters. Let me try a city example. Have separate templates for a school, fire departnemt and house block. Build all Detroit schools using the same school.yml template, just supply different parameters for each. Don't copy-paste code from school.yml into detroit.yml. Actually, there should be no detroit.yml, leave city level to CI/CD job.
> I have to say google_compute or azure_load_balancer or aws_dns
Multicloud? It rarely makes sense. All you get is triple the infra code, triple monitoring tools, triple devops competence requirements. Properly designed solution with HA and AZ/regional redundancy is sufficient on a single cloud platform.
I'm not suggesting multicloud as in using more than one at a time, I mean porting to another cloud or defining some OSS infrastructure in $magic_terraform that any cloud user could deploy without translation.
It would be limited to least-common denominator just because of the vagueness of objects that it would support, but the example I suggested would be incredibly useful.
> Cloudformation is the best tool for aws infra-as-a-code.
Talk about damning with faint praise.
> It rolls back,
Sometimes.
> no leftovers on delete,
Well, on successful delete, maybe. DELETE_FAILED with partial stack deletion is a thing (and a thing CF could fairly trivially avoid in some common cases by simply querying resources for deletion protection.)
> full control of resource properties
Except the AWS resources it doesn't support, and properties of supported resources it doesn't support, because CF always lags the underlying services and their APIs.
Oh, yeah, it lags and it is painful, but it is continuously moving forward (but chasing a moving target), even if the UI isn't (it's been probably more than a year since I looked at the UI.)