How do you handle the huge number of possible permutations of Dockerfiles / Teraform files?
So let's imagine you have 20 microservices, each in a separate repository. Each microservice needs to be deployed into 5+ environments: dev local, dev cloud, test, staging, prod. Understandably all configuration will be different in each environment. However, you don't need to put it into the Dockerfile - in fact you don't put any configuration into the Dockerfile. Your app gets all required parameters from environment variables, or may be from a config file, if it gets too complex. 

So you would have one Dockerfile per project, and if your developers need to spin it up locally, they will just use docker-compose, which has all environment variables set up, and maybe a config file mounted as volume. In stage envs the configuration will be totally different, but the Docker image will be the same. Your app will need to be able to read this configuration at start up time, and bail out early if something doesn't work.

Read https://12factor.net/config for some inspiration on how to configure your app.

Now, provisioning the infrastructure with Terraform would follow the same approach. You would have a generic module which provisions a set of some resources, and it would accept a bunch of parameters which would depend on environment. You can use workspaces to manage this quite easily.

As for the last question, large companies solve this with a CI/CD system. This can be Jenkins, or Spinnaker, or any of the other tools, whichever you prefer.