- GcsStore overview – Google Cloud Storage and Apps Script
- GcsStore examples
- Cloud Storage and Apps Script
- Google cloud storage and CORS
- Using the service account to enable access to cloud storage
- Squeezing more into (and getting more out of) Cache services
There are a number of ways to get access tokens to get to cloud storage such as editing the manifest file (the simplest) or using Goa (or some other library) to create an interactive OAUTH2 flow or service account flow. The choice all depends on who owns the data (the user or the app), and whether you want to share across platforms or projects. Generally, though, I want to share data belonging to an App across platforms and potentially across cloud projects too. This makes the service account the simplest and easiest choice for me, so for Apps Script I use these libraries.
- for Oauth2, Goa – 1v_l4xN3ICa0lAW315NQEzAHPSoNiFdWHsMEwj2qA5t9cgZ5VWci2Qxv2
- for accessing cloud storage, GcsStore – 1w0dgijlIMA_o5p63ajzcaa_LJeUMYnrrSgfOzLKHesKZJqDCzw36qorl
Setting up
Like all Goa projects
- create your service account in cloud storage and download the JSON file.
- create and run one-off function to set up the Goa service in your property service referencing that file – something like this. You can delete it after you’ve run it. Neither the function nor the file is required again.
function oneOffgcs() { // used by all using this script var propertyStore = PropertiesService.getScriptProperties(); // DriveApp.createFile(blob).. comment forces Drive scope cGoa.GoaApp.setPackage (propertyStore , cGoa.GoaApp.createServiceAccount (DriveApp , { packageName: 'gcs_artifacts', fileId:'xxx-the json file id-xxx', scopes : cGoa.GoaApp.scopesGoogleExpand (['cloud-platform']), service:'google_service' })); }
- Now you can use goa to get tokens whenever they are required. To simplify things, let’s use a namespace to handle all that, which will set everything up whenever the script is invoked.
var gcs = (function (ns) { function getStore (account , bucket , folderKey) { const goa = cGoa.make (account,PropertiesService.getScriptProperties()); return new cGcsStore.GcsStore() .setAccessToken(goa.getToken()) .setBucket(bucket) .setExpiryLog (false) .setFolderKey (folderKey); }; ns.init = function () { ns.artifacts = getStore ('gcs_artifacts','project-artifacts',"" ); return ns; }; return ns; })({});
- It’s now easy to read and write to storage with a pattern like this, where all the token management is happening behind the scenes.
var myob = gcs.artifacts.get (somekey);
Using multiple service accounts.
That’s all fine, but sometimes you want to read in one part of the app and write or delete or upload in another. It’s always best to give the least possible permissions, so you can set up multiple service accounts with different permissions, and further tailor the scopes when you initialise your Goa. This needs to be able to read and write to cloud storage, and occasionally delete and upload. Rather than using a single service account, and a single scope, you can use different accounts for different operations. Another usage would be to access data from multiple projects or different buckets, each of which could need a different service account.
- Create multiple service accounts with appropriate, different permissions according to what access is required.
- Modify and run the one-off function to maintain multiple service accounts.
function oneOffgcs() { // used by all using this script var propertyStore = PropertiesService.getScriptProperties(); // DriveApp.createFile(blob).. comment forces Drive scope cGoa.GoaApp.setPackage (propertyStore , cGoa.GoaApp.createServiceAccount (DriveApp , { packageName: 'gcs_artifacts', fileId:'xxx-the json file id-xxx', scopes : cGoa.GoaApp.scopesGoogleExpand (['cloud-platform']), service:'google_service' })); cGoa.GoaApp.setPackage (propertyStore , cGoa.GoaApp.createServiceAccount (DriveApp , { packageName: 'gcs_artifacts_delete', fileId:'xxx-the other json file id-xxx', scopes : cGoa.GoaApp.scopesGoogleExpand (['devstorage.full_control']), service:'google_service' })); cGoa.GoaApp.setPackage (propertyStore , cGoa.GoaApp.createServiceAccount (DriveApp , { packageName: 'gcs_another_project', fileId:'xxx-the other project json file id-xxx', scopes : cGoa.GoaApp.scopesGoogleExpand (['cloud-platform']), service:'google_service' })); }
- add more handles to the gcs namespace
var gcs = (function (ns) { function getStore (account , bucket , folderKey) { const goa = cGoa.make (account,PropertiesService.getScriptProperties()); return new cGcsStore.GcsStore() .setAccessToken(goa.getToken()) .setBucket(bucket) .setExpiryLog (false) .setFolderKey (folderKey); }; ns.init = function () { ns.artifacts = getStore ('gcs_artifacts','bucket-artifacts',"" ); ns.artifactsDelete = getStore ('gcs_artifacts_delete','bucket-artifacts',"" ); ns.anotherProject = getStore ('gcs_another_project','bucket-from-another-project',"" ); return ns; }; return ns; })({});
- Now you can use multiple handles depending on the operation – this minimises the risk of accidentally deleting objects (using the wrong handle wouldn’t have the permission to do so), or seamlessly working across projects.
var myob = gcs.artifacts.get (somekey); var result = gcs.artifactsDelete.remove (somekey); var otherob = gcs.anotherProject.get (somekey);