Skip to content

Instantly share code, notes, and snippets.

@detro
Last active July 22, 2022 15:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save detro/5236dbd011d9b708f7ed8e0419dc73ef to your computer and use it in GitHub Desktop.
Save detro/5236dbd011d9b708f7ed8e0419dc73ef to your computer and use it in GitHub Desktop.
Compiling Terraform against a target OS/ARCH, not available from HashiCorp

Background

Sometimes you need a specific version of Terraform a build for your combination of Operating System / Architecture doesn't exist. This can be because that combination came out later than the version of Terraform you are after, or some other reason.

But, no fear, Golang compiler is great, and the Terraform codebase is pretty easy.

Disclaimer

This describes a build process that produces a working binary, BUT(!) this is not a combination of Golang and Terraform code version that is supported in any official capacity by anyone at HashiCorp.

Plese read the process described above, as well as the resulting binary, as your personal effort that you can perform thanks to the beauty of open source. Any bug/issue reported against a non-HashiCorp released binary, it's very likely going to be rejected.

Additionally, the resulting binary is NOT signed by HashiCorp, so share it at your own risk.

Requirement

Have a version of Golang installed, that supports your Go/Arch combination (duh!).

Example: I need TF 0.12.* for my Apple MacBook Pro M1 Pro

Step 0. As my target combination is darwin / arm64, I'll use my current local Golang version 1.17.8. For a list of all the available Golang versions, see the official download page.

Step 1. Work out the latet version of TF 0.12 available: at the time of writing is v0.12.31 (tag tree).

Step 2. Procure the specific code tree:

$ git clone git@github.com:hashicorp/terraform.git
$ cd terraform
$ git checkout v0.12.31

Step 3. This is the only "tricky" bit, and it's not even that much. Open in your editor ./scripts/build.sh. In there, apply a patch like this one:

diff --git i/scripts/build.sh w/scripts/build.sh
index 72af007ee0..07af5d8f0f 100755
--- i/scripts/build.sh
+++ w/scripts/build.sh
@@ -15,9 +15,9 @@ GIT_COMMIT=$(git rev-parse HEAD)
 GIT_DIRTY=$(test -n "`git status --porcelain`" && echo "+CHANGES" || true)

 # Determine the arch/os combos we're building for
-XC_ARCH=${XC_ARCH:-"386 amd64 arm"}
-XC_OS=${XC_OS:-linux darwin windows freebsd openbsd solaris}
-XC_EXCLUDE_OSARCH="!darwin/arm !darwin/386"
+XC_ARCH=${XC_ARCH:-"386 amd64 arm64"}
+XC_OS=${XC_OS:-darwin}
+XC_EXCLUDE_OSARCH="!darwin/386"

 # Delete the old dir
 echo "==> Removing old directory..."

Step 4. Run make fmt, otherwise the build will fail because it does a check of golang formatting beforehand:

...
make: *** [fmtcheck] Error 1
./signal_unix.go
You can use the command: `make fmt` to reformat code.

$ make fmt
gofmt -w $(find . -not -path "./vendor/*" -type f -name '*.go')

Step 5. Build it:

$ make bin
==> Checking that code complies with gofmt requirements...
GOFLAGS=-mod=vendor go generate ./...
# go fmt doesn't support -mod=vendor but it still wants to populate the
# module cache with everything in go.mod even though formatting requires
# no dependencies, and so we're disabling modules mode for this right
# now until the "go fmt" behavior is rationalized to either support the
# -mod= argument or _not_ try to install things.
GO111MODULE=off go fmt command/internal_plugin_list.go > /dev/null
==> Removing old directory...
==> Building...
Number of parallel builds: 9

-->    darwin/arm64: github.com/hashicorp/terraform
-->    darwin/amd64: github.com/hashicorp/terraform
==> Packaging...
--> darwin_amd64
  adding: terraform (deflated 70%)
--> darwin_arm64
  adding: terraform (deflated 71%)

==> Results:
total 90976

Step 6. ... Profit!

$ ls pkg/*
pkg/darwin_amd64.zip  pkg/darwin_arm64.zip

pkg/darwin_amd64:
terraform*

pkg/darwin_arm64:
terraform*

Rosetta's much?

The above notes are of course "generic", but you might have spotted the special interest for the build target Darwin/ARM64. This is because the issue I was solving related to own MacBook Pro.

In case you are also running on MacBook Pro, on Apple Silicon, another way it to rely on Rosetta, the transparent translation layer that macOS provides, to run Darwin/AMD64 binaries on Darwin/ARM64.

After testing, older builds of Terraform targeting AMD64 seem to work just fine, even when interacting with Terraform Providers compiled for ARM64 (because of how awesome the software architecture of Terraform is :) ).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment