The only reason why I don't consider TF to be IaD in spite of being declarative is the statefile. I. updated the post to make that a little more clear, as I can see why the original write-up would be confusing.
You're right that you end up in a weird chicken-and-egg thing if you use a tool like Crossplane to provision a k8s cluster, as you already need a k8s cluster with Crossplane installed. Crossplane came up with a solution called Upbound (https://upbound.io) to address that. I played around with it a bit, and it's pretty neat.
In terms of security and tenant isolation in cluster creation and app deployment, a GitOps tool like ArgoCD (https://argoproj.github.io/argo-cd/) can definitely help with that. You can define policies that allow only certain people or teams to deploy to certain clusters or even namespaces, if I recall correctly. And if you really want to bump up your namespace game, you may want to consider looking into hierarchical namespaces, which were released in 2020 (https://kubernetes.io/blog/2020/08/14/introducing-hierarchical-namespaces/).