{
  "name": "ms365 dev",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -1008,
        1040
      ],
      "id": "ccd5c8f5-2be0-4420-97ed-e23e2f67c86f",
      "name": "Schedule Trigger"
    },
    {
      "parameters": {
        "jsCode": "const azureUsers = items[0].json.value || [];\nconst huduUsers = items[0].json.assets || [];\n\nconst output = [];\n\nfunction getFieldValue(fields, label) {\n  const field = fields.find(f => f.label.toLowerCase() === label.toLowerCase());\n  return field ? field.value : '';\n}\n\nfunction normalize(value) {\n  if (!value || value.toLowerCase() === 'empty') return '';\n  return value.replace(/\\s+/g, '').trim().toLowerCase();\n}\n\nfunction hasChanges(azureUser, huduUser) {\n  const emailField = normalize(getFieldValue(huduUser.fields, 'Email Address'));\n  const cellField = normalize(getFieldValue(huduUser.fields, 'Cell Phone Number'));\n  const titleField = normalize(getFieldValue(huduUser.fields, 'Title'));\n  const teamsField = normalize(getFieldValue(huduUser.fields, 'Teams number'));\n  const deptField = normalize(getFieldValue(huduUser.fields, 'Department'));\n\n  const azureEmail = normalize(azureUser.mail);\n  const azureCell = normalize(azureUser.mobilePhone);\n  const azureTitle = normalize(azureUser.jobTitle);\n  const azureTeams = normalize((azureUser.businessPhones && azureUser.businessPhones[0]) || '');\n  const azureDept = normalize(azureUser.department || '');\n\n  return (\n    emailField !== azureEmail ||\n    cellField !== azureCell ||\n    titleField !== azureTitle ||\n    teamsField !== azureTeams ||\n    deptField !== azureDept\n  );\n}\n\n// Main loop\nfor (const user of azureUsers) {\n  if (!user. mail) {\n      console.log(`Skipping ${user.displayName}: no email address`);\n      continue;\n    }\n  \n  const huduMatch = huduUsers.find(huduUser => {\n    const emailField = getFieldValue(huduUser.fields, 'Email Address');\n    return emailField && emailField.toLowerCase() === (user.mail || '').toLowerCase();\n  });\n\n  console.log(`Checking ${user.mail} against Hudu emails:`, huduUsers.map(u => getFieldValue(u.fields, 'Email Address')));\n\n  if (!huduMatch) {\n    output.push({\n      json: {\n        action: 'create',\n        body: {\n          asset: {\n            name: user.displayName,\n            asset_layout_id: 2,\n            custom_fields: [\n              { 'Email Address': user.mail },\n              { 'Teams number': (user.businessPhones && user.businessPhones[0]) || '' },\n              { 'Cell Phone Number': user.mobilePhone || '' },\n              { 'Title': user.jobTitle || '' },\n              { 'Department': user.department || '' }\n            ]\n          }\n        }\n      }\n    });\n    console.log(`Creating ${user.displayName}: not found in Hudu`);\n  } else {\n    if (hasChanges(user, huduMatch)) {\n      output.push({\n        json: {\n          action: 'update',\n          id: huduMatch.id,\n          body: {\n            asset: {\n              name: user.displayName,\n              asset_layout_id: 2,\n              custom_fields: [\n                { 'Email Address': user.mail },\n                { 'Teams number': (user.businessPhones && user.businessPhones[0]) || '' },\n                { 'Cell Phone Number': user.mobilePhone || '' },\n                { 'Title': user.jobTitle || '' },\n                { 'Department': user.department || getFieldValue(huduMatch.fields, 'Department') || '' }\n              ]\n            }\n          }\n        }\n      });\n      console.log(`Updating ${user.displayName}: changes detected`);\n    } else {\n      console.log(`Skipping ${user.displayName}: no changes`);\n    }\n  }\n}\n\nreturn output;\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -96,
        1088
      ],
      "id": "2479bad6-5f9a-43e0-849f-4a813b2f83c5",
      "name": "Transform & Filter"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=HUDU IP/api/v1/companies/1/assets",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.body) }}\n\n",
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        336,
        992
      ],
      "id": "1dfcf58f-0437-4e3e-bd74-3bec5a147141",
      "name": "Create Users in Hudu"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "40e4ea7c-dbcf-4834-971c-f6cd0982bc6e",
              "leftValue": "={{ $json[\"action\"] }}",
              "rightValue": "create",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        96,
        1088
      ],
      "id": "0634ba8b-15a8-44c9-bd1c-914b4fb8b975",
      "name": "If"
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineByPosition",
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        -288,
        1088
      ],
      "id": "5bf70ed1-169a-4a0b-8d83-950596da2594",
      "name": "Merge"
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "=HUDU IP/api/v1/companies/1/assets/{{ $json.id }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.body) }}\n\n",
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "id": "da4e777c-346b-4a66-8247-4f006e6c17d5",
      "name": "Update asset",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        336,
        1200
      ]
    },
    {
      "parameters": {
        "jsCode": "// Aggregate all Hudu assets into one array\nconst allAssets = items.map(item => item.json);\nreturn [{ json: { assets: allAssets } }];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -544,
        1216
      ],
      "id": "0d33684a-9f25-4b0b-abeb-f2c86d99bfe2",
      "name": "Aggregate"
    },
    {
      "parameters": {
        "jsCode": "\nconst allAssets = [];\nlet page = 1;\n\nwhile (true) {\n  const response = await this.helpers.request({\n    method: 'GET',\n    uri: `HUDU IP/api/v1/assets?company_id=1&asset_layout_id=2&include=fields&page=${page}`,\n    headers: {\n      'accept': 'application/json',\n      'x-api-key': 'KEY'\n    },\n    json: true,\n    rejectUnauthorized: false\n  });\n\n  if (!response.assets || response.assets.length === 0) break;\n\n  allAssets.push(...response.assets);\n  page++;\n}\n\nreturn allAssets.map(asset => ({ json: asset }));\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -752,
        1216
      ],
      "id": "bc69a359-3501-446c-b136-68cb81a0c319",
      "name": "Pull all hudu users",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineByPosition",
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        -544,
        960
      ],
      "id": "056415aa-6c0e-48e2-a319-688646b392f3",
      "name": "Merge1"
    },
    {
      "parameters": {
        "url": "https://graph.microsoft.com/v1.0/groups/Group ID/members?$select=id,displayName,mail,userPrincipalName,jobTitle,mobilePhone,businessPhones,department",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        -752,
        880
      ],
      "id": "3cc5d2c3-0c10-4513-9f56-fdb783552011",
      "name": "Get distribution group member data - ASC"
    },
    {
      "parameters": {
        "url": "https://graph.microsoft.com/v1.0/groups/Group ID/members?$select=id,displayName,mail,userPrincipalName,jobTitle,mobilePhone,businessPhones,department",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        -752,
        1040
      ],
      "id": "3e5ed43e-9f95-406a-8ed5-6ed1561c7844",
      "name": "Get distribution group member data - PoA2"
    },
    {
      "parameters": {
        "maxItems": 5000
      },
      "type": "n8n-nodes-base.limit",
      "typeVersion": 1,
      "position": [
        -112,
        720
      ],
      "id": "36a43857-8b29-4dad-abd1-0a25e1e705ee",
      "name": "Limit"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://graph.microsoft.com/v1.0/$batch",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json }}",
        "options": {
          "pagination": {}
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        272,
        720
      ],
      "id": "6aaeaf42-6508-4347-abcd-9726c2edc53b",
      "name": "Get user group membership in batches"
    },
    {
      "parameters": {
        "jsCode": "\n// Function node (Run Once for All Items)\n// Takes incoming items with item.json.id and emits N items,\n// each containing a { requests: [...] } body of up to 20 sub-requests.\n\nconst CHUNK_SIZE = 3;\n\n// Helper: chunk an array into arrays of size n\nfunction chunk(arr, n) {\n  const out = [];\n  for (let i = 0; i < arr.length; i += n) {\n    out.push(arr.slice(i, i + n));\n  }\n  return out;\n}\n\n// Build the requests for each user item\nconst requests = items.map(item => {\n  const userId = item.json?.id;\n  if (!userId) {\n    throw new Error('Missing item.json.id for one of the inputs');\n  }\n\n  const url = `/users/${encodeURIComponent(userId)}/memberOf/microsoft.graph.group` +\n              `?$select=id,displayName,groupTypes,mailEnabled,securityEnabled,mail&$top=999`;\n\n  return {\n    id: userId,\n    method: 'GET',\n    url,\n  };\n});\n\n// Chunk into batches of ≤ 20\nconst batches = chunk(requests, CHUNK_SIZE);\n\n// Emit one item per batch, ready to feed into the HTTP Request node (POST /v1.0/$batch)\nreturn batches.map(reqs => ({ json: { requests: reqs } }));\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        80,
        720
      ],
      "id": "3472568b-c457-4251-9563-11b3c8365748",
      "name": "Batch requests",
      "executeOnce": true
    },
    {
      "parameters": {
        "fieldToSplitOut": "value",
        "options": {}
      },
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        -304,
        720
      ],
      "id": "9e3de02b-1b34-4668-ae4a-28d878ff5ca7",
      "name": "Extract user data"
    },
    {
      "parameters": {
        "jsCode": "\n// Function node (Run Once for All Items)\n// Input: multiple items, each with item.json.responses = [ ... ] from Graph $batch\n// Output: one item per sub-response (flattened across all batches)\n\nconst out = [];\n\nfor (const item of items) {\n  const batch = item.json;\n  const responses = batch?.responses;\n\n  if (!Array.isArray(responses)) {\n    const candidate =\n      batch?.body?.responses ||\n      batch?.data?.responses ||\n      batch?.value?.responses;\n\n    if (!Array.isArray(candidate)) {\n      continue;\n    }\n\n    // Use discovered array\n    for (const r of candidate) {\n      out.push({ json: r });\n    }\n    continue;\n  }\n\n  for (const r of responses) {\n    out.push({ json: r });\n  }\n}\n\nreturn out;\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        448,
        720
      ],
      "id": "8068fb84-00d5-413a-905a-bf807b38b2bc",
      "name": "Explode batches"
    },
    {
      "parameters": {
        "url": "HUDU IP/api/v1/lists/29",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "id": "4f639d6b-9b85-4a23-bb84-1e10b1421e4a",
      "name": "Get Hudu Distribution groups",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        272,
        560
      ],
      "executeOnce": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "oxyBWYYA4p8RmXAq",
          "name": "Hudu account header"
        }
      }
    },
    {
      "parameters": {
        "url": "HUDU IP/api/v1/lists/31",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "id": "40e70ed2-c070-4975-8ced-e4bdb73a52f8",
      "name": "Get Hudu Security group",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        16,
        384
      ],
      "executeOnce": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "oxyBWYYA4p8RmXAq",
          "name": "Hudu account header"
        }
      }
    },
    {
      "parameters": {
        "url": "HUDU IP/api/v1/lists/32",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "id": "021ef52a-7793-494d-920e-7e98c955c772",
      "name": "Get Hudu Collaboration groups",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -112,
        304
      ],
      "executeOnce": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "oxyBWYYA4p8RmXAq",
          "name": "Hudu account header"
        }
      }
    },
    {
      "parameters": {
        "url": "HUDU IP/api/v1/lists/30",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "id": "6f2e24b5-96d3-43db-b18e-3e6bd459264d",
      "name": "Mail-enabled security groups",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        144,
        480
      ],
      "executeOnce": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "oxyBWYYA4p8RmXAq",
          "name": "Hudu account header"
        }
      }
    },
    {
      "parameters": {
        "content": "## Create and update users\nIncludes user data: Name, email, phone number, title, department",
        "height": 416,
        "width": 928,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -336,
        960
      ],
      "id": "07a53d2a-48a9-4cde-99f7-3917b84a11a6",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "jsCode": "const EXPECTED_LIST_IDS = new Set([29, 30, 31, 32]);\n\n// If you want to use the group's mail address as the Hudu list item name,\n// set this to true. Otherwise, it uses displayName.\nconst USE_MAIL_AS_NAME = false;\n\nfunction normalizeName(s) {\n  return (s || '').trim().toLowerCase();\n}\n\nfunction pickItemNameFromGroup(g) {\n  if (USE_MAIL_AS_NAME) {\n    return (g.mail && String(g.mail).trim()) || (g.displayName && String(g.displayName).trim()) || '';\n  }\n  return (g.displayName && String(g.displayName).trim()) || '';\n}\n\n// Classification rules:\n// - Collaboration group: groupTypes includes \"Unified\"          -> listId 32\n// - Mail-enabled security group: mailEnabled && securityEnabled -> listId 30\n// - Distribution group: mailEnabled && !securityEnabled         -> listId 29\n// - Security group: !mailEnabled && securityEnabled             -> listId 31\n// - Fallback: Security group (31)\nfunction classifyGroup(g) {\n  const types = Array.isArray(g.groupTypes) ? g.groupTypes : [];\n  const isUnified = types.map(String).includes('Unified');\n  const mailEnabled = !!g.mailEnabled;\n  const securityEnabled = !!g.securityEnabled;\n\n  if (isUnified) return 32;\n  if (mailEnabled && securityEnabled) return 30;\n  if (mailEnabled && !securityEnabled) return 29;\n  if (!mailEnabled && securityEnabled) return 31;\n  return 31;\n}\n\n// 1) Capture existing Hudu list items (case-insensitive) and the EXACT list name from the Hudu responses\nconst existingByListId = new Map();         // Map<number, Set<string>>\nconst listNameByListId = new Map();         // Map<number, string>\n\nfor (const item of items) {\n  const j = item.json;\n\n  if (j && typeof j.id === 'number' && EXPECTED_LIST_IDS.has(j.id) && Array.isArray(j.list_items)) {\n    const listId = j.id;\n    const set = existingByListId.get(listId) || new Set();\n    for (const li of j.list_items) {\n      if (li && typeof li.name === 'string') set.add(normalizeName(li.name));\n    }\n    existingByListId.set(listId, set);\n\n    if (typeof j.name === 'string' && j.name.trim().length) {\n      listNameByListId.set(listId, j.name.trim());\n    }\n    continue;\n  }\n\n  // Hudu list snapshot (nested shape: { list: { id, name, list_items: [...] } })\n  if (j && j.list && typeof j.list.id === 'number' && EXPECTED_LIST_IDS.has(j.list.id) && Array.isArray(j.list.list_items)) {\n    const listId = j.list.id;\n    const set = existingByListId.get(listId) || new Set();\n    for (const li of j.list.list_items) {\n      if (li && typeof li.name === 'string') set.add(normalizeName(li.name));\n    }\n    existingByListId.set(listId, set);\n\n    if (typeof j.list.name === 'string' && j.list.name.trim().length) {\n      listNameByListId.set(listId, j.list.name.trim());\n    }\n    continue;\n  }\n}\n\n// 2) Flatten all Graph groups emitted by \"Explode batches\"\nconst incomingGroups = [];\nfor (const item of items) {\n  const j = item.json;\n  if (j && j.body && Array.isArray(j.body.value)) {\n    for (const g of j.body.value) {\n      if (!g) continue;\n      const name = pickItemNameFromGroup(g);\n      if (!name) continue; // skip nameless groups\n      incomingGroups.push(g);\n    }\n  }\n}\n\n// 3) De-dupe within the batch and bucket by listId\nconst candidatesByListId = new Map(); // Map<number, Map<normalizedName, originalName>>\nfor (const listId of EXPECTED_LIST_IDS) {\n  candidatesByListId.set(listId, new Map());\n}\n\nfor (const g of incomingGroups) {\n  const listId = classifyGroup(g);\n  const original = pickItemNameFromGroup(g);\n  if (!original) continue;\n\n  const norm = normalizeName(original);\n  const bucket = candidatesByListId.get(listId);\n  if (!bucket.has(norm)) {\n    bucket.set(norm, original);\n  }\n}\n\n// 4) Remove those already present in Hudu\nconst toCreateByListId = new Map();\nfor (const [listId, bucket] of candidatesByListId.entries()) {\n  const existing = existingByListId.get(listId) || new Set();\n  const names = [];\n  for (const [norm, original] of bucket.entries()) {\n    if (!existing.has(norm)) names.push(original);\n  }\n  toCreateByListId.set(listId, names);\n}\n\n// 5) Emit one item per list that has new entries, using the EXACT list name from Hudu response\nconst out = [];\nfor (const [listId, names] of toCreateByListId.entries()) {\n  if (!names.length) continue;\n\n  const listName = listNameByListId.get(listId);\n  if (!listName || !listName.trim().length) {\n    continue;\n  }\n\n  out.push({\n    json: {\n      listId,\n      listName,\n      payload: {\n        list: {\n          name: listName,\n          list_items_attributes: names.map(n => ({ name: n })),\n        },\n      },\n    },\n  });\n}\n\nreturn out;\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        864,
        368
      ],
      "id": "eee899f3-be00-48b1-b247-a5a0fc177c83",
      "name": "Extract and forward new group items",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "numberInputs": 5
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        704,
        320
      ],
      "id": "9d7626eb-70e4-4038-bae4-f95f34fa8de1",
      "name": "Wait for all"
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "=Hudu IP/api/v1/lists/{{ $json.listId }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.payload }}",
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        1024,
        368
      ],
      "id": "d1ca7c9d-6b0e-48ad-8844-f4ddb85ecff0",
      "name": "Create list item",
      "alwaysOutputData": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "oxyBWYYA4p8RmXAq",
          "name": "Hudu account header"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        1648,
        704
      ],
      "id": "9196965d-9dc0-4564-9731-13ac9852dc75",
      "name": "Wait for above"
    },
    {
      "parameters": {
        "content": "## Get updated lists",
        "height": 640,
        "width": 262,
        "color": 4
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1328,
        -64
      ],
      "id": "a7131a43-7e7e-4e07-b01c-c16c3bfe2dbc",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "content": "## Get MS Graph groups per user and Hudu list data\nCollects MS graph groups based on users in batches and explodes them to single items.",
        "height": 656,
        "width": 928,
        "color": 4
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -336,
        224
      ],
      "id": "f7a44948-f300-4e31-b091-d68b7da11d7b",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "content": "## Process and create new list items\nAdds new list items to hudu lists.",
        "height": 352,
        "width": 512,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        656,
        224
      ],
      "id": "2ffbe840-d7ed-4508-a36f-c6bedf358406",
      "name": "Sticky Note4"
    },
    {
      "parameters": {
        "content": "## Get all users from Hudu and relevant MS365 users\nMS365 users. In our case based on 2 distribution groups that holds all active employees.",
        "height": 656,
        "width": 432,
        "color": 4
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -832,
        720
      ],
      "id": "d16a91fa-a5ec-42ac-937d-ca6625b322a7",
      "name": "Sticky Note5"
    },
    {
      "parameters": {
        "url": "Hudu IP/api/v1/lists/29",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "id": "25a7e1ce-f668-44cd-ae48-bafc7deb3fdd",
      "name": "Get Hudu Distribution groups updated",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1408,
        0
      ],
      "executeOnce": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "oxyBWYYA4p8RmXAq",
          "name": "Hudu account header"
        }
      }
    },
    {
      "parameters": {
        "url": "Hudu IP/api/v1/lists/30",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "id": "e2bb0960-47a9-4e86-bc07-54104b761d1c",
      "name": "Mail-enabled security groups updated",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1408,
        144
      ],
      "executeOnce": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "oxyBWYYA4p8RmXAq",
          "name": "Hudu account header"
        }
      }
    },
    {
      "parameters": {
        "url": "Hudu IP/api/v1/lists/31",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "id": "acbfa052-a183-4747-9430-edb4bec5dcba",
      "name": "Get Hudu Security group updated",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1408,
        272
      ],
      "executeOnce": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "oxyBWYYA4p8RmXAq",
          "name": "Hudu account header"
        }
      }
    },
    {
      "parameters": {
        "url": "Hudu IP/api/v1/lists/32",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "id": "693ae990-b012-423d-b546-c3ac8b6ee527",
      "name": "Get Hudu Collaboration groups updated",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1408,
        400
      ],
      "executeOnce": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "oxyBWYYA4p8RmXAq",
          "name": "Hudu account header"
        }
      }
    },
    {
      "parameters": {
        "content": "## Add group list items to user\nAdds a list of all collaboration groups, security groups, mail-enabled security groups and distribution groups to the user asset.",
        "height": 976,
        "width": 2576,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -352,
        -80
      ],
      "id": "553688d2-975b-4cb6-b567-8cbd9ab3acd4",
      "name": "Sticky Note6"
    },
    {
      "parameters": {
        "mode": "combine",
        "fieldsToMatchString": "id",
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        848,
        704
      ],
      "id": "b04fc33a-cb65-4f44-a921-5cdebbc57ba0",
      "name": "Match ID to populate with user data"
    },
    {
      "parameters": {
        "mode": "combine",
        "advanced": true,
        "mergeByFields": {
          "values": [
            {
              "field1": "userPrincipalName",
              "field2": "fields[0].value"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        1040,
        704
      ],
      "id": "e6a230f6-def6-48f9-9a6a-37e983458dfb",
      "name": "Add hudu ID to object"
    },
    {
      "parameters": {
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "id",
              "renameField": true,
              "outputFieldName": "userID"
            },
            {
              "fieldToAggregate": "mail",
              "renameField": true,
              "outputFieldName": "userEmail"
            },
            {
              "fieldToAggregate": "body.value",
              "renameField": true,
              "outputFieldName": "memberOf"
            },
            {
              "fieldToAggregate": "fields",
              "renameField": true,
              "outputFieldName": "userFields"
            },
            {
              "fieldToAggregate": "displayName",
              "renameField": true,
              "outputFieldName": "userassetname"
            }
          ]
        },
        "options": {
          "keepMissing": true
        }
      },
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        1232,
        704
      ],
      "id": "9c25e623-2827-4c3c-8d0f-fadc5b0241e7",
      "name": "Clean up input"
    },
    {
      "parameters": {
        "jsCode": "\nconst COMPANY_ID = 1;\nconst ASSET_LAYOUT_ID = 2;\nconst USE_MAIL_AS_NAME = false;\n\nconst FIELD_LABELS = {\n  distribution: \"Distribution groups\",\n  collaboration: \"Collaboration groups\",\n  security: \"Security groups\",\n  mailEnabledSecurity: \"Mail-enabled security groups\",\n};\n\nconst LIST_ID_BY_KIND = {\n  distribution: 29,\n  mailEnabledSecurity: 30,\n  security: 31,\n  collaboration: 32,\n};\n\nfunction normalizeAdvanced(s) {\n  if (!s) return \"\";\n  // Normalize unicode, collapse spaces, and fix common double-encoded ampersands\n  let out = String(s).normalize(\"NFC\").replace(/\\s+/g, \" \").trim().toLowerCase();\n  out = out.replace(/&amp;amp;amp;/g, \"&\");\n  out = out.replace(/&amp;amp;/g, \"&\");\n  out = out.replace(/&amp;/g, \"&\");\n  return out;\n}\nfunction pickItemName(g) {\n  if (USE_MAIL_AS_NAME) {\n    return (g.mail && String(g.mail).trim()) || (g.displayName && String(g.displayName).trim()) || \"\";\n  }\n  return (g.displayName && String(g.displayName).trim()) || \"\";\n}\nfunction classifyGroup(g) {\n  const types = Array.isArray(g.groupTypes) ? g.groupTypes : [];\n  const isUnified = types.map(String).includes(\"Unified\");\n  const mailEnabled = !!g.mailEnabled;\n  const securityEnabled = !!g.securityEnabled;\n  if (isUnified) return \"collaboration\";\n  if (mailEnabled && securityEnabled) return \"mailEnabledSecurity\";\n  if (mailEnabled && !securityEnabled) return \"distribution\";\n  if (!mailEnabled && securityEnabled) return \"security\";\n  return \"security\";\n}\nfunction safeParseListIds(value) {\n  if (!value || typeof value !== \"string\") return [];\n  try {\n    const obj = JSON.parse(value);\n    if (obj && Array.isArray(obj.list_ids)) {\n      return obj.list_ids.map(n => Number(n)).filter(n => !Number.isNaN(n));\n    }\n  } catch (_) {}\n  return [];\n}\nfunction sameIdSet(a, b) {\n  const A = Array.isArray(a) ? a.slice().sort((x,y)=>x-y) : [];\n  const B = Array.isArray(b) ? b.slice().sort((x,y)=>x-y) : [];\n  if (A.length !== B.length) return false;\n  for (let i = 0; i < A.length; i++) if (A[i] !== B[i]) return false;\n  return true;\n}\nfunction sameNameSet(intendedMap, existingArr) {\n  const intendedNorms = Array.from(intendedMap.keys()).sort();\n  const existingNorms = (existingArr || []).map(normalizeAdvanced).sort();\n  if (intendedNorms.length !== existingNorms.length) return false;\n  for (let i = 0; i < intendedNorms.length; i++) {\n    if (intendedNorms[i] !== existingNorms[i]) return false;\n  }\n  return true;\n}\n\nconst nameToIdByListId = new Map(); // listId -> Map<normName, id>\nconst idToNameByListId  = new Map(); // listId -> Map<id, name>\nconst listNameById      = new Map(); // listId -> list name\n\nfor (const item of items) {\n  const j = item.json;\n  if (j && typeof j.id === \"number\" && Array.isArray(j.list_items) && [29,30,31,32].includes(j.id)) {\n    const listId = j.id;\n    const nameMap = new Map();\n    const idMap   = new Map();\n    for (const li of j.list_items) {\n      if (li && typeof li.id === \"number\" && typeof li.name === \"string\") {\n        nameMap.set(normalizeAdvanced(li.name), li.id);\n        idMap.set(li.id, li.name);\n      }\n    }\n    nameToIdByListId.set(listId, nameMap);\n    idToNameByListId.set(listId, idMap);\n    listNameById.set(listId, j.name || \"\");\n  }\n}\n\nconsole.log(\"Append lists loaded:\", {\n  listsLoaded: Array.from(nameToIdByListId.keys()).sort(),\n  countsByList: {\n    29: nameToIdByListId.get(29)?.size || 0,\n    30: nameToIdByListId.get(30)?.size || 0,\n    31: nameToIdByListId.get(31)?.size || 0,\n    32: nameToIdByListId.get(32)?.size || 0,\n  }\n});\n\nconst out = [];\n\nfor (const item of items) {\n  const j = item.json || {};\n  const hasUserId = Array.isArray(j.userID) || typeof j.userID === \"number\";\n  const hasMember = Array.isArray(j.memberOf);\n  const hasFields = Array.isArray(j.userFields);\n  if (!(hasUserId && hasMember && hasFields)) continue;\n\n  const assetId = Array.isArray(j.userID) ? Number(j.userID[0]) : Number(j.userID);\n  if (!assetId || Number.isNaN(assetId)) continue;\n\n  const userAssetName =\n    typeof j.userassetname === \"string\" ? j.userassetname :\n    (Array.isArray(j.userassetname) ? j.userassetname[0] : undefined);\n\n  if (!userAssetName || typeof userAssetName !== \"string\" || !userAssetName.trim().length) {\n    console.log(\"WARN: Missing userassetname for asset\", assetId, \"— update will be skipped.\");\n    continue;\n  }\n\n  const groups =\n    Array.isArray(j.memberOf[0]) && Array.isArray(j.memberOf[0]) && typeof j.memberOf[0][0] === \"object\"\n      ? j.memberOf[0]\n      : (Array.isArray(j.memberOf) ? j.memberOf : []);\n\n  const fields =\n    Array.isArray(j.userFields[0]) && typeof j.userFields[0][0] === \"object\"\n      ? j.userFields[0]\n      : (Array.isArray(j.userFields) ? j.userFields : []);\n\n  const intendedNamesByKind = { distribution:new Map(), collaboration:new Map(), security:new Map(), mailEnabledSecurity:new Map() };\n  const intendedIdsByKind   = { distribution:[], collaboration:[], security:[], mailEnabledSecurity:[] };\n\n  for (const g of groups) {\n    if (!g) continue;\n    const kind = classifyGroup(g);\n    const name = pickItemName(g);\n    if (!name) continue;\n    const norm = normalizeAdvanced(name);\n    if (!intendedNamesByKind[kind].has(norm)) intendedNamesByKind[kind].set(norm, name);\n\n    const listId = LIST_ID_BY_KIND[kind];\n    const id = nameToIdByListId.get(listId)?.get(norm);\n    if (typeof id === \"number\") intendedIdsByKind[kind].push(id);\n  }\n\n  function getExistingField(label) {\n    return fields.find(ff => ff && typeof ff.label === \"string\" && ff.label.trim() === label);\n  }\n  function getExistingIds(label) {\n    const f = getExistingField(label);\n    return f ? safeParseListIds(f.value) : [];\n  }\n\n  const existingIdsByKind = {\n    distribution: getExistingIds(FIELD_LABELS.distribution),\n    collaboration: getExistingIds(FIELD_LABELS.collaboration),\n    security: getExistingIds(FIELD_LABELS.security),\n    mailEnabledSecurity: getExistingIds(FIELD_LABELS.mailEnabledSecurity),\n  };\n\n  const changedFieldObjects = [];\n\n  for (const kind of Object.keys(FIELD_LABELS)) {\n    const listId = LIST_ID_BY_KIND[kind];\n    const nameMap = nameToIdByListId.get(listId) || new Map();\n\n    const intendedNamesArr = Array.from(intendedNamesByKind[kind].values());\n    const intendedIds      = intendedIdsByKind[kind];\n    const existingIds      = existingIdsByKind[kind];\n\n    const anyUnmapped = intendedNamesArr.some(nm => !nameMap.has(normalizeAdvanced(nm)));\n    const idsMatch    = sameIdSet(intendedIds, existingIds);\n\n    let needsUpdate   = anyUnmapped || !idsMatch;\n\n    if (kind === \"collaboration\") {\n      const appendLoadedCount = nameMap.size;\n      const existingNames = existingIds\n        .map(id => idToNameByListId.get(listId)?.get(id))\n        .filter(nm => typeof nm === \"string\");\n      const namesMatch = sameNameSet(intendedNamesByKind.collaboration, existingNames);\n\n      console.log(\"DEBUG Collaboration for user:\", assetId, {\n        listId,\n        listName: listNameById.get(listId),\n        appendLoadedCount,\n        intendedNames: intendedNamesArr,\n        intendedIdsSorted: intendedIds.slice().sort((a,b)=>a-b),\n        existingIdsSorted: existingIds.slice().sort((a,b)=>a-b),\n        existingNames,\n        anyUnmapped,\n        idsMatch,\n        namesMatch,\n        decisionBeforeFallback: needsUpdate\n      });\n\n      if (!idsMatch && namesMatch) {\n        needsUpdate = false;\n        console.log(\"DEBUG Collaboration fallback: names matched, IDs not — skipping update for collaboration.\");\n      }\n\n      // Guard: if Append lists has 0 items, avoid false positives\n      if (appendLoadedCount === 0) {\n        if (namesMatch) {\n          needsUpdate = false;\n          console.log(\"DEBUG Collaboration guard: appendLoadedCount=0 but names match — skipping update.\");\n        } else {\n          console.log(\"WARN Collaboration: appendLoadedCount=0 and names mismatch — updating with names. Ensure Append lists provides full list_items for list 32.\");\n        }\n      }\n    }\n\n    if (needsUpdate) {\n      const label = FIELD_LABELS[kind];\n      const obj = {};\n      obj[label] = intendedNamesArr;\n      changedFieldObjects.push(obj);\n    }\n  }\n\n  if (changedFieldObjects.length === 0) {\n    continue;\n  }\n\n  const body = {\n    asset: {\n      name: userAssetName,\n      asset_layout_id: ASSET_LAYOUT_ID,\n      custom_fields: changedFieldObjects\n    }\n  };\n\n  out.push({\n    json: {\n      companyId: COMPANY_ID,\n      assetId,\n      payload: body\n    }\n  });\n}\n\nreturn out;\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1840,
        704
      ],
      "id": "bef8de65-9cf8-4c02-a411-5854a97615b1",
      "name": "Processing"
    },
    {
      "parameters": {
        "numberInputs": 4
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        1696,
        176
      ],
      "id": "1f3436ff-6164-4020-a7ea-8d1c698b9cd3",
      "name": "Append lists"
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "=Hudu IP/api/v1/companies/1/assets/{{ $json.assetId }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.payload }}",
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "id": "7fcfc3bb-95ea-4705-b048-782b399d1219",
      "name": "Update user groups in Hudu",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2032,
        704
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "oxyBWYYA4p8RmXAq",
          "name": "Hudu account header"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Output: One item per index across all arrays\n// Combines serial and serial2 into 1 column\n\nconst input = items[0].json;\n\nconst keys = Object.keys(input).filter(k => k !== 'serial2');\n\n// Determine number of rows (based on longest array)\nconst rowCount = Math.max(...Object.values(input).map(arr => Array.isArray(arr) ? arr.length : 0));\n\nconst newItems = [];\nfor (let i = 0; i < rowCount; i++) {\n  const newItem = {};\n  let isEmptyRow = true;\n\n  keys.forEach(key => {\n    const arr = Array.isArray(input[key]) ? input[key] : [];\n    let cellValue = arr[i] !== undefined && arr[i] !== null ? arr[i] : '';\n\n    // Special rule: if serial is \"poasnmp\", replace with serial2[i]\n    if (key === 'serial' && cellValue === 'poasnmp') {\n      const serial2Arr = Array.isArray(input['serial2']) ? input['serial2'] : [];\n      cellValue = serial2Arr[i] !== undefined && serial2Arr[i] !== null ? serial2Arr[i] : '';\n    }\n\n    newItem[key] = cellValue;\n    if (cellValue) isEmptyRow = false;\n  });\n\n  // Skip if name equals \"UnknownLocation\"\n  if (newItem.name === 'UnknownLocation') {\n    continue;\n  }\n\n  if (!isEmptyRow) {\n    newItems.push({ json: newItem });\n  }\n}\n\nreturn newItems;\n\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1440,
        704
      ],
      "id": "104ab398-ef60-415a-a694-18dbd3998fff",
      "name": "split to items"
    },
    {
      "parameters": {
        "content": "## Process and update user's groups in Hudu",
        "height": 256,
        "width": 1456,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        752,
        624
      ],
      "id": "1943dcf9-78d8-464e-8e4d-f9be157458b5",
      "name": "Sticky Note8"
    }
  ],
  "pinData": {},
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get distribution group member data - ASC",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get distribution group member data - PoA2",
            "type": "main",
            "index": 0
          },
          {
            "node": "Pull all hudu users",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transform & Filter": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "node": "Create Users in Hudu",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update asset",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Transform & Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Pull all hudu users": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          },
          {
            "node": "Add hudu ID to object",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge1": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          },
          {
            "node": "Extract user data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get distribution group member data - ASC": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get distribution group member data - PoA2": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Limit": {
      "main": [
        [
          {
            "node": "Batch requests",
            "type": "main",
            "index": 0
          },
          {
            "node": "Match ID to populate with user data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Get user group membership in batches": {
      "main": [
        [
          {
            "node": "Explode batches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Batch requests": {
      "main": [
        [
          {
            "node": "Get user group membership in batches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract user data": {
      "main": [
        [
          {
            "node": "Limit",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Hudu Distribution groups",
            "type": "main",
            "index": 0
          },
          {
            "node": "Mail-enabled security groups",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Hudu Security group",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Hudu Collaboration groups",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Explode batches": {
      "main": [
        [
          {
            "node": "Wait for all",
            "type": "main",
            "index": 4
          },
          {
            "node": "Match ID to populate with user data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Hudu Distribution groups": {
      "main": [
        [
          {
            "node": "Wait for all",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Get Hudu Security group": {
      "main": [
        [
          {
            "node": "Wait for all",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Get Hudu Collaboration groups": {
      "main": [
        [
          {
            "node": "Wait for all",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mail-enabled security groups": {
      "main": [
        [
          {
            "node": "Wait for all",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Extract and forward new group items": {
      "main": [
        [
          {
            "node": "Create list item",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for all": {
      "main": [
        [
          {
            "node": "Extract and forward new group items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create list item": {
      "main": [
        [
          {
            "node": "Get Hudu Distribution groups updated",
            "type": "main",
            "index": 0
          },
          {
            "node": "Mail-enabled security groups updated",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Hudu Security group updated",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Hudu Collaboration groups updated",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for above": {
      "main": [
        [
          {
            "node": "Processing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Hudu Distribution groups updated": {
      "main": [
        [
          {
            "node": "Append lists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mail-enabled security groups updated": {
      "main": [
        [
          {
            "node": "Append lists",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Get Hudu Security group updated": {
      "main": [
        [
          {
            "node": "Append lists",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Get Hudu Collaboration groups updated": {
      "main": [
        [
          {
            "node": "Append lists",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Match ID to populate with user data": {
      "main": [
        [
          {
            "node": "Add hudu ID to object",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add hudu ID to object": {
      "main": [
        [
          {
            "node": "Clean up input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean up input": {
      "main": [
        [
          {
            "node": "split to items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Processing": {
      "main": [
        [
          {
            "node": "Update user groups in Hudu",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append lists": {
      "main": [
        [
          {
            "node": "Wait for above",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "split to items": {
      "main": [
        [
          {
            "node": "Wait for above",
            "type": "main",
            "index": 1
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "94b38a79-3d94-4a4b-b92f-791ad35ea92d",
  "meta": {
    "instanceId": "87f71bd5cac6e2ad059da8f22935450a5ddb0f09c07c6e8b70c2456b412db937"
  },
  "id": "JQEqWIvup4HvOasf",
  "tags": []
}