diff --git a/grunt/shrinkwrap.js b/grunt/shrinkwrap.js
index d3292b49877cf623c482938e48484f9ae0414761..6701c6d6c8b2e98e97fdc4314caa17bd239b1d07 100644
--- a/grunt/shrinkwrap.js
+++ b/grunt/shrinkwrap.js
@@ -14,13 +14,26 @@ var NON_CANONICAL_FILE = 'npm-shrinkwrap.json';
 var DEST_FILE = 'test-infra/npm-shrinkwrap.canonical.json';
 
 
+function cleanup(shrinkwrap) {
+  // Remove `resolved` property to avoid irrelevant changes
+  // See https://github.com/npm/npm/issues/3581
+  if ((typeof shrinkwrap) === 'string') {
+    return shrinkwrap;
+  }
+  delete shrinkwrap.resolved;
+  for (var key in shrinkwrap) {
+    shrinkwrap[key] = cleanup(shrinkwrap[key]);
+  }
+  return shrinkwrap;
+}
+
 function updateShrinkwrap(grunt) {
   // Assumption: Non-canonical shrinkwrap already generated by prerequisite Grunt task
   var shrinkwrapData = grunt.file.readJSON(NON_CANONICAL_FILE);
   grunt.log.writeln('Deleting ' + NON_CANONICAL_FILE.cyan + '...');
   grunt.file.delete(NON_CANONICAL_FILE);
   // Output as Canonical JSON in correct location
-  grunt.file.write(DEST_FILE, canonicallyJsonStringify(shrinkwrapData));
+  grunt.file.write(DEST_FILE, canonicallyJsonStringify(cleanup(shrinkwrapData)));
   grunt.log.writeln('File ' + DEST_FILE.cyan + ' updated.');
 }