Check Change History of Others

Changes by Google / Other Users

Never miss unnoticed changes again. You’ll immediately receive an email (or once every 24h, whatever you prefer) with the changes that have been made and in which account. This works at the MCC level. So you don’t need to go through all your accounts to implement this script.

This is how it works.

  1. Go to your MCC account and click on Scripts under Tools & Settings.
  2. Create a new script.
  3. Paste the code below.
  4. Change the “xxx” and “xx” fields in the script. Don’t forget to also create a new Spreadsheet and paste the URL in the script.
  5. Happy scripting!
/**
*
* MCC Level Change History Alerts
*
* This script checks the change history of all accounts under a Google Ads MCC, 
* and sends an alert via email if a change is made by a user not in the 'recognized' users list.
*
*/

// CHANGE SETTINGS HERE
var SPREADSHEET_URL = "xxx"; // Fill in your Spreadsheet URL Here
var EMAIL_ADDRESSES = "xxx"; // Your primary e-mail address you are using for making changes in accounts
var IGNORE_USERS = ['xx', 'xx', 'xx']; // Can be filled to ignore users, for instance Channable, or another script

var SEND_EMAIL = true;
var EMAIL_SUBJECT = "[GAds Script] - WARNING - Change by person outside of organisation";
var PERIOD = "YESTERDAY";

var EMAIL_BODY = 
    "\n"+
    "***\n"+
    "\n"+
    "This script checks changes in the 'Change history':\n"+
    "\n"+
    "For all changes during "+PERIOD+" \n"+
    "    check if there is a change being made by users other than "+IGNORE_USERS+" \n"+
    "    if so, alerts are logged in Google Sheet: "+SPREADSHEET_URL+" \n"+
    "\n"+
    "If there is an alert an email is sent to:\n"+ EMAIL_ADDRESSES +"\n";

function processAccounts() {
  var accountSelector = AdsManagerApp.accounts();
  var accountIterator = accountSelector.get();
  
  while (accountIterator.hasNext()) {
    var account = accountIterator.next();
    AdsManagerApp.select(account);
    main();
  }
}

function main() { 
  Logger.log("Processing account: "+AdsApp.currentAccount().getName());
  var changeAlerts = getChangeAlerts();

  if (changeAlerts.length > 0) {
    reportResults(changeAlerts);
    sendEmail(changeAlerts.length);
  } else {
    Logger.log("Could not find any changes in change history that were made by users other than "+IGNORE_USERS+" \n");
    logNoChanges();
  }
}

function logNoChanges() {
  var spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
  var sheet = spreadsheet.getActiveSheet();
  var today = new Date();
  sheet.appendRow([Utilities.formatDate(today, AdsApp.currentAccount().getTimeZone(), "yyyy-MM-dd"), "No changes made by others"]);
}

function getChangeAlerts() {
  var accountName = AdsApp.currentAccount().getName();
  var changes = [];
  var query = "SELECT " + 
              "campaign.name, " +
              "ad_group.name, " +
              "change_event.change_date_time, " +
              "change_event.change_resource_type, " +
              "change_event.changed_fields, " +
              "change_event.client_type, " +
              "change_event.feed, " +
              "change_event.feed_item, " +
              "change_event.new_resource, " +
              "change_event.old_resource, " +
              "change_event.resource_change_operation, " +
              "change_event.resource_name, " +
              "change_event.user_email " +
              "FROM change_event " +
              "WHERE change_event.change_date_time DURING "+PERIOD+" " + 
              "AND change_event.user_email NOT IN ('"+IGNORE_USERS.join("', '")+"') "+
              "ORDER BY change_event.change_date_time DESC "+ 
              "LIMIT 9999 ";

  var result = AdsApp.search(query);
  
  while (result.hasNext()) {
    var row = result.next();
    var campaignName = "";
    var adGroupName = "";
    try {
      campaignName = row.campaign.name;
      adGroupName = row.adGroup.name;
    } catch(e) {
    }
    
    try {    
      var change = [
        row.changeEvent.changeDateTime,
        accountName,
        row.changeEvent.userEmail,
        row.changeEvent.clientType,
        campaignName,
        adGroupName,
        row.changeEvent.changeResourceType,
        row.changeEvent.changedFields,
        row.changeEvent.feed,
        row.changeEvent.feedItem,
        row.changeEvent.newResource,
        row.changeEvent.oldResource,
        row.changeEvent.resourceChangeOperation
      ];
      changes.push(change);
      
    } catch(e) {
      Logger.log("Issue with parsing results from search API: "+e);
    }
  }
  return changes;
}

function reportResults(changes) {
  var sheet = prepareOutputSheet();
  addOutputToSheet(changes, sheet);  
}

function prepareOutputSheet() {
  var spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
  var sheet = spreadsheet.getActiveSheet();
  var numberOfRows = sheet.getLastRow();
  
  if (numberOfRows == 0) {
    addHeaderToOutputSheet(sheet);
  }
  return sheet;
}

function addHeaderToOutputSheet(sheet) {
  var header = [
    "date",
    "account", 
    "user",
    "clientType", 
    "campaignName",
    "adGroupName",
    "changeResourceType",
    "changedFields",
    "feed",
    "feedItem",
    "newResource",
    "oldResource",
    "resourceChangeOperation"
  ];
  sheet.appendRow(header);
}

function addOutputToSheet(output, sheet) {
  var numberOfRows = sheet.getLastRow();
  sheet.insertRowsBefore(2, output.length);
  var startRow = 2;
  var range = sheet.getRange(startRow, 1, output.length, output[0].length);
  range.setValues(output); 
  Logger.log("\nNumber of rows added to output sheet: "+output.length+"\n\n");
}

function sendEmail(numberOfalerts) {
  var accountName = AdsApp.currentAccount().getName();
  if (SEND_EMAIL) {
    var emailBody = 
        "Number of changes: " + numberOfalerts + "\n" + 
        "See details: "+ SPREADSHEET_URL+ "\n" + EMAIL_BODY;
    MailApp.sendEmail(EMAIL_ADDRESSES, EMAIL_SUBJECT+" | "+accountName, emailBody);
    Logger.log("Sending alert mail");
  }  
}

function alert(string) {
  Logger.log("### "+string);
}

// Run processAccounts function to execute the script across all accounts managed by the MCC
processAccounts();



Want to read something similar?

Articles that are related to this one

Applying Cialdini's techniques of influence
How do you determine your hourly rate as an online marketer?
Save time with the CONCATENATE and SPLIT functions in Spreadsheets

Your aLL IN ONE HOLY GRaIL

Get Timehacker for free in your inbox right now

Join over 1.000+ savvy marketeers who benefit from thisFREE inspirational newsletter!