google-cloud-platformterraformopensslssl-certificate

Upload public key to GCP using terraform


According to the terraform documentation (https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_service_account_key):

public_key_data (Optional) Public key data to create a service account key for given service account. The expected format for this field is a base64 encoded X509_PEM and it conflicts with public_key_type and private_key_type.

Apparently its possible to upload a public key to a GCP service account. I'm trying to do exactly that.

I generated a private/public key pair using openssl:

openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
    -keyout /path/to/private_key.pem \
    -out /path/to/public_key.pem \
    -subj "/CN=unused"

I then took that public key, eliminated the header, footer and newlines, then pasted it to a file and fed that file to Terraform:

resource "google_service_account_key" "service-account-key" {
  service_account_id = google_service_account.api-service-account.name
  public_key_data = file("../public_keys/certificate.pem")
}

This validated fine. But when Terraform tried to create the resource, I got this error:

╷
│ Error: Error creating service account key: googleapi: Error 400: The given public key data is invalid., badRequest
│ 
│   with google_service_account_key.service-account-key,
│   on main.tf line XX, in resource "google_service_account_key" "service-account-key":
│   XX: resource "google_service_account_key" "service-account-key" {
│ 
╵

I have tried everything I can think of:

I verified the key and its a valid base64, so that's not the issue. The format should be x509 too, so I don't think that's the issue either. But I'm out of ideas at this point.

I can't find a single proper, complete example on the internet, and I'm out of ideas.


Solution

  • Although the PEM file you created contains a base64 public key, Google expects the whole file to be base64 encoded (so including the -----BEGIN CERTIFICATE----- / -----END CERTIFICATE----- )

    Change :

    resource "google_service_account_key" "service-account-key" {
      service_account_id = google_service_account.api-service-account.name
      public_key_data = file("../public_keys/certificate.pem")
    }
    

    to:

    resource "google_service_account_key" "service-account-key" {
      service_account_id = google_service_account.api-service-account.name
      public_key_data = filebase64("../public_keys/certificate.pem")
    }
    

    and it will work.