Blog

How to create a private terraform provider registry using Google Cloud Storage

20 Mar, 2021
Xebia Background Header Wave

Since version 0.13 terraform supports the notion of a provider registry. You can build and publish your own provider in the public registry. At the moment, there is no private registry implementation available. In this blog, I will show you how to create a private terraform provider registry on Google Cloud Storage. A utility will generate all the required documents to create a static registry.

Before I explain how to create your own terraform provider registry, I will show you the expected protocol observed by a registry. Then I will show the anatomy of a terraform provider release. Finally, I will show you how to create the registry and release a provider to it.

terraform provider registry protocol

The protocol of the registry, consists of the three document types:

  • discovery document per registry
  • versions document per provider
  • download document per document

In the following sections I will show samples of these document types.

the discovery document

This document allows terraform to query the endpoint of the resources::

$ curl $REGISTRY_URL/.well-known/terraform.json
{"providers.v1": "/v1/providers/"}

the versions document

The versions documents returns the available versions and platform binaries per provider.

$ curl $REGISTRY_URL/v1/providers/jianyuan/sentry/versions
{
  "versions": [{
      "version": "0.6.0",
      "protocols": ["5.0"],
      "platforms": [{"os": "darwin","arch": "amd64"}, {"os": "linux","arch": "amd64"}]
    }]
}

the download document

Finally, the download document contains the metadata needed to download a platform binary.

$ curl $REGISTRY_URL/v1/providers/jianyuan/sentry/0.6.0/download/darwin/amd64
{
  "protocols": [
    "5.0"
  ],
  "os": "darwin",
  "arch": "amd64",
  "filename": "terraform-provider-sentry_0.6.0_darwin_amd64.zip",
  "download_url": "$REGISTRY_URL/binaries/jianyuan/terraform-provider-sentry/v0.6.0/terraform-provider-sentry_0.6.0_darwin_amd64.zip",
  "shasums_url": "$REGISTRY_URL/binaries/jianyuan/terraform-provider-sentry/v0.6.0/terraform-provider-sentry_0.6.0_SHA256SUMS",
  "shasums_signature_url": "$REGISTRY_URL/binaries/jianyuan/terraform-provider-sentry/v0.6.0/terraform-provider-sentry_0.6.0_SHA256SUMS.sig",
  "shasum": "a2c5881ea67e1c397cb26c6162d81829e058d5a993801bcb69df9982412d27e9",
  "signing_keys": {
    "gpg_public_keys": [
      {
        "key_id": "8B15B898C0AA84DC7A7B0E46B851229EAFE0F521",
        "ascii_armor": "-----BEGIN PGP PUBLIC KEY BLOCK-----...-----END PGP PUBLIC KEY BLOCK-----",
        "trust_signature": "",
        "source": "",
        "source_url": null
      }
    ]
  }

So, now we know what is required lets take a look at a terraform provider release.

terraform provider releases

A terraform provider release is a collection of zipped binaries. A new release of a provider which meets the requirements of terraform looks like this:

terraform-provider-sentry_0.6.0_SHA256SUMS
terraform-provider-sentry_0.6.0_SHA256SUMS.sig
terraform-provider-sentry_0.6.0_darwin_amd64.zip
terraform-provider-sentry_0.6.0_freebsd_386.zip
terraform-provider-sentry_0.6.0_freebsd_amd64.zip
terraform-provider-sentry_0.6.0_freebsd_arm.zip
terraform-provider-sentry_0.6.0_freebsd_arm64.zip
terraform-provider-sentry_0.6.0_linux_386.zip
terraform-provider-sentry_0.6.0_linux_amd64.zip
terraform-provider-sentry_0.6.0_linux_arm.zip
terraform-provider-sentry_0.6.0_linux_arm64.zip
terraform-provider-sentry_0.6.0_windows_386.zip
terraform-provider-sentry_0.6.0_windows_amd64.zip

A release consists of a:

  • zip file containing the built provider binary for each architecture
  • checksum file with the checksum for each of the zip files
  • signature file containing the signature of the checksum file.

The only thing missing in the distribution is the public key used to sign the checksum file.

the terraform provider registry generator

As shown in the previous paragraph, a release contains everything to implement the terraform provider registry protocol. The terraform provider registry api generator generates all required JSON documents. It reads a distribution on the bucket, loads the PGP public key and writes all required documents.

create your own private terraform provider registry

To create your own private terraform provider registry, configure a cloud storage bucket to host a static website as follows:

$ git clone https://github.com/mollie/tf-provider-registry-api-generator.git
$ cd tf-provider-registry-api-generator/example/terraform
$ terraform init
$ read -p "project id:" TF_VAR_project_id
$ read -p "dns zone name:" TF_VAR_dns_managed_zone
$ terraform apply

To obtain the registry URL, type:

$ REGISTRY_URL=https://registry.$(gcloud dns managed-zones 
     describe $TF_VAR_dns_managed_zone 
    --format 'value(dns_name)' | sed -e 's/.$//')

After a few minutes you should get a 404 on$REGISTRY_URL.

build and release to your own private registry

For the purpose of simplicity, I am going to build and release an existing terraform provider to my private registry. To do so, I need to:

  • build the provider
  • copy the release the bucket
  • generate the terraform provider registry api documents

build the terraform provider

To build the terraform provider, type:

$ git clone https://github.com/jianyuan/terraform-provider-sentry.git
$ cd terraform-provider-sentry
$ git checkout v0.6.0

$ curl -sS -o /tmp/goreleaser.yaml  https://raw.githubusercontent.com/hashicorp/terraform-provider-scaffolding/main/.goreleaser.yml
$ read -p  "pgp key fingerprint:" PGP_FINGERPRINT
$ goreleaser release -f /tmp/goreleaser.yaml --rm-dist

copy the release the bucket

To copy the newly built release onto the bucket, type:

$ TF_REGISTRY_BUCKET=${TF_VAR_project_id}-tf-registry
$ gsutil cp . 
  gs://$TF_REGISTRY_BUCKET/binaries/terraform-provider-sentry/v0.6.0/

generate the terraform provider registry API documents

To generate the terraform provider registry API documents, type:

$ go get github.com/mollie/tf-provider-registry-api-generator
$ $GOPATH/bin/tf-provider-registry-api-generator 
  --bucket-name $TF_REGISTRY_BUCKET 
  --prefix binaries/terraform-provider-sentry/v0.6.0/ 
  --namespace jianyuan 
  --fingerprint $PGP_FINGERPRINT 
  --protocols 5.0 
  --url $REGISTRY_URL

Now you should be able to query the registry:

$ curl $REGISTRY_URL/.well-known/terraform.json
$ curl $REGISTRY_URL/v1/providers/jianyuan/sentry/versions
$ curl $REGISTRY_URL/v1/providers/jianyuan/sentry/0.6.0/download/darwin/amd64

Note that all the above steps can be combined into a single go release specification.

referencing your private registry

To use the provider from your private registry, add the following snippet to your terraform definition:

terraform {
  required_providers {
    sentry = {
      source = "<DOMAIN_NAME>/jianyuan/sentry"
    }
  }
}

conclusion

For most purposes, I recommend to use the public terraform provider registry. It is easy to use, turn-key solution. If for some reason you want to keep your providers private, it is quite easy to make your own private provider registry.

Photo by Dayne Topkin on Unsplash

Mark van Holsteijn
Mark van Holsteijn is a senior software systems architect at Xebia Cloud-native solutions. He is passionate about removing waste in the software delivery process and keeping things clear and simple.
Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts