Roll out web analytics at scale with Google Apps Script pt 1: Google Tag Manager

Spend less time implementing and more time optimising by automating your web analytics roll out with Google Apps Script.

Why would you want to do this?

As digital marketers, we have a responsibility to ensure that the we deliver value to our customers. Web analytics tools are one of the ways in which we can demonstrate this value. However, one of the challenging parts about working at scale is the time commitment involved from both the digital marketer and the customer. Unfortunately, this can often mean delays in the roll out of web analytics tools, like Google Analytics. This series of blog posts is about automating as much manual work as possible during the roll out process.

What is Google Apps Script?

Google Apps Script is a serverless computing environment which makes it easy to interact with Google’s services. No infrastructure is needed and authentication with Google’s products is handled for you. Scripts are written in a language based on JavaScript and can be scheduled to run based off time intervals, events, Google Form submissions and more. The following solution outlines the basics you need to start building up Google Tag Manager containers on behalf of your customers. Go to https://script.google.com to get started.

The Solution

Background & pre-requisites

The idea is that you can modify this code to meet your own needs, depending on what and how you want to track.

Turning on the Google Tag Manager API

In Apps Script – go to Resources > Advanced Google Services and turn on the Tag Manager API.

Also, follow the link to the Google API Console and in the side bar go to APIs & Services > Library, search for the Google Tag Manager API and enable it. In your case, ‘MANAGE’ will say ‘ENABLE’ until you turn it on.

No account insert operation

Unfortunately there is no account insert operation in the Google Tag Manager API, so to work around this you will only be creating a container for each customer, within large ‘holding accounts’ – which you will create in the Google Tag Manager UI.

Exponential backoff

As you’ll be hitting Google Tag Manager API endpoints quickly when creating and configuring multiple containers, you need a way to ensure that your script keeps going when it hits the rate limit. Exponential backoff is a way to solve this problem, where a script will stop and wait an exponentially increasing amount of time each time it hits the rate limit. This is based off https://gist.github.com/peterherrmann/2700284 with the only addition being it only starts the backoff process if it hits the rate limit errors from Google APIs.

 function call(func, optLoggerFunction) {
            for (var n = 0; n < 6; n++) {
                try {
                    return func()
                } catch (e) {
                    Logger.log(e);
                    if (e == "GoogleJsonResponseException: Quota Error: User Rate Limit Exceeded." || e == "GoogleJsonResponseException: Quota Error: Rate limit for writes exceeded.") {
                        if (optLoggerFunction) {
                            optLoggerFunction("GASRetry " + n + ": " + e)
                        }
                        if (n == 5) {
                            throw e
                        }
                        Utilities.sleep((Math.pow(2, n) * 1000) + (Math.round(Math.random() * 1000)))
                    } else {
                        throw e
                    }
                }
            }
        } 

A configuration object

It’s generally easier to re-use this script if you have some sort of configuration settings which you can easily change for each customer. That way, for each iteration of the script you only have to change one thing, rather than lots of things. My script is being triggered by an http request but I have shown what these values might look like hard-coded below.
 
var params = {
            'gtmAccountId': postData.gtmAccountId,
            'customerName': postData.customerName,
            'analyticsAccountId': postData.analyticsAccountId,
            'websiteUrl': postData.websiteUrl,
            'callTrackingId': postData.callTrackingId, //optional
            'analyticsPropertyId': postData.analyticsPropertyId //optional
        };

var paramsHardcoded = {
     		'gtmAccountId': '1234567',
            'customerName': 'Billy Bob',
            'analyticsAccountId': '1242535',
            'websiteUrl': 'https://billybob.co.nz',
            'callTrackingId': '1234567', //optional
            'analyticsPropertyId': 'UA-123456' //optional
} 

Creating Containers

Remember, every time we call the Tag Manager API we want to use our exponential backoff function. The below code creates the web Google Tag Manager container, and stores values we need for later API calls as variables. Note that this is utilising the configuration object we created before.
 
var path = 'accounts/' + params.gtmAccountId;

        var container = call(function() {
            return TagManager.Accounts.Containers.create({
                    'name': params.customerName,
                    'usageContext': ['WEB']
                },
                path)
        });

        var containerPath = container.path;

        var containerId = container.accountId; 

Creating Workspaces

// Creates a workspace in the container to track entity changes.
        var workspace = call(function() {
            return TagManager.Accounts.Containers.Workspaces.create({
                    'name': 'Automated Workspace',
                    'description': 'Some description.'
                },
                containerPath)
        });

        var workspacePath = workspace.path; 

Creating Triggers

 
/*FULL RESOURCE INFO CAN BE FOUND HERE: https://developers.google.com/tag-manager/api/v1/reference/accounts/containers/triggers#resource*/

//DOM Ready trigger
var trigger1 = call(function() {
            return TagManager.Accounts.Containers.Workspaces.Triggers.create({
                    'name': 'DOM Ready',
                    'type': 'domReady'
                },
                workspacePath)
        });

//Page View trigger
var trigger3 = call(function() {
            return TagManager.Accounts.Containers.Workspaces.Triggers.create({
                    'name': 'Page View - All Pages',
                    'type': 'pageview'
                },
                workspacePath)
        }); 

Creating Tags

 //Creating a regular GA pageview tag
var tag2 = call(function() {
            return TagManager.Accounts.Containers.Workspaces.Tags.create({
                    'name': 'UA Page Views All Pages',
                    'type': 'ua',
                    'liveOnly': false,

                    'parameter': [

                        {
                            'type': 'template',
                            'key': 'trackingId',
                            'value': params.analyticsPropertyId
                        },

                        {
                            'type': 'template',
                            'key': 'trackType',
                            'value': 'TRACK_PAGEVIEW'
                        },

                        {
                            "key": "fieldsToSet",
                            "type": "list",
                            "list": [{
                                "type": "map",
                                "map": [{
                                        "type": "template",
                                        "key": "fieldName",
                                        "value": "cookieDomain"
                                    },
                                    {
                                        "type": "template",
                                        "key": "value",
                                        "value": "auto"
                                    }
                                ]
                            }]
                        }
                    ],

                    'firingTriggerId': [trigger3.triggerId]
                },
                workspacePath)
        }); 

Creating a Version

var version = call(function() {
            return TagManager.Accounts.Containers.Workspaces
                .create_version({
                    'name': 'GTM Automation'
                }, workspacePath)
                .containerVersion
        }); 

Publishing

 var publish = call(function() {
            return TagManager.Accounts.Containers.Versions.publish(version.path)
        }); 

Next steps

There’s probably a few more things that need to be convered here:
  1. More complicated trigger examples
  2. Creation of Google Analytics properties if one isn’t provided
  3. Distribution of containers to customers
  4. Checking the installation status of containers and alerting when they are installed
  5. Approaches to running this script for hundreds of customers
  6. These will be covered in some later blog posts.
In the meantime – have a play around with this and see if you can make some cool stuff before I post my solution!

Comments

There are no comments on this entry.