terraform-provider-null: Allow defining triggers that are sensitive information

Terraform Version

Terraform v0.12.20

  • provider.null v2.1.2

Affected Resource(s)

  • null_resource

Terraform Configuration Files

variable "mysecret" {
  type = string
}

resource null_resource example {
  triggers = {
    secret = var.mysecret
  }

  provisioner "local-exec" {
    command = "echo Create"
    environment = {
      SECRET = self.triggers.mysecret
    }
  }
  
  provisioner "local-exec" {
    command = "echo Destroy"
    environment = {
      SECRET = self.triggers.mysecret
    }
  }
}

Debug Output

❯ terraform apply
var.mysecret
  Enter a value: supersecret


An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # null_resource.example will be created
  + resource "null_resource" "example" {
      + id       = (known after apply)
      + triggers = {
          + "secret" = "supersecret"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions in workspace "play"?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: no

Apply cancelled.

Expected Behavior

A way to provide sensitive values as triggers, so that the plan does only print (sensitive) and not the actual value of a sensitive trigger.

Actual Behavior

No way to define sensitive triggers.

Steps to Reproduce

  1. terraform apply

Important Factoids

The null_resource should also take a sensitive_triggers map that is obfuscated in the plan output.

Other providers, like the local provider use a similar approach: https://www.terraform.io/docs/providers/local/r/file.html

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 23
  • Comments: 20

Commits related to this issue

Most upvoted comments

I’ve been using sha1 in triggers to work around this issue.

  triggers = {
    secret = sha1(var.mysecret)
  }

Right now this behavior is blocking our ability to migrate to terraform 0.13 with the error:

Error: Invalid reference from destroy provisioner … Destroy-time provisioners and their connection configurations may only reference attributes of the related resource, via ‘self’, ‘count.index’, or ‘each.key’.

Since we use some secrets to fill in the information in our connection{} block, it is by nature sensitive. Putting the values in the triggers allows us to upgrade but then dumps private keys into our diffs 😕.

What I would like to see happen is something like this:

resource "null_resource" "register-pa-core-rds" {
  triggers = {
    rds_cluster_id         = module.pa-core-rds.rds_cluster_id
    host                   = aws_route53_record.bastion.fqdn
    user                   = var.provisioning_user
  }
  sensitive_triggers  = {  # Values are redacted in the diff
    private_key            = tls_private_key.provisioning.private_key_pem
    management_token       = random.management-token.result
  }

  provisioner "remote-exec" {
    environment = {
      MANAGEMENT_TOKEN = self.sensitive_triggers.mysecret
    }

    inline = [
      "echo register-rds ${self.triggers.rds_cluster_id}",
    ]
  }

  provisioner "remote-exec" {
    environment = {
      CONSUL_MANAGEMENT_TOKEN = self.sensitive_triggers.mysecret
    }
    inline = [
      "echo deregister-rds ${self.triggers.rds_cluster_id}",
    ]
    when = destroy
  }

  connection {
    type        = "ssh"
    user        = self.triggers.user
    host        = self.triggers.host
    private_key = self.sensitive_triggers.private_key
  }

FYI I opened PR #48 to implement this.

If anyone is interested, can you please give it a spin and report whether it works for you?

Build instructions

You need Go 1.15 installed to build the provider. It’s also possible to use Docker if you don’t want to install Go.

With Go 1.15 installed

git clone -b sensitive https://github.com/jgiannuzzi/terraform-provider-null
cd terraform-provider-null
make build

The provider can then be found in $GOPATH/bin.

With Docker installed

Linux

First spin up the Go 1.15 container:

docker run --rm -ti -v $PWD:/go/bin -w /root golang:1.15

Then within that container, do the following:

git clone -b sensitive https://github.com/jgiannuzzi/terraform-provider-null
cd terraform-provider-null
make build

You can then exit the container and the plugin will be in you current working directory.

macOS

First spin up the Go 1.15 container:

docker run --rm -ti -v $PWD:/go/bin/darwin_amd64 -w /root golang:1.15

Then within that container, do the following:

git clone -b sensitive https://github.com/jgiannuzzi/terraform-provider-null
cd terraform-provider-null
make build GOOS=darwin

You can then exit the container and the plugin will be in you current working directory.

Windows

First spin up the Go 1.15 container from a PowerShell terminal:

docker run --rm -ti -v ${pwd}:/go/bin/windows_amd64 -w /root golang:1.15

Then within that container, do the following:

git clone -b sensitive https://github.com/jgiannuzzi/terraform-provider-null
cd terraform-provider-null
make build GOOS=windows

You can then exit the container and the plugin will be in you current working directory.

Install instructions

Linux

mkdir -p ~/.local/share/terraform/plugins/registry.terraform.io/hashicorp/null/3.0.0/darwin_amd64
mv terraform-provider-null ~/.local/share/terraform/plugins/registry.terraform.io/hashicorp/null/3.0.0/darwin_amd64/

macOS

mkdir -p "~/Library/Application Support/io.terraform/plugins/registry.terraform.io/hashicorp/null/3.0.0/darwin_amd64"
mv terraform-provider-null "~/Library/Application Support/io.terraform/plugins/registry.terraform.io/hashicorp/null/3.0.0/darwin_amd64/"

Windows

New-Item ${APPDATA}/HashiCorp/Terraform/plugins -ItemType Directory -ea 0
Move-Item terraform-provider-null ${APPDATA}/HashiCorp/Terraform/plugins/

Usage instructions

Upgrade your project to use the custom version of the plugin:

terraform init -upgrade

@tmatilai Marking variables as sensitive was not a terraform feature when the ticket was opened. It got introduced with 0.14. But yes, it seems to solve the issue. Another alternative is the sensitive function (which came with terraform 0.15): https://www.terraform.io/language/functions/sensitive

Checking with the following code:

variable "mysecret" {
  type = string
  sensitive = true
}

resource null_resource example {
  triggers = {
    secret = var.mysecret
  }

  provisioner "local-exec" {
    command = "echo Create"
    environment = {
      SECRET = self.triggers.secret
    }
  }
  
  provisioner "local-exec" {
    command = "echo Destroy"
    environment = {
      SECRET = self.triggers.secret
    }
  }
}

The cli output of a plan looks like this:

❯ terraform plan
var.mysecret
  Enter a value: supersecret


Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # null_resource.example will be created
  + resource "null_resource" "example" {
      + id       = (known after apply)
      + triggers = {
          + "secret" = (sensitive)
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

Given that both of the above solve the issue in a straightforward way, I’m gonna close the ticket as it can be solved with a terraform-native feature without any modification necessary to the provider anymore.

Just checking in, did this go anywhere? Seems to have died 😕

Hi @reegnz, I should have included more context.

Unless I’m misunderstanding the use of sha1() in a terraform context, that doesn’t solve the problem for our use case which is due to the removal of the ability to use variables in when = destroy.

We have destroy actions that require variables/files for cleanup actions on services that terraform doesn’t interact with natively. Now variables aren’t allowed in destroy the use of self.triggers.* is the only way to pass values into the local-exec.

In these instances we need the actual value, not to know it was different.