are a convenient way of managing dynamic environments for the purpose of reviewing changes before merging into your main branch. GitLab has great Kubernetes support and can easily deploy to your clusters, but if your application is not nicely containerized or your team is not ready to take on Kubernetes, what do you do? You Terraform! GitLab Review Apps Terraform is an excellent cloud-agnostic tool for developing your infrastructure as code. By combining a couple features of Terraform, we can pretty easily build a system for deploying Review Apps. Terraform 1. Workspaces provide a way of provisioning and managing multiple sets of identical infrastructure without copying Terraform configuration files. If you are familiar with Terraform and its files, you can think of Workspaces as creating and managing new arbitrarily named files. Workspaces .tfstate .tfstate For example, creates a new Workspace which is named with the value of the environment variable . This creates a new blank , meaning a will provision a new set of resources that are managed independently of any resources managed by any other workspace. terraform workspace new $BRANCH $BRANCH .tfstate terraform apply 2. Remote State is a way to persist Terraform state across multiple machines by storing files in one of several supported remote storage mechanisms, such as Amazon S3. While this is most commonly used to allow people to collaboratively work on infrastructure, it can easily be used within GitLab jobs to provision and keep track of infrastructure managed by Terraform. Remote State .tfstate The key here is that Remote State persists Workspaces, allowing GitLab CI/CD to reference the Workspaces created for each branch across different jobs that will be ran at different times from different machines. GitLab CI/CD The configuration for this is pretty straightforward, but there are a few key points. .gitlab-ci.yml There are three important pieces of configuration here, all within the block. environment is the dynamically built name of the environment itself. In this case it is based on the the variable which is the URL-friendly representation of the branch name. name CI_COMMIT_REF_SLUG is the URL that GitLab associates to the environment. Note that GitLab does not create the DNS record for this, it is up to you to create it. url tells GitLab which job should be triggered when the branch is closed or the environment is manually stopped, thus finishing off what makes this a and not just a dynamically named environment. on_stop Review App The portion of the configuration is where Terraform comes in. script We need to store the Terraform configuration outside of this repository because we need the configuration files to be available after the branch is closed. Referencing a particular Git tag when cloning this repository is necessary to ensure that the infrastructure used to provision these applications does not change without you knowing. As mentioned earlier, Terraform Workspaces is one of the big features that makes Review Apps with Terraform possible. We can define the infrastructure required to run the application and then use to manage isolated copies of this infrastructure. Workspaces terraform workspace select $CI_COMMIT_REF_SLUG || terraform workspace new $CI_COMMIT_REF_SLUG Here we are selecting the Workspace named with the value of or we are creating it if it does not exist. This ensures that we can run the the job multiple times from a single branch without having issues. $CI_COMMIT_REF_SLUG StartReview The rest of the script is a standard followed by some CodeDeploy specific stuff. I am not going into deployment specifics in this article since there are many tools for doing it, but if you are interested in setting up CodeDeploy . terraform apply I have written about it in the past The job is very similar to . StopReview StartReview The environment variable set to makes sure that GitLab does not try to clone the branch when running this job. This is necessary because this job may be ran after the branch is closed, and thus with nothing to clone. GIT_STRATEGY none The combination of and the property is what tells GitLab which environment this job is meant to stop. The here needs to match the set in . action: stop name name name StartReview Getting to the script, we again clone the Terraform repository with the same tag used earlier. We select the Terraform Workspace corresponding to this branch (it will already be created if we are running this job) and run to de-provision the infrastructure that was created for this branch’s Review App. terraform destroy -auto-approve To clean everything up, we then switch the Workspace and delete the Workspace we created for this branch. Terraform does not let you delete the currently selected Workspace, which is why we need to switch back to . default default At this point you should have infrastructure that can be provisioned at will dynamically based on branch name and then destroyed either at will or automatically when the branch is closed. Integrating your deployment tool of choice on top of this will give you fully functional Review Apps with their own infrastructure even if your application is not ready for Kubernetes. Follow for more DevOps goodness! Jared Ready 👏 if you enjoyed the read!