Google Cloud Build and App Engine enviroment variables - google-app-engine

I have a secret token on my App Engine app.yaml
env_variables:
TOKEN: super-secret-token
And obviously this token is out of git. Using Google Cloud Build, how can I set this parameter TOKEN at build time or before?

You can use Secret Manager within Cloud Build to get the actual secret and replace the super-secret-token placeholder value in app.yaml prior to deploying your app to App Engine. That would look something like this:
steps:
- name: gcr.io/cloud-builders/gcloud
entrypoint: 'bash'
args: [ '-c', "gcloud secrets versions access latest --secret=secret-name --format='get(payload.data)' | tr '_-' '/+' | base64 -d > decrypted-data.txt" ]
- name: 'gcr.io/cloud-builders/gcloud'
entrypoint: /bin/sh
args:
- '-c'
- |
sed "s/super-secret-token/g" $(cat decrypted-data.txt)
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: 'bash'
args: ['-c', 'gcloud config set app/cloud_build_timeout 1600 && gcloud app deploy']
timeout: '1600s'
Having said that, your secret token will still be available unencrypted in your App Engine's environment variable which is not optimal security-wise. Instead you may want to query Secret Manager from within your App Engine code directly. You'll find code samples to do so here.

Related

deploying to app engine flex custom runtime with cloud build causes a large amount of builds to run

i am Kinda new to cloud build so i am kind of confused about what is happening.
first this is my file structure
cloudbuild.yaml
backend/
Dockerfile
app.yaml
I had an application which i dockerized and deployed to app engine felx in custom runtime
here's my Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build
WORKDIR /app
# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
COPY . ./
RUN dotnet publish -c Release -o out
FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base
ENV ASPNETCORE_URLS=http://+:80;
WORKDIR /app
COPY --from=build /app/out .
EXPOSE 80
ENTRYPOINT ["dotnet", "myapp.dll"]
and this is my app engine flex file
runtime: custom
env: flex
manual_scaling:
instances: 1
resources:
cpu: 1
memory_gb: 0.5
disk_size_gb: 10
service: backend
network:
name: my-network
subnetwork_name: my-network-subnet
instance_tag: "backend"
forwarded_ports:
I have successfully deployed this app on app engine flex using this command
gcloud app deploy --appyaml=app.yaml
Then i added a cloudbuild.yaml file following this google doc
steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: 'bash'
args: ['-c', 'gcloud config set app/cloud_build_timeout 2000 && gcloud app deploy --appyaml=backend/app.yaml']
as you can see in the cloudbuild.yaml i didnt add the timeout attribute because it gave me this error each time i tried to submit the build.
Error Response: [13] Error parsing cloudbuild.yaml for runtime custom: Argument is not an object: "2000s"
after removing the timeout attribute, cloud build started behaving in a weird way, it kept creating build jobs on its own until it reached over 20 builds.
i had to stop these builds manually because it exceeded the 120 minute free quota limit.
can some one tell me if my cloudbuild.yaml is the thing causing the issue or if its a problem with google cloud.
So the problem was writing the cloudbuild as a yaml file, instead i re-wrote it as a json file, i am not entirely sure why is the cloudbuild.yaml file was giving me errors but that was my solution.
{
"steps": [
{
"name": "gcr.io/google.com/cloudsdktool/cloud-sdk",
"entrypoint": "bash",
"args": [
"-c",
"gcloud config set app/cloud_build_timeout 1600 && gcloud app deploy --appyaml=app.yaml"
]
}
],
"timeout": "1600s"
}
Also the cloudbuild and app.yaml must be in the root of the branch with the cloudbuild file and Dockerfile.

How to make App Engine use Cloud Build substitute variables for environmental variables?

I am trying to add Cloud Build on top of my App Engine Flask app. Everything works, but for some reason, I can't access the substitution variables I declared in the trigger.
Env vars are still being fetched from app.yaml. And they are parsed literally, not as variables. When I remove it from app.yaml Python throws a NoneType error.
[Trigger][1]: https://i.stack.imgur.com/Ii6Jv.png
[App.yaml][2]: https://i.stack.imgur.com/bg646.png
runtime: python310
instance_class: F4
automatic_scaling:
max_instances: 8
env_variables:
_CONFIG_TYPE: ${_CONFIG_TYPE}
[cloudbuild][3] https://i.stack.imgur.com/jo0PN.png
steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: 'bash'
args: ['-c', 'gcloud config set app/cloud_build_timeout 1600 && gcloud app deploy']
timeout: '1600s'
substitutions:
_CONFIG_TYPE: ${_CONFIG_TYPE}
It won't work because gcloud app deploy command start a new Cloud Build, behind the scene, to build a container with your code and to deploy it. Your env var will change anything
The solution is to perform a bash replacement, with sed for instance.
app.yaml file
runtime: python310
instance_class: F4
automatic_scaling:
max_instances: 8
env_variables:
_CONFIG_TYPE: ##_CONFIG_TYPE##
Cloud Build Step with env var usage
steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: 'bash'
env-vars:
- CONFIG_TYPE: ${_CONFIG_TYPE}
args:
- '-c'
- |
sed -i "s/##_CONFIG_TYPE##/$${CONFIG_TYPE}/g" app.yaml
gcloud config set app/cloud_build_timeout 1600
gcloud app deploy
timeout: '1600s'
substitutions:
_CONFIG_TYPE: ${_CONFIG_TYPE}
Cloud Build Step without env var usage
steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: 'bash'
args:
- '-c'
- |
sed -i "s/##_CONFIG_TYPE##/${_CONFIG_TYPE}/g" app.yaml
gcloud config set app/cloud_build_timeout 1600
gcloud app deploy
timeout: '1600s'
substitutions:
_CONFIG_TYPE: ${_CONFIG_TYPE}

How to pass the env secret variable in google app engine and gitlab CI

I am using gitlab and deploying it to google app engine for my nodejs application.
Google Service access is added as variable in gitlab settings
SERVICE_ACCOUNT_KEY:
{
"type": "service_account",
"project_id": "node-us",
"private_key_id": "",
"private_key": "",
"client_email": "gitlab-demo-service-account#node-us.iam.gserviceaccount.com",
"client_id": "",
"auth_uri": "",
"token_uri": "",
"auth_provider_x509_cert_url": "",
"client_x509_cert_url": ""
}
.gitlab-ci.yml
image: node:latest
cache:
paths:
- node_modules/
before_script:
- echo "deb http://packages.cloud.google.com/apt cloud-sdk-jessie main" | tee /etc/apt/sources.list.d/google-cloud-sdk.list
- curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
- apt-get update
- apt-get -qq -y install google-cloud-sdk
deploy_production:
stage: deploy
environment: Production
only:
- master
script:
- echo $SERVICE_ACCOUNT_KEY > /tmp/$CI_PIPELINE_ID.json
- gcloud auth activate-service-account --key-file /tmp/$CI_PIPELINE_ID.json
- gcloud --quiet --project node-us app deploy app.yaml
after_script:
- rm /tmp/$CI_PIPELINE_ID.json
my root folder has app.yaml file and .env file
As of now I was testing the flow which worked fine and deployed successfully to google app engine. (it does not contain any secret keys)
However I want the my env variables (containing secret keys) need to be ignored in .gitignore also not to be part of app.yaml file.
How can I pass my env secret keys?
Don't pass it!
Use Secret Manager to pass your secret. So, in your repository, use the secret manager URI to reference the secret, with the secret version. Like this, no secret in your code or in the app.yaml/.env files.
If you need to update the secret, do it manually. Some tasks are hard, or expensive, to automate.
Note: The article that you mention has been released 6 months before Secret Manager release (early this year 2020)

Permission error on Gitlab CI deployment with google app engine

I try to make a full process of integrated deployment with Gitlab and Google AppEngine.
But the deployment fail because it seems I miss rights. I really don't know which right is needed here.
I activated the App Engine API and the Cloud Build API.
The rights my service has:
my gitlab-ci.yml file:
Staging Deployment:
stage: Deploy
environment:
name: Staging
before_script:
- echo "deb http://packages.cloud.google.com/apt cloud-sdk-jessie main" | tee /etc/apt/sources.list.d/google-cloud-sdk.list
- curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
- apt-get update
- apt-get -qq -y install google-cloud-sdk
script:
- echo $DEPLOY_KEY_FILE_PRODUCTION > /tmp/$CI_PIPELINE_ID.json
- gcloud auth activate-service-account --key-file /tmp/$CI_PIPELINE_ID.json
- gcloud config list
- gcloud config set project $PROJECT_ID_PRODUCTION
- gcloud --quiet --project $PROJECT_ID_PRODUCTION app deploy gae/test.yml
only:
refs:
- master
And finally my test.yml:
# [START django_app]
runtime: python37
service: testing
entrypoint: gunicorn -b :$PORT manta.wsgi --timeout 120
default_expiration: "5m"
env_variables:
DJANGO_SETTINGS_MODULE: manta.settings.dev
handlers:
# This configures Google App Engine to serve the files in the app's
# static directory.
- url: /static
static_dir: static/
# This handler routes all requests not caught above to the main app.
# It is required when static routes are defined, but can be omitted
# (along with the entire handlers section) when there are no static
# files defined.
- url: /.*
script: auto
# [END django_app]
And the error I have during deployment:
ERROR: (gcloud.app.deploy) Permissions error fetching application [apps/testing]. Please make sure you are using the correct project ID and that you have permission to view applications on the project.

Google App Engine does not deploy two services (seem to be overriding each other)

I have a Django backend and an Angular frontend and i'm trying to deploy both of them to a Google App Engine Standard app as two services within the same app.
The directory looks like this:
-backend
--cloudbuild.yaml
--app.yaml
-- ...
-frontend
--cloudbuild.yaml
--app.yaml
-- ...
-cloudbuild.yaml
The main cloudbuild.yaml goes through each folder and looks for a cloudbuild.yaml file and submits the build:
steps:
- name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-c'
- |
for d in */; do
config="${d}cloudbuild.yaml"
if [[ ! -f "${config}" ]]; then
continue
fi
echo "Building $d ... "
(
gcloud builds submit $d --config=${config}
) &
done
wait
When i push the code to my Github repo, they get deployed successfully. However, both logs show something like below:
Step #2: Beginning deployment of service [default]...
Step #2: #============================================================#
Step #2: #= Uploading 0 files to Google Cloud Storage =#
Step #2: #============================================================#
Step #2: File upload done.
Step #2: Updating service [default]...
Step #2: ................................done.
Step #2: Setting traffic split for service [default]...
Step #2: .....done.
Step #2: Deployed service [default] to [https://thisapp.appspot.com]
So it seems like one is overriding the other as the default service. When I go to the services page, there is only one service which is the default one.
I know that we first have to deploy the default service and then the rest. However i thought since the main cloudbuild.yaml submits the build one by one, then there would be a default service and the second build would create a second service.
The other two cloudbuild.yaml files are as below:
steps:
# Install node packages
- name: 'gcr.io/cloud-builders/npm'
args: [ 'install' ]
# Build productive files
- name: 'gcr.io/cloud-builders/npm'
args: [ 'run', 'build', '--prod']
# Deploy to google cloud app egnine
- name: 'gcr.io/cloud-builders/gcloud'
args: ['app', 'deploy', '--version=prod']
steps:
- name: 'python:3.7'
entrypoint: python3
args: ['-m', 'pip', 'install', '-t', '.', '-r', 'requirements.txt']
- name: 'python:3.7'
entrypoint: python3
args: ['./manage.py', 'collectstatic', '--noinput']
- name: 'gcr.io/cloud-builders/gcloud'
args: ['app', 'deploy', '--version=prod']
I'd be grateful if you can help me on this.
Thanks
It is happening because in your app.yaml file, you have not provided the service tag. If service tag is not provided, the service will be deployed as default service. For e.g. when you deploy your frontend, it is getting deployed as default service. And then you deploy your backend, it is also getting deployed as default service overriding the existing default service. or the vice versa.
Add the service tag in your frontend's app.yaml:
service: frontend
runtime: nodejs10
You may keep the backend as default service i.e. no service tag in backend's app.yaml

Resources