Terraform: Terraform Cloud environment variable not allowing newlines needed for GCP auth JSON file

Created on 13 Sep 2019  路  12Comments  路  Source: hashicorp/terraform

Summary

As suggest in #22782 - trying to add a GCP JSON file as an environment variable to auth to Google Cloud API, returns an error about newlines not being allowed.

Terraform Version

Terraform Cloud v0.12.8

Terraform Configuration Files

_Using Terraform Cloud_

Debug Output

Error saving variable
Value cannot contain newlines in environment variables

Expected Behavior

Environment variable containing a GCP Service Account JSON is stored on Terraform Cloud as a local file, allowing Terraform Cloud to authenticate against the GCP API to run Terraform. This is the behavior that Terraform Cloud uses to write Terraform Cloud Variables to the terraform.tfvars. The same behavior would allow a GCP user to write a local JSON file called account.json to then be referenced by the Terraform Cloud environment variable entered.

Actual Behavior

When attempting to save the contents of the JSON file as the value an error is displayed:

Error saving variable
Value cannot contain newlines in environment variables

Steps to Reproduce

  • Need to authenticate Terraform Cloud to GCP
  • Create Terraform GCP Service Account on GCP
  • Download GCP Service Account JSON file
  • Create new environment variable on Terraform Cloud WebUI
  • Set key as GOOGLE_CREDENTIALS
  • Paste contents of GCP Service Account JSON file as value
  • Mark as sensitive
  • Click Save variable

Additional Context

GCP does not use a key value pair of text to authenticate to the GCP API like AWS, it uses a key value pair that refers to a local file on disk, that file is JSON and contains the private key along with several other fields. Terraform Cloud needs a method to reference the JSON file in order to authenticate to the GCP API.

AWS API key & value:

access key ID = AKIAIOSFODNN7EXAMPLE
secret access key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

GCP API key & value:

key = GOOGLE_CREDENTIALS
value = ~/.gcp/credentials/account.json

Terraform Cloud needs a way to replicate the standard behavior used in Terraform OSS to authenticate to Terraform Cloud -

terraform.io/docs/providers/google/index.html

References

  • #22782
enhancement

Most helpful comment

Hi there,

new lines do not work. As a workaround just remove all new lines and you should be good to go.

Your original json file might look like this:
json { "type": "service_account", "project_id": "project-id", "private_key_id": "private_key_id", "private_key": "-----BEGIN PRIVATE KEY-----\nprivatekey\n-----END PRIVATE KEY-----\n", "client_email": "[email protected]", "client_id": "client_id", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/someone%40someproject.iam.gserviceaccount.com" }

But it should look like this:
json { "type": "service_account", "project_id": "project-id", "private_key_id": "private_key_id", "private_key": "-----BEGIN PRIVATE KEY-----\nprivatekey\n-----END PRIVATE KEY-----\n", "client_email": "[email protected]", "client_id": "client_id", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/someone%40someproject.iam.gserviceaccount.com" }

The easiest way to remove the lines is to copy and paste it into VSCode and then select your data and use the shortcut CTRL+J.

However, I fully support that Terraform Cloud should do this reformatting automatically or at least offer a checkbox option to do so (as it might break the content when remove new lines by default).

All 12 comments

Hi there,

new lines do not work. As a workaround just remove all new lines and you should be good to go.

Your original json file might look like this:
json { "type": "service_account", "project_id": "project-id", "private_key_id": "private_key_id", "private_key": "-----BEGIN PRIVATE KEY-----\nprivatekey\n-----END PRIVATE KEY-----\n", "client_email": "[email protected]", "client_id": "client_id", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/someone%40someproject.iam.gserviceaccount.com" }

But it should look like this:
json { "type": "service_account", "project_id": "project-id", "private_key_id": "private_key_id", "private_key": "-----BEGIN PRIVATE KEY-----\nprivatekey\n-----END PRIVATE KEY-----\n", "client_email": "[email protected]", "client_id": "client_id", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/someone%40someproject.iam.gserviceaccount.com" }

The easiest way to remove the lines is to copy and paste it into VSCode and then select your data and use the shortcut CTRL+J.

However, I fully support that Terraform Cloud should do this reformatting automatically or at least offer a checkbox option to do so (as it might break the content when remove new lines by default).

Can this be added to the documentation because I spent way to much time on trying to figure this out until I came across this?

Error: google: error getting credentials using GOOGLE_APPLICATION_CREDENTIALS environment variable: open { "type": "service_account", "project_id": "my-demo-XXXX-XXXX", "private_key_id": "XXXXXXXXXXXXXXXXXXXXX", "private_key": "-----BEGIN PRIVATE KEY-----\nPRIVATEKEYxxxxxxxxxxxxxxxxxxxxxxxxxxx-----END PRIVATE KEY-----\n", "client_email": "[email protected]", "client_id": "XXXXXXXXXXXXXXXX", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/test-service-accct%40my-demo-XXXX-XXXX.iam.gserviceaccount.com"}: file name too long

UPDATE:
So if you use GOOGLE_CREDENTIALS instead of GOOGLE_APPLICATION_CREDENTIALS this will work but a new error comes up.

private key should be a PEM or plain PKCS1 or PKCS8; parse error: asn1: structure error: tags don't match (16 vs {class:0 tag:13 length:45 isCompound:true}) {optional:false explicit:false application:false private:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} pkcs1PrivateKey @2

Which after researching this error I believe it is the result of removing the \n causing the certificate to break.
I am not sure if I am doing something wrong or I am missing something.

I'm having trouble here also. I am setting the GOOGLE_APPLICATION_CREDENTIALS and have given the contents of JSON file with no line breaks as an argument. When I queue the plan i get an error stating that the file name is too long. Haven't done anything different from what you guys are doing! I can see the variables are successfully passed to the environment.

any help greatly appreciated, this has been driving me crazy :D

image

image

Just verified this bug in Terraform Enterprise V4.

Any file sanitizing that is needed by Terraform should be done on the backend, hidden from the user, keeping the user pasting in a json file as they would do with any other authentication setup via the web.

Yes, there is a documented (in a github issue) work around using VSCode, however, the customers I am consulting with are now being required to change their toolsets in order to clean up a json file so that TF will accept it and run without errors.

Can we get the sanitizing of json on the server not the customers dev machine?

The documentation states that the text fields for variable values can handle multi-line text (typed or pasted) without any special effort.

This is clearly not the case because trying to save a value with new lines results in this error:
image

I need to put an RSA key into a variable and cannot get it to work. I tried replacing the new lines with \n but that just makes the key invalid.

Any help here would be appreciated.

@mikesigs I think that the documentation there is specifically for the Terraform Variables not Environment Variables.

But I have the same need for multiline in environment variables . It would be nice if the Environment Variables could support multiline as well.

Reliable workaround if you have node installed locally:

const fs = require('fs')
const json = fs.readFileSync('project-12345-abc123.json').toString()
console.log(JSON.stringify(JSON.parse(json)))

Or you could just run the last line in your browser's console (after replacing JSON.parse(json) with the JSON value you got from GCP).

+1 on a proper solution in the UI though.

Ran into this problem today, and used the following Ruby version of @gnarea's Node workaround:

$ ruby -e "require 'json'; print JSON.parse(File.read('account.json')).to_json" | pbcopy 

鈽濓笍 Uses the json gem to parse the contents of the account.json credentials file, strip the newlines, and copy it to the clipboard with pbcopy.

_Can we get the sanitizing of json on the server not the customers dev machine?_

I am holding steady in my quest for sane UX/UI for those using GCP. Ill paste my json file into a webform, Hashicorp you can do whatever formatting you like to that json to make your tool accept it as valid.

Reliable workaround if you have node installed locally:

const fs = require('fs')
const json = fs.readFileSync('project-12345-abc123.json').toString()
console.log(JSON.stringify(JSON.parse(json)))

Or you could just run the last line in your browser's console (after replacing JSON.parse(json) with the JSON value you got from GCP).

+1 on a proper solution in the UI though

Thanks a lot, it worked for me.

Hey everyone,

thanks a lot for all your suggestions on solving this issue. It is also happening outside of the Terraform Cloud and was wondering if that is an issue with the limit of the ENV VARIABLES when doing export gcs_sa_key="${gcs_sa_key}" or mostly related to the file function.

So, the case is that I would like to pass a GCP auth JSON as a Terraform variable and then use it as a template user_data script while exporting as an ENV variable. The end goal is to write the content of the JSON file into a specific path (to the new VM instance) by just doing echo -n "$gcs_sa_key" > /etc/logstash/gcs_sa_key.json which is then required for the inputs-google_cloud_storage plugin to be able to read the gcp bucket(s).

The error message is something like this: line 21: export: -----END': not a valid identifier which seems to come from the space/tab in the "private_key": "-----BEGIN PRIVATE KEY-----\nprivatekey\n-----END PRIVATE KEY-----\n",. As soon as I remove the space and \n it is able to parse and export to ENV but for sure it is not going to be a valid key, right?

Did someone experience the same issue? or is there any workaround. I also tried other options i.e. copying/provision the file to the instance or add the file already to the pre-builded image, but this seems the more convenient approach (at least to me) to use such service accounts when applying terraform via .tvars.

Many thanks in advance for your time.

Best regards,

Hi @! Thanks for reporting this and sorry for the delayed response.

We use issues in this repository to represent bugs and feature requests in Terraform CLI. Terraform Cloud is developed by other teams who don't typically monitor this repository, and they prefer to hear about bug reports and feature requests via the customer support system.

If you have a paid account on Terraform Cloud, please send this feedback to your usual support contacts. Otherwise, you can contact the Terraform Cloud support team at [email protected]. If you contact the support team, be sure to include your Terraform Cloud username and organization name in your request.

Thanks again!

Was this page helpful?
0 / 5 - 0 ratings