Skip to main content

Avoiding AWS secrets in Terraform statefiles

I’ve been using Terraform for managing my AWS account for a while. It’s pretty snazzy, but there are still a couple of things that Terraform doesn’t fully handle. For example, making an IAM access key in Terraform stores the secret key in the statefile. They’ve added support to store the secret key encrypted with a GPG key, but I’d much prefer to not have it end up in the statefile at all.

Why do they even do this?

When I first realized that the iam_access_key resource dropped the secret key into the statefile, I was puzzled. Terraform statefiles need to be accessible to anybody looking to update the Terraform, so if your use case is generating admin IAM keys, having them be stored there isn’t ideal.

The other use case for IAM keys is why it’s set up this way. If you’re generating the IAM keys for services/systems instead of humans, storing them in the statefile is necessary. Terraform needs to know how to figure out the full desired state of resources on each run, so anything you’re providing as input to other resources has to be written down. So in this case, storing the secret key in the statefile is what enables handing that secret key to other terraform resources.

Time for some monkeypatching

I looked into ways to get around this for my use case. One thing I wanted to avoid was rewriting the code for IAM key management. Based on that, I decided to shim the existing terraform-provider-aws codebase and add just what I needed.

After reading Terraform’s official provider docs, and skimming the terraform-provider-aws code, this turns out to be pretty easy. Making a provider takes some initial boilerplate:

I’m only defining the one resource, but I’m copying the schema of the upstream AWS provider, and I’ve written up some wrapper code to instantiate a copy of that provider so I can use its ConfigureFunc:

So that gets me a working provider that can call the actual AWS provider. For most of the methods on my new resource, I just pass through directly to the real resource. I’m only concerned with the “Create” method, which I want to wrap so that it writes the access key to a local file and then deletes it from the resource so that it doesn’t end up in the statefile:

Using my new provider

Now that I’ve got my provider, using it in Terraform is straightforward:

This drops my new secret key into the creds/admin_user file. Success!

The full codebase for this is is available on GitHub.