We've used Terraform extensively to create and manage cloud infrastructure. In our experience with larger setups, where code is divided into modules that are included in different ways, teams eventually hit a wall of unavoidable repetition caused by a lack of flexibility. We've addressed this by using Terragrunt, a thin wrapper for Terraform that implements the practices advocated by Yevgeniy Brikman’s Terraform: Up and Running. We've found Terragrunt helpful because it encourages versioned modules and reusability for different environments. Lifecycle hooks are another useful feature providing additional flexibility. In terms of packaging, Terragrunt has the same limitations as Terraform: there is no proper way to define packages or dependencies between packages. As a workaround, you can use modules and specify a version associated with a Git tag.
We widely use Terraform as code to configure a cloud infrastructure. Terragrunt is a thin wrapper for Terraform that implements the practices advocated by the Terraform: Up and Running book. We've found Terragrunt helpful as it encourages versioned modules and reusability for different environments with some handy features, including recursive code execution in subdirectories. We'd like to see the tool evolve to support CD practices natively, where all code can be packaged, versioned and reused across different environments on CD pipelines. Our team achieves this today with workarounds.