Terraform conditionally creates a resource based on external data?

As part of the setup, I create TLS certificates and store them on S3. Creation of certificates is done through a data source external

that runs a command to generate certificates. I then use those outputs to create an S3 resource

s bucket object .

This works pretty well on first launch terraform apply

. However, if I change any other (non-certified) variable, resource, etc. And rerun, it reruns the command external

that generates a new key / certificate pair, uploads them to S3, and rips whatever is already running.

Is there a way to create a resource conditionally? What model can you use to create certificates only if they don't exist?

I've looked at storing the generated keys / certificates locally, but this is sensitive key stuff; I don't want it to be stored on a local drive (and there are keys for the environment).

Generation and storage of keys / certificates:

data "external" "ca" {
  program = ["sh","-c","jq '.root|fromjson' | cfssl gencert -initca -"]
  #
  query = {root = "${ data.template_file.etcd-ca-csr.rendered }"}
  # the result will be saved in
  # data.external.etcd-ca.result.key
  # data.external.etcd-ca.result.csr
  # data.external.etcd-ca.result.cert
}

resource "aws_s3_bucket_object" "ca_cert" {
  bucket = "${aws_s3_bucket.my_bucket.id}"
  key = "ca.pem"
  content = "${data.external.ca.result.cert}"
}
resource "aws_s3_bucket_object" "ca_key" {
  bucket = "${aws_s3_bucket.my_bucket.id}"
  key = "ca-key.pem"
  content = "${data.external.ca.result.key}"
}

      

We would be happy to consider using some kind of conditional or completely different generation model.

+3


source to share


1 answer


The reason for this behavior is that the external

source of the data is, and thus Terraform expects it to be read-only and without side effects. It reruns the data sources for each plan.

To do this with an external script, it will be necessary to use a resource provisioner to run the script and load it into S3, as there is currently no equivalent external

for resources that are allowed to have side effects, and conductors are only side effects (that is, they cannot give results for use elsewhere in config.)

Another approach, however, is to use Terraform's built-in TLS provider , which lets you create certificates in Terraform itself. In this case, it looks like you are trying to create a new CA certificate and key, which can be accomplished withtls_self_signed_cert



in the following way:
resource "tls_private_key" "ca" {
  algorithm = "RSA"
  rsa_bits = 2048
}

resource "tls_self_signed_cert" "ca" {
  key_algorithm   = "RSA"
  private_key_pem = "${tls_private_key.ca.private_key_pem}"

  # ... subject and validity settings, as appropriate

  is_ca_certificate = true

  allowed_uses = ["cert_signing"]      
}

resource "aws_s3_bucket_object" "ca_cert" {
  bucket  = "${aws_s3_bucket.my_bucket.id}"
  key     = "ca.pem"
  content = "${resource.tls_self_signed_cert.ca.cert_pem}"
}

resource "aws_s3_bucket_object" "ca_key" {
  bucket  = "${aws_s3_bucket.my_bucket.id}"
  key     = "ca-key.pem"
  content = "${resource.tls_self_signed_cert.ca.private_key_pem}"
}

      

The generated private key will be included in the state for use in future runs, so it is important to ensure that the state is stored securely. Note that this is also true when using a data source external

, as the results of the data source are also persisted in state. So this approach is equivalent in terms of where the secrets are stored.

I wrote more details on using Terraform to manage TLS certificates in an article on my website . Its coverage is broader than your requirements here, but may be of some interest.

+2


source







All Articles