1

I am following the docs on how to backup datastore using AppEngine. I am performing a gcloud app deploy cron.yaml command on a GCE VM that is meant to update a cronjob in AppEngine. the GCE VM and AppEngine cron are in the same project, and I have granted AppEngine admin to the GCE VM via a default Service Account. When I run this on my local machine, it updates fine. However on the GCE instance, thats where issues arise

Here are the files

app.yaml

runtime: python27
api_version: 1
threadsafe: true
service: cloud-datastore-admin
libraries:
- name: webapp2
  version: "latest"
handlers:
- url: /cloud-datastore-export
  script: cloud_datastore_admin.app
  login: admin

cron.yaml

cron:
- description: "Daily Cloud Datastore Export"
  url: /cloud-datastore-export?namespace_id=&output_url_prefix=gs://<my-project-id>-bucket
  target: cloud-datastore-admin
  schedule: every 24 hours

cloud_datastore_export.yaml

import datetime
import httplib
import json
import logging
import webapp2
from google.appengine.api import app_identity
from google.appengine.api import urlfetch

class Export(webapp2.RequestHandler):
  def get(self):
    access_token, _ = app_identity.get_access_token(
        'https://www.googleapis.com/auth/datastore')
    app_id = app_identity.get_application_id()
    timestamp = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
    output_url_prefix = self.request.get('output_url_prefix')
    assert output_url_prefix and output_url_prefix.startswith('gs://')
    if '/' not in output_url_prefix[5:]:
      # Only a bucket name has been provided - no prefix or trailing slash
      output_url_prefix += '/' + timestamp
    else:
      output_url_prefix += timestamp
    entity_filter = {
        'kinds': self.request.get_all('kind'),
        'namespace_ids': self.request.get_all('namespace_id')
    }
    request = {
        'project_id': app_id,
        'output_url_prefix': output_url_prefix,
        'entity_filter': entity_filter
    }
    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + access_token
    }
    url = 'https://datastore.googleapis.com/v1/projects/%s:export' % app_id
    try:
      result = urlfetch.fetch(
          url=url,
          payload=json.dumps(request),
          method=urlfetch.POST,
          deadline=60,
          headers=headers)
      if result.status_code == httplib.OK:
        logging.info(result.content)
      elif result.status_code >= 500:
        logging.error(result.content)
      else:
        logging.warning(result.content)
      self.response.status_int = result.status_code
    except urlfetch.Error:
      logging.exception('Failed to initiate export.')
      self.response.status_int = httplib.INTERNAL_SERVER_ERROR
app = webapp2.WSGIApplication(
    [
        ('/cloud-datastore-export', Export),
    ], debug=True)

The Error I'm getting is

Configurations to update:
descriptor:      [/usr/local/sbin/pluto/<my-project-id>/datastore/cron.yaml]
type:            [cron jobs]
target project:  [<my-project-id>]
Do you want to continue (Y/n)?  
Updating config [cron]...
failed.
ERROR: (gcloud.app.deploy) Server responded with code [403]:
  Forbidden Unexpected HTTP status 403.
  You do not have permission to modify this app (app_id=u'e~<my-project-id>').

I have checked other posts related to this, however they seem to deal with an old version/deployment of appengine

Service Accounts!

Service Account Permissions

martinomburajr
  • 1,235
  • 14
  • 29

2 Answers2

3

From Deploying using IAM roles:

To grant a user account the ability to deploy to App Engine:

  1. Click Add member to add the user account to the project and then select all of the roles for that account by using the dropdown menu:

    • Required roles to allow an account to deploy to App Engine:

      a. Set the one of the following roles:

      • Use the App Engine > App Engine Deployer role to allow the account to deploy a version of an app.
      • To also allow the dos.yaml or dispatch.yaml files to be deployed with an app, use the App Engine > App Engine Admin role instead.

      The user account now has adequate permission to use the Admin API to deploy apps.

      b. To allow use of App Engine tooling to deploy apps, you must also give the user account the Storage > Storage Admin role so that the tooling has permission to upload to Cloud Storage.

    • Optional. Give the user account the following roles to grant permission for uploading additional configuration files:

      • Cloud Scheduler > Cloud Scheduler Admin role: Permissions for uploading cron.yaml files.

Potentially of interest:

Dan Cornilescu
  • 39,470
  • 12
  • 57
  • 97
1

Okay after some tinkering. I added the project editor role to the service account linked to the GCE instance running my server. I am not fully aware if this is the role with least priviledge to enable this to work.

enter image description here

martinomburajr
  • 1,235
  • 14
  • 29
  • -1 Suggesting to add Project Editor role just to enable cron deployment for AppEngine is a massive over-provision of permissions. Project Editor encapsulates over 1000 individual permissions so from a security standpoint nobody should do this. – John Nov 22 '19 at 16:11