Compiling a Custom Provider and Including for Terraform Cloud
- You are familiar with the basics of setting up
Goand can run basic Go commands like
go installand don’t need much guidance on that specific part.
- You have a good familiarity with Terraform and the concept of providers.
- You need to include a custom provider which isn’t included in the current registry (or perhaps you’ve geeked out and modified one yourself 😁).
- You want to run things in Terraform Enterprise ☁.
For Terraform Cloud, bundling is not allowed.
Instead, the “legacy” way of running this is to include the plugin directly in the directory that Terraform will be invoked on with
terraform.d/plugins/linux_amd64 as the path containing the provider. See discussion: Using Community Providers With Terraform Cloud.
Part of my current walk-through (primarily using terraform-bundle) is relevant only for Terraform Enterprise, not Terraform Cloud. I missed the ending documentation section on the custom bundle requiring installation and not being supported in Terraform Cloud.
For the directions below, disregard the bundle aspect for Terraform Cloud, and instead focus on building the custom provider and including in the project directory as shown.
If you are willing to explore Atlantis, I bet something can be done with custom providers in there.
After following the custom provider build steps below, create a
.terraformignore file in your project directory and put in the config below.
With a backend like below, I was actually able to get terraform cloud to run the custom provider and return the plan.
If you get an error the first time you run this, see the troubleshooting section at the end.
Custom Providers Bundling
As of the time of this post, to include a custom provider with Terraform Enterprise, you need to create a custom terraform bundle bundle to package up the terraform package and any desired custom plugins.
This terraform bundle includes the terraform program, as well as any range of other providers that you want to include for running in the remote terraform workspace.
Before you go down this route, you should make sure that the Terraform Registry doesn’t already include the provider you need.
Source Of Truth
For the most up to date directions, you can go through these directions:
Compiling the custom provider
In this example, I’m working with a provider for Jfrog Artifactory, which IMO has an atrocious management experience on the web. By compiling this custom provider, my goal was to provide a clean user, repository, and group management experience.
You need to target the platform for Go in the build step, as the Terraform Enterprise environment expects
amd64 as the target.
Get Stuff Setup
To checkout a specific tagged version (recommended):
Quick little #devhack… Use Git Graph in Visual Studio Code to make working with busy repositories much easier. Yes, I’m no Vim magician. Sometimes a little visual help is much better than trying to do it all in cli. #heresy
Next, you’ll want to install and validate your install worked.
go install ensures that dependencies are downloaded, so once again the magic of
Go wins the day.
If you flip to a new tagged version, make sure to rerun the install so you have the correct version of the tooling available.
For some reason, I had issues with the path picking it up in my current session, so for expediency, I just ran the next steps with the fully qualified path:
/Users/sheldonhull/go/bin/terraform-bundle instead of
Grab an example of the configuration
hcl file for the terraform-bundler from the link mentioned above.
Then you can create this in the project directory or qualify it to a subdirectory if you want to save various bundle configuration files.
Here is a trimmed down example config with what worked for me. See the bottom troubleshooting section for more details on why I did not adhere to the documented config from the README.
We need to include this plugin in a specific location for the bundle tool to do it’s magic.
Also ensure you follow the naming convention for a provider.
To be recognized as a valid plugin, the file must have a name of the form
This is where PowerShell shines, and it’s easy to make this path without issue using
Join-Path in a way that also is fully cross-platform with macOS, Linux, or Windows (pick your poison)
Now to bundle this up
Problems Parsing the bundle configuration file
I ran into some issues with it parsing the configuration file as soon as I added the custom plugin. It reported
unknown type for string *ast.ObjectType.
Here’s what I looked at:
In the project, there is a
tools/terraform-bundle/config.go that is responsible for parsing the hcl file.
First, the configuration looks correct in taking a string slice for the versions, and the source is a normal string.
This seems to mean the configuration syntax of meets with the schema required by the configuration code.
It looks like the configuration syntax from the example is a bit different from what is being successfully parsed.
Instead of using the fully designated schema, I adjusted it to
artifactory = ["0.0.0"] and it succeeded in parsing the configuration.
terraform-bundle package --help also provides an example indicating to just use the simple syntax and let it look for the provider in the default directory of
Failed to resolve artifactory provider 0.1: no provider exists with the given name
This next piece was a bit trickier to figure out.
Once I enabled
$ENV:TF_LOG = 'TRACE' I found some output showing an issue with the version of the provider.
I went back to the provider project and installed goreleaser using:
brew install goreleaser/tap/goreleaser which provided me the same tool to build the various packages for this provider.
Build the provider by running
goreleaser build --snapshot.
After reviewing the help in more detail, the following CLI content conflicts with the main README.md, so I had to experiment with various output methods and finally… success! 🎉
The message did provide a warning:
found legacy provider "terraform-provider-artifactory-v2.0.0".
I tested and found it matched the local provider with
0.0.0 by running
terraform providers and seeing the output:
However, what to bundle correctly required simplifying the output to no nested directories.
The output of the bundle was successful with
Terraform Cloud Fails with terraform.tfstate detected
Since the local plugins seem to generate some tfstate for mapping the local plugin directory, I ensure you have a
.terraformignore file in the root of your directory per the notes I provided at the beginning.
Once I added the
.terraformignore the apparent conflict with uploading a local tfstate on the plugins was resolved and the plan succeeded.