Skip to content

Instantly share code, notes, and snippets.

@emteeoh
Created November 12, 2023 22:10
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 emteeoh/a2871778dc3502e2014e76a05360f4b1 to your computer and use it in GitHub Desktop.
Save emteeoh/a2871778dc3502e2014e76a05360f4b1 to your computer and use it in GitHub Desktop.
HTTP Backend behaviour

Terraform supports several backends.

  1. The default is a local file, which, of course, doesn't scale very well.
  2. It also supports using AWS's S3 to store the state. This scales much better, but its rather finicky if you're not and AWS user, and instead using an S3 compatible object store such as DigitalOcean's Spaces. In fact, right now, I think it's utterly broken: I have configs that used to work that do not work with TF 1.6.
  3. A third option is the HTTP backend. In short, this backend uses HTTP(S) to GET, POST, LOCK, and UNLOCK the state on a webserver. A single URL can be used, or you can use several URLs and exchange LOCK and UNLOCK for POST, or possibly other http request types. It seems very simple, but there's actually very sparse documentation about what is expected from the HTTP server in this case. This gist aims to fill in the details as I learn them writing my own HTTP server for this backend.

But first, why write my own server? There are many implementaitons of this backend out there. Why one more? simple: I want to get familiar with serverless programming. AWS has Lambda, and DigitalOcean has Functions. I, being a DO customer, am interested in learning to develop Functions apps. Since no-one else is doing it, I am! I am partially motivated by not spending 5 dollars a month to support a service that is more idle than active on an otherwise nearly useless virtual machine.

Short Forms Glossary

All shortforms will be in all-caps.

  • DO: Digital Ocean
  • FN: DO's serverless Functions product
  • TF: Terraform

Platform limits

  • FN seems to only support GET, PUT and POST. Trying to use LOCK and UNLOCK generated errors before my code was even called

The State File

  • a JSON dictionary
  • must contain a "version" key with a decimal value greater than 0
  • NOT in state file: __ow_headers, __ow_method, __ow_path
    • these are deprecated tags from an older implementation of FN. DO says not to use them
  • NOT in state file: http
    • this is how info like headers is passed to the called function in FN
  • 'Created': an ISO 8601 Zulu timestamp only LOCK and UNLOCK payloads
  • 'ID': a UUID. only in LOCKs, UNLOCKs, and locked POSTed state files.
  • 'Info': only in LOCKs, UNLOCKs, and always an empty string.
  • 'Operation': one of ['OperationTypePlan', 'OperationTypeApply'], only in LOCK and UNLOCK
  • 'Path': only in LOCK and UNLOCK, and always an empty string.
  • 'Version': only in LOCK and UNLOCK. It's the Semantic Version of TF. Note: not the same as 'version'
  • 'Who': only in LOCK and UNLOCK. a user@host of the user who who invoked TF
  • 'check_results': only in locked POSTed states, value is None.
  • 'lineage': an empty string in locked POSTed state files.
  • 'outputs': empty string in locked POSTed state files
  • 'resources': a JSON dictionary with lots of stuff in it.
  • 'serial': an integer
  • 'terraform_version': TF's semver. only in locked POSTed state files

State GET

  • returns a valid state file,
  • if none is available, return HTTP error 404
  • creating an empty&minimally valid state file seems to lead to problems later.

State POST

  • write file to permanent storage and return a 200
  • or fail and return 500
  • other implementations just write "request data". FN mixes internals into the same data structure, so I'm making a list of what is and isn't in the state file as I learn.

State LOCK

  • write request data to a lock file and return 200
  • if lock already exists return 409 Conflict
  • or fail and return 500

State UNLOCK

  • remove lock file and return 200
  • if no lock file, return 404
  • or fail and return 500

Other Actions

  • https://github.com/ivoronin/tfstated/ implements a DELETE function, but I have no idea where that came from. So far, in debug logs, DELETE is not used unless you tell TF to use DELETE instead of UNLOCK. This might come into play later if I ever see enough of a working backend to try a TF destroy.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment