Overview
While we can check for unused triggers in our GTM container through the GTM interface, we can not do that for variables. We should always clean up unused variables to keep our GTM container clean. This tutorial explains how we can achieve that through Google AppScript, Google Sheet, Google Tag Manager API.
Once again, please excuse my bad coding practice. I will update the tutorial with more robust code in due course.
End Result

Google AppScript Configuration
Algorithm
var varList = getVariableNamesList()
var tagList = getTagList()
var unusedVarList =[]
for each variable in varList:
var flag1=0;
var flag2=0;
var flag3=0;
for each tag in tagList:
if "{{"variable"}}" in tag
flag1=1;
for each variable in variableList:
if "{{"variable"}}" in variable
flag2=1;
for each trigger in triggerList:
if "{{"variable"}}" in trigger
flag3=1;
if flag1==0 && flag2==1 && flag3==1:
unusedVarList.push(var)
Main Logic
function libraries() {
var path="accounts/364874/containers/31314328/workspaces/861";
//Logger.log(TagManager.Accounts.Containers.Workspaces.Tags.list(path).tag[50]);
var k=TagManager.Accounts.Containers.Workspaces.Variables.list(path);
var a=TagManager.Accounts.Containers.Workspaces.Variables.list(path).variable[1];
var b=TagManager.Accounts.Containers.Workspaces.Variables.list(path).variable[1].getEnablingTriggerId();
//console.log(TagManager.Accounts.Containers.Workspaces.Variables.list(path));
}
function getAllVariables(path){
var names=TagManager.Accounts.Containers.Workspaces.Variables.list(path).variable.map(a => a.name);
//var namesAndUrls = TagManager.Accounts.Containers.Workspaces.Variables.list(path).variable.map(({variableId, name, tagManagerUrl}) => ({variableId, name, tagManagerUrl}));
return names;
}
function getVariable(path){ //cartProducts
if(!path){
path = "accounts/364874/containers/31314328/workspaces/861/variables/401";
//path = "accounts/364874/containers/31314328/workspaces/861/variables/357";
}
var variable = TagManager.Accounts.Containers.Workspaces.Variables.get(path);
return variable;
}
function getTag(path){
if(!path){
//path = "accounts/364874/containers/31314328/workspaces/861/tags/1263";
//path = "accounts/364874/containers/31314328/workspaces/861/tags/1456"; //great example
path = "accounts/364874/containers/31314328/workspaces/861/tags/969";
}
var tag = TagManager.Accounts.Containers.Workspaces.Tags.get(path);
return tag;
}
function printTagMonitoringStatus(path){
if(!path){
path = "accounts/364874/containers/31314328/workspaces/861";
}
var tags = TagManager.Accounts.Containers.Workspaces.Tags.list(path).tag;
tags.forEach(function(tag){
var flag = 0;
if(tag.monitoringMetadata && tag.monitoringMetadata.map){
tag.monitoringMetadata.map.forEach(function(mapItem){
if(mapItem.key && mapItem.key == "monitor" && mapItem.value && mapItem.value=="true"){
flag=1;
}
});
}
console.log(tag.name,flag);
});
}
function getAllVariableReferencesInTriggers(path){
if(!path){
path = "accounts/364874/containers/31314328/workspaces/861";
}
var triggers = TagManager.Accounts.Containers.Workspaces.Triggers.list(path).trigger;
var params=[];
triggers.forEach(function(trigger){
if(trigger.filter){
trigger.filter.forEach(function(filterItem){
if(filterItem.parameter){
filterItem.parameter.forEach(function(parameterItem){
if(parameterItem.value){
params.push(parameterItem.value);
}
});
}
});
}
});
return params;
}
function getAllVariableReferencesInTags(path){
if(!path){
path = "accounts/364874/containers/31314328/workspaces/861";
}
var b=[];
var tags = TagManager.Accounts.Containers.Workspaces.Tags.list(path).tag;
var params=[];
//tags.forEach((tag) => tag.parameter.forEach((param) => params.push(param.value)));
tags.forEach(function(tag){
if(tag.parameter){
//tag.parameter.forEach((param) => params.push(param.value));
tag.parameter.forEach(function(param){
if(param.value){ //field values
params.push(param.value)
}else if(param.list){ //cd values
//params.push(param.list.forEach(function(listItem){
param.list.forEach(function(listItem){
listItem.map.forEach(function(mapItem){ //NEED TO FIX THIS FOR GA4 AS THEY DON'T HAVE INDEX ANYMORE
if(mapItem.value){ // console.log(mapItem.value); //this will get the cd index as well which we don't need but that's fine
//return mapItem.value;
params.push(mapItem.value);
}
});
});
}
})
}
})
return params;
}
function getAllVariableReferencesInVariables(path){
if(!path){
path = "accounts/364874/containers/31314328/workspaces/861";
}
var b=[];
var variables = TagManager.Accounts.Containers.Workspaces.Variables.list(path).variable;
var params=[];
//tags.forEach((tag) => tag.parameter.forEach((param) => params.push(param.value)));
variables.forEach(function(variable){
if(variable.parameter){
//variable.parameter.forEach((param) => params.push(param.value));
variable.parameter.forEach(function(param){
if(param.value){ //field values
params.push(param.value)
}else if(param.list){ //cd values
//params.push(param.list.forEach(function(listItem){
param.list.forEach(function(listItem){
listItem.map.forEach(function(mapItem){ //NEED TO FIX THIS FOR GA4 AS THEY DON'T HAVE INDEX ANYMORE
if(mapItem.value){ // console.log(mapItem.value); //this will get the cd index as well which we don't need but that's fine
//return mapItem.value;
params.push(mapItem.value);
}
});
});
}
})
}
})
return params;
}
function gtmFindUnusedVariables(){
var path="accounts/364874/containers/31314328/workspaces/861";
var variables = getAllVariables(path);
var variableReferencesInTags = getAllVariableReferencesInTags(path);
var variableReferencesInVariables = getAllVariableReferencesInVariables(path);
var variableReferencesInTriggers = getAllVariableReferencesInTriggers(path);
var unusedVariables=[];
variables.forEach(function(variable){
var flag1=0;
var flag2=0;
var flag3=0;
variableReferencesInTags.forEach(function(variableReferenceInTag){
var n = "{{"+variable+"}}";
if(variableReferenceInTag && (variableReferenceInTag.match(n))){ //|| variableReferenceInTag.match(variable))){
flag1=1;
}
});
variableReferencesInVariables.forEach(function(variableReferenceInVariables){
var n = "{{"+variable+"}}";
if(variableReferenceInVariables &&
(variableReferenceInVariables.match(n)) // || variableReferenceInVariables.match(variable))
){
flag2=1;
}
});
variableReferencesInTriggers.forEach(function(variableReferenceInTriggers){
var n = "{{"+variable+"}}";
if(variableReferenceInTriggers && (variableReferenceInTriggers.match(n))){// || variableReferenceInTriggers.match(variable))){
flag3=1;
}
});
//console.log(variable, flag1, flag2, flag3);
if(flag1==0 && flag2==0 && flag3==0){
unusedVariables.push(variable);
}
});
var headerData =[["Unused Variable Name"]];
var contentData = [];
while(unusedVariables.length) contentData.push(unusedVariables.splice(0,1));
writeToSheet("gtm_unsed_variables_report", headerData, contentData);
}
//algo
/*
var varList = getVariableNamesList()
var tagList = getTagList()
var unusedVarList =[]
for each variable in varList:
var flag1=0;
var flag2=0;
var flag3=0;
for each tag in tagList:
if "{{"variable"}}" in tag
flag1=1;
for each variable in variableList:
if "{{"variable"}}" in variable
flag2=1;
for each trigger in triggerList:
if "{{"variable"}}" in trigger
flag3=1;
if flag1==0 && flag2==1 && flag3==1:
unusedVarList.push(var)
*/
UI Code
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Tag Monitor Menu')
.addItem('Fetch New Tag Status Data', 'menuFetchNewData')
.addToUi();
ui.createMenu('GTM Utilities')
.addItem('Fetch Unused Variables', 'menuGtmFindUnusedVariables')
.addToUi();
}
function menuFetchNewData() {
ga_tag_monitor_v1_main();
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.alert('New Data Loaded in sheet ga_tag_monitor_tag_status_data');
}
function menuGtmFindUnusedVariables(){
gtmFindUnusedVariables();
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.alert('New Data Loaded in sheet gtm_unsed_variables_report');
}
function writeHeadersToSheet(sheetName, data){
//var sheetName ='ga_tag_monitor_tag_status_data'; // Google sheet sheet
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
//const spreadsheet = SpreadsheetApp.create('Google Analytics Report');
var sheet = spreadsheet.getSheetByName(sheetName);
//get the range to write and write the results
var writeRange = sheet.getRange(1, 1, data.length, data[0].length) //// Read reference for getRange arguments
//getRange(row: any, column: any, numRows: any, numColumns: any): SpreadsheetApp.Range
writeRange.setValues(data);
}
function writeToSheet(sheetName, headerData, contentData){
//var sheetName ='ga_tag_monitor_tag_status_data'; // Google sheet sheet
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
//const spreadsheet = SpreadsheetApp.create('Google Analytics Report');
var sheet = spreadsheet.getSheetByName(sheetName);
sheet.clearContents();
writeHeadersToSheet(sheetName,headerData);
//get the range to write and write the results
var writeRange = sheet.getRange(2, 1, contentData.length, contentData[0].length) //// Read reference for getRange arguments
//getRange(row: any, column: any, numRows: any, numColumns: any): SpreadsheetApp.Range
writeRange.setValues(contentData);
Logger.log('Report spreadsheet created: %s',spreadsheet.getUrl());
}