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:
Hardcoding the key
Not removing the footer and header
Not removing the newlines
Removing them but programattically
Encoding it programatically with terraform
Decoding it programatically with terraform
Generating a certificate using:
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl req -new -x509 -key private_key.pem -out certificate.pem -days 365 -subj "/CN=unused"
Then I tried everything again with the certificate.
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.
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.