Terraform modules are a way to encapsulate reusable business logic in order to be DRY. For example, you might use a module to define a jenkins setup, then invoke that module every time you want a jenkins server. In other words, you can instantiate modules as many times as you need them to achieve the same outcome.
On the other hand, Terraform workspaces are a way to isolate terraform state between environments. For example, you don't want to mingle state between a production and staging environments.
In practice, we highly recommend writing modules for everything. This is why we have over 100+ modules. Writing modules is like writing "functions" in other languages. Generally, it's considered a bad practice to stick all logic in a single function or scope; modules help us avoid that.
Workspaces are nice in principle, but in practice we recommend to share nothing between production and staging environments, including terraform state. Workspaces violate this principle. Thus we architect things so that production state remains in the production account and staging state in the staging account. We accomplish this with our terraform-aws-tfstate-backend
module.
———
For the record, “we” refers to Cloud Posse, a DevOps professional services company. I am the founder. The views expressed are our own and may not represent the views of the community at large. :)