A Go novice's experience with Terraform providers

5 minute read
Content level: Expert
1

Explains how to work on the VMC HCX Terraform provider without any Go experience

Situation

We use Terraform to deploy HCX for VMware Cloud on AWS, primarily for our Immersion Day Workshops. The code for the HCX provider is no longer actively maintained. The HCX API calls are fairly static, so this hasn't been an issue. However, team members with M1 Macs cannot deploy from their laptops because the original author did not publish a binary for that architecture.

Task

I set out to publish an M1-compatible release of the provider.

Actions

  1. The provider is written in Go. I know nothing about Go. I took advantage of the Pluralsight 50% off Black Friday deal and got myself access to some training. I'm about halfway through this introductory course. You don't actually need to be able to program in Go to be able to publish the provider, but I also eventually want to be able to make changes to the code.

  2. I forked the repo into my repo.

  3. By default, GitHub Actions are disabled for a fork, but I needed them turned on to publish binaries. I enabled the workflows.

  4. The release workflow is configured to use Goreleaser to compile Go binaries. Since it hadn't been touched in 3 years, most of the packages referenced in the file were outdated.

I bumped checkout, setup-go, goreleaser, and go-version to the latest versions. I also had to rename GITHUB_TOKEN to GH_TOKEN as it is no longer possible to create a GitHub secret that starts with GITHUB. I also had to remove the deprecated --rm-dist release flag and replace it with --clean

release.yaml

name: release
on:
  push:
    tags:
      - 'v*'
jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      -
        name: Checkout
        uses: actions/checkout@v4.1.1
      -
        name: Unshallow
        run: git fetch --prune --unshallow
      -
        name: Set up Go
        uses: actions/setup-go@v4.1.0
        with:
          go-version: 1.21.4
      -
        name: Import GPG key
        id: import_gpg
        uses: paultyng/ghaction-import-gpg@v2.1.0
        env:
          GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
          PASSPHRASE: ${{ secrets.PASSPHRASE }}
      -
        name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v5.0.0
        with:
          version: latest
          args: release --clean
        env:
          GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
  1. Generate a GitHub personal token. I followed the GitHub instructions to create a token for GitHub actions to use to build releases

  2. Generate a GPG key. I followed the GitHub instructions for generating a key.

  3. Add secrets to Settings>Security>Secrets and variables>Actions. GH_TOKEN contains the personal token, GPG_PRIVATE_KEY and PASSPHRASE are what I generated with GPG.

Repo secrets

  1. Configure goreleaser with .goreleaser.yml

As mentioned earlier, Goreleaser builds Go binaries automatically. You configure it with .goreleaser.yml

Most of the configurations in this file worked fine the way the original author set them. I did have to add windows/arm64 to the ignore section as it is unsupported. At first I was not sure why the original author did not have a binary for M1 Macs - the configuration appears to support it. After investigating, I found that Goreleaser did not support this until v0.156.0, released on Feb 16, 2021. The container running Goreleaser was originally configured as v2.0.0, released on May 10, 2020. There was no support for the ARM version, so the directive was ignored.

# Visit https://goreleaser.com for documentation on how to customize this
# behavior.
before:
  hooks:
    # this is just an example and not a requirement for provider building/publishing
    - go mod tidy
builds:
- env:
    # goreleaser does not work with CGO, it could also complicate
    # usage by users in CI/CD systems like Terraform Cloud where
    # they are unable to install libraries.
    - CGO_ENABLED=0
  mod_timestamp: '{{ .CommitTimestamp }}'
  flags:
    - -trimpath
  ldflags:
    - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}'
  goos:
    - freebsd
    - windows
    - linux
    - darwin
  goarch:
    - amd64
    - '386'
    - arm
    - arm64
  ignore:
    - goos: darwin
      goarch: 386
    - goos: windows
      goarch: arm64
  binary: '{{ .ProjectName }}_v{{ .Version }}'
archives:
- format: zip
  name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}'
checksum:
  name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS'
  algorithm: sha256
signs:
  - artifacts: checksum
    args:
      # if you are using this is a GitHub action or some other automated pipeline, you 
      # need to pass the batch flag to indicate its not interactive.
      - "--batch"
      - "--local-user"
      - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key
      - "--output"
      - "${signature}"
      - "--detach-sign"
      - "${artifact}"
release:
  # If you want to manually examine the release before its live, uncomment this line:
  # draft: true
changelog:
  skip: true
  1. Push a release

Terraform expects you to tag your code with a version number, this gets published in the Terraform registry and is how you reference the version in your Terraform code.

git tag v0.4.2
git push origin v0.4.2

This creates a release tag and kicks off the build process. When successful, the build will publish a release with all of the compiled binaries.

  1. The releases that were built include the build for M1 Mac (Darwin Arm64)

Darwin Arm64

  1. I followed these Hashicorp instructions to publish the GitHub releases to the Terraform registry.
  • Sign in with my GitHub account
  • Add my GPG key
  • Clicked Publish>Provider and point the publication to my GitHub repo
  1. After 5-10 minutes, the initial release was visible in the Terraform registry Provider Published

  2. Now, I can reference the published provider directly in Terraform. This is an example of versions.tf

    hcx = {
      source  = "kremerpatrick/hcx"
      version = "= 0.4.2"
    }

Result My team members with M1 Macs were able to use their laptops with the newly built provider to stand up HCX.

profile pictureAWS
EXPERT
published 5 months ago896 views