When upgrading from an Apple Intel to Apple Silicon machine, I ran into an issue where some Terraform plugins didn’t support ARM architectures. I’ve found one way of working around this plugin issue is by building and installing a local plugin rather than waiting for the official Terraform registry to be updated. To demonstrate this workaround, I’ll use DNSimple as an example. Specifically, the DNSimple Terraform Plugin. I’ll walk you through how to build this plugin locally so you can work, unencumbered, even though DNSimple is slow to provide ARM 64 architecture support. 😢 For the curious, there is an outstanding issue on this very subject which might be of interest to those wishing to wait for official support.
I’m not a Go engineer and mine might not be the most elegant solution, but these are the steps I went through to set up the DNSimple Terraform Plugin for local compilation and installation:
cd $HOME/Scratch brew install go export GOPATH="$HOME/.cache/go" git clone https://github.com/dnsimple/terraform-provider-dnsimple.git cd terraform-provider-dnsimple make build cd .. rm -rf terraform-provider-dnsimple
The above ended up producing the following binary:
With the newly built binary for my ARM 64 architecture in hand, next I needed to inform Terraform where this binary was located.
I’m a huge fan of leveraging the XDG specification whenever I can since it keeps my Dotfiles well organized. In addition, I maintain the XDG gem which is a Ruby implementation of the XDG Specification.
Terraform, luckily, provides support for customizing where your
configuration is located in addition
to where you store your
cache and data
directories, all of which, can fit nicely within an XDG structure. For me, the solution was to the
following entry to my Dotfiles
Next, I needed to install the previously built DNSimple binary into a directory structure Terraform would recognize. I did this by running the following steps:
mkdir -p $HOME/.cache/terraform/plugins/registry.terraform.io/dnsimple/dnsimple/0.5.1/darwin_arm64 cp $HOME/.cache/go/bin/terraform-provider-dnsimple \ $HOME/.cache/terraform/plugins/registry.terraform.io/dnsimple/dnsimple/0.5.1/darwin_arm64/terraform-provider-dnsimple_v0.5.1
Local Plugin Structure
Terraform’s local directory structure for the final destination of the binary might be unfamiliar to
some -- it definitely was to me. I found that in order run
terraform plan, etc.,
Terraform needs a local plugin directory structure that mimics the official Terraform registry.
Using Exa, here’s a tree representation of my
$HOME/.cache/terraform/plugins root directory:
. └── registry.terraform.io ├── dnsimple │ └── dnsimple │ └── 0.5.1 │ └── darwin_arm64 │ └── terraform-provider-dnsimple_v0.5.1 └── hashicorp └── aws └── 3.37.0 └── darwin_arm64 └── terraform-provider-aws_v3.37.0_x5
The format for this directory structure roughly translates as follows:
I don’t believe it’s necessary to suffix your locally built binary with the exact version used in the folder structure above. Instead, I added the suffix to mimic other plugins so it might be possible to remove the suffix entirely if you don’t want that duplication.
Tying Everything Together
Now that we have the DNSimple plugin built for our ARM 64 architecture and the XDG cache
configured via our Dotfiles, we can run
terraform init. Before we do so, though, I want to point
out that it’s a good idea to delete the following from your current Terraform project:
rm -rf .terraform/providers rm -f .terraform.lock.hcl
Should you be uneasy about this destructive action, feel free to make a backup of the plugins
directory and lock file for extra protection. I point this out because I noticed Terraform will not
always update the above information if previously generated. Once deleted, now you can run
terraform init which will yield the following structure within your
.terraform ├── modules │ └── modules.json ├── providers │ └── registry.terraform.io │ ├── dnsimple │ │ └── dnsimple │ │ └── 0.5.1 │ │ └── darwin_arm64 -> /Users/bkuhlmann/.cache/terraform/plugins/registry.terraform.io/dnsimple/dnsimple/0.5.1/darwin_arm64 │ └── hashicorp │ └── aws │ └── 3.37.0 │ └── darwin_arm64 -> /Users/bkuhlmann/.cache/terraform/plugins/registry.terraform.io/hashicorp/aws/3.37.0/darwin_arm64 └── terraform.tfstate
Notice: Terraform made symbolic links for both plugins, to the local XDG Cache we configured earlier. I haven’t yet figured out how to teach Terraform to use a hybrid configuration, in which you could have local and remote plugins coexist. For now, Terraform seems to have an all or nothing approach. Even so, with this workaround, you should be able to run your Terraform scripts with your locally built plugin without error. 🎉