Aligned with our mission of pushing the boundaries of the software industry, and our goal of lowering the barrier of entry for newcomers to Nix, we’ve integrated nixpkgs-terraform with devenv. For those of you not familiar with these tools, nixpkgs-terraform is an in-house project we developed to further increase the flexibility and control of Nix-based development environments for Terraform projects. Read more about it in our initial announcement: Combining Nix with Terraform for better DevOps. Devenv is a well established project in the Nix ecosystem with the goal of creating fast, declarative, reproducible, and composable developer environments using Nix.
We’ve been using devenv across many of our own projects, and it has helped us streamline the creation and usage of our development environments across different stages in the development process. Having the ability to specify a Terraform version directly from the languages module is way easier than looking for it in old commits of nixpkgs and a great quality of life improvement for us, and hopefully for the rest of the Nix community. There are a few plug-and-play templates at the end of this article, but let’s take a moment to reflect on how we got here to really appreciate the power of these tools.
Traditional approaches
If you’ve been in the software industry long enough, you are no stranger to long readmes with detailed instructions on how to install all the tooling necessary to work on a project. And this is a best case scenario where everything is well documented and the docs are up-to-date and cross-platform friendly, but most of the times they are outdated or plain wrong. In this case, we would have something like this:
$ wget https://releases.hashicorp.com/terraform/1.7.5/terraform_1.7.5_darwin_amd64.zip
$ chmod +x terraform
$ sudo mv terraform /usr/local/bin/
When managing multiple projects that utilize different versions of Terraform, you may find yourself adding yet another version manager to your already bustling development environment. A tool like tfenv could be a solution, but it also requires installation instructions in the readme of every project.
tfenv, like most version managers, gets the job done; there is a single configuration file that is used as a single source of truth to enforce a specific Terraform version across the team. However, as the project grows, more things would be required for development, whether they are linters, security scanners, or additional binaries required to work on a project. This is where Nix comes in handy because it takes a declarative approach for building portable reproducible environments. A general rule of thumb with tools like devenv is to use a declarative approach to describe all of the requirements needed to work on a project and then use it everywhere.
If you finally decide to give Nix a try to simplify your developer environment and remove all those version managers you installed over the years, you may have a nix-shell looking something like:
# shell.nix
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = [ pkgs.terraform pkgs.jq pkgs.curl ];
}
Everything works great until one of your projects requires a specific version of Terraform, which is not currently available in nixpkgs. This is perfectly fine with Nix as long as you track down and pin which revision of nixpkgs contains that version. This is something you can do thanks to Lazamar’s website for old nixpkgs versions. All of this together would leave you with:
# shell.nix
{ pkgs ? import <nixpkgs> {} }:
let
nixpkgs_terraform_160 = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/9957cd48326fe8dbd52fdc50dd2502307f188b0d.tar.gz") {};
terraform_160 = nixpkgs_terraform_160.terraform;
in
pkgs.mkShell {
buildInputs = [ terraform_160 pkgs.jq pkgs.curl ];
}
This approach is fine for one tool, but imagine doing that for a fully-fledged development environment. Mixing and matching versions becomes a real hassle, even if we ignore the impact on the performance of this method.
The Power of devenv and nixpkgs-terraform
Now that we know how hard it can be to manually craft a development environment, let’s see how easy it can be using devenv and nixpkgs-terraform thanks to our contribution to integrate both tools:
# devenv.nix
{ pkgs, ... }:
{
packages = [ pkgs.jq pkgs.curl ];
languages.terraform = {
enable = true;
version = "1.8.4";
};
}
# devenv.yaml
inputs:
nixpkgs-terraform:
url: github:stackbuilders/nixpkgs-terraform
As you can see, there’s no need to go hunting for each version across different channels and revisions of nixpkgs; we provide you with a collection of packages with everything you may need. This is way easier and much more similar to how other version managers work. At this stage, running devenv shell would leave you in a development shell with everything in it, and switching Terraform versions is as easy as changing the value in devenv.nix. Even better, since this PR got merged, you can also pin Terraform to a minor version using aliases. That way you can stay on the latest patch for a certain minor with minimal effort
Plug-and-Play Templates
If you want to give it a try, here you have a few templates to kickstart your Nix journey. Remember that you’ll need to install Nix and devenv first. Notice we are purposely using older versions of the tool to showcase the potential for older environments, but you can also use this for the latest and greatest since we update the list of available versions daily and automatically.
Regular devenv
# devenv.nix
{ pkgs, ... }:
{
packages = [ pkgs.jq pkgs.curl ];
languages.terraform = {
enable = true;
version = "1.7.5";
};
# Check linting with devenv test
pre-commit.hooks = {
tflint.enable = true;
terraform-format.enable = true;
};
}
# devenv.yaml
inputs:
nixpkgs-terraform:
url: github:stackbuilders/nixpkgs-terraform
Advanced devenv
If we use the devenv init command, we can get a more complete and user-friendly experience thanks to direnv, which automatically spawns a devenv shell everytime you cd into the project’s directory.
$ devenv init
• Creating .envrc
• Creating devenv.nix
• Creating devenv.yaml
• Creating .gitignore
$ devenv inputs add nixpkgs-terraform github:stackbuilders/nixpkgs-terraform
Then just add your packages, languages, etc. Here’s the full list of features devenv provides.
With these two templates, you are pretty much set to start building your own development environment. Hopefully, you found this as useful as we did and as always, happy nixing!
Read our full white paper which explores the larger terrain of DevOps tooling and technologies: Practical DevOps: Navigating the Complexities of Modern Deployment and Hosting Solutions