├── AddCrosIDfromSN.py ├── AddOrgUnit.py ├── AddPermissions.py ├── AppendUserData.py ├── BrowserExtensions.py ├── CSVKMD.py ├── CheckMembership.py ├── CheckOUGroupMembership.py ├── CollectAttendeesInfo.py ├── CombineCourseParticipants.py ├── CombineKeyValues.py ├── ConvertCSVtoJSON.py ├── ConvertGroupColsToJSON.py ├── ConvertGroupUsersToCanvas.py ├── ConvertGroupUsersToUserGroupParents.py ├── ConvertGroupUsersToUserGroups.py ├── CountCSVRows.py ├── CountGroupsByDomain.py ├── DeleteCalendarAttendees.py ├── DeleteDuplicateFiles.py ├── DeleteDuplicateRows.py ├── DeleteFutureEvents.py ├── DeleteOldContacts.py ├── DeleteProtectedRanges.py ├── ExchangeDomainSharedContacts.py ├── ExpandSharedDriveGroupMembers.py ├── ExtractProtectedRanges.py ├── FindCommonEmails.py ├── FindUserChanges.py ├── GetAllowFileDiscoveryDriveACLs.py ├── GetDailyMimeTypeCreations.py ├── GetDriveActivityEmailAddresses.py ├── GetEMCAliases.py ├── GetEmptyGroups.py ├── GetExternalShareCounts.py ├── GetFilePermissionsWithPaths.py ├── GetGroupTypeCounts.py ├── GetGroupsOwnedByUser.py ├── GetGroupsWithExternalMembers.py ├── GetGroupsWithMatchingMembers.py ├── GetGroupsWithOnlyExternalMembers.py ├── GetGuardianStudentEmails.py ├── GetLabelsCountSize.py ├── GetLicenseHolders.py ├── GetLinkSharedDriveACLs.py ├── GetLinkSharedTeamDriveACLs.py ├── GetMultipleParentsRoot.py ├── GetNonDomainDriveACLs.py ├── GetNonDomainFilterForwards.py ├── GetNonDomainTeamDriveACLs.py ├── GetNonSharedFiles.py ├── GetOrgUnitCrOSCounts.py ├── GetOrgUnitUserCounts.py ├── GetOrgUnitUserCrOSCounts.py ├── GetPermissionsByPath.py ├── GetSharedExternallyDriveACLs.py ├── GetSharedExternallyTeamDriveACLs.py ├── GetSharedFileDeletedPermissions.py ├── GetSharedFilePermissions.py ├── GetSharedFilePermissionsTypeRoleLists.py ├── GetSharedFiles.py ├── GetSharedOnlyExternallyDriveACLs.py ├── GetSharedWithAnyoneDriveACLs.py ├── GetSharedWithAnyoneTeamDriveACLs.py ├── GetSharedWithDomainDriveACLs.py ├── GetSharedWithDomainTeamDriveACLs.py ├── GetSharedWithGroupDriveACLs.py ├── GetSharedWithGroupTeamDriveACLs.py ├── GetSharedWithListOfDisabledUsersDriveACLs.py ├── GetSharedWithListOfDisabledUsersSharedDriveACLs.py ├── GetSharedWithListOfUsersDriveACLs.py ├── GetSharedWithListOfUsersTeamDriveACLs.py ├── GetSharedWithNonAccountUsersDriveACLs.py ├── GetSharedWithUserDriveACLs.py ├── GetSharedWithUserTeamDriveACLs.py ├── GetSuspendedUserSharedDriveACLs.py ├── GetTeamDriveACLsExpandGroups.py ├── GetTeamDriveCountsSize.py ├── GetTeamDriveDeletedPermissions.py ├── GetTeamDriveDeletedUsersACLs.py ├── GetTeamDriveFileACLs.py ├── GetTeamDriveFileCounts.py ├── GetTeamDriveFileLists.py ├── GetTeamDriveGuestMembers.py ├── GetTeamDriveLastModified.py ├── GetTeamDriveMembers.py ├── GetTeamDriveNameACLs.py ├── GetTeamDriveOrganizers.py ├── GetTeamDriveStorageInfo.py ├── GetTeamDriveSuspendedUsersACLs.py ├── GetTypeWithLinkDriveACLs.py ├── GetUserCCOrgs.py ├── GetUserGroupAccessCounts.py ├── GetUserNonOwnerDomainDriveACLs.py ├── GetUserNonOwnerDriveACLs.py ├── GetUserNonOwnerDrivePermissions.py ├── GetUserShareCounts.py ├── GetUsersGroupCounts.py ├── GetUsersNoGroups.py ├── MakeGroupMembersSyncs.py ├── MakeGroupMembersUpdates.py ├── MakeOneAttendeePerRowEvents.py ├── MakeOneItemPerRowACLs.py ├── MakeOneParentPerRow.py ├── MergeGroupInfoMembers.py ├── MergeSendasUsers.py ├── MergeUserData.py ├── PrintOrgUnitTree.py ├── README.md ├── SelectiveDelete.py ├── ShowDelegators.py ├── ShowGroupMemberTree.py ├── ShowNestedGroupTree.py ├── ShowUserNonOwnerDriveACLs.py ├── UpdateOwnerFromPermissions.py ├── UpdateVacationToHTML.py └── UpgradeWritersToContentManagers.py /AddCrosIDfromSN.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a CSV file containing CrOS device serial numbers and other data, create a new CSV file with the same columns 4 | # plus a deviceId column based on the serial number. The updated file can be used to more efficiently process large 5 | # numbers of CrOS devices. 6 | # 7 | # Old update method, requires an additional API call per device to convert the serial number to the device ID that the update requires 8 | # $ gam csv CrosData.csv gam update cros cros_sn "~serialNumber" ... 9 | # New update method 10 | # $ gam redirect csv ./CrosSNIDMap.csv print cros fields serialnumber 11 | # $ python3 AddCrosIDfromSN.py ./CrosSNIDMap.csv ./CrosData.csv ./CrosDataID.csv 12 | # An error message is generated for any serial number in CrosData.csv that is not in CrosSNIDMap.csv and the return code is 1 13 | # $ gam config_csv_input_row_filter "deviceId:regex:^.+$" csv CrosDataID.csv gam update cros "~deviceId" ... 14 | # 15 | # Customize: DATA_SN_HEADER 16 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 17 | # $ python -V or python3 -V 18 | # Python 3.x.y 19 | # Usage: 20 | # 1: Generate a list of CrOS deviceIds and serial numbers 21 | # gam print cros fields serialnumber > CrosSNIDMap.csv 22 | # 2: Generate an output CSV file with the same headers as CrosData.csv plus a header for the deviceId 23 | # $ python3 AddCrosIDfromSN.py ./CrosSNIDMap.csv ./CrosData.csv ./CrosDataID.csv 24 | """ 25 | 26 | import csv 27 | import sys 28 | 29 | # Do not change these values 30 | CROS_SN_HEADER = 'serialNumber' 31 | CROS_DEVICEID_HEADER = 'deviceId' 32 | 33 | # Indicate the header in CrosData.csv that contains the serial number 34 | DATA_SN_HEADER = 'serialNumber' 35 | 36 | QUOTE_CHAR = '"' # Adjust as needed 37 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 38 | 39 | crosSNIDMap = {} 40 | crosSNIDMapFileName = sys.argv[1] 41 | inputFile = open(crosSNIDMapFileName, 'r', encoding='utf-8') 42 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 43 | crosSNIDMap[row[CROS_SN_HEADER].upper()] = row[CROS_DEVICEID_HEADER] 44 | inputFile.close() 45 | 46 | inputFileName = sys.argv[2] 47 | inputFile = open(inputFileName, 'r', encoding='utf-8') 48 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 49 | 50 | outputFieldNames = inputCSV.fieldnames[:] 51 | if DATA_SN_HEADER not in outputFieldNames: 52 | sys.stderr.write(f'Error: field {DATA_SN_HEADER} is not in Data file {inputFileName} field names: {",".join(outputFieldNames)}\n') 53 | sys.exit(3) 54 | if CROS_DEVICEID_HEADER not in outputFieldNames: 55 | index = outputFieldNames.index(DATA_SN_HEADER) 56 | outputFieldNames.insert(index+1, CROS_DEVICEID_HEADER) 57 | 58 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 59 | outputCSV = csv.DictWriter(outputFile, outputFieldNames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 60 | outputCSV.writeheader() 61 | 62 | sysRC = 0 63 | for row in inputCSV: 64 | deviceId = crosSNIDMap.get(row[DATA_SN_HEADER].upper(), '') 65 | if deviceId: 66 | row[CROS_DEVICEID_HEADER] = deviceId 67 | else: 68 | sys.stderr.write(f'Error: Serial number {row[DATA_SN_HEADER]} is not in Serial Number/DeviceID file {crosSNIDMapFileName}\n') 69 | sysRC = 1 70 | outputCSV.writerow(row) 71 | 72 | inputFile.close() 73 | outputFile.close() 74 | sys.exit(sysRC) 75 | -------------------------------------------------------------------------------- /AddOrgUnit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a CSV file, look up user email addresses and add Org Unit information 4 | # Customize: DATA_EMAIL_HEADER, DATA_ORGUNIT_HEADER, USER_EMAIL_HEADER, USER_ORGUNIT_HEADER, UNKNOWN_ORGUNIT 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Generate a list of users and their Org Units 10 | # $ gam redirect csv ./Users.csv print users fields primaryemail,ou 11 | # 2: Generate some data 12 | # $ gam redirect csv ./Data.csv ... 13 | # 3: From those two files, generate an output CSV file with the same headers as Data.csv plus a header for the users's Org Unit 14 | # $ python3 AddOrgUnit.py ./Data.csv ./Users.csv ./DataWithOrgUnit.csv 15 | """ 16 | 17 | import csv 18 | import sys 19 | 20 | # You have to indicate the header in Data.csv that contains the user email addresses 21 | # and the desired Org Unit header in DataWithOrgUnit.csv 22 | # Common values are: 23 | # report login - actor.email, actor.orgUnitPath 24 | # report user - email, orgUnitPath 25 | DATA_EMAIL_HEADER = 'email' 26 | DATA_ORGUNIT_HEADER = 'orgUnitPath' 27 | 28 | # Email header in Users.csv CSV file 29 | USER_EMAIL_HEADER = 'primaryEmail' 30 | 31 | # OrgUnit header in Users.csv CSV file 32 | USER_ORGUNIT_HEADER = 'orgUnitPath' 33 | 34 | # Unkown Org Unit value 35 | UNKNOWN_ORGUNIT = 'Unknown' 36 | 37 | QUOTE_CHAR = '"' # Adjust as needed 38 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 39 | 40 | userOrgUnits = {} 41 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 42 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 43 | userOrgUnits[row[USER_EMAIL_HEADER]] = row[USER_ORGUNIT_HEADER] 44 | inputFile.close() 45 | 46 | if sys.argv[1] != '-': 47 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 48 | else: 49 | inputFile = sys.stdin 50 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 51 | 52 | outputFieldNames = inputCSV.fieldnames[:] 53 | if outputFieldNames is None: 54 | sys.stderr.write(f'Error: no headers in Data file {sys.argv[1]}\n') 55 | sys.exit(2) 56 | if DATA_EMAIL_HEADER not in outputFieldNames: 57 | sys.stderr.write(f'Error: field {DATA_EMAIL_HEADER} is not in Data file {sys.argv[1]} field names: {",".join(outputFieldNames)}\n') 58 | sys.exit(3) 59 | index = outputFieldNames.index(DATA_EMAIL_HEADER) 60 | outputFieldNames.insert(index+1, DATA_ORGUNIT_HEADER) 61 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 62 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 63 | else: 64 | outputFile = sys.stdout 65 | outputCSV = csv.DictWriter(outputFile, outputFieldNames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 66 | outputCSV.writeheader() 67 | 68 | for row in inputCSV: 69 | row[DATA_ORGUNIT_HEADER] = userOrgUnits.get(row[DATA_EMAIL_HEADER], UNKNOWN_ORGUNIT) 70 | outputCSV.writerow(row) 71 | 72 | if inputFile != sys.stdin: 73 | inputFile.close() 74 | if outputFile != sys.stdout: 75 | outputFile.close() 76 | -------------------------------------------------------------------------------- /AddPermissions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Add to a list of files/folders 4 | # Definitions: 5 | # :: =commenter|editor|organizer|owner|reader|writer 6 | # ::= anyone|anyonewithlink|user:|group:|domain:|domainwithlink: 7 | # ::= ; 8 | # ::= "(, print filelist [anyowner|(showownedby any|me|others)] 15 | # [query ] [fullquery ] [select |orphans] [depth ] [showparent] 16 | # For a full description of print filelist, see: https://github.com/taers232c/GAMADV-XTD/wiki/Users-Drive-Files 17 | # $ gam redirect csv ./filelist.csv user testuser@domain.com print filelist id ... 18 | # 2: From that list of files, output a CSV file with headers "Owner,driveFileId,permissions" 19 | # that lists the driveFileIds and permissions to be added 20 | # $ python3 AddPermissions.py filelist.csv addperms.csv '' 21 | # 3: Add the ACLs 22 | # Parallel, faster: 23 | # $ gam csv addperms.csv gam user "~Owner" add permissions "~driveFileId" "~permissions" 24 | # Serial, cleaner output: 25 | # $ gam csvkmd users addperms.csv keyfield Owner subkeyfield driveFileId datafield permissions delimiter "," add permissions csvsubkey driveFileId csvdata permissions 26 | """ 27 | 28 | import csv 29 | import sys 30 | 31 | QUOTE_CHAR = '"' # Adjust as needed 32 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 33 | 34 | DRIVEFILE_ACL_ROLES = ['commenter', 'editor', 'owner', 'read', 'reader', 'writer',] 35 | DRIVEFILE_ACL_PERMISSION_TYPES = ['anyone', 'anyonewithlink', 'domain', 'domainwithlink', 'group', 'user',] 36 | 37 | if len(sys.argv) < 4: 38 | sys.stderr.write('ERROR: "" not specified\n') 39 | sys.exit(1) 40 | permissions = sys.argv[3] 41 | errors = 0 42 | for permission in permissions.replace(',', ' ').split(): 43 | try: 44 | scope, role = permission.split(';', 1) 45 | if scope.find(':') != -1: 46 | permType, value = scope.split(':', 1) 47 | else: 48 | permType = scope 49 | if permType not in DRIVEFILE_ACL_PERMISSION_TYPES: 50 | sys.stderr.write(f'ERROR: Type ({permType}) must be in list ({",".join(DRIVEFILE_ACL_PERMISSION_TYPES)}), permission ({permission})\n') 51 | errors += 1 52 | if role not in DRIVEFILE_ACL_ROLES: 53 | sys.stderr.write(f'ERROR: Role ({role}) must be in list ({",".join(DRIVEFILE_ACL_ROLES)}), permission ({permission})\n') 54 | errors += 1 55 | except ValueError: 56 | sys.stderr.write(f'ERROR: Permisson must be (scope;role), permission ({permission})\n') 57 | errors += 1 58 | if errors: 59 | sys.exit(1) 60 | 61 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 62 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 63 | else: 64 | outputFile = sys.stdout 65 | outputCSV = csv.DictWriter(outputFile, ['Owner', 'driveFileId', 'permissions'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 66 | outputCSV.writeheader() 67 | 68 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 69 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 70 | else: 71 | inputFile = sys.stdin 72 | 73 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 74 | outputCSV.writerow({'Owner': row['Owner'], 75 | 'driveFileId': row['id'], 76 | 'permissions': permissions}) 77 | 78 | if inputFile != sys.stdin: 79 | inputFile.close() 80 | if outputFile != sys.stdout: 81 | outputFile.close() 82 | -------------------------------------------------------------------------------- /CheckMembership.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a list of group members and a list of users, produce a CSV file that lists the users that are not group members 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get group members 9 | # $ gam redirect csv ./Members.csv group print 10 | # 2: Get users; replace as desired, e.g. ou /Teachers 11 | # $ gam redirect csv ./Users.csv print 12 | # 3: Make a CSV file NonMembers.csv that lists the users that are not group members 13 | # $ python3 CheckMembership.py Members.csv Users.csv NonMembers.csv 14 | """ 15 | 16 | import csv 17 | import sys 18 | 19 | # Default is that Members.csv does not have a header row; the following sets a field name 20 | MembersEmailField = 'primaryEmail' 21 | MembersFieldNames = [MembersEmailField] 22 | # If Members.csv does have a header row, edit the following line and remove the # from both lines 23 | #MembersEmailField = 'primaryEmail' 24 | #MembersFieldNames = None 25 | 26 | # Default is that Users.csv does not have a header row; the following sets a field name 27 | UsersEmailField = 'primaryEmail' 28 | UsersFieldNames = [UsersEmailField] 29 | # If Users.csv does have a header row, edit the following line and remove the # from both lines 30 | #UsersEmailField = 'primaryEmail' 31 | #UsersFieldNames = None 32 | 33 | # Edit the following row if you want a different header for NonMembers.csv 34 | NonMembersEmailField = 'primaryEmail' 35 | NonMembersFieldNames = [NonMembersEmailField] 36 | 37 | QUOTE_CHAR = '"' # Adjust as needed 38 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 39 | 40 | MembersSet = set() 41 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 42 | inputCSV = csv.DictReader(inputFile, fieldnames=MembersFieldNames, quotechar=QUOTE_CHAR) 43 | for row in inputCSV: 44 | MembersSet.add(row[MembersEmailField]) 45 | inputFile.close() 46 | 47 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 48 | inputCSV = csv.DictReader(inputFile, fieldnames=UsersFieldNames, quotechar=QUOTE_CHAR) 49 | 50 | if (len(sys.argv) > 3) and (sys.argv[2] != '-'): 51 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 52 | else: 53 | outputFile = sys.stdout 54 | outputCSV = csv.DictWriter(outputFile, NonMembersFieldNames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 55 | outputCSV.writeheader() 56 | 57 | for row in inputCSV: 58 | if row[UsersEmailField] not in MembersSet: 59 | outputCSV.writerow({NonMembersEmailField: row[UsersEmailField]}) 60 | 61 | inputFile.close() 62 | if outputFile != sys.stdout: 63 | outputFile.close() 64 | -------------------------------------------------------------------------------- /CheckOUGroupMembership.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Reconcile OU and Group membership. 4 | # Customize: set OU_HEADER if OUMembers.csv has a header row 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get OU members 10 | # $ gam redirect csv ./OUMembers.csv ou /Path/To/OU print users 11 | # 2: Get Group members; you can select more that one group if desired 12 | # $ gam redirect csv ./GroupMembers.csv print group-members select group1@domain.com,group2@domain.com,... recursive noduplicates 13 | # 3: From those two lists, output one CSV file showing the OU members that are not in the groups and 14 | # and another CSV file showing the group members that are not in the OU 15 | # $ python3 CheckOUGroupMembership.py ./OUMembers.csv ./GroupMembers.csv ./OUNotGroupMembers.csv ./GroupNotOU.csv 16 | """ 17 | 18 | import csv 19 | import sys 20 | 21 | QUOTE_CHAR = '"' # Adjust as needed 22 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 23 | 24 | OU_HEADER = '' 25 | 26 | OUMembers = set() 27 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 28 | if OU_HEADER: 29 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 30 | OUMembers.add(row[OU_HEADER]) 31 | else: 32 | for row in csv.reader(inputFile, quotechar=QUOTE_CHAR): 33 | OUMembers.add(row[0]) 34 | inputFile.close() 35 | 36 | GroupMembers = set() 37 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 38 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 39 | GroupMembers.add(row['email']) 40 | inputFile.close() 41 | 42 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 43 | outputCSV = csv.DictWriter(outputFile, ['primaryEmail'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 44 | outputCSV.writeheader() 45 | for email in OUMembers-GroupMembers: 46 | outputCSV.writerow({'primaryEmail': email}) 47 | outputFile.close() 48 | 49 | outputFile = open(sys.argv[4], 'w', encoding='utf-8', newline='') 50 | outputCSV = csv.DictWriter(outputFile, ['primaryEmail'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 51 | outputCSV.writeheader() 52 | for email in GroupMembers-OUMembers: 53 | outputCSV.writerow({'primaryEmail': email}) 54 | outputFile.close() 55 | -------------------------------------------------------------------------------- /CollectAttendeesInfo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Convert output from print events to get unique list of attendee emal/names; you can filter for specific attendees. 4 | # Customize: Set ATTENDEE_LIST, DOMAIN_LIST, ATTENDEE_PATTERN, SHOW_ATENDEES_WITH_NO_NAME 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Generate a list of events that contain the attendees you wish to save 10 | # See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Calendars-Events#display-calendar-events 11 | # For example, to get events with any attendees from the domain bar.com; omit 12 | # $ gam redirect csv ./AllEvents.csv user user@domain.com print events matchfield attendeespattern "^.*@bar.com$" fields attendees 13 | # 2: From that list of files, output a CSV file with columns email,name that lists one attendee per row 14 | # $ python3 CollectAttendeesInfo.py AllEvents.csv AttendeesInfo.csv 15 | """ 16 | 17 | import csv 18 | import re 19 | import sys 20 | 21 | # Specify specific attendees(s), e.g., ATTENDEE_LIST = ['user1@domain.com'] ATTENDEE_LIST = ['user1@domain.com', 'user2@domain.com'] 22 | # The list should be empty if you're only specifiying domains in DOMAIN_LIST, e.g. ATTENDEE_LIST = [] 23 | ATTENDEE_LIST = [] 24 | 25 | # Specify specific domain(s) if you want all attendees in the domain, e.g., DOMAIN_LIST = ['domain.com'] DOMAIN_LIST = ['domain1.com', 'domain2.com'] 26 | # The list should be empty if you're only specifiying attendees in ATTENDEE_LIST, e.g. DOMAIN__LIST = [] 27 | DOMAIN_LIST = [] 28 | 29 | # Specify attendees that match a pattern 30 | # None: ATTENDEE_PATTERN = None 31 | # Pattern: ATTENDEE_PATTERN = re.compile(r'^.*@bar.com$') 32 | ATTENDEE_PATTERN = None 33 | 34 | # Should attendees with no name be shown 35 | # False: attendee with no name will not be shown 36 | # True: attendee with no name will have name set to email address 37 | SHOW_ATTENDEES_WITH_NO_NAME = True 38 | 39 | QUOTE_CHAR = '"' # Adjust as needed 40 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 41 | 42 | ATTENDEES_N_EMAIL = re.compile(r"attendees.(\d+).email") 43 | 44 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 45 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 46 | else: 47 | outputFile = sys.stdout 48 | 49 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 50 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 51 | else: 52 | inputFile = sys.stdin 53 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 54 | 55 | outputCSV = csv.DictWriter(outputFile, ['email', 'name'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 56 | outputCSV.writeheader() 57 | 58 | attendees = set() 59 | for row in inputCSV: 60 | for k, v in iter(row.items()): 61 | mg = ATTENDEES_N_EMAIL.match(k) 62 | if mg: 63 | attendees_N = mg.group(1) 64 | if not v: 65 | continue 66 | _, domain = v.split('@') 67 | if ((v not in attendees) and 68 | (not DOMAIN_LIST or domain in DOMAIN_LIST) and 69 | (not ATTENDEE_LIST or v in ATTENDEE_LIST) and 70 | (not ATTENDEE_PATTERN or ATTENDEE_PATTERN.match(v))): 71 | attendees.add(v) 72 | name = row.get(f'attendees.{attendees_N}.displayName') 73 | if not name: 74 | if not SHOW_ATTENDEES_WITH_NO_NAME: 75 | continue 76 | name = v 77 | outputCSV.writerow({'email': v, 'name': name}) 78 | 79 | if inputFile != sys.stdin: 80 | inputFile.close() 81 | if outputFile != sys.stdout: 82 | outputFile.close() 83 | -------------------------------------------------------------------------------- /CombineCourseParticipants.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Convert a CSV file showing course participants one per row to single row per course 4 | # Customize: Set DELIMITER to the single character that will separate participants 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get course participants 10 | # $ gam config csv_output_header_filter "courseId,courseName,userRole,profile.emailAddress" redirect csv ./CourseParticipants.csv print course-participants 11 | # See: https://github.com/taers232c/GAMADV-XTD3/wiki/Classroom-Membership#display-course-membership 12 | # 2: From that list of group members, output a CSV file with headers primaryEmail,GroupsCount,Groups that shows the groups for each user 13 | # $ python3 CombineCourseParticipants.py ./CourseParticipants.csv ./CombinedCourseParticipants.csv 14 | 15 | """ 16 | 17 | import csv 18 | import sys 19 | 20 | DELIMITER = ' ' 21 | QUOTE_CHAR = '"' # Adjust as needed 22 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 23 | 24 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 25 | outputCSV = csv.DictWriter(outputFile, ['courseId', 'courseName', 'Teacher', 'Student'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 26 | outputCSV.writeheader() 27 | 28 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 29 | 30 | CourseParticipants = {} 31 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 32 | courseId = row['courseId'] 33 | CourseParticipants.setdefault(courseId, {'courseName': row['courseName'], 'Teacher': [], 'Student': []}) 34 | CourseParticipants[courseId][row['userRole'].capitalize()].append(row['profile.emailAddress']) 35 | 36 | for courseId, courseInfo in sorted(iter(CourseParticipants.items())): 37 | outputCSV.writerow({'courseId': courseId, 38 | 'courseName': courseInfo['courseName'], 39 | 'Teacher': DELIMITER.join(courseInfo['Teacher']), 40 | 'Student': DELIMITER.join(courseInfo['Student'])}) 41 | 42 | inputFile.close() 43 | outputFile.close() 44 | -------------------------------------------------------------------------------- /CombineKeyValues.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Make a CSV file that merges all values for a given key. 4 | # Customize: Set KEY_FIELD and VALUE_FIELD 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Generate CSV file KeySingleValue.csv with columns KEY_FIELD and VALUE_FIELD, one key and value per row 10 | # 2: Output an updated CSV file with columns KEY_FIELD and VALUE_FIELD containing a row per key 11 | # with its merged (space separated) values 12 | # $ python3 CombineKeyValues.py ./KeyValue.csv ./KeyMergedValues.csv 13 | """ 14 | 15 | import csv 16 | import sys 17 | 18 | # Name of key field 19 | KEY_FIELD = 'key' 20 | # Name of value field 21 | VALUE_FIELD = 'value' 22 | 23 | QUOTE_CHAR = '"' # Adjust as needed 24 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 25 | 26 | keyValues = {} 27 | 28 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 29 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 30 | else: 31 | inputFile = sys.stdin 32 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 33 | key = row[KEY_FIELD] 34 | keyValues.setdefault(key, set()) 35 | keyValues[key].add(row[VALUE_FIELD]) 36 | 37 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 38 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 39 | else: 40 | outputFile = sys.stdout 41 | 42 | outputCSV = csv.DictWriter(outputFile, [KEY_FIELD, VALUE_FIELD], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 43 | outputCSV.writeheader() 44 | 45 | for key, values in sorted(iter(keyValues.items())): 46 | outputCSV.writerow({KEY_FIELD: key, VALUE_FIELD: ' '.join(values)}) 47 | 48 | inputFile.close() 49 | if outputFile != sys.stdout: 50 | outputFile.close() 51 | -------------------------------------------------------------------------------- /ConvertCSVtoJSON.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a CSV file with JSON columns, produce a file with no header row (optional) and only JSON data. 4 | # Customize: Set INPUT_QUOTE_CHAR, OUTPUT_QUOTE_CHAR, LINE_TERMINATOR, MERGE_NON_JSON_DATA, NON_JSON_DATA_SKIP_FIELDS, MAKE_LIST, HEADER_ROW 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Produce a CSV file Input.csv 10 | # 2: Produce a JSON file Output.json 11 | # $ python3 ./ConvertCSVtoJSON.py Input.csv Output.json 12 | """ 13 | 14 | import csv 15 | import json 16 | import sys 17 | 18 | INPUT_QUOTE_CHAR = "'" # Adjust as needed 19 | OUTPUT_QUOTE_CHAR = "'" # Adjust as desired; can be empty "" 20 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 21 | 22 | MERGE_NON_JSON_DATA = False # True - Merge data from non-JSON columns; False to omit data from non-JSON columns 23 | NON_JSON_DATA_SKIP_FIELDS = [] # List of non-JSON columns that should not be merged, e.g, ['a',] ['a', 'b'] 24 | MAKE_LIST = False 25 | HEADER_ROW = True # True - Header row JSON; False - no header row. Only applies when MAKE_LIST = False 26 | # When MAKE_LIST = True: output is 27 | # [ 28 | # {"key": "value", "key": "value"}, 29 | # {"key": "value", "key": "value"}, 30 | # {"key": "value", "key": "value"} 31 | # ] 32 | # When MAKE_LIST = False, HEADER_ROW = False: output is 33 | # '{"key": "value", "key": "value"}' 34 | # '{"key": "value", "key": "value"}' 35 | # When MAKE_LIST = False, HEADER_ROW = True: output is 36 | # JSON 37 | # '{"key": "value", "key": "value"}' 38 | # '{"key": "value", "key": "value"}' 39 | 40 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 41 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 42 | else: 43 | outputFile = sys.stdout 44 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 45 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 46 | else: 47 | inputFile = sys.stdin 48 | inputCSV = csv.DictReader(inputFile, quotechar=INPUT_QUOTE_CHAR) 49 | plainFields = [] 50 | jsonFields = [] 51 | for fieldName in inputCSV.fieldnames: 52 | if fieldName.startswith('JSON'): 53 | jsonFields.append(fieldName) 54 | elif MERGE_NON_JSON_DATA and fieldName not in NON_JSON_DATA_SKIP_FIELDS: 55 | plainFields.append(fieldName) 56 | jsonRows = [] 57 | for row in inputCSV: 58 | jsonRow = {} 59 | for k in plainFields: 60 | jsonRow[k] = row[k] 61 | for k in jsonFields: 62 | jsonRow.update(json.loads(row[k])) 63 | jsonRows.append(jsonRow) 64 | if MAKE_LIST: 65 | outputFile.write('['+LINE_TERMINATOR) 66 | lineTerminator = ','+LINE_TERMINATOR 67 | for jsonRow in jsonRows: 68 | outputFile.write(' '+json.dumps(jsonRow, ensure_ascii=False, sort_keys=True)+lineTerminator) 69 | outputFile.seek(outputFile.tell()-(1+len(LINE_TERMINATOR)), 0) 70 | outputFile.write(LINE_TERMINATOR+']'+LINE_TERMINATOR) 71 | else: 72 | if HEADER_ROW: 73 | outputFile.write('JSON'+LINE_TERMINATOR) 74 | for jsonRow in jsonRows: 75 | outputFile.write(OUTPUT_QUOTE_CHAR+json.dumps(jsonRow, ensure_ascii=False, sort_keys=True)+OUTPUT_QUOTE_CHAR+LINE_TERMINATOR) 76 | if inputFile != sys.stdin: 77 | inputFile.close() 78 | if outputFile != sys.stdout: 79 | outputFile.close() 80 | -------------------------------------------------------------------------------- /ConvertGroupColsToJSON.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Convert a CSV file showing group settigs to one with the settings in JSON format 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # $ python ConvertGroupColsToJSON.py ./Groups.csv ./GroupsJSON.csv 9 | """ 10 | 11 | import csv 12 | import json 13 | import sys 14 | 15 | QUOTE_CHAR = '"' # Adjust as needed 16 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 17 | 18 | GROUP_JSON_SKIP_FIELDS = [ 19 | 'adminCreated', 20 | 'directMembersCount', 21 | 'members', 22 | 'aliases', 23 | 'nonEditableAliases', 24 | 'kind', 25 | ] 26 | 27 | # Set INCLUDE_XXX_ATTRIBUTES = True 28 | # to include deprecated or merged attributes 29 | 30 | INCLUDE_DEPRECATED_ATTRIBUTES = False 31 | GROUP_DEPRECATED_ATTRIBUTES = [ 32 | 'allowGoogleCommunication', 33 | 'favoriteRepliesOnTop', 34 | 'maxMessageBytes', 35 | 'messageDisplayFont', 36 | 'whoCanAddReferences', 37 | 'whoCanMarkFavoriteReplyOnOwnTopic', 38 | ] 39 | 40 | INCLUDE_DISCOVER_ATTRIBUTES = False 41 | GROUP_DISCOVER_ATTRIBUTES = [ 42 | 'showInGroupDirectory', 43 | ] 44 | 45 | INCLUDE_ASSIST_CONTENT_ATTRIBUTES = False 46 | GROUP_ASSIST_CONTENT_ATTRIBUTES = [ 47 | 'whoCanAssignTopics', 48 | 'whoCanEnterFreeFormTags', 49 | 'whoCanHideAbuse', 50 | 'whoCanMakeTopicsSticky', 51 | 'whoCanMarkDuplicate', 52 | 'whoCanMarkFavoriteReplyOnAnyTopic', 53 | 'whoCanMarkNoResponseNeeded', 54 | 'whoCanModifyTagsAndCategories', 55 | 'whoCanTakeTopics', 56 | 'whoCanUnassignTopic', 57 | 'whoCanUnmarkFavoriteReplyOnAnyTopic', 58 | ] 59 | 60 | INCLUDE_MODERATE_CONTENT_ATTRIBUTES = False 61 | GROUP_MODERATE_CONTENT_ATTRIBUTES = [ 62 | 'whoCanApproveMessages', 63 | 'whoCanDeleteAnyPost', 64 | 'whoCanDeleteTopics', 65 | 'whoCanLockTopics', 66 | 'whoCanMoveTopicsIn', 67 | 'whoCanMoveTopicsOut', 68 | 'whoCanPostAnnouncements', 69 | ] 70 | 71 | INCLUDE_MODERATE_MEMBERS_ATTRIBUTES = False 72 | GROUP_MODERATE_MEMBERS_ATTRIBUTES = [ 73 | 'whoCanAdd', 74 | 'whoCanApproveMembers', 75 | 'whoCanBanUsers', 76 | 'whoCanInvite', 77 | 'whoCanModifyMembers', 78 | ] 79 | 80 | def includeFields(include, fields): 81 | if not include: 82 | for ifield in fields: 83 | row.pop(ifield, None) 84 | 85 | with open(sys.argv[1], 'r', encoding='utf-8') as inputFile: 86 | with open(sys.argv[2], 'w', encoding='utf-8', newline='') as outputFile: 87 | outputCSV = csv.DictWriter(outputFile, ['email', 'id', 'JSON-settings'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 88 | outputCSV.writeheader() 89 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 90 | groupEmail = row.pop('email') 91 | groupId = row.pop('id', '') 92 | groupName = row.pop('name', '') 93 | groupDescription = row.pop('description', '') 94 | includeFields(False, GROUP_JSON_SKIP_FIELDS) 95 | includeFields(INCLUDE_DEPRECATED_ATTRIBUTES, GROUP_DEPRECATED_ATTRIBUTES) 96 | includeFields(INCLUDE_DISCOVER_ATTRIBUTES, GROUP_DISCOVER_ATTRIBUTES) 97 | includeFields(INCLUDE_ASSIST_CONTENT_ATTRIBUTES, GROUP_ASSIST_CONTENT_ATTRIBUTES) 98 | includeFields(INCLUDE_MODERATE_CONTENT_ATTRIBUTES, GROUP_MODERATE_CONTENT_ATTRIBUTES) 99 | includeFields(INCLUDE_MODERATE_MEMBERS_ATTRIBUTES, GROUP_MODERATE_MEMBERS_ATTRIBUTES) 100 | for field, value in iter(row.items()): 101 | if value == 'TRUE': 102 | row[field] = 'true' 103 | elif value == 'FALSE': 104 | row[field] = 'false' 105 | outputCSV.writerow({'email': groupEmail, 106 | 'id': groupId, 107 | 'JSON-settings': json.dumps(row, ensure_ascii=False, sort_keys=True)}) 108 | -------------------------------------------------------------------------------- /ConvertGroupUsersToCanvas.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Convert a CSV file showing group members in JSON format to a JSON file importable by Canvas 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get group members 9 | # $ gam redirect csv ./GroupUsers.json print group-members group group@domain.com fields email userfields fullname formatjson quotechar " " nogroupemail 10 | # 2: From that list of group members, output a JSON file importable by Canvas 11 | # $ python3 ConvertGroupUsersToCanvas.py ./GroupUsers.csv ./CanvasUsers.json 12 | """ 13 | 14 | import csv 15 | import json 16 | import sys 17 | 18 | with open(sys.argv[1], 'r', encoding='utf-8') as inputFile: 19 | inputCSV = csv.DictReader(inputFile, quotechar=' ') 20 | canvasData = {"result": []} 21 | for row in inputCSV: 22 | canvasData["result"].append({"student": json.loads(row['JSON'])}) 23 | 24 | with open(sys.argv[2], 'w', encoding='utf-8', newline='') as outputFile: 25 | outputFile.write(json.dumps(canvasData, indent=2, sort_keys=True)) 26 | -------------------------------------------------------------------------------- /ConvertGroupUsersToUserGroupParents.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Convert a CSV file showing group members to one showing groups and their parents for each user 4 | # Customize: Set DELIMITER to the single character that will separate parent groups 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get group members; omit field role if you're not interested the user's role 10 | # $ gam redirect csv ./GroupUsers.csv print group-members fields email,type,role 11 | # 2: From that list of group members, output a CSV file with headers primaryEmail,Group,Role,ParentsCount,Parents that shows the groups and their parents for each user 12 | # $ python3 ConvertGroupUsersToUserGroupParents.py ./GroupUsers.csv ./UserGroupParents.csv 13 | """ 14 | 15 | import csv 16 | import sys 17 | 18 | DELIMITER = ' ' 19 | QUOTE_CHAR = '"' # Adjust as needed 20 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 21 | 22 | def getGroupParents(groupEmail): 23 | groupParents[groupEmail] = {'parents': []} 24 | for parentGroup in GroupGroups[groupEmail]: 25 | groupParents[groupEmail]['parents'].append(parentGroup) 26 | if parentGroup not in groupParents: 27 | getGroupParents(parentGroup) 28 | 29 | def printGroupParents(groupEmail, urow): 30 | if groupParents[groupEmail]['parents']: 31 | for parentEmail in groupParents[groupEmail]['parents']: 32 | urow['parents'].append(parentEmail) 33 | printGroupParents(parentEmail, urow) 34 | del urow['parents'][-1] 35 | else: 36 | csvRow = {'primaryEmail': urow['primaryEmail'], 'Group': urow['Group'], 37 | 'ParentsCount': len(urow['parents']), 'Parents': DELIMITER.join(urow['parents'])} 38 | if includeRole: 39 | csvRow['Role'] = urow['Role'] 40 | outputCSV.writerow(csvRow) 41 | 42 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 43 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 44 | else: 45 | inputFile = sys.stdin 46 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 47 | 48 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 49 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 50 | else: 51 | outputFile = sys.stdout 52 | outputFieldNames = ['primaryEmail', 'Group', 'ParentsCount', 'Parents'] 53 | if 'role' in inputCSV.fieldnames: 54 | outputFieldNames.insert(2, 'Role') 55 | includeRole = True 56 | else: 57 | includeRole = False 58 | outputCSV = csv.DictWriter(outputFile, outputFieldNames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 59 | outputCSV.writeheader() 60 | 61 | groupParents = {} 62 | GroupGroups = {} 63 | UserGroups = {} 64 | for row in inputCSV: 65 | if row['type'] == 'USER': 66 | GroupGroups.setdefault(row['group'], []) 67 | email = row['email'].lower() 68 | UserGroups.setdefault(email, {'role': None, 'groups': []}) 69 | UserGroups[email]['groups'].append(row['group'].lower()) 70 | if includeRole: 71 | UserGroups[email]['role'] = row['role'] 72 | elif row['type'] == 'GROUP': 73 | GroupGroups.setdefault(row['group'], []) 74 | email = row['email'].lower() 75 | GroupGroups.setdefault(email, []) 76 | GroupGroups[email].append(row['group'].lower()) 77 | 78 | for group in groupParents: 79 | groupParents[group].sort() 80 | for user, info in sorted(iter(UserGroups.items())): 81 | for group in sorted(info['groups']): 82 | if group not in groupParents: 83 | getGroupParents(group) 84 | printGroupParents(group, {'primaryEmail': user, 'Group': group, 'Role': info['role'], 'parents': []}) 85 | 86 | if inputFile != sys.stdin: 87 | inputFile.close() 88 | if outputFile != sys.stdout: 89 | outputFile.close() 90 | -------------------------------------------------------------------------------- /ConvertGroupUsersToUserGroups.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Convert a CSV file showing group members to one showing groups for each user 4 | # Customize: Set DELIMITER to the single character that will separate groups 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get group members; omit field role if you're not interested the user's role 10 | # $ gam redirect csv ./GroupUsers.csv print group-members fields email,type,role 11 | # 2: From that list of group members, output a CSV file with headers primaryEmail,GroupsCount,Groups that shows the groups for each user 12 | # $ python3 ConvertGroupUsersToUserGroups.py ./GroupUsers.csv ./UserGroups.csv 13 | """ 14 | 15 | import csv 16 | import sys 17 | 18 | DELIMITER = ' ' 19 | QUOTE_CHAR = '"' # Adjust as needed 20 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 21 | 22 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 23 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 24 | else: 25 | inputFile = sys.stdin 26 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 27 | 28 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 29 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 30 | else: 31 | outputFile = sys.stdout 32 | outputFieldNames = ['primaryEmail', 'GroupsCount', 'Groups'] 33 | if 'role' in inputCSV.fieldnames: 34 | outputFieldNames.insert(1, 'Role') 35 | includeRole = True 36 | else: 37 | includeRole = False 38 | outputCSV = csv.DictWriter(outputFile, outputFieldNames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 39 | outputCSV.writeheader() 40 | 41 | UserGroups = {} 42 | for row in inputCSV: 43 | if row['type'] == 'USER': 44 | email = row['email'].lower() 45 | UserGroups.setdefault(email, {'role': None, 'groups': []}) 46 | if includeRole: 47 | UserGroups[email]['role'] = row['role'] 48 | UserGroups[email]['groups'].append(row['group'].lower()) 49 | 50 | for user, info in sorted(iter(UserGroups.items())): 51 | csvRow = {'primaryEmail': user, 'GroupsCount': len(info['groups']), 'Groups': DELIMITER.join(sorted(info['groups']))} 52 | if includeRole: 53 | csvRow['Role'] = info['role'] 54 | outputCSV.writerow(csvRow) 55 | 56 | if inputFile != sys.stdin: 57 | inputFile.close() 58 | if outputFile != sys.stdout: 59 | outputFile.close() 60 | -------------------------------------------------------------------------------- /CountCSVRows.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Count rows in a CSV file 4 | # 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # python3 CountCSVRows.py File.csv 10 | # 11 | """ 12 | 13 | import csv 14 | import sys 15 | 16 | QUOTE_CHAR = '"' # Adjust as needed 17 | 18 | if sys.argv[1] != '-': 19 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 20 | else: 21 | inputFile = sys.stdin 22 | rows = 0 23 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 24 | rows += 1 25 | print(rows) 26 | if inputFile != sys.stdin: 27 | inputFile.close() 28 | -------------------------------------------------------------------------------- /CountGroupsByDomain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Make a CSV file showing the number of groups per domain in a multi-domain workspace 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get groups 9 | # $ gam redirect csv ./Groups.csv print groups email 10 | # 2: From that list of groups, output a CSV file with headers "Domain,Groups" that shows the 11 | # number of groups per domain 12 | # $ python3 CountGroupsByDomain.py ./Groups.csv ./GroupsPerDomain.csv 13 | """ 14 | 15 | import csv 16 | import sys 17 | 18 | QUOTE_CHAR = '"' # Adjust as needed 19 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 20 | 21 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 22 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 23 | else: 24 | outputFile = sys.stdout 25 | outputCSV = csv.DictWriter(outputFile, ['Domain', 'Groups'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 26 | outputCSV.writeheader() 27 | 28 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 29 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 30 | else: 31 | inputFile = sys.stdin 32 | 33 | domainGroupCounts = {} 34 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 35 | email = row.get('email', row.get('Email')) 36 | if email: 37 | name, domain = email.split('@') 38 | domainGroupCounts.setdefault(domain, 0) 39 | domainGroupCounts[domain] += 1 40 | for domain, count in sorted(iter(domainGroupCounts.items())): 41 | outputCSV.writerow({'Domain': domain, 'Groups': count}) 42 | 43 | if inputFile != sys.stdin: 44 | inputFile.close() 45 | if outputFile != sys.stdout: 46 | outputFile.close() 47 | -------------------------------------------------------------------------------- /DeleteDuplicateFiles.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), delete all duplicate drive files 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get information for all files, if you don't want all users, replace all users with your user selection in the command below 9 | # These fields are required: fields id,title,createddate,mimetype fullpath 10 | # You can add additional fields that will be preserved in the output. 11 | # You can add a select option if you want to only process files in a specific folder 12 | # If you don't want to delete folders, add showmimetype not gfolder 13 | # $ gam redirect csv ./UserFiles.csv multiprocess all users print filelist fields id,title,createddate,mimetype,owners.emailaddress fullpath 14 | # $ gam redirect csv ./UserFiles.csv user user@domain.com print filelist fields id,title,createddate,mimetype,owners.emailaddress fullpath 15 | # select drivefilename "Folder Name" showmimetype not gfolder 16 | # 2: From that list of files, output a CSV file with the same headers as the input CSV file 17 | # that lists the drive file Ids that have the same owner, title, mimeType and paths with a createdDate older than the most recent createdDate 18 | # $ python3 DeleteDuplicateFiles.py ./UserFiles.csv ./DuplicateFiles.csv 19 | # 3: Inspect DuplicateFiles.csv, verify that it makes sense and then proceed 20 | # 4: Delete the duplicate files 21 | # $ gam redirect stdout ./DeleteDuplicateFiles.log multiprocess redirect stderr stdout csv ./DuplicateFiles.csv gam user "~Owner" delete drivefile "~id" 22 | """ 23 | 24 | import csv 25 | import sys 26 | 27 | FILE_NAME = 'name' 28 | ALT_FILE_NAME = 'title' 29 | CREATED_DATE = 'createdTime' 30 | ALT_CREATED_DATE = 'createdDate' 31 | 32 | QUOTE_CHAR = '"' # Adjust as needed 33 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 34 | 35 | def rowPaths(crow): 36 | paths = set() 37 | for i in range(0, int(crow['paths'])): 38 | paths.add(crow[f'path.{i}']) 39 | return paths 40 | 41 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 42 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 43 | else: 44 | outputFile = sys.stdout 45 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 46 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 47 | else: 48 | inputFile = sys.stdin 49 | 50 | prevOwner = None 51 | prevTitle = None 52 | prevMimeType = None 53 | prevCreatedDate = None 54 | prevPaths = None 55 | 56 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 57 | outputCSV = csv.DictWriter(outputFile, inputCSV.fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 58 | outputCSV.writeheader() 59 | 60 | rows = sorted(inputCSV, key=lambda k: k.get(CREATED_DATE, k.get(ALT_CREATED_DATE)), reverse=True) 61 | for row in sorted(rows, key=lambda k: (k['owners.0.emailAddress'], k.get(FILE_NAME, k.get(ALT_FILE_NAME)), k['mimeType'], k['paths'])): 62 | if ((row['owners.0.emailAddress'] == prevOwner) 63 | and (row.get(FILE_NAME, row.get(ALT_FILE_NAME)) == prevTitle) 64 | and (row['mimeType'] == prevMimeType) 65 | and (row.get(CREATED_DATE, row.get(ALT_CREATED_DATE)) < prevCreatedDate) 66 | and (rowPaths(row) == prevPaths)): 67 | outputCSV.writerow(row) 68 | else: 69 | prevOwner = row['owners.0.emailAddress'] 70 | prevTitle = row.get(FILE_NAME, row.get(ALT_FILE_NAME)) 71 | prevMimeType = row['mimeType'] 72 | prevCreatedDate = row.get(CREATED_DATE, row.get(ALT_CREATED_DATE)) 73 | prevPaths = rowPaths(row) 74 | if inputFile != sys.stdin: 75 | inputFile.close() 76 | if outputFile != sys.stdout: 77 | outputFile.close() 78 | -------------------------------------------------------------------------------- /DeleteDuplicateRows.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a CSV file, delete the duplcate rows based an a field. You can optionally delete unwanted fields. 4 | # Customize: Set ID_FIELD, DELETE_FIELDS, LINE_TERMINATOR 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Produce a CSV file Input.csv 10 | # 2: Delete the duplicate rows 11 | # $ python3 ./DeleteDuplicateRows.py Input.csv Output.csv 12 | """ 13 | 14 | import csv 15 | import sys 16 | 17 | ID_FIELD = 'id' # Field name to use for duplicate checking 18 | DELETE_FIELDS = [] # Fields to delete; Single field ['Field',]; multiple fields ['Field1', 'Field2', ...] 19 | 20 | QUOTE_CHAR = '"' # Adjust as needed 21 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 22 | 23 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 24 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 25 | else: 26 | outputFile = sys.stdout 27 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 28 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 29 | else: 30 | inputFile = sys.stdin 31 | 32 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 33 | outputFieldnames = inputCSV.fieldnames[:] 34 | deleteFieldnames = [] 35 | for field in DELETE_FIELDS: 36 | if field in outputFieldnames: 37 | outputFieldnames.remove(field) 38 | deleteFieldnames.append(field) 39 | outputCSV = csv.DictWriter(outputFile, outputFieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 40 | outputCSV.writeheader() 41 | 42 | previousId = None 43 | for row in sorted(inputCSV, key=lambda row: (row[ID_FIELD]), reverse=False): 44 | currentId = row[ID_FIELD] 45 | if currentId != previousId: 46 | for field in deleteFieldnames: 47 | row.pop(field, None) 48 | outputCSV.writerow(row) 49 | previousId = currentId 50 | 51 | if inputFile != sys.stdin: 52 | inputFile.close() 53 | if outputFile != sys.stdout: 54 | outputFile.close() 55 | -------------------------------------------------------------------------------- /DeleteFutureEvents.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), output a CSV that shows all user organized events with a start date >= a specified date; they can then be deleted. 4 | # Customize: Set DELETE_EVENTS_WITH_ATTENDEES = True or False to determine whether events with attendees will be deleted. 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get calendar events for a user 10 | # $ Example, user's primary calendar: gam redirect csv ./UserEvents.csv user user@domain.com print events primary singleevents orderby starttime maxattendees 1 11 | # $ Example, all calendars a user owns: gam redirect csv ./UserEvents.csv user user@domain.com print events minaccessrole owner singleevents orderby starttime maxattendees 1 12 | # 2: From that list of Events, output a CSV file with only the rows with an event start date >= a specified date 13 | # $ python3 DeleteFutureEvents.py yyyy-mm-dd UserEvents.csv UserFutureEvents.csv 14 | # 3: Delete the events 15 | # Parallel, faster: 16 | # $ gam csv UserFutureEvents.csv gam user "~primaryEmail" delete event calendars "~calendarId" events "~id" doit 17 | # Serial, cleaner output: 18 | # $ gam csvkmd users UserFutureEvents.csv keyfield primaryEmail subkeyfield calendarId datafield id delete event calendars csvsubkey calendarId events csvdata id doit 19 | # 4: Empty the calendars trash 20 | # $ gam csvkmd users UserFutureEvents.csv keyfield primaryEmail datafield calendarId empty calendartrash calendars csvdata calendarId 21 | """ 22 | 23 | import csv 24 | import datetime 25 | import sys 26 | 27 | DELETE_EVENTS_WITH_ATTENDEES = False 28 | 29 | YYYYMMDD_FORMAT = '%Y-%m-%d' 30 | 31 | QUOTE_CHAR = '"' # Adjust as needed 32 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 33 | 34 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 35 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 36 | else: 37 | outputFile = sys.stdout 38 | 39 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 40 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 41 | else: 42 | inputFile = sys.stdin 43 | 44 | if len(sys.argv) > 1: 45 | startDate = sys.argv[1] 46 | try: 47 | datetime.datetime.strptime(startDate, YYYYMMDD_FORMAT) 48 | except ValueError: 49 | sys.stderr.write(f'ERROR: date ({startDate}) is not valid, it must be (yyyy-mm-dd)\n') 50 | sys.exit(1) 51 | else: 52 | startDate = datetime.datetime.now().strftime(YYYYMMDD_FORMAT) 53 | 54 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 55 | outputCSV = csv.DictWriter(outputFile, inputCSV.fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 56 | outputCSV.writeheader() 57 | 58 | for row in inputCSV: 59 | if row['primaryEmail'] != row.get('creator.email'): 60 | continue 61 | if row.get('start.date'): 62 | if row['start.date'] < startDate: 63 | continue 64 | elif row.get('start.dateTime'): 65 | if row['start.dateTime'][:10] < startDate: 66 | continue 67 | else: 68 | continue 69 | if not DELETE_EVENTS_WITH_ATTENDEES: 70 | numAttendees = row.get('attendees', '') 71 | if numAttendees and int(numAttendees) > 0: 72 | continue 73 | outputCSV.writerow(row) 74 | if inputFile != sys.stdin: 75 | inputFile.close() 76 | if outputFile != sys.stdout: 77 | outputFile.close() 78 | -------------------------------------------------------------------------------- /DeleteOldContacts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Delete old contacts from user's personal contacts 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Generate OldContacts.csv, header row primaryEmail, one email address per row 9 | # You can generate this file by hand, or, for example, if you have an OU of users to remove from all other user's contact lists, 10 | # $ gam redirect csv ./OldContacts.csv ou "/Path/to/OU" print users primaryemail 11 | # 2: Get current contacts for all users, if you don't want all users, replace all users with your user selection in the command below 12 | # $ gam redirect csv ./CurrentContacts.csv all users print contacts fields email,name 13 | # 3: From that list of user's contacts, output a CSV file with headers: User,ContactID,Name,Email 14 | # that shows user's contacts with an email address from OldContacts.csv 15 | # $ python3 DeleteOldContacts.py ./OldContacts.csv ./CurrentContacts.csv ./DeleteContacts.csv 16 | # 4: Inspect DeleteContacts.csv, verify that it makes sense and then proceed 17 | # 5: If desired, delete the contacts 18 | # $ gam csv DeleteContacts.csv gam user "~User" delete contact "~ContactID" 19 | """ 20 | 21 | import csv 22 | import re 23 | import sys 24 | 25 | QUOTE_CHAR = '"' # Adjust as needed 26 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 27 | 28 | EMAILS_N_ADDRESS = re.compile(r"Emails.(\d+).address") 29 | 30 | OldContacts = set() 31 | 32 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 33 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 34 | OldContacts.add(row['primaryEmail'].lower()) 35 | inputFile.close() 36 | 37 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 38 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 39 | else: 40 | inputFile = sys.stdin 41 | 42 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 43 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 44 | else: 45 | outputFile = sys.stdout 46 | outputCSV = csv.DictWriter(outputFile, ['User','ContactID','Name','Email'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 47 | outputCSV.writeheader() 48 | 49 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 50 | for k, v in iter(row.items()): 51 | mg = EMAILS_N_ADDRESS.match(k) 52 | if mg and v.lower() in OldContacts: 53 | outputCSV.writerow({'User': row['User'], 54 | 'ContactID': row['ContactID'], 55 | 'Name': row['Name'], 56 | 'Email': v}) 57 | 58 | if inputFile != sys.stdin: 59 | inputFile.close() 60 | if outputFile != sys.stdout: 61 | outputFile.close() 62 | -------------------------------------------------------------------------------- /DeleteProtectedRanges.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Extract sheet protected sheet ranges from a Google Sheet so they can be deleted 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Produce a CSV file showing the protected ranges in JSON format with a quote character on single quote; 9 | # the headers must be User, spreadsheetId and JSON 10 | # $ gam redirect csv ./ProtectedRanges.csv user user@domain.com print sheet query "'me' in owners and mimeType = 'application/vnd.google-apps.spreadsheet'" sheetsfields protectedranges formatjson quotechar "'" 11 | # 2: Produce a CSV file DeleteProtectedRanges.csv with requests to delete the protected ranges for each spreadsheet 12 | # $ python3 ./DeleteProtectedRanges.py ProtectedRanges.csv DeleteProtectedRanges.csv 13 | # 3: Delete the protected ranges 14 | # $ gam redirect stdout ./DeleteProtectedRanges.txt multiprocess redirect stderr stdout csv DeleteProtectedRanges.csv quotechar "'" gam user "~User" update sheet "~spreadsheetId" json "~JSON" 15 | 16 | """ 17 | 18 | import csv 19 | import json 20 | import sys 21 | 22 | QUOTE_CHAR = "'" # Must be "'" 23 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 24 | 25 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 26 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 27 | 28 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 29 | outputCSV = csv.DictWriter(outputFile, ['User', 'spreadsheetId', 'JSON'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 30 | outputCSV.writeheader() 31 | 32 | for row in inputCSV: 33 | deleteProtectedRanges = {'requests': []} 34 | jsonData = json.loads(row['JSON']) 35 | for sheet in jsonData.get('sheets', []): 36 | for protectedRange in sheet.get('protectedRanges', []): 37 | deleteProtectedRanges['requests'].append({'deleteProtectedRange': {'protectedRangeId': protectedRange['protectedRangeId']}}) 38 | if deleteProtectedRanges['requests']: 39 | outputCSV.writerow({'User': row['User'], 40 | 'spreadsheetId': row['spreadsheetId'], 41 | 'JSON': json.dumps(deleteProtectedRanges)}) 42 | 43 | inputFile.close() 44 | outputFile.close() 45 | -------------------------------------------------------------------------------- /ExpandSharedDriveGroupMembers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Read a CSV file showing Group Membership and a CSV file showing Shared Drive membership 4 | # and output a CSV file showing the individual group members as members of the Shared Drives. 5 | # The output CSV file has additional columns: email, level, subgroup 6 | # email - The email address of the user 7 | # level - At what level of the group expansion does the user appear; level 0 is the top level 8 | # subgroup - The group that contains the user 9 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 10 | # $ python -V or python3 -V 11 | # Python 3.x.y 12 | # Usage: 13 | # 1: Get Group Membership 14 | # $ gam redirect csv ./GroupMembers.csv print group-members fields email,type recursive 15 | # 2: Get Shared Drive membership 16 | # $ gam config csv_output_header_drop_filter "User,createdTime,permission.photoLink,permission.permissionDetails" redirect csv ./SharedDriveMembers.csv print shareddriveacls oneitemperrow 17 | # 3: From that list of Shared Drive members, expand ACLs of type group to show individual members 18 | # $ python3 ExpandSharedDriveGroupMembers.py ./GroupMembers.csv ./SharedDriveMembers.csv ./ExpandedSharedDriveMembers.csv 19 | """ 20 | 21 | import csv 22 | import sys 23 | 24 | QUOTE_CHAR = '"' # Adjust as needed 25 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 26 | 27 | # Group Membership file 28 | GroupMembers = {} 29 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 30 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 31 | if row['type'] == 'USER': 32 | group = row['group'].lower() 33 | GroupMembers.setdefault(group, []) 34 | GroupMembers[group].append({'email': row['email'], 'level': row['level'], 'subgroup': row['subgroup']}) 35 | inputFile.close() 36 | 37 | # Shared Drive Membership file 38 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 39 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 40 | inputFieldNames = inputCSV.fieldnames 41 | 42 | # Expanded Shared Drive Membership file 43 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 44 | outputFieldNames = inputFieldNames+['email', 'level', 'subgroup'] 45 | outputCSV = csv.DictWriter(outputFile, outputFieldNames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 46 | outputCSV.writeheader() 47 | 48 | # Expand Shared Drive group members 49 | for row in inputCSV: 50 | email = row['permission.emailAddress'].lower() 51 | if row['permission.type'] != 'group' or email not in GroupMembers: 52 | outputCSV.writerow(row) 53 | else: 54 | for member in GroupMembers[email]: 55 | row.update(member) 56 | outputCSV.writerow(row) 57 | 58 | inputFile.close() 59 | outputFile.close() 60 | -------------------------------------------------------------------------------- /ExtractProtectedRanges.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Extract sheet protected ranges from a Google Sheet so they can be applied to a copied Google Sheet 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Produce a CSV file Input.json eom the original Google Sheet 9 | # $ gam redirect stdout ./Input.json user user@domain.com show sheet formatjson fields sheets 10 | # 2: Produce a JSON file Output.json with the protected ranges 11 | # $ python3 ./ExtractProtectedRanges.py Input.json Output.json 12 | # 3: Copy the Google Sheet 13 | # $ gam user user@domain.com copy drivefile newfilename CopiedSheet copyfilepermissions true 14 | # 4: Update copied Google Sheet with protected ranges from original Google Sheet 15 | # $ gam user user@domain.com update sheet json file Output.json 16 | 17 | """ 18 | 19 | import json 20 | import sys 21 | 22 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 23 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 24 | else: 25 | outputFile = sys.stdout 26 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 27 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 28 | else: 29 | inputFile = sys.stdin 30 | 31 | updateProtectedRanges = {'requests': []} 32 | jsonData = json.load(inputFile) 33 | for sheet in jsonData['JSON'].get('sheets', []): 34 | for protectedRange in sheet.get('protectedRanges', []): 35 | updateProtectedRanges['requests'].append({'updateProtectedRange': {'protectedRange': protectedRange, 'fields': 'editors'}}) 36 | json.dump(updateProtectedRanges, outputFile, indent=2) 37 | if inputFile != sys.stdin: 38 | inputFile.close() 39 | if outputFile != sys.stdout: 40 | outputFile.close() 41 | -------------------------------------------------------------------------------- /FindCommonEmails.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Create a CSV file showing email addresses that appear in all CSV files generated by separate gam report commands 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Issue various report commands: 9 | # $ gam redirect csv ./num_emails_sent.csv report users filter "gmail:num_emails_sent<1" parameters gmail:num_emails_sent 10 | # $ gam redirect csv ./creation_time.csv report users filter "accounts:creation_time<2017-09-01T00:00:00.000Z" parameters accounts:creation_time 11 | # $ gam redirect csv ./last_interaction_time.csv report users filter "gmail:last_interaction_time<2016-01-01T00:00:00.000Z" parameters gmail:last_interaction_time 12 | # 2: From that list of files, output a CSV file with the header email that shows the email addresses that appear in all files 13 | # $ python3 FindCommonEmails.py ./CommonEmails.csv ./num_emails_sent.csv ./creation_time.csv ./last_interaction_time.csv 14 | """ 15 | 16 | import csv 17 | import sys 18 | 19 | QUOTE_CHAR = '"' # Adjust as needed 20 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 21 | 22 | if sys.argv[1] != '-': 23 | outputFile = open(sys.argv[1], 'w', encoding='utf-8', newline='') 24 | else: 25 | outputFile = sys.stdout 26 | outputCSV = csv.DictWriter(outputFile, ['email',], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 27 | outputCSV.writeheader() 28 | 29 | users = {} 30 | allFilesCount = len(sys.argv)-2 31 | for i in range(2, len(sys.argv)): 32 | inputFile = open(sys.argv[i], 'r', encoding='utf-8') 33 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 34 | email = row['email'] 35 | users.setdefault(email, 0) 36 | users[email] += 1 37 | inputFile.close() 38 | for user, count in sorted(iter(users.items())): 39 | if count == allFilesCount: 40 | outputCSV.writerow({'email': user}) 41 | 42 | if outputFile != sys.stdout: 43 | outputFile.close() 44 | -------------------------------------------------------------------------------- /GetAllowFileDiscoveryDriveACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), get all drive file ACLs for files shared with anyone/domain allowfilediscovery 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 9 | # $ gam config auto_batch_min 1 redirect csv ./filelistperms.csv multiprocess all users print filelist fields id,name,permissions,owners.emailaddress,mimetype query "(visibility='anyoneCanFind' or visibility='domainCanFind')" 10 | # 2: From that list of ACLs, output a CSV file with headers "Owner,driveFileId,driveFileTitle,permissionId,role,type,domain,allowFileDiscovery" 11 | # that lists the driveFileIds, permissionIds and details for all ACLs shared with anyone/domain allowFileDiscovery 12 | # $ python3 GetAllowFileDiscoveryDriveACLs.py filelistperms.csv deleteperms.csv 13 | # 3: Inspect deleteperms.csv, verify that it makes sense and then proceed 14 | # 4: If desired, delete the ACLs 15 | # $ gam csv ./deleteperms.csv gam user "~Owner" delete drivefileacl "~driveFileId" "~permissionId" 16 | """ 17 | 18 | import csv 19 | import re 20 | import sys 21 | 22 | FILE_NAME = 'name' 23 | ALT_FILE_NAME = 'title' 24 | 25 | QUOTE_CHAR = '"' # Adjust as needed 26 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 27 | 28 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 29 | 30 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 31 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 32 | else: 33 | outputFile = sys.stdout 34 | outputCSV = csv.DictWriter(outputFile, 35 | ['Owner', 'driveFileId', 'driveFileTitle', 'mimeType', 36 | 'permissionId', 'role', 'type', 'allowFileDiscovery', 'domain'], 37 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 38 | outputCSV.writeheader() 39 | 40 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 41 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 42 | else: 43 | inputFile = sys.stdin 44 | 45 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 46 | for k, v in iter(row.items()): 47 | mg = PERMISSIONS_N_TYPE.match(k) 48 | if mg and v in {'anyone', 'domain'}: 49 | permissions_N = mg.group(1) 50 | allowFileDiscovery = row.get(f'permissions.{permissions_N}.allowFileDiscovery', str(row.get(f'permissions.{permissions_N}.withLink') == 'False')) 51 | if allowFileDiscovery == 'True': 52 | outputCSV.writerow({'Owner': row['owners.0.emailAddress'], 53 | 'driveFileId': row['id'], 54 | 'driveFileTitle': row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown')), 55 | 'mimeType': row['mimeType'], 56 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 57 | 'role': row[f'permissions.{permissions_N}.role'], 58 | 'type': row[f'permissions.{permissions_N}.type'], 59 | 'domain': row.get(f'permissions.{permissions_N}.domain', ''), 60 | 'allowFileDiscovery': allowFileDiscovery}) 61 | 62 | if inputFile != sys.stdin: 63 | inputFile.close() 64 | if outputFile != sys.stdout: 65 | outputFile.close() 66 | -------------------------------------------------------------------------------- /GetDailyMimeTypeCreations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), output a CSV file showing the number of files created by day by mimeType 4 | # Customize: Set REVERSE 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 10 | # $ gam config auto_batch_min 1 redirect csv ./filelist.csv multiprocess all users print filelist fields id,createdtime,mimetype 11 | # 2: From that list of ACLs, output a CSV file with headers: 12 | # Owner,createdTime,mimeType 13 | # $ python3 GetDailyMimeTypeCreations.py filelist.csv mimetypecreations.csv 14 | """ 15 | 16 | import csv 17 | import sys 18 | 19 | # Set REVERSE = True for createdTime newest to oldest 20 | # Set REVERSE = False for createdTime oldest to newest 21 | REVERSE = True 22 | QUOTE_CHAR = '"' # Adjust as needed 23 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 24 | 25 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 26 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 27 | else: 28 | inputFile = sys.stdin 29 | 30 | userDailyMimeTypeCounts = {} 31 | mimeTypesSet = set() 32 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 33 | owner = row['Owner'] 34 | createdDate, createdTime = row['createdTime'].split('T') 35 | mimeType = row['mimeType'] 36 | mimeTypesSet.add(mimeType) 37 | userDailyMimeTypeCounts.setdefault(owner, {}) 38 | userDailyMimeTypeCounts[owner].setdefault(createdDate, {}) 39 | userDailyMimeTypeCounts[owner][createdDate].setdefault(mimeType, 0) 40 | userDailyMimeTypeCounts[owner][createdDate][mimeType] += 1 41 | 42 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 43 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 44 | else: 45 | outputFile = sys.stdout 46 | outputFieldNames = ['Owner', 'createdTime'] 47 | outputFieldNames.extend(sorted(mimeTypesSet)) 48 | outputCSV = csv.DictWriter(outputFile, outputFieldNames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 49 | outputCSV.writeheader() 50 | 51 | for owner, createdTimes in sorted(userDailyMimeTypeCounts.items()): 52 | for createdTime, mimeTypes in sorted(createdTimes.items(), reverse=REVERSE): 53 | row = {'Owner': owner, 'createdTime': createdTime} 54 | for mimeType, count in sorted(mimeTypes.items()): 55 | row[mimeType] = count 56 | for mimeType in mimeTypesSet: 57 | if mimeType not in row: 58 | row[mimeType] = 0 59 | outputCSV.writerow(row) 60 | 61 | if inputFile != sys.stdin: 62 | inputFile.close() 63 | if outputFile != sys.stdout: 64 | outputFile.close() 65 | -------------------------------------------------------------------------------- /GetDriveActivityEmailAddresses.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Get email addresses for users identified by permissionId in gam print driveactivity when the v2 option is not used 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get permissionId from Drive Settings. Substitute for all users if applicable. 9 | # $ Example: gam config auto_batch_min 1 redirect csv ./DriveSettings.csv multiprocess all users print drivesettings fields permissionid 10 | # 2: Generate drive activity 11 | # $ Example: gam config auto_batch_min 1 redirect csv ./DriveActivity.csv multiprocess print driveactivity ... 12 | # 3: From DriveSettings.csv and DriveActivity.csv generate DriveActivityEmail.csv with the additional column user.emailAddress 13 | # $ python3 GetDriveActivityEmailAddresses.py DriveSettings.csv DriveActivity.csv DriveActivityEmail.csv 14 | """ 15 | 16 | import csv 17 | import sys 18 | 19 | QUOTE_CHAR = '"' # Adjust as needed 20 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 21 | 22 | USER_PERMISSIONID = 'user.permissionId' 23 | USER_EMAILADDRESS = 'user.emailAddress' 24 | 25 | permissionIdEmailMap = {} 26 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 27 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 28 | permissionIdEmailMap[row['permissionId']] = row['email'] 29 | inputFile.close() 30 | 31 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 32 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 33 | fieldnames = inputCSV.fieldnames[:] 34 | fieldnames.insert(1, USER_EMAILADDRESS) 35 | 36 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 37 | outputCSV = csv.DictWriter(outputFile, fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 38 | outputCSV.writeheader() 39 | 40 | for row in inputCSV: 41 | permissionId = row[USER_PERMISSIONID] 42 | if permissionId not in permissionIdEmailMap: 43 | permissionIdEmailMap[permissionId] = 'Unknown' 44 | row[USER_EMAILADDRESS] = permissionIdEmailMap[permissionId] 45 | outputCSV.writerow(row) 46 | 47 | inputFile.close() 48 | outputFile.close() 49 | -------------------------------------------------------------------------------- /GetEMCAliases.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Create a CSV file with columns: User, Alias; from EMC exported data with columns: DisplayName, PrimarySmtpAddress, EmailAddresses. 4 | # For each alias email address in the space separated list "EmailAddresses", output a row with PrimarySmtpAddress in the User column and the alias email address in the Alias column. 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Export EMC data to EMCData.csv 10 | # 2: python3 GetEMCAliases.py EMCData.csv EMCAliases.csv 11 | # 3: Inspect EMCAliases.csv to make sure that it is reasonable 12 | # 4: Create the aliases in Gam 13 | # $ gam csv ./EMCAliases.csv gam create alias "~Alias" user "~User" 14 | """ 15 | 16 | import csv 17 | import sys 18 | 19 | QUOTE_CHAR = '"' # Adjust as needed 20 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 21 | 22 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 23 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 24 | else: 25 | outputFile = sys.stdout 26 | outputCSV = csv.DictWriter(outputFile, ['User', 'Alias'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 27 | outputCSV.writeheader() 28 | 29 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 30 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 31 | else: 32 | inputFile = sys.stdin 33 | 34 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 35 | for alias in row['EmailAddresses'].split(): 36 | outputCSV.writerow({'User': row['PrimarySmtpAddress'], 37 | 'Alias': alias}) 38 | 39 | if inputFile != sys.stdin: 40 | inputFile.close() 41 | if outputFile != sys.stdout: 42 | outputFile.close() 43 | -------------------------------------------------------------------------------- /GetEmptyGroups.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Make a CSV file showing groups with no members 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get group member counts 9 | # $ gam redirect csv ./GroupCounts.csv print groups memberscount managerscount ownerscount 10 | # 2: From that list of groups, output a CSV file with headers "group" for those groups with no members 11 | # $ python3 GetEmptyGroups.py ./GroupCounts.csv ./EmptyGroups.csv 12 | """ 13 | 14 | import csv 15 | import sys 16 | 17 | QUOTE_CHAR = '"' # Adjust as needed 18 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 19 | 20 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 21 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 22 | else: 23 | outputFile = sys.stdout 24 | outputCSV = csv.DictWriter(outputFile, ['group'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 25 | outputCSV.writeheader() 26 | 27 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 28 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 29 | else: 30 | inputFile = sys.stdin 31 | 32 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 33 | total = int(row.get('MembersCount', '0'))+int(row.get('ManagersCount', '0'))+int(row.get('OwnersCount', '0')) 34 | if total == 0: 35 | outputCSV.writerow({'group': row.get('email', row.get('Email', 'Unknown'))}) 36 | 37 | if inputFile != sys.stdin: 38 | inputFile.close() 39 | if outputFile != sys.stdout: 40 | outputFile.close() 41 | -------------------------------------------------------------------------------- /GetFilePermissionsWithPaths.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User, show drive file permissions and file paths 4 | # Customize: Set INCLUDE_OWNER 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get ACLs/paths for the user's files 10 | # To select a folder as a starting point rather than My Drive, add: select 11 | # To have that folder included in the output, add: showparent 12 | # $ gam redirect csv ./filelistperms.csv user user@domain.com print filelist fields id,name,mimetype,permissions,owners.emailaddress filepath showownedby any 13 | # 2: From that list of ACLs, output a CSV file that lists the shared file permissions; the file paths are included on each line 14 | # $ python3 GetFilePermissionsWithPaths.py filelistperms.csv deleteperms.csv 15 | # 3: Inspect deleteperms.csv, verify that it makes sense and then proceed if desired 16 | # 4: If desired, delete the ACLs 17 | # $ gam csv ./deleteperms.csv gam user "~Owner" delete drivefileacl "~driveFileId" "~permissionId" 18 | """ 19 | 20 | import csv 21 | import re 22 | import sys 23 | 24 | FILE_NAME = 'name' 25 | ALT_FILE_NAME = 'title' 26 | 27 | QUOTE_CHAR = '"' # Adjust as needed 28 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 29 | 30 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 31 | 32 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 33 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 34 | else: 35 | inputFile = sys.stdin 36 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 37 | inputFieldNames = inputCSV.fieldnames 38 | pathFieldNames = [field for field in inputFieldNames if field.startswith('path')] 39 | 40 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 41 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 42 | else: 43 | outputFile = sys.stdout 44 | outputFieldNames = ['User', 'Owner', 'driveFileId', 'driveFileTitle', 'mimeType', 'permissionId', 45 | 'role', 'type', 'emailAddress', 'domain', 'allowFileDiscovery']+pathFieldNames 46 | outputCSV = csv.DictWriter(outputFile, outputFieldNames, 47 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 48 | outputCSV.writeheader() 49 | 50 | for row in inputCSV: 51 | prow = {} 52 | for field in pathFieldNames: 53 | prow[field] = row[field] 54 | for k, v in iter(row.items()): 55 | mg = PERMISSIONS_N_TYPE.match(k) 56 | if mg and v: 57 | permissions_N = mg.group(1) 58 | if v in ['user', 'group']: 59 | allowFileDiscovery = '' 60 | emailAddress = row[f'permissions.{permissions_N}.emailAddress'].lower() 61 | domain = emailAddress[emailAddress.find('@')+1:] 62 | elif v == 'domain': 63 | allowFileDiscovery = row.get(f'permissions.{permissions_N}.allowFileDiscovery', 64 | str(row.get(f'permissions.{permissions_N}.withLink') == 'False')) 65 | emailAddress = '' 66 | domain = row[f'permissions.{permissions_N}.domain'].lower() 67 | else: #anyone 68 | allowFileDiscovery = row.get(f'permissions.{permissions_N}.allowFileDiscovery', 69 | str(row.get(f'permissions.{permissions_N}.withLink') == 'False')) 70 | emailAddress = '' 71 | domain = '' 72 | orow = {'User': row['Owner'], 73 | 'Owner': row['owners.0.emailAddress'], 74 | 'driveFileId': row['id'], 75 | 'driveFileTitle': row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown')), 76 | 'mimeType': row.get('mimeType', ''), 77 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 78 | 'role': row[f'permissions.{permissions_N}.role'], 79 | 'type': v, 80 | 'emailAddress': emailAddress, 81 | 'domain': domain, 82 | 'allowFileDiscovery': allowFileDiscovery} 83 | orow.update(prow) 84 | outputCSV.writerow(orow) 85 | 86 | if inputFile != sys.stdin: 87 | inputFile.close() 88 | if outputFile != sys.stdout: 89 | outputFile.close() 90 | -------------------------------------------------------------------------------- /GetGroupTypeCounts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Produce a CSV file showing groups with with a count of their member types 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get group members 9 | # $ gam redirect csv ./GroupMembers.csv print group-members fields type 10 | # 2: From that list of group members, output a CSV file with headers group,customercount,groupcount,usercount 11 | # $ python3 GetGroupTypeCounts.py ./GroupMembers.csv ./GroupTypeCounts.csv 12 | """ 13 | 14 | import csv 15 | import sys 16 | 17 | DELIMITER = ' ' # Character to separate domains in output CSV 18 | QUOTE_CHAR = '"' # Adjust as needed 19 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 20 | 21 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 22 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 23 | else: 24 | outputFile = sys.stdout 25 | outputCSV = csv.DictWriter(outputFile, ['group', 'CUSTOMER', 'GROUP', 'USER', 'UNKNOWN'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 26 | outputCSV.writeheader() 27 | 28 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 29 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 30 | else: 31 | inputFile = sys.stdin 32 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 33 | 34 | Groups = {} 35 | for row in inputCSV: 36 | mtype = row.get('type', 'UNKNOWN') 37 | group = row['group'] 38 | Groups.setdefault(group, {'CUSTOMER': 0, 'GROUP': 0, 'USER': 0, 'UNKNOWN': 0}) 39 | if mtype in {'CUSTOMER', 'GROUP', 'USER'}: 40 | Groups[group][mtype] += 1 41 | else: 42 | Groups[group]['UNKNOWN'] += 1 43 | 44 | for group, counts in sorted(iter(Groups.items())): 45 | outputCSV.writerow({'group': group, 46 | 'CUSTOMER': counts['CUSTOMER'], 47 | 'GROUP': counts['GROUP'], 48 | 'USER': counts['USER'], 49 | 'UNKNOWN': counts['UNKNOWN']}) 50 | 51 | if inputFile != sys.stdin: 52 | inputFile.close() 53 | if outputFile != sys.stdout: 54 | outputFile.close() 55 | -------------------------------------------------------------------------------- /GetGroupsOwnedByUser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Make a CSV file showing groups owned by users 4 | # Customize: Set SelectedUsers or pass a CSV file:field reference on the command line 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get group owners 10 | # $ gam redirect csv ./GroupOwners.csv print groups owners delimiter " " 11 | # 2: From that list of groups, output a CSV file with headers "User,GroupsOwnedByUser 12 | # $ python3 GetGroupsOwnedByUser.py ./GroupOwners.csv ./GroupsOwnedByUser.csv 13 | # 3: If you only want groups owned by a select list of users, specify the CSV file name and field name that lists the users 14 | # $ python3 GetGroupsOwnedByUser.py ./GroupOwners.csv ./GroupsOwnedByUser.csv ./: 15 | """ 16 | 17 | import csv 18 | import sys 19 | 20 | QUOTE_CHAR = '"' # Adjust as needed 21 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 22 | 23 | # Leave SelectedUsers empty to show groups owned by any user 24 | # Set to a specific set of users, e.g., SelectedUsers = set('user1@domain.com', 'user2@domain.com') 25 | # Set to a list of users read from a CSV file passed on the command line 26 | SelectedUsers = set() 27 | 28 | GroupsOwnedByUser = {} 29 | 30 | if len(sys.argv) > 3: 31 | filename, fieldname = sys.argv[3].split(':') 32 | inputFile = open(filename, 'r', encoding='utf-8') 33 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 34 | SelectedUsers.add(row[fieldname].lower()) 35 | inputFile.close() 36 | 37 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 38 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 39 | else: 40 | outputFile = sys.stdout 41 | outputCSV = csv.DictWriter(outputFile, ['User', 'GroupsOwnedByUser'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 42 | outputCSV.writeheader() 43 | 44 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 45 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 46 | else: 47 | inputFile = sys.stdin 48 | 49 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 50 | for k, v in iter(row.items()): 51 | if int(row['OwnersCount']) != 0: 52 | for owner in row['Owners'].lower().split(' '): 53 | if (not SelectedUsers) or (owner in SelectedUsers): 54 | GroupsOwnedByUser.setdefault(owner, []) 55 | GroupsOwnedByUser[owner].append(row.get('email', row.get('Email', 'Unknown'))) 56 | for user, groups in sorted(iter(GroupsOwnedByUser.items())): 57 | outputCSV.writerow({'User': user, 58 | 'GroupsOwnedByUser': ' '.join(groups)}) 59 | 60 | if inputFile != sys.stdin: 61 | inputFile.close() 62 | if outputFile != sys.stdout: 63 | outputFile.close() 64 | -------------------------------------------------------------------------------- /GetGroupsWithExternalMembers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Produce a CSV file showing groups with external members, i.e, those in domains other than ones you specify. 4 | # Customize: DELIMITER, DOMAIN_LIST, AGGREGATE_DOMAINS 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get group members 10 | # $ gam redirect csv ./GroupMembers.csv print group-members fields email,type 11 | # 2: From that list of group members, output a CSV file with headers group,domain,count 12 | # $ python3 GetGroupsWithExternalMembers.py ./GroupMembers.csv ./GroupsWithExternalMembers.csv 13 | # 3: If you want a list of the external members, add another filename to the command, the external members will be output to that file 14 | # $ python3 GetGroupsWithExternalMembers.py ./GroupMembers.csv ./GroupsWithExternalMembers.csv ./ExternalMembers.csv 15 | # 4: If you want to delete the external members from their groups, you can do the following which uses one API call per member 16 | # $ gam csv ./ExternalMembers.csv gam update group "~group" delete member "~email" 17 | # 5: You can delete the members in batches 18 | # $ gam update group csvkmd ./ExternalMembers.csv keyfield group datafield email delete member csvdata email 19 | """ 20 | 21 | import csv 22 | import sys 23 | 24 | DELIMITER = ' ' # Character to separate domains in output CSV 25 | QUOTE_CHAR = '"' # Adjust as needed 26 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 27 | 28 | # Substitute your internal domain(s) in the list below, e.g., DOMAIN_LIST = ['domain.com',] DOMAIN_LIST = ['domain1.com', 'domain2.com',] 29 | DOMAIN_LIST = ['domain.com',] 30 | 31 | # False - show one group/one external domain per line 32 | # True - show one group/all external domains per line 33 | AGGREGATE_DOMAINS = True 34 | 35 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 36 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 37 | else: 38 | outputFile = sys.stdout 39 | outputCSV = csv.DictWriter(outputFile, ['group', 'domain', 'count'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 40 | outputCSV.writeheader() 41 | 42 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 43 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 44 | else: 45 | inputFile = sys.stdin 46 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 47 | 48 | if len(sys.argv) > 3: 49 | matchFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 50 | matchCSV = csv.DictWriter(matchFile, inputCSV.fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 51 | matchCSV.writeheader() 52 | else: 53 | matchFile = None 54 | 55 | Groups = {} 56 | for row in inputCSV: 57 | if row['type'] in ['USER', 'GROUP']: 58 | group = row['group'] 59 | emailAddress = row['email'].lower() 60 | atLoc = emailAddress.find('@') 61 | if atLoc > 1: 62 | domain = emailAddress[atLoc+1:] 63 | else: 64 | domain = 'unknown' 65 | if domain not in DOMAIN_LIST: 66 | Groups.setdefault(group, {}) 67 | Groups[group].setdefault(domain, 0) 68 | Groups[group][domain] += 1 69 | if matchFile: 70 | matchCSV.writerow(row) 71 | 72 | if matchFile: 73 | matchFile.close() 74 | 75 | for group, domains in sorted(iter(Groups.items())): 76 | if not AGGREGATE_DOMAINS: 77 | for domain, count in sorted(iter(domains.items())): 78 | outputCSV.writerow({'group': group, 79 | 'domain': domain, 80 | 'count': count}) 81 | else: 82 | total = 0 83 | domainsList = [] 84 | for domain, count in sorted(iter(domains.items())): 85 | domainsList.append(domain) 86 | total += count 87 | outputCSV.writerow({'group': group, 88 | 'domain': DELIMITER.join(domainsList), 89 | 'count': total}) 90 | 91 | if inputFile != sys.stdin: 92 | inputFile.close() 93 | if outputFile != sys.stdout: 94 | outputFile.close() 95 | -------------------------------------------------------------------------------- /GetGroupsWithOnlyExternalMembers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Produce a CSV file showing groups with only external members, i.e, those in domains other than ones you specify. 4 | # Customize: DELIMITER, DOMAIN_LIST, AGGREGATE_DOMAINS 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get group members 10 | # $ gam redirect csv ./GroupMembers.csv print group-members fields email,type 11 | # 2: From that list of group members, output a CSV file with headers group,domain,count 12 | # $ python3 GetGroupsWithOnlyExternalMembers.py ./GroupMembers.csv ./GroupsWithOnlyExternalMembers.csv 13 | """ 14 | 15 | import csv 16 | import sys 17 | 18 | DELIMITER = ' ' # Character to separate domains in output CSV 19 | QUOTE_CHAR = '"' # Adjust as needed 20 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 21 | 22 | # Substitute your internal domain(s) in the list below, e.g., DOMAIN_LIST = ['domain.com',] DOMAIN_LIST = ['domain1.com', 'domain2.com',] 23 | DOMAIN_LIST = ['domain.com',] 24 | 25 | # False - show one group/one external domain per line 26 | # True - show one group/all external domains per line 27 | AGGREGATE_DOMAINS = True 28 | 29 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 30 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 31 | else: 32 | outputFile = sys.stdout 33 | outputCSV = csv.DictWriter(outputFile, ['group', 'domain', 'count'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 34 | outputCSV.writeheader() 35 | 36 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 37 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 38 | else: 39 | inputFile = sys.stdin 40 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 41 | 42 | Groups = {} 43 | GroupsWithInternalMembers = set() 44 | 45 | for row in inputCSV: 46 | if row['type'] in ['USER', 'GROUP']: 47 | group = row['group'] 48 | emailAddress = row['email'].lower() 49 | atLoc = emailAddress.find('@') 50 | if atLoc > 1: 51 | domain = emailAddress[atLoc+1:] 52 | else: 53 | domain = 'unknown' 54 | if domain not in DOMAIN_LIST: 55 | Groups.setdefault(group, {}) 56 | Groups[group].setdefault(domain, 0) 57 | Groups[group][domain] += 1 58 | else: 59 | GroupsWithInternalMembers.add(group) 60 | 61 | for group, domains in sorted(iter(Groups.items())): 62 | if group in GroupsWithInternalMembers: 63 | continue 64 | if not AGGREGATE_DOMAINS: 65 | for domain, count in sorted(iter(domains.items())): 66 | outputCSV.writerow({'group': group, 67 | 'domain': domain, 68 | 'count': count}) 69 | else: 70 | total = 0 71 | domainsList = [] 72 | for domain, count in sorted(iter(domains.items())): 73 | domainsList.append(domain) 74 | total += count 75 | outputCSV.writerow({'group': group, 76 | 'domain': DELIMITER.join(domainsList), 77 | 'count': total}) 78 | 79 | if inputFile != sys.stdin: 80 | inputFile.close() 81 | if outputFile != sys.stdout: 82 | outputFile.close() 83 | -------------------------------------------------------------------------------- /GetGuardianStudentEmails.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Make a CSV file showing guardians with student emails 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get guardians 9 | # $ gam print guardians > ./Guardians.csv 10 | # 2: Get student emails, 11 | # Use one of the following to select a collection of users 12 | # ::= 13 | # (all users)| 14 | # (users )| 15 | # (group|group_ns )| 16 | # (groups|groups_ns )| 17 | # (ou|ou_ns )| 18 | # (ou_and_children|ou_and_children_ns )| 19 | # (ous|ous_ns )| 20 | # (ous_and_children|ous_and_children_ns )| 21 | # (courseparticipants )| 22 | # (students )| 23 | # (teachers )| 24 | # (file [charset ] [delimiter ])| 25 | # (csvfile (:)+ [charset ] [columndelimiter ] [quotechar ] 26 | # [fields ] (matchfield )* [delimiter ]) 27 | # $ gam print users fields primaryemail,id > ./Students.csv 28 | # 3: Output an updated version of Guardians.csv with student emails obtained from Students.csv 29 | # $ python3 GetGuardianStudentEmails.py ./Students.csv ./Guardians.csv ./UpdatedGuardians.csv 30 | """ 31 | 32 | import csv 33 | import sys 34 | 35 | QUOTE_CHAR = '"' # Adjust as needed 36 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 37 | 38 | studentEmails = {} 39 | 40 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 41 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 42 | else: 43 | inputFile = sys.stdin 44 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 45 | studentEmails[row['id']] = row['primaryEmail'] 46 | if inputFile != sys.stdin: 47 | inputFile.close() 48 | 49 | if len(sys.argv) > 2: 50 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 51 | else: 52 | sys.stderr.write('Error: Guardians file missing') 53 | sys.exit(1) 54 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 55 | 56 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 57 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 58 | else: 59 | outputFile = sys.stdout 60 | outputCSV = csv.DictWriter(outputFile, inputCSV.fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 61 | outputCSV.writeheader() 62 | 63 | for row in inputCSV: 64 | row['studentEmail'] = studentEmails.get(row['studentId'], row['studentEmail']) 65 | outputCSV.writerow(row) 66 | 67 | inputFile.close() 68 | if outputFile != sys.stdout: 69 | outputFile.close() 70 | -------------------------------------------------------------------------------- /GetLabelsCountSize.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Create a CSV file that totals message label data: count and size 4 | # Customize: DELIMITER, SHOW_TOTALS 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get the label data 10 | # Single user 11 | # $ gam redirect csv ./LabelData.csv user user@domain.com print messages showlabels showsize headers "" delimiter '|' 12 | # Multiple users; replace all users as desired 13 | # $ gam config auto_batch_min 1 redirect csv ./LabelData.csv multiprocess all users print messages showlabels showsize headers "" delimiter '|' 14 | # 2: python3 GetLabelsCountSize.py LabelData.csv LabelSummary.csv 15 | """ 16 | 17 | import csv 18 | import sys 19 | 20 | QUOTE_CHAR = '"' # Adjust as needed 21 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 22 | 23 | DELIMITER = '|' # Must match delimiter from command line 24 | SHOW_TOTALS = False # False: Don't show total label counts/size for each user; True: Do show 25 | 26 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 27 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 28 | else: 29 | outputFile = sys.stdout 30 | outputCSV = csv.DictWriter(outputFile, ['User', 'Label', 'Count', 'SizeEstimate'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 31 | outputCSV.writeheader() 32 | 33 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 34 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 35 | else: 36 | inputFile = sys.stdin 37 | 38 | Users = {} 39 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 40 | user = row['User'] 41 | Users.setdefault(user, {}) 42 | size = int(row['SizeEstimate']) 43 | for label in row['Labels'].split(DELIMITER): 44 | Users[user].setdefault(label, {'Count': 0, 'SizeEstimate': 0}) 45 | Users[user][label]['Count'] +=1 46 | Users[user][label]['SizeEstimate'] += size 47 | 48 | for user in sorted(Users): 49 | count = 0 50 | size = 0 51 | for label, data in sorted(iter(Users[user].items())): 52 | count += data['Count'] 53 | size += data['SizeEstimate'] 54 | outputCSV.writerow({'User': user, 55 | 'Label': label, 56 | 'Count': data['Count'], 57 | 'SizeEstimate': data['SizeEstimate']}) 58 | if SHOW_TOTALS: 59 | outputCSV.writerow({'User': user, 60 | 'Label': 'Total', 61 | 'Count': count, 62 | 'SizeEstimate': size}) 63 | 64 | if inputFile != sys.stdin: 65 | inputFile.close() 66 | if outputFile != sys.stdout: 67 | outputFile.close() 68 | -------------------------------------------------------------------------------- /GetLicenseHolders.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: From a collection of users, show the ones holding licenses. 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get Licenses, all or selected 9 | # $ gam redirect csv ./Licenses.csv print licenses 10 | # $ gam redirect csv ./Licenses.csv print licenses sku 11 | # 2: Get Users 12 | # gam redirect csv ./Users.csv print users primaryEmail 13 | # 3: From those lists of Licenses and Users, output a CSV file showing the licenses held by users in Users.csv 14 | # $ python3 GetLicenseHolders.py ./Licenses.csv ./Users.csv ./LicenseHolders.csv 15 | # 4: Inspect LicenseHolders.csv, verify that it makes sense and then proceed if desired 16 | # 5: Process LicenseHolders.csv 17 | # Delete licenses: gam csv ./LicenseHolders.csv gam user "~userId" delete license "~skuId" 18 | # Update licenses: gam csv ./LicenseHolders.csv gam user "~userId" update license from "~skuId" 19 | """ 20 | 21 | import csv 22 | import sys 23 | 24 | QUOTE_CHAR = '"' # Adjust as needed 25 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 26 | 27 | licenses = {} 28 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 29 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 30 | licenseFieldNames = inputCSV.fieldnames 31 | for row in inputCSV: 32 | user = row['userId'] 33 | licenses.setdefault(user, []) 34 | licenses[user].append(row) 35 | inputFile.close() 36 | 37 | if sys.argv[2] != '-': 38 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 39 | else: 40 | inputFile = sys.stdin 41 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 42 | 43 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 44 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 45 | else: 46 | outputFile = sys.stdout 47 | outputCSV = csv.DictWriter(outputFile, licenseFieldNames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 48 | outputCSV.writeheader() 49 | 50 | for row in inputCSV: 51 | for userLicense in licenses.get(row['primaryEmail'], []): 52 | outputCSV.writerow(userLicense) 53 | 54 | if inputFile != sys.stdin: 55 | inputFile.close() 56 | if outputFile != sys.stdout: 57 | outputFile.close() 58 | -------------------------------------------------------------------------------- /GetLinkSharedDriveACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), get all drive file ACLs for files shared with anyone/domain withlink 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 9 | # $ gam config auto_batch_min 1 redirect csv ./filelistperms.csv multiprocess all users print filelist fields id,name,permissions,owners.emailaddress,linksharemetadata,resourcekey,mimetype,webviewlink query "(visibility='anyoneWithLink' or visibility='domainWithLink')" 10 | # Note!!! The visibility query will find files shared to your primary domain; it will not find files shared only to other domains. 11 | # 2: From that list of ACLs, output a CSV file with headers "Owner,driveFileId,driveFileTitle,mimeType,permissionId,role,allowFileDiscovery,resourceKey,linkShareMetadata.securityUpdateEligible,linkShareMetadata.securityUpdateEnabled,webViewLink" 12 | # that lists the driveFileIds, permissionIds and link share details for all ACLs shared with anyone/domain withlink 13 | # $ python3 GetLinkSharedDriveACLs.py filelistperms.csv linksharedperms.csv 14 | """ 15 | 16 | import csv 17 | import re 18 | import sys 19 | 20 | FILE_NAME = 'name' 21 | ALT_FILE_NAME = 'title' 22 | 23 | QUOTE_CHAR = '"' # Adjust as needed 24 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 25 | 26 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 27 | 28 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 29 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 30 | else: 31 | outputFile = sys.stdout 32 | outputCSV = csv.DictWriter(outputFile, 33 | ['Owner', 'driveFileId', 'driveFileTitle', 'mimeType', 'permissionId', 'role', 'allowFileDiscovery', 34 | 'resourceKey', 'linkShareMetadata.securityUpdateEligible', 'linkShareMetadata.securityUpdateEnabled', 35 | 'webViewLink'], 36 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 37 | outputCSV.writeheader() 38 | 39 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 40 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 41 | else: 42 | inputFile = sys.stdin 43 | 44 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 45 | for k, v in iter(row.items()): 46 | mg = PERMISSIONS_N_TYPE.match(k) 47 | if mg and v in {'anyone', 'domain'}: 48 | permissions_N = mg.group(1) 49 | allowFileDiscovery = row.get(f'permissions.{permissions_N}.allowFileDiscovery', str(row.get(f'permissions.{permissions_N}.withLink') == 'False')) 50 | if allowFileDiscovery == 'False': 51 | outputCSV.writerow({'Owner': row['owners.0.emailAddress'], 52 | 'driveFileId': row['id'], 53 | 'driveFileTitle': row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown')), 54 | 'mimeType': row['mimeType'], 55 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 56 | 'role': row[f'permissions.{permissions_N}.role'], 57 | 'allowFileDiscovery': allowFileDiscovery, 58 | 'linkShareMetadata.securityUpdateEligible': row.get('linkShareMetadata.securityUpdateEligible', ''), 59 | 'linkShareMetadata.securityUpdateEnabled': row.get('linkShareMetadata.securityUpdateEnabled', ''), 60 | 'resourceKey': row.get('resourceKey', ''), 61 | 'webViewLink': row.get('webViewLink', '')}) 62 | 63 | if inputFile != sys.stdin: 64 | inputFile.close() 65 | if outputFile != sys.stdout: 66 | outputFile.close() 67 | -------------------------------------------------------------------------------- /GetMultipleParentsRoot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User, delete root as a parent of files that have root as a parent and other parents 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get all of the files for testuser@domain.com 9 | # $ gam redirect csv ./userfiles.csv user testuser@domain.com print filelist fields id,title,parents,owners.emailaddress 10 | # 2: From that list of files, output a CSV file with headers "Owner,driveFileId,driveFileTitle" 11 | # that lists the driveFileIds for all files that have root as a parent and other parents 12 | # $ python3 GetMultipleParentsRoot.py ./userfiles.csv ./rootparents.csv 13 | # 3: Inspect rootparents.csv, verify that it makes sense and then proceed 14 | # 4: If desired, delete root as parent 15 | # $ gam redirect stdout ./deleterootparent.out multiprocess csv ./rootparents.csv gam user "~Owner" update drivefile "~driveFileId" removeparents root 16 | """ 17 | 18 | import csv 19 | import re 20 | import sys 21 | 22 | FILE_NAME = 'name' 23 | ALT_FILE_NAME = 'title' 24 | 25 | QUOTE_CHAR = '"' # Adjust as needed 26 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 27 | 28 | PARENTS_N_ID = re.compile(r"parents.(\d+).id") 29 | 30 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 31 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 32 | else: 33 | outputFile = sys.stdout 34 | outputCSV = csv.DictWriter(outputFile, ['Owner', 'driveFileId', 'driveFileTitle'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 35 | outputCSV.writeheader() 36 | 37 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 38 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 39 | else: 40 | inputFile = sys.stdin 41 | 42 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 43 | if row['parents'] and int(row['parents']) <= 1: 44 | continue 45 | for k, v in iter(row.items()): 46 | mg = PARENTS_N_ID.match(k) 47 | if mg and v: 48 | parents_N = mg.group(1) 49 | if row[f'parents.{parents_N}.isRoot'] == 'True': 50 | outputCSV.writerow({'Owner': row['owners.0.emailAddress'], 51 | 'driveFileId': row['id'], 52 | 'driveFileTitle': row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown'))}) 53 | continue 54 | 55 | if inputFile != sys.stdin: 56 | inputFile.close() 57 | if outputFile != sys.stdout: 58 | outputFile.close() 59 | -------------------------------------------------------------------------------- /GetNonDomainFilterForwards.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For Gmail User(s), list/delete all filters that forward email outside of a list of specified domains 4 | # Customize: Set DOMAIN_LIST. 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get filters, if you don't want all users, replace all users with your user selection in the command below 10 | # $ Example: gam config auto_batch_min 1 redirect csv ./filters.csv multiprocess all users print filters 11 | # 2: From that list of filters, output a CSV file that lists the filters that forward email outside of the specified domains. 12 | # $ python3 GetNonDomainFilterForwards.py filters.csv outsidefilters.csv 13 | # 3: Inspect outsidefilters.csv, verify that it makes sense and then proceed 14 | # 4: If desired, delete the filters 15 | # $ gam csv ./outsidefilters.csv gam user "~User" delete filter "~id" 16 | """ 17 | 18 | import csv 19 | import re 20 | import sys 21 | 22 | # Substitute your domain(s) in the list below, e.g., DOMAIN_LIST = ['domain.com',] DOMAIN_LIST = ['domain1.com', 'domain2.com',] 23 | DOMAIN_LIST = ['domain.com',] 24 | 25 | QUOTE_CHAR = '"' # Adjust as needed 26 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 27 | 28 | FORWARD_DOMAIN = re.compile(r"^forward .*@(.*)$") 29 | 30 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 31 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 32 | else: 33 | outputFile = sys.stdout 34 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 35 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 36 | else: 37 | inputFile = sys.stdin 38 | 39 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 40 | outputCSV = csv.DictWriter(outputFile, inputCSV.fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 41 | outputCSV.writeheader() 42 | 43 | for row in inputCSV: 44 | v = row.get('forward', '') 45 | if v: 46 | mg = FORWARD_DOMAIN.match(v) 47 | if mg and mg.group(1) not in DOMAIN_LIST: 48 | outputCSV.writerow(row) 49 | 50 | if inputFile != sys.stdin: 51 | inputFile.close() 52 | if outputFile != sys.stdout: 53 | outputFile.close() 54 | -------------------------------------------------------------------------------- /GetNonSharedFiles.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), show all non-shared drive files 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 9 | # $ gam config auto_batch_min 1 redirect csv ./filelistperms.csv multiprocess all users print filelist id title permissions 10 | # $ gam redirect csv ./filelistperms.csv user user@domain.com print filelist id title permissions 11 | # 2: From that list of ACLs, output a CSV file that lists only the non-shared files. 12 | # $ python3 GetNonSharedFiles.py filelistperms.csv nonsharedfiles.csv 13 | """ 14 | 15 | import csv 16 | import re 17 | import sys 18 | 19 | FILE_NAME = 'name' 20 | ALT_FILE_NAME = 'title' 21 | 22 | QUOTE_CHAR = '"' # Adjust as needed 23 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 24 | 25 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 26 | 27 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 28 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 29 | else: 30 | outputFile = sys.stdout 31 | 32 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 33 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 34 | else: 35 | inputFile = sys.stdin 36 | 37 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 38 | outputCSV = csv.DictWriter(outputFile, inputCSV.fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 39 | outputCSV.writeheader() 40 | 41 | for row in inputCSV: 42 | shared = False 43 | for k, v in iter(row.items()): 44 | mg = PERMISSIONS_N_TYPE.match(k) 45 | if mg: 46 | if v == 'user': 47 | permissions_N = mg.group(1) 48 | role = row[f'permissions.{permissions_N}.role'] 49 | emailAddress = row.get(f'permissions.{permissions_N}.emailAddress', '').lower() 50 | if (role and role != 'owner') or (emailAddress and emailAddress != row['Owner'].lower()): 51 | shared = True 52 | elif v: 53 | shared = True 54 | if not shared: 55 | outputCSV.writerow(row) 56 | 57 | if inputFile != sys.stdin: 58 | inputFile.close() 59 | if outputFile != sys.stdout: 60 | outputFile.close() 61 | -------------------------------------------------------------------------------- /GetOrgUnitCrOSCounts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Show the number of CrOS devices in each Org Unit 4 | # Customize: Set SHOW_STATUS,SHOW_TOTALS 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get OrgUnits 10 | # $ gam redirect csv ./OrgUnits.csv print ous 11 | # 2: Get CrOS devices; omit status if you don't want status info 12 | # $ gam redirect csv ./CrOS.csv print cros ou status 13 | # 3: From those lists of Org Units and CrOS devices, output a CSV file with CrOS device counts for each Org Unit 14 | # $ python3 GetOrgUnitCrOSCounts.py ./OrgUnits.csv ./CrOS.csv ./OrgUnitCrOSCounts.csv 15 | """ 16 | 17 | import csv 18 | import sys 19 | 20 | SHOW_STATUS = True # False if you don't want status information 21 | SHOW_TOTALS = True # False if you don't want totals 22 | 23 | QUOTE_CHAR = '"' # Adjust as needed 24 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 25 | 26 | orgUnits = {} 27 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 28 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 29 | inputFieldNames = inputCSV.fieldnames 30 | if 'orgUnitPath' not in inputFieldNames: 31 | sys.stderr.write(f'Error: no header orgUnitPath in Org Units file {sys.argv[1]} field names: {",".join(inputFieldNames)}\n') 32 | sys.exit(1) 33 | for row in inputCSV: 34 | orgUnits[row['orgUnitPath']] = {'devices' : 0, 'statusValues': {}} 35 | inputFile.close() 36 | 37 | if sys.argv[2] != '-': 38 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 39 | else: 40 | inputFile = sys.stdin 41 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 42 | inputFieldNames = inputCSV.fieldnames 43 | if 'orgUnitPath' not in inputFieldNames: 44 | sys.stderr.write(f'Error: no header orgUnitPath in CrOS file {sys.argv[2]} field names: {",".join(inputFieldNames)}\n') 45 | sys.exit(1) 46 | fieldnames = ['orgUnitPath', 'devices'] 47 | checkStatus = SHOW_STATUS and 'status' in inputFieldNames 48 | statusValues = set() 49 | 50 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 51 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 52 | else: 53 | outputFile = sys.stdout 54 | 55 | totals = {'devices' : 0, 'statusValues': {}} 56 | for row in inputCSV: 57 | orgUnitPath = row['orgUnitPath'] 58 | if orgUnitPath not in orgUnits: 59 | orgUnits[orgUnitPath] = {'devices' : 0, 'statusValues': {}} 60 | orgUnits[orgUnitPath]['devices'] += 1 61 | if checkStatus: 62 | statusValue = row['status'] 63 | if statusValue not in statusValues: 64 | statusValues.add(statusValue) 65 | orgUnits[orgUnitPath]['statusValues'].setdefault(statusValue, 0) 66 | orgUnits[orgUnitPath]['statusValues'][statusValue] += 1 67 | if checkStatus: 68 | for statusValue in sorted(statusValues): 69 | fieldnames.append(f'status.{statusValue}') 70 | totals['statusValues'][statusValue] = 0 71 | 72 | outputCSV = csv.DictWriter(outputFile, fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 73 | outputCSV.writeheader() 74 | for orgUnit, counts in sorted(iter(orgUnits.items())): 75 | row = {'orgUnitPath': orgUnit, 'devices': counts['devices']} 76 | totals['devices'] += counts['devices'] 77 | if checkStatus: 78 | for statusValue in statusValues: 79 | count = counts['statusValues'].get(statusValue, 0) 80 | row[f'status.{statusValue}'] = count 81 | totals['statusValues'][statusValue] += count 82 | outputCSV.writerow(row) 83 | if SHOW_TOTALS: 84 | row = {'orgUnitPath': 'Totals', 'devices': totals['devices']} 85 | if checkStatus: 86 | for statusValue, count in iter(totals['statusValues'].items()): 87 | row[f'status.{statusValue}'] = count 88 | outputCSV.writerow(row) 89 | 90 | if inputFile != sys.stdin: 91 | inputFile.close() 92 | if outputFile != sys.stdout: 93 | outputFile.close() 94 | -------------------------------------------------------------------------------- /GetPermissionsByPath.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User, show all drive file ACLs except those indicating the user as owner, one ACL per row per file path 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Use print filelist to get selected ACLs 9 | # Syntax: gam print filelist [anyowner|(showownedby any|me|others)] 10 | # [query ] [fullquery ] [select |orphans] [depth ] [showparent] [filepath|fullpath] 11 | # For a full description of print filelist, see: https://github.com/taers232c/GAMADV-XTD/wiki/Users-Drive-Files 12 | # Example: gam redirect csv ./filelistperms.csv user testuser@domain.com print filelist id title permissions fullpath 13 | # 2: From that list of ACLs, output a CSV file with headers "path,type,value,role" 14 | # that lists the file path and ACL for all ACLs except those indicating the user as owner. 15 | # There is one row per ACL per file path 16 | # $ python3 GetPermissionsByPath.py filelistperms.csv pathperms.csv 17 | """ 18 | 19 | import csv 20 | import re 21 | import sys 22 | 23 | FILE_NAME = 'name' 24 | ALT_FILE_NAME = 'title' 25 | 26 | QUOTE_CHAR = '"' # Adjust as needed 27 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 28 | 29 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 30 | 31 | def getWithLink(r, n): 32 | withLink = r.get(f'permissions.{n}.withLink') 33 | if withLink is not None: 34 | return withLink == 'True' 35 | withLink = r.get(f'permissions.{n}.allowFileDiscovery') 36 | if withLink is not None: 37 | return withLink == 'False' 38 | return False 39 | 40 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 41 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 42 | else: 43 | outputFile = sys.stdout 44 | outputCSV = csv.DictWriter(outputFile, ['path', 'type', 'value', 'role'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 45 | outputCSV.writeheader() 46 | 47 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 48 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 49 | else: 50 | inputFile = sys.stdin 51 | 52 | pathPerms = [] 53 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 54 | numPaths = int(row.get('paths', '0')) 55 | if numPaths > 0: 56 | pathList = [] 57 | for p in range(0, numPaths): 58 | pathList.append(row[f'path.{p}']) 59 | else: 60 | pathList = [row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown'))] 61 | for k, v in iter(row.items()): 62 | mg = PERMISSIONS_N_TYPE.match(k) 63 | if mg and v: 64 | permissions_N = mg.group(1) 65 | if v == 'domain': 66 | value = row[f'permissions.{permissions_N}.domain'] 67 | if getWithLink(row, permissions_N): 68 | v += 'WithLink' 69 | elif v in ['user', 'group']: 70 | if row.get(f'permissions.{permissions_N}.deleted') == 'True': 71 | continue 72 | value = row[f'permissions.{permissions_N}.emailAddress'] 73 | else: 74 | value = '' 75 | if getWithLink(row, permissions_N): 76 | v += 'WithLink' 77 | role = row[f'permissions.{permissions_N}.role'] 78 | if v != 'user' or role != 'owner' or value != row['Owner']: 79 | for path in pathList: 80 | pathPerms.append({'path': path, 'type': v, 'value': value, 'role': role}) 81 | outputCSV.writerows(sorted(pathPerms, key=lambda row: row['path'])) 82 | 83 | if inputFile != sys.stdin: 84 | inputFile.close() 85 | if outputFile != sys.stdout: 86 | outputFile.close() 87 | -------------------------------------------------------------------------------- /GetSharedFileDeletedPermissions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), show all shared drive file permissions for deleted groups/users 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 9 | # $ gam config auto_batch_min 1 redirect csv ./filelistperms.csv multiprocess all users print filelist fields id,title,permissions,owners.emailaddress,mimetype pm deleted true em pmfilter 10 | # $ gam redirect csv ./filelistperms.csv user user@domain.com print filelist fields id,title,permissions,owners.emailaddress,mimetype pm deleted true em pmfilter 11 | # 2: From that list of ACLs, output a CSV file that lists the shared file permissions 12 | # $ python3 GetSharedFileDeletedPermissions.py filelistperms.csv deleteperms.csv 13 | # 3: Inspect deleteperms.csv, verify that it makes sense and then proceed 14 | # 4: If desired, delete the ACLs 15 | # $ gam csv ./deleteperms.csv gam user "~Owner" delete drivefileacl "~driveFileId" "~permissionId" 16 | """ 17 | 18 | import csv 19 | import re 20 | import sys 21 | 22 | FILE_NAME = 'name' 23 | ALT_FILE_NAME = 'title' 24 | 25 | QUOTE_CHAR = '"' # Adjust as needed 26 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 27 | 28 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 29 | 30 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 31 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 32 | else: 33 | outputFile = sys.stdout 34 | outputCSV = csv.DictWriter(outputFile, ['Owner', 'driveFileId', 'driveFileTitle', 'mimeType', 35 | 'permissionId', 'role', 'type'], 36 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 37 | outputCSV.writeheader() 38 | 39 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 40 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 41 | else: 42 | inputFile = sys.stdin 43 | 44 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 45 | for k, v in iter(row.items()): 46 | mg = PERMISSIONS_N_TYPE.match(k) 47 | if mg and v: 48 | permissions_N = mg.group(1) 49 | if row.get(f'permissions.{permissions_N}.deleted') == 'True': 50 | outputCSV.writerow({'Owner': row['owners.0.emailAddress'], 51 | 'driveFileId': row['id'], 52 | 'driveFileTitle': row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown')), 53 | 'mimeType': row['mimeType'], 54 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 55 | 'role': row[f'permissions.{permissions_N}.role'], 56 | 'type': v}) 57 | 58 | if inputFile != sys.stdin: 59 | inputFile.close() 60 | if outputFile != sys.stdout: 61 | outputFile.close() 62 | -------------------------------------------------------------------------------- /GetSharedFilePermissions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), show all shared file permissions 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 9 | # $ gam config auto_batch_min 1 redirect csv ./filelistperms.csv multiprocess all users print filelist fields id,title,permissions,owners.emailaddress,mimetype pm not role owner em pmfilter 10 | # $ gam redirect csv ./filelistperms.csv user user@domain.com print filelist fields id,title,permissions,owners.emailaddress pm not role owner em pmfilter 11 | # 2: From that list of ACLs, output a CSV file that lists the shared file permissions 12 | # $ python3 GetSharedFilePermissions.py filelistperms.csv deleteperms.csv 13 | # 3: Inspect deleteperms.csv, verify that it makes sense and then proceed 14 | # 4: If desired, delete the ACLs 15 | # $ gam csv ./deleteperms.csv gam user "~Owner" delete drivefileacl "~driveFileId" "~permissionId" 16 | """ 17 | 18 | import csv 19 | import re 20 | import sys 21 | 22 | FILE_NAME = 'name' 23 | ALT_FILE_NAME = 'title' 24 | 25 | QUOTE_CHAR = '"' # Adjust as needed 26 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 27 | 28 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 29 | 30 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 31 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 32 | else: 33 | outputFile = sys.stdout 34 | outputCSV = csv.DictWriter(outputFile, ['Owner', 'driveFileId', 'driveFileTitle', 'mimeType', 35 | 'permissionId', 'role', 'type', 'emailAddress', 'domain', 'allowFileDiscovery'], 36 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 37 | outputCSV.writeheader() 38 | 39 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 40 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 41 | else: 42 | inputFile = sys.stdin 43 | 44 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 45 | for k, v in iter(row.items()): 46 | mg = PERMISSIONS_N_TYPE.match(k) 47 | if mg and v: 48 | permissions_N = mg.group(1) 49 | if row[f'permissions.{permissions_N}.role'] == 'owner': 50 | continue 51 | if v in ['user', 'group']: 52 | allowFileDiscovery = '' 53 | emailAddress = row[f'permissions.{permissions_N}.emailAddress'].lower() 54 | domain = emailAddress[emailAddress.find('@')+1:] 55 | elif v == 'domain': 56 | allowFileDiscovery = row.get(f'permissions.{permissions_N}.allowFileDiscovery', 57 | str(row.get(f'permissions.{permissions_N}.withLink') == 'False')) 58 | emailAddress = '' 59 | domain = row[f'permissions.{permissions_N}.domain'].lower() 60 | else: #anyone 61 | allowFileDiscovery = row.get(f'permissions.{permissions_N}.allowFileDiscovery', 62 | str(row.get(f'permissions.{permissions_N}.withLink') == 'False')) 63 | emailAddress = '' 64 | domain = '' 65 | outputCSV.writerow({'Owner': row['owners.0.emailAddress'], 66 | 'driveFileId': row['id'], 67 | 'driveFileTitle': row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown')), 68 | 'mimeType': row['mimeType'], 69 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 70 | 'role': row[f'permissions.{permissions_N}.role'], 71 | 'type': v, 72 | 'emailAddress': emailAddress, 73 | 'domain': domain, 74 | 'allowFileDiscovery': allowFileDiscovery}) 75 | 76 | if inputFile != sys.stdin: 77 | inputFile.close() 78 | if outputFile != sys.stdout: 79 | outputFile.close() 80 | -------------------------------------------------------------------------------- /GetSharedFiles.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), show all shared drive files 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 9 | # $ gam config auto_batch_min 1 redirect csv ./filelistperms.csv multiprocess all users print filelist fields id,title,permissions,mimetype pm not role owner em pmfilter 10 | # $ gam redirect csv ./filelistperms.csv user user@domain.com print filelist fields id,title,permissions pm not role owner em pmfilter 11 | # 2: From that list of ACLs, output a CSV file that lists only the shared files. 12 | # $ python3 GetSharedFiles.py filelistperms.csv sharedfiles.csv 13 | """ 14 | 15 | import csv 16 | import re 17 | import sys 18 | 19 | FILE_NAME = 'name' 20 | ALT_FILE_NAME = 'title' 21 | 22 | QUOTE_CHAR = '"' # Adjust as needed 23 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 24 | 25 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 26 | 27 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 28 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 29 | else: 30 | outputFile = sys.stdout 31 | 32 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 33 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 34 | else: 35 | inputFile = sys.stdin 36 | 37 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 38 | outputCSV = csv.DictWriter(outputFile, inputCSV.fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 39 | outputCSV.writeheader() 40 | 41 | for row in inputCSV: 42 | shared = False 43 | for k, v in iter(row.items()): 44 | mg = PERMISSIONS_N_TYPE.match(k) 45 | if mg: 46 | if v == 'user': 47 | permissions_N = mg.group(1) 48 | role = row[f'permissions.{permissions_N}.role'] 49 | emailAddress = row.get(f'permissions.{permissions_N}.emailAddress', '').lower() 50 | if (role and role != 'owner') or (emailAddress and emailAddress != row['Owner'].lower()): 51 | shared = True 52 | elif v: 53 | shared = True 54 | if shared: 55 | outputCSV.writerow(row) 56 | 57 | if inputFile != sys.stdin: 58 | inputFile.close() 59 | if outputFile != sys.stdout: 60 | outputFile.close() 61 | -------------------------------------------------------------------------------- /GetSharedWithAnyoneDriveACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), get all drive file ACLs for files shared with anyone 4 | # Customize: Set DESIRED_ALLOWFILEDISCOVERY. 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 10 | # You can minimize the number of files downloaded by using a query with the visibility keyword. 11 | # DESIRED_ALLOWFILEDISCOVERY = 'Any' - query "(visibility='anyoneCanFind' or visibility='anyoneWithLink')" 12 | # DESIRED_ALLOWFILEDISCOVERY = 'True' - query "visibility='anyoneCanFind'" 13 | # DESIRED_ALLOWFILEDISCOVERY = 'False' - query "visibility='anyoneWithLink'" 14 | # Change the query as desired. 15 | # $ gam config auto_batch_min 1 redirect csv ./filelistperms.csv multiprocess all users print filelist fields id,name,basicpermissions,owners.emailaddress,mimetype 16 | # 2: From that list of ACLs, output a CSV file with headers "Owner,driveFileId,driveFileTitle,mimeType,permissionId,role,allowFileDiscovery" 17 | # that lists the driveFileIds and permissionIds for all ACLs shared with anyone 18 | # (n.b., driveFileTitle, mimeType, role and allowFileDiscovery are not used in the next step, they are included for documentation purposes) 19 | # $ python3 GetSharedWithAnyoneDriveACLs.py filelistperms.csv deleteperms.csv 20 | # 3: Inspect deleteperms.csv, verify that it makes sense and then proceed 21 | # 4: If desired, delete the ACLs 22 | # $ gam csv ./deleteperms.csv gam user "~Owner" delete drivefileacl "~driveFileId" "~permissionId" 23 | """ 24 | 25 | import csv 26 | import re 27 | import sys 28 | 29 | FILE_NAME = 'name' 30 | ALT_FILE_NAME = 'title' 31 | 32 | # Specify desired value of allowFileDiscovery field: 'True', 'False', 'Any' (matches True and False) 33 | # allowFileDiscovery False = withLink True 34 | # allowFileDiscovery True = withLink False 35 | DESIRED_ALLOWFILEDISCOVERY = 'Any' 36 | 37 | QUOTE_CHAR = '"' # Adjust as needed 38 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 39 | 40 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 41 | 42 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 43 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 44 | else: 45 | outputFile = sys.stdout 46 | outputCSV = csv.DictWriter(outputFile, ['Owner', 'driveFileId', 'driveFileTitle', 'mimeType', 47 | 'permissionId', 'role', 'allowFileDiscovery'], 48 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 49 | outputCSV.writeheader() 50 | 51 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 52 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 53 | else: 54 | inputFile = sys.stdin 55 | 56 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 57 | for k, v in iter(row.items()): 58 | mg = PERMISSIONS_N_TYPE.match(k) 59 | if mg and v == 'anyone': 60 | permissions_N = mg.group(1) 61 | allowFileDiscovery = row.get(f'permissions.{permissions_N}.allowFileDiscovery', 62 | str(row.get(f'permissions.{permissions_N}.withLink') == 'False')) 63 | if DESIRED_ALLOWFILEDISCOVERY in ('Any', allowFileDiscovery): 64 | outputCSV.writerow({'Owner': row['owners.0.emailAddress'], 65 | 'driveFileId': row['id'], 66 | 'driveFileTitle': row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown')), 67 | 'mimeType': row['mimeType'], 68 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 69 | 'role': row[f'permissions.{permissions_N}.role'], 70 | 'allowFileDiscovery': allowFileDiscovery}) 71 | 72 | if inputFile != sys.stdin: 73 | inputFile.close() 74 | if outputFile != sys.stdout: 75 | outputFile.close() 76 | -------------------------------------------------------------------------------- /GetSharedWithGroupDriveACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), show all drive file ACLs for files shared with selected groups. 4 | # Customize: Set GROUP_LIST and DOMAIN_LIST. 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 10 | # $ gam config auto_batch_min 1 redirect csv ./filelistperms.csv multiprocess all users print filelist fields id,title,permissions,owners.emailaddress,mimetype pm type group em pmfilter 11 | # 2: From that list of ACLs, output a CSV file with headers "Owner,driveFileId,driveFileTitle,mimeType,permissionId,role,emailAddress" 12 | # that lists the driveFileIds and permissionIds for all ACLs with the desired groups 13 | # (n.b., driveFileTitle, mimeType, role, and emailAddress are not used in the next step, they are included for documentation purposes) 14 | # $ python3 GetSharedWithGroupDriveACLs.py filelistperms.csv deleteperms.csv 15 | # 3: Inspect deleteperms.csv, verify that it makes sense and then proceed 16 | # 4: If desired, delete the ACLs 17 | # $ gam csv ./deleteperms.csv gam user "~Owner" delete drivefileacl "~driveFileId" "~permissionId" 18 | """ 19 | 20 | import csv 21 | import re 22 | import sys 23 | 24 | FILE_NAME = 'name' 25 | ALT_FILE_NAME = 'title' 26 | 27 | # You can operate on specific groups or specific domains or operate on all groups in all domains. 28 | # For all groups in all domains, set GROUP_LIST = [] and DOMAIN_LIST = [] 29 | 30 | # Substitute your specific group(s) in the list below, e.g., GROUP_LIST = ['group1@domain.com',] GROUP_LIST = ['group1@domain.com', 'group2@domain.com',] 31 | # The list should be empty if you're only specifiying domains in DOMAIN_LIST, e.g. GROUP_LIST = [] 32 | GROUP_LIST = ['group@domain.com',] 33 | # Substitute your specific domain(s) in the list below if you want all groups in the domain, e.g., DOMAIN_LIST = ['domain.com',] DOMAIN_LIST = ['domain1.com', 'domain2.com',] 34 | # The list should be empty if you're only specifiying groups in GROUP_LIST, e.g. DOMAIN__LIST = [] 35 | DOMAIN_LIST = ['domain.com',] 36 | 37 | QUOTE_CHAR = '"' # Adjust as needed 38 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 39 | 40 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 41 | 42 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 43 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 44 | else: 45 | outputFile = sys.stdout 46 | outputCSV = csv.DictWriter(outputFile, ['Owner', 'driveFileId', 'driveFileTitle', 'mimeType', 47 | 'permissionId', 'role', 'emailAddress'], 48 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 49 | outputCSV.writeheader() 50 | 51 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 52 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 53 | else: 54 | inputFile = sys.stdin 55 | 56 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 57 | for k, v in iter(row.items()): 58 | mg = PERMISSIONS_N_TYPE.match(k) 59 | if mg and v == 'group': 60 | permissions_N = mg.group(1) 61 | emailAddress = row.get(f'permissions.{permissions_N}.emailAddress', '').lower() 62 | domain = row[f'permissions.{permissions_N}.domain'].lower() 63 | if ((not GROUP_LIST and not DOMAIN_LIST) or 64 | (GROUP_LIST and emailAddress in GROUP_LIST) or 65 | (DOMAIN_LIST and domain in DOMAIN_LIST)): 66 | outputCSV.writerow({'Owner': row['owners.0.emailAddress'], 67 | 'driveFileId': row['id'], 68 | 'driveFileTitle': row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown')), 69 | 'mimeType': row['mimeType'], 70 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 71 | 'role': row[f'permissions.{permissions_N}.role'], 72 | 'emailAddress': emailAddress}) 73 | 74 | if inputFile != sys.stdin: 75 | inputFile.close() 76 | if outputFile != sys.stdout: 77 | outputFile.close() 78 | -------------------------------------------------------------------------------- /GetSharedWithListOfDisabledUsersDriveACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), show all drive file ACLs for files shared exclusively with a list of users from a CSV file 4 | # Customize: Set USER_HEADERS 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Prepare a CSV file with a list of user email addresses; set USER_HEADERS to identify the column(s) containing the email addresses 10 | # $ more Users.csv 11 | # email 12 | # testuser1@domain1.com 13 | # testuser2@domain1.com 14 | # testuser1@domain2.com 15 | # testuser2@domain2.com 16 | # ... 17 | # $ more QuadUsers.csv 18 | # email1,email2,email3,email4 19 | # testuser1@domain1.com,testuser2@domain1.com,testuser1@domain2.com,testuser2@domain2.com 20 | # ... 21 | # 2: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 22 | # If you don't want all files, use query/fullquery 23 | # $ gam config auto_batch_min 1 redirect csv ./filelistperms.csv multiprocess all users print filelist fields id,title,permissions,owners.emailaddress,mimetype 24 | # 3: From that list of ACLs, output a CSV file with the same headers as the input CSV file 25 | # only including files that are shared exclusively with the users in Users.csv 26 | # $ python3 GetSharedWithListOfDisabledUsersDriveACLs.py filelistperms.csv disabledusersperms.csv Users.csv 27 | # 4: Inspect disabledusersperms.csv, verify that it makes sense and then proceed 28 | """ 29 | 30 | import csv 31 | import re 32 | import sys 33 | 34 | FILE_NAME = 'name' 35 | ALT_FILE_NAME = 'title' 36 | 37 | # The headers in the CSV file that contain the user email addresses 38 | USER_HEADERS = ['email'] # USER_HEADERS = ['email1', 'email2', 'email3', 'email4'] 39 | 40 | QUOTE_CHAR = '"' # Adjust as needed 41 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 42 | 43 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 44 | 45 | userSet = set() 46 | inputFile = open(sys.argv[3], 'r', encoding='utf-8') 47 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 48 | for header in USER_HEADERS: 49 | user = row[header].lower() 50 | if user: 51 | userSet.add(user) 52 | inputFile.close() 53 | 54 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 55 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 56 | 57 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 58 | outputCSV = csv.DictWriter(outputFile, inputCSV.fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 59 | outputCSV.writeheader() 60 | 61 | for row in inputCSV: 62 | shared = False 63 | for k, v in iter(row.items()): 64 | mg = PERMISSIONS_N_TYPE.match(k) 65 | if mg: 66 | if v in {'anyone', 'domain', 'group'}: 67 | break 68 | permissions_N = mg.group(1) 69 | if row.get(f'permissions.{permissions_N}.deleted') == 'True': 70 | continue 71 | if row[f'permissions.{permissions_N}.role'] == 'owner': 72 | continue 73 | emailAddress = row.get(f'permissions.{permissions_N}.emailAddress', '').lower() 74 | if not emailAddress: 75 | continue 76 | if emailAddress not in userSet: 77 | break 78 | shared = True 79 | else: 80 | if shared: 81 | outputCSV.writerow(row) 82 | 83 | inputFile.close() 84 | outputFile.close() 85 | -------------------------------------------------------------------------------- /GetSharedWithListOfDisabledUsersSharedDriveACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), show all drive file ACLs for files shared exclusively with a list of users from a CSV file 4 | # Customize: Set USER_HEADERS 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: If you want to include all Team Drives, do this step and then skip to step 4, otherwise start at step 2. 10 | # $ gam redirect csv ./TeamDrives.csv print teamdrives fields id,name 11 | # 2: Get ACLs for all Team Drives 12 | # $ gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls "~id" fields id,emailaddress,role,type 13 | # 3: Get suspended accounts 14 | # $ gam redirect csv ./Users.csv print users issuspended true 15 | # 4: From that list of ACLs, output a CSV file with headers "id,name,organizers" 16 | # that shows the organizers for each Team Drive 17 | # $ python3 GetTeamDriveOrganizers.py TeamDriveACLs.csv TeamDrives.csv TeamDriveOrganizers.csv 18 | # 5: Get Shared Drive files ACL with non-inherited permissions 19 | # $ gam config csv_input_row_filter "organizers:regex:^.+$" redirect csv ./TeamDriveFileFilesPermission.csv multiprocess csv ./TeamDriveOrganizers.csv gam user "~organizers" print filelist select teamdriveid "~id" fields id,name,permissions pmfilter pm deleted false inherited false em 20 | # 6: Get Ids to remove ACLs 21 | # $ python3 GetSharedWithListOfDisabledUsersSharedDriveACLs.py TeamDriveFileFilesPermission.csv Users.csv 22 | # 7: Cleanup 23 | # $ gam csv ./cleanup.csv gam user ~owner delete drivefileacl "~id" "~emailAddress" 24 | """ 25 | 26 | import csv 27 | import re 28 | import sys 29 | 30 | # The headers in the CSV file that contain the user email addresses 31 | USER_HEADERS = ['primaryEmail'] 32 | 33 | QUOTE_CHAR = '"' # Adjust as needed 34 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 35 | 36 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 37 | 38 | userSet = set() 39 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 40 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 41 | for header in USER_HEADERS: 42 | user = row[header].lower() 43 | if user: 44 | userSet.add(user) 45 | inputFile.close() 46 | 47 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 48 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 49 | outputFile = open('cleanup.csv', 'w', encoding='utf-8', newline='') 50 | outputCSV = csv.DictWriter(outputFile, ['owner', 'id', 'emailAddress'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 51 | outputCSV.writeheader() 52 | 53 | for row in inputCSV: 54 | shared = False 55 | for k, v in iter(row.items()): 56 | mg = PERMISSIONS_N_TYPE.match(k) 57 | if mg: 58 | if v in {'anyone', 'domain', 'group'}: 59 | break 60 | permissions_N = mg.group(1) 61 | if row.get(f'permissions.{permissions_N}.deleted') == 'True': 62 | continue 63 | if row[f'permissions.{permissions_N}.role'] == 'owner': 64 | continue 65 | emailAddress = row.get(f'permissions.{permissions_N}.emailAddress', '').lower() 66 | if not emailAddress: 67 | continue 68 | if emailAddress not in userSet: 69 | break 70 | shared = True 71 | outputCSV.writerow({'owner': row['Owner'], 72 | 'id': row['id'], 73 | 'emailAddress': emailAddress}) 74 | 75 | inputFile.close() 76 | outputFile.close() 77 | -------------------------------------------------------------------------------- /GetSharedWithNonAccountUsersDriveACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), show all drive file ACLs for files shared with users outside of your account. 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get users in account 9 | # $ gam redirect csv ./accountusers.csv print users 10 | # 2: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 11 | # $ gam config auto_batch_min 1 redirect csv ./filelistperms.csv multiprocess all users print filelist fields id,title,permissions,owners.emailaddress,mimetype 12 | # 3: From that list of ACLs, output a CSV file with headers "Owner,driveFileId,driveFileTitle,mimeType,permissionId,role,emailAddress" 13 | # that lists the driveFileIds and permissionIds for all ACLs with the non-account users 14 | # (n.b., driveFileTitle,mimeType, role, and emailAddress are not used in the next step, they are included for documentation purposes) 15 | # $ python3 GetSharedWithNonAccountUsersDriveACLs.py accountusers.csv filelistperms.csv deleteperms.csv 16 | # 4: Inspect deleteperms.csv, verify that it makes sense and then proceed 17 | # 5: If desired, delete the ACLs 18 | # $ gam csv ./deleteperms.csv gam user "~Owner" delete drivefileacl "~driveFileId" "~permissionId" 19 | """ 20 | 21 | import csv 22 | import re 23 | import sys 24 | 25 | FILE_NAME = 'name' 26 | ALT_FILE_NAME = 'title' 27 | 28 | QUOTE_CHAR = '"' # Adjust as needed 29 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 30 | 31 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 32 | 33 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 34 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 35 | else: 36 | outputFile = sys.stdout 37 | outputCSV = csv.DictWriter(outputFile, ['Owner', 'driveFileId', 'driveFileTitle', 'mimeType', 38 | 'permissionId', 'role', 'emailAddress'], 39 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 40 | outputCSV.writeheader() 41 | 42 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 43 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 44 | else: 45 | inputFile = sys.stdin 46 | 47 | accountUsers = set() 48 | usersFile = open(sys.argv[1], 'r', encoding='utf-8') 49 | for row in csv.DictReader(usersFile, quotechar=QUOTE_CHAR): 50 | accountUsers.add(row['primaryEmail'].lower()) 51 | usersFile.close() 52 | 53 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 54 | for k, v in iter(row.items()): 55 | mg = PERMISSIONS_N_TYPE.match(k) 56 | if mg and v == 'user': 57 | permissions_N = mg.group(1) 58 | if row.get(f'permissions.{permissions_N}.deleted') == 'True': 59 | continue 60 | emailAddress = row[f'permissions.{permissions_N}.emailAddress'].lower() 61 | if row[f'permissions.{permissions_N}.role'] != 'owner' and emailAddress not in accountUsers: 62 | outputCSV.writerow({'Owner': row['owners.0.emailAddress'], 63 | 'driveFileId': row['id'], 64 | 'driveFileTitle': row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown')), 65 | 'mimeType': row['mimeType'], 66 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 67 | 'role': row[f'permissions.{permissions_N}.role'], 68 | 'emailAddress': emailAddress}) 69 | 70 | if inputFile != sys.stdin: 71 | inputFile.close() 72 | if outputFile != sys.stdout: 73 | outputFile.close() 74 | -------------------------------------------------------------------------------- /GetSuspendedUserSharedDriveACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Get all shared drive ACLs for a list of suspended users from a CSV file; 4 | # the user's primary email and aliases are checked 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Prepare a CSV file with a list of suspended user email addresses and aliases 10 | # $ gam redirect csv ./SuspendedUsers.csv print users query "isSuspended=True" fields primaryemail aliases 11 | # 2: Get Shared Drive ACLs 12 | # $ gam config csv_output_header_drop_filter "User" redirect csv ./SharedDriveACLs.csv print shareddriveacls fields id,emailaddress,role,type 13 | # 3: From that list of ACLs, output a CSV file with headers "id,name,createdTime,permissionId,role,emailAddress" 14 | # that lists the Shared Drive id, name and createdTime, permissionId, role and email address for the specified users 15 | # $ python3 GetSuspendedUserSharedDriveACLs.py SharedDriveACLs.csv SuspendedUserSharedDriveACLS.csv SuspendedUsers.csv 16 | """ 17 | 18 | import csv 19 | import re 20 | import sys 21 | 22 | # The header in the CSV file that contains the user email addresses 23 | USER_HEADER = 'primaryEmail' 24 | 25 | QUOTE_CHAR = '"' # Adjust as needed 26 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 27 | 28 | ALIASES_N = re.compile(r"aliases.(\d+)") 29 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 30 | 31 | userSet = set() 32 | inputFile = open(sys.argv[3], 'r', encoding='utf-8') 33 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 34 | user = row['primaryEmail'].lower() 35 | if user: 36 | userSet.add(user) 37 | for k, v in iter(row.items()): 38 | mg = ALIASES_N.match(k) 39 | if mg and v: 40 | userSet.add(v.lower()) 41 | inputFile.close() 42 | 43 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 44 | outputCSV = csv.DictWriter(outputFile, ['id', 'name', 'createdTime', 'permissionId', 'role', 'emailAddress'], 45 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 46 | outputCSV.writeheader() 47 | 48 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 49 | 50 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 51 | for k, v in iter(row.items()): 52 | mg = PERMISSIONS_N_TYPE.match(k) 53 | if mg and v == 'user': 54 | permissions_N = mg.group(1) 55 | if row.get(f'permissions.{permissions_N}.deleted', '') == 'True': 56 | continue 57 | emailAddress = row[f'permissions.{permissions_N}.emailAddress'].lower() 58 | if emailAddress in userSet: 59 | outputCSV.writerow({'id': row['id'], 60 | 'name': row['name'], 61 | 'createdTime': row['createdTime'], 62 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 63 | 'role': row[f'permissions.{permissions_N}.role'], 64 | 'emailAddress': emailAddress}) 65 | 66 | inputFile.close() 67 | outputFile.close() 68 | -------------------------------------------------------------------------------- /GetTeamDriveACLsExpandGroups.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Get ACLs for Team Drives, expand type group ACls into the constituent type user ACLs. permission.id is deleted for the users as it is not known 4 | # Customize: Set RETAIN_GROUP_ACL_ROW as desired 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get ACLs for all Shared Drives 10 | # $ gam redirect csv ./TeamDriveACLs.csv print teamdriveacls fields id,domain,emailaddress,role,type,deleted oneitemperrow 11 | # 2: Get group members 12 | # $ gam redirect csv ./GroupMembers.csv print groups roles members,managers,owners delimiter " " 13 | # 3: Generate a CSV file with the same headers as TeamDriveACls.csv with type group ACLs replaced with type user ACLs for each member 14 | # There is an additional header, permission.group, that shows the group email address from which the user ACLs are derived. 15 | # $ python3 GetTeamDriveACLsExpandGroups.py TeamDriveACLs.csv GroupMembers.csv TeamDriveACLsExpandedGroups.csv 16 | """ 17 | 18 | import csv 19 | import sys 20 | 21 | MEMBER_DELIMITER = ' ' 22 | 23 | RETAIN_GROUP_ACL_ROW = False # False: delete type group ACL rows, True: retain type grpup ACL rows 24 | 25 | QUOTE_CHAR = '"' # Adjust as needed 26 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 27 | 28 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 29 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 30 | fieldnames = inputCSV.fieldnames[:] 31 | fieldnames.append('permission.group') 32 | 33 | GroupMembers = {} 34 | groupFile = open(sys.argv[2], 'r', encoding='utf-8') 35 | for row in csv.DictReader(groupFile, quotechar=QUOTE_CHAR): 36 | group = row['email'] 37 | GroupMembers[group] = [] 38 | for field in ['Members', 'Managers', 'Owners']: 39 | if row[field]: 40 | GroupMembers[group].extend(row[field].split(MEMBER_DELIMITER)) 41 | groupFile.close() 42 | 43 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 44 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 45 | else: 46 | outputFile = sys.stdout 47 | outputCSV = csv.DictWriter(outputFile, fieldnames, 48 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 49 | outputCSV.writeheader() 50 | 51 | for row in inputCSV: 52 | if row['permission.type'] != 'group': 53 | outputCSV.writerow(row) 54 | continue 55 | if RETAIN_GROUP_ACL_ROW: 56 | outputCSV.writerow(row) 57 | row['permission.type'] = 'user' 58 | row['permission.id'] = '' 59 | group = row['permission.emailAddress'] 60 | row['permission.group'] = group 61 | for member in GroupMembers.get(group, []): 62 | row['permission.emailAddress'] = member 63 | _, domain = member.split('@') 64 | row['permission.domain'] = domain 65 | outputCSV.writerow(row) 66 | 67 | inputFile.close() 68 | if outputFile != sys.stdout: 69 | outputFile.close() 70 | -------------------------------------------------------------------------------- /GetTeamDriveCountsSize.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Get file counts/total size for Team Drives 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: If you want to include all Team Drives, do this step and then skip to step 4, otherwise start at step 2. 9 | # $ gam redirect csv ./TeamDrives.csv print teamdrives fields id,name 10 | # 2: If you want Team Drives for a specific set of organizers, replace with your user selection in the command below 11 | # $ gam redirect csv ./AllTeamDrives.csv print teamdrives role organizer fields id,name 12 | # 3: Delete duplicate Team Drives (some may have multiple organizers). Make sure that ID_FIELD = 'id' in DeleteDuplicateRows.py 13 | # $ python3 DeleteDuplicateRows.py ./AllTeamDrives.csv ./TeamDrives.csv 14 | # 4: Get ACLs for all Team Drives 15 | # $ gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls "~id" fields emailaddress,role,type 16 | # 5: From that list of ACLs, output a CSV file with headers "id,name,organizers" 17 | # that shows the organizers for each Team Drive 18 | # 6: Customize GetTeamDriveOrganizers.py 19 | # Set DOMAIN_LIST as desired 20 | # Set ONE_ORGANIZER = True 21 | # Set SHOW_GROUP_ORGANIZERS = False 22 | # Set SHOW_USER_ORGANIZERS = True 23 | # $ python3 GetTeamDriveOrganizers.py TeamDriveACLs.csv TeamDrives.csv TeamDriveOrganizers.csv 24 | # 7: Get Team Drive files 25 | # $ gam config csv_input_row_filter "organizers:regex:^.+$" redirect csv ./TeamDriveFiles.csv multiprocess csv ./TeamDriveOrganizers.csv gam user "~organizers" print filelist select teamdriveid "~id" fields id,name,driveid,size 26 | # 8: Get Team Drive counts/size info 27 | # $ python3 GetTeamDriveCountsSize.py TeamDriveFiles.csv TeamDrives.csv TeamDriveCountsSize.csv 28 | 29 | """ 30 | 31 | import csv 32 | import sys 33 | 34 | QUOTE_CHAR = '"' # Adjust as needed 35 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 36 | 37 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 38 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 39 | else: 40 | outputFile = sys.stdout 41 | outputCSV = csv.DictWriter(outputFile, ['id', 'name', 'count', 'size'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 42 | outputCSV.writeheader() 43 | 44 | teamDriveNames = {} 45 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 46 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 47 | teamDriveNames[row['id']] = row['name'] 48 | inputFile.close() 49 | 50 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 51 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 52 | else: 53 | inputFile = sys.stdin 54 | 55 | teamDriveInfo = {} 56 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 57 | driveId = row.get('driveId') 58 | if not driveId: 59 | continue 60 | if driveId not in teamDriveNames: 61 | teamDriveNames[driveId] = driveId 62 | if driveId not in teamDriveInfo: 63 | teamDriveInfo[driveId] = {'name': teamDriveNames[driveId], 'count': 0, 'size': 0} 64 | teamDriveInfo[driveId]['count'] += 1 65 | size = row.get('size', row.get('fileSize', '0')) 66 | if size: 67 | teamDriveInfo[driveId]['size'] += int(size) 68 | 69 | for k, v in iter(teamDriveInfo.items()): 70 | outputCSV.writerow({'id': k, 'name': v['name'], 'count': v['count'], 'size': v['size']}) 71 | 72 | if inputFile != sys.stdin: 73 | inputFile.close() 74 | if outputFile != sys.stdout: 75 | outputFile.close() 76 | -------------------------------------------------------------------------------- /GetTeamDriveDeletedPermissions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Get ACLs for Team Drives that reference deleted groups/users 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get all Team Drives 9 | # $ gam redirect csv ./TeamDrives.csv print teamdrives fields id,name 10 | # 2: Get ACLs for all Team Drives 11 | # $ gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls "~id" fields id,emailaddress,role,type,deleted pm deleted true em pmfilter 12 | # 3: From the list of ACLs, output a CSV file with headers "id,name,permissionId,role,type" 13 | # $ python3 GetTeamDriveDeletedUsersACLs.py TeamDriveACLs.csv TeamDrives.csv TeamDriveDeletedUsersACLs.csv 14 | # 4: Inspect TeamDriveSuspendUsersACLs.csv, verify that it makes sense and then proceed if desired 15 | # 5: If desired, delete the ACLs 16 | # $ gam redirect stdout ./DeleteTeamDriveDeletedUsersACLs.log multiprocess redirect stderr stdout csv ./TeamDriveDeletedUsersACLs.csv gam delete drivefileacl "~id" "~permissionId" 17 | """ 18 | 19 | import csv 20 | import re 21 | import sys 22 | 23 | QUOTE_CHAR = '"' # Adjust as needed 24 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 25 | 26 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 27 | 28 | teamDriveNames = {} 29 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 30 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 31 | teamDriveNames[row['id']] = row['name'] 32 | inputFile.close() 33 | 34 | if sys.argv[1] != '-': 35 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 36 | else: 37 | inputFile = sys.stdin 38 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 39 | 40 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 41 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 42 | else: 43 | outputFile = sys.stdout 44 | outputCSV = csv.DictWriter(outputFile, ['id', 'name', 'permissionId', 'role', 'type'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 45 | outputCSV.writeheader() 46 | 47 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 48 | for k, v in iter(row.items()): 49 | mg = PERMISSIONS_N_TYPE.match(k) 50 | if mg and v: 51 | permissions_N = mg.group(1) 52 | if row.get(f'permissions.{permissions_N}.deleted') == 'True': 53 | outputCSV.writerow({'id': row['id'], 54 | 'name': teamDriveNames.get(row['id'], row['id']), 55 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 56 | 'role': row[f'permissions.{permissions_N}.role'], 57 | 'type': v}) 58 | 59 | if inputFile != sys.stdin: 60 | inputFile.close() 61 | if outputFile != sys.stdout: 62 | outputFile.close() 63 | -------------------------------------------------------------------------------- /GetTeamDriveDeletedUsersACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Get ACLs for Team Drives that reference deleted users 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get all Team Drives 9 | # $ gam redirect csv ./TeamDrives.csv print teamdrives fields id,name 10 | # 2: Get ACLs for all Team Drives 11 | # $ gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls "~id" fields id,emailaddress,role,type,deleted pm type user deleted true em pmfilter 12 | # 3: From the list of ACLs, output a CSV file with headers "id,name,permissionId,role,type" 13 | # $ python3 GetTeamDriveDeletedUsersACLs.py TeamDriveACLs.csv TeamDrives.csv TeamDriveDeletedUsersACLs.csv 14 | # 4: Inspect TeamDriveSuspendUsersACLs.csv, verify that it makes sense and then proceed if desired 15 | # 5: If desired, delete the ACLs 16 | # $ gam redirect stdout ./DeleteTeamDriveDeletedUsersACLs.log multiprocess redirect stderr stdout csv ./TeamDriveDeletedUsersACLs.csv gam delete drivefileacl "~id" "~permissionId" 17 | """ 18 | 19 | import csv 20 | import re 21 | import sys 22 | 23 | QUOTE_CHAR = '"' # Adjust as needed 24 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 25 | 26 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 27 | 28 | teamDriveNames = {} 29 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 30 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 31 | teamDriveNames[row['id']] = row['name'] 32 | inputFile.close() 33 | 34 | if sys.argv[1] != '-': 35 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 36 | else: 37 | inputFile = sys.stdin 38 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 39 | 40 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 41 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 42 | else: 43 | outputFile = sys.stdout 44 | outputCSV = csv.DictWriter(outputFile, ['id', 'name', 'permissionId', 'role', 'type'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 45 | outputCSV.writeheader() 46 | 47 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 48 | for k, v in iter(row.items()): 49 | mg = PERMISSIONS_N_TYPE.match(k) 50 | if mg and v == 'user': 51 | permissions_N = mg.group(1) 52 | if row.get(f'permissions.{permissions_N}.deleted') == 'True': 53 | outputCSV.writerow({'id': row['id'], 54 | 'name': teamDriveNames.get(row['id'], row['id']), 55 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 56 | 'role': row[f'permissions.{permissions_N}.role'], 57 | 'type': v}) 58 | 59 | if inputFile != sys.stdin: 60 | inputFile.close() 61 | if outputFile != sys.stdout: 62 | outputFile.close() 63 | -------------------------------------------------------------------------------- /GetTeamDriveFileLists.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Get file lists for Team Drives 4 | # Customize: DOMAIN_LIST 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: If you want to include all Team Drives, do this step and then skip to step 4, otherwise start at step 2. 10 | # $ gam redirect csv ./TeamDrives.csv print teamdrives fields id,name 11 | # 2: If you want Team Drives for a specific set of organizers, replace with your user selection in the command below 12 | # $ gam redirect csv ./AllTeamDrives.csv print teamdrives role organizer fields id,name 13 | # 3: Delete duplicate Team Drives (some may have multiple organizers). Make sure that ID_FIELD = 'id' in DeleteDuplicateRows.py 14 | # $ python3 DeleteDuplicateRows.py ./AllTeamDrives.csv ./TeamDrives.csv 15 | # 4: Customize GetTeamDriveOrganizers.py for this task: 16 | # Set DOMAIN_LIST as required 17 | # Set ONE_ORGANIZER = True 18 | # Set SHOW_GROUP_ORGANIZERS = False 19 | # Set SHOW_USER_ORGANIZERS = True 20 | # 5: Get ACLs for all Team Drives 21 | # $ gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls "~id" fields emailaddress,role,type 22 | # 6: From that list of ACLs, output a CSV file with headers "id,name,organizer" 23 | # that shows an organizer/fileOrganizer for each Team Drive 24 | # $ python3 GetTeamDriveOrganizers.py TeamDriveACLs.csv TeamDrives.csv TeamDriveOrganizers.csv 25 | # 7: From that list of organizers, get the file lists for all Team Drives that have an organizer (matchfield organizer "^.+$") 26 | # Add additional arguments to the following command to specify desired fields/options; e.g., 27 | # fields id,name,driveid,mimetype,size filepath 28 | # $ gam config csv_input_row_filter "organizers:regex:^.+$" redirect csv ./TeamDriveFileLists.csv multiprocess csv ./TeamDriveOrganizers.csv gam user "~organizers" print filelist select teamdriveid "~id" 29 | # 8: You can identify all Team Drives without an organizer 30 | # $ gam csv ./TeamDriveOrganizers.csv skipfield organizers "^.+$" gam info teamdrive teamdriveid "~id" 31 | """ 32 | 33 | import csv 34 | import re 35 | import sys 36 | 37 | # If you want to limit organizers to a specific list of domains, use the list below, e.g., DOMAIN_LIST = ['domain.com',] DOMAIN_LIST = ['domain1.com', 'domain2.com',] 38 | DOMAIN_LIST = [] 39 | 40 | QUOTE_CHAR = '"' # Adjust as needed 41 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 42 | 43 | PERMISSIONS_N_ROLE = re.compile(r"permissions.(\d+).role") 44 | 45 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 46 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 47 | else: 48 | outputFile = sys.stdout 49 | outputCSV = csv.DictWriter(outputFile, ['id', 'name', 'organizer'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 50 | outputCSV.writeheader() 51 | 52 | teamDriveNames = {} 53 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 54 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 55 | teamDriveNames[row['id']] = row['name'] 56 | inputFile.close() 57 | 58 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 59 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 60 | else: 61 | inputFile = sys.stdin 62 | 63 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 64 | organizer = '' 65 | for k, v in iter(row.items()): 66 | mg = PERMISSIONS_N_ROLE.match(k) 67 | if mg and v in ['organizer', 'fileOrganizer']: 68 | permissions_N = mg.group(1) 69 | if row[f'permissions.{permissions_N}.type'] != 'user': 70 | continue 71 | emailAddress = row[f'permissions.{permissions_N}.emailAddress'].lower() 72 | if DOMAIN_LIST: 73 | domain = emailAddress[emailAddress.find('@')+1:] 74 | if domain not in DOMAIN_LIST: 75 | continue 76 | organizer = emailAddress 77 | break 78 | outputCSV.writerow({'id': row['id'], 79 | 'name': teamDriveNames.get(row['id'], row['id']), 80 | 'organizer': organizer}) 81 | 82 | if inputFile != sys.stdin: 83 | inputFile.close() 84 | if outputFile != sys.stdout: 85 | outputFile.close() 86 | -------------------------------------------------------------------------------- /GetTeamDriveLastModified.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Get latest modifiedTime for Team Drives 4 | # Customize: SORT_HEADER 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: If you want to include all Team Drives, do this step and then skip to step 4, otherwise start at step 2. 10 | # $ gam redirect csv ./TeamDrives.csv print teamdrives fields id,name 11 | # 2: If you want Team Drives for a specific set of organizers, replace with your user selection in the command below 12 | # $ gam redirect csv ./AllTeamDrives.csv print teamdrives role organizer fields id,name 13 | # 3: Delete duplicate Team Drives (some may have multiple organizers). Make sure that ID_FIELD = 'id' in DeleteDuplicateRows.py 14 | # $ python3 DeleteDuplicateRows.py ./AllTeamDrives.csv ./TeamDrives.csv 15 | # 4: Get ACLs for all Team Drives 16 | # $ gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls "~id" fields emailaddress,role,type 17 | # 5: From that list of ACLs, output a CSV file with headers "id,name,organizers" 18 | # that shows the organizers for each Team Drive. 19 | # Set the following items in GetTeamDriveOrganizers.py 20 | # ONE_ORGANIZER = True 21 | # SHOW_GROUP_ORGANIZERS = False 22 | # SHOW_USER_ORGANIZERS = True 23 | # $ python3 GetTeamDriveOrganizers.py TeamDriveACLs.csv TeamDrives.csv TeamDriveOrganizers.csv 24 | # 6: Using the list of Team Drives and organizers, get a list of file ids and names sorted by modifiedTime descending 25 | # $ gam config csv_input_row_filter "organizers:regex:^.+$" redirect csv ./TeamDriveFileList.csv multiprocess csv ./TeamDriveOrganizers.csv gam user "~organizers" print filelist select teamdriveid "~id" query "mimeType != 'application/vnd.google-apps.folder'" fields teamDriveId,id,name,modifiedtime orderby modifiedtime descending maxfiles 1 26 | # 7: From that list of files, output a CSV file with headers "id,name,driveFileId,driveFileName,modifiedTime' 27 | # that show the most recently modified file for each Team Drive 28 | # $ python3 GetTeamDriveLastModified.py TeamDriveFileList.csv TeamDrives.csv TeamDriveLastModified.csv 29 | """ 30 | 31 | import csv 32 | import sys 33 | 34 | SORT_HEADER = 'name' # 'name' - Team Drive name, 'id' - Team Drive ID, 'modifiedTime' - file modifiedTime 35 | 36 | QUOTE_CHAR = '"' # Adjust as needed 37 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 38 | 39 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 40 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 41 | else: 42 | outputFile = sys.stdout 43 | outputCSV = csv.DictWriter(outputFile, ['id', 'name', 'driveFileId', 'driveFileName', 'modifiedTime'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 44 | outputCSV.writeheader() 45 | 46 | teamDriveNames = {} 47 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 48 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 49 | teamDriveNames[row['id']] = row['name'] 50 | inputFile.close() 51 | 52 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 53 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 54 | else: 55 | inputFile = sys.stdin 56 | 57 | teamDriveProcessed = set() 58 | csvRows = [] 59 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 60 | teamDriveId = row['driveId'] 61 | if teamDriveId not in teamDriveProcessed: 62 | teamDriveProcessed.add(teamDriveId) 63 | csvRows.append({'id': teamDriveId, 64 | 'name': teamDriveNames.get(teamDriveId, teamDriveId), 65 | 'driveFileId': row['id'], 66 | 'driveFileName': row['name'], 67 | 'modifiedTime': row['modifiedTime']}) 68 | csvRows.sort(key=lambda k: k[SORT_HEADER]) 69 | outputCSV.writerows(csvRows) 70 | 71 | if inputFile != sys.stdin: 72 | inputFile.close() 73 | if outputFile != sys.stdout: 74 | outputFile.close() 75 | -------------------------------------------------------------------------------- /GetTeamDriveMembers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Get organizers and members for Team Drives 4 | # Customize: DELIMITER, DOMAIN_LIST, INCLUDE_TYPES 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: If you want to include all Team Drives, do this step and then skip to step 4, otherwise start at step 2. 10 | # $ gam redirect csv ./TeamDrives.csv print teamdrives fields id,name 11 | # 2: If you want Team Drives for a specific set of organizers, replace with your user selection in the command below 12 | # $ gam redirect csv ./AllTeamDrives.csv print teamdrives fields id,name 13 | # 3: Delete duplicate Team Drives (some may have multiple organizers). Make sure that ID_FIELD = 'id' in DeleteDuplicateRows.py 14 | # $ python3 DeleteDuplicateRows.py ./AllTeamDrives.csv ./TeamDrives.csv 15 | # 4: Get ACLs for all Team Drives 16 | # $ gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls "~id" fields id,emailaddress,role,type,deleted 17 | # 5: From that list of ACLs, output a CSV file with headers "id,name,organizers,members" 18 | # that shows the organizers for each Team Drive 19 | # $ python3 GetTeamDriveMembers.py TeamDriveACLs.csv TeamDrives.csv TeamDriveMembers.csv 20 | """ 21 | 22 | import csv 23 | import re 24 | import sys 25 | 26 | DELIMITER = ' ' # character that separates list members 27 | 28 | # If you want to limit organizers/members to a specific list of domains, use the list below, e.g., DOMAIN_LIST = ['domain.com',] DOMAIN_LIST = ['domain1.com', 'domain2.com',] 29 | DOMAIN_LIST = [] 30 | 31 | INCLUDE_TYPES = { 32 | 'user': True, # False - don't show user organizers/members, True - show user organizers/members 33 | 'group': True, # False - don't show group organizers/members, True - show group organizers/members 34 | } 35 | 36 | QUOTE_CHAR = '"' # Adjust as needed 37 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 38 | 39 | PERMISSIONS_N_ROLE = re.compile(r"permissions.(\d+).role") 40 | 41 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 42 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 43 | else: 44 | outputFile = sys.stdout 45 | outputCSV = csv.DictWriter(outputFile, ['id', 'name', 'organizers', 'members'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 46 | outputCSV.writeheader() 47 | 48 | teamDriveNames = {} 49 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 50 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 51 | teamDriveNames[row['id']] = row['name'] 52 | inputFile.close() 53 | 54 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 55 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 56 | else: 57 | inputFile = sys.stdin 58 | 59 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 60 | organizers = [] 61 | members = [] 62 | for k, v in iter(row.items()): 63 | mg = PERMISSIONS_N_ROLE.match(k) 64 | if mg and v: 65 | roleList = organizers if v == 'organizer' else members 66 | permissions_N = mg.group(1) 67 | if row.get(f'permissions.{permissions_N}.deleted') == 'True': 68 | continue 69 | if not INCLUDE_TYPES[row[f'permissions.{permissions_N}.type']]: 70 | continue 71 | member = row[f'permissions.{permissions_N}.emailAddress'] 72 | if DOMAIN_LIST and member[member.find('@')+1:] not in DOMAIN_LIST: 73 | continue 74 | roleList.append(member) 75 | outputCSV.writerow({'id': row['id'], 76 | 'name': teamDriveNames.get(row['id'], row['id']), 77 | 'organizers': DELIMITER.join(organizers), 78 | 'members': DELIMITER.join(members)}) 79 | 80 | if inputFile != sys.stdin: 81 | inputFile.close() 82 | if outputFile != sys.stdout: 83 | outputFile.close() 84 | -------------------------------------------------------------------------------- /GetTeamDriveNameACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Get ACLs for Team Drives, add Team Drive name (and optional additional fields) to row 4 | # Customize: Set ONE_ACL_PER_ROW,ADDITIONAL_TEAM_DRIVE_FIELDS as desired 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get all Team Drives 10 | # $ gam redirect csv ./TeamDrives.csv print teamdrives fields id,name 11 | # 2: Get ACLs for all Team Drives; adjust the fields list as desired 12 | # $ gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls "~id" fields id,domain,emailaddress,role,type,deleted 13 | # 3: From that list of ACLs, output a CSV file with the same headers as TeamDriveACLs.csv with the Team Drive name as the third column 14 | # $ python3 GetTeamDriveNameACLs.py TeamDriveACLs.csv TeamDrives.csv TeamDriveNameACLs.csv 15 | """ 16 | 17 | import csv 18 | import re 19 | import sys 20 | 21 | ONE_ACL_PER_ROW = False # Set True for one ACL per row 22 | ADDITIONAL_TEAM_DRIVE_FIELDS = ['createdTime'] # Team Drive fields in addition to name to add to row, e.g., ADDITIONAL_TEAM_DRIVE_FIELDS = ['createdTime'] 23 | 24 | QUOTE_CHAR = '"' # Adjust as needed 25 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 26 | 27 | PERMISSIONS_N_FIELD = re.compile(r"permissions.\d+.(.*)") 28 | 29 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 30 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 31 | else: 32 | outputFile = sys.stdout 33 | 34 | teamDriveData = {} 35 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 36 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 37 | teamDriveData[row['id']] = row 38 | inputFile.close() 39 | 40 | if sys.argv[1] != '-': 41 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 42 | else: 43 | inputFile = sys.stdin 44 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 45 | fieldnames = inputCSV.fieldnames[0:2]+['name']+ADDITIONAL_TEAM_DRIVE_FIELDS 46 | if not ONE_ACL_PER_ROW: 47 | fieldnames += inputCSV.fieldnames[2:] 48 | else: 49 | permFieldNames = set() 50 | for k in inputCSV.fieldnames[3:]: 51 | mg = PERMISSIONS_N_FIELD.match(k) 52 | if mg: 53 | permFieldNames.add(mg.group(1)) 54 | fieldnames.extend([f'permissions.{k}' for k in sorted(permFieldNames)]) 55 | 56 | outputCSV = csv.DictWriter(outputFile, fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 57 | outputCSV.writeheader() 58 | 59 | if not ONE_ACL_PER_ROW: 60 | for row in inputCSV: 61 | td = teamDriveData.get(row['id'], {}) 62 | row['name'] = td.get('name', row['id']) 63 | for field in ADDITIONAL_TEAM_DRIVE_FIELDS: 64 | row[field] = td.get(field, '') 65 | outputCSV.writerow(row) 66 | else: 67 | for row in inputCSV: 68 | td = teamDriveData.get(row['id'], {}) 69 | orow = {'Owner': row['Owner'], 'id': row['id'], 'name': td.get('name', row['id'])} 70 | for field in ADDITIONAL_TEAM_DRIVE_FIELDS: 71 | orow[field] = td.get(field, '') 72 | for permissions_N in range(0, int(row['permissions'])): 73 | prow = orow.copy() 74 | for k in permFieldNames: 75 | if row.get(f'permissions.{permissions_N}.{k}'): 76 | prow[f'permissions.{k}'] = row[f'permissions.{permissions_N}.{k}'] 77 | outputCSV.writerow(prow) 78 | 79 | if inputFile != sys.stdin: 80 | inputFile.close() 81 | if outputFile != sys.stdout: 82 | outputFile.close() 83 | -------------------------------------------------------------------------------- /GetTeamDriveStorageInfo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Get storage info for Team Drives 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: If you want to include all Team Drives, do this step and then skip to step 4, otherwise start at step 2. 9 | # $ gam redirect csv ./TeamDrives.csv print teamdrives fields id,name 10 | # 2: If you want Team Drives for a specific set of organizers, replace with your user selection in the command below 11 | # $ gam redirect csv ./AllTeamDrives.csv print teamdrives role organizer fields id,name 12 | # 3: Delete duplicate Team Drives (some may have multiple organizers). Make sure that ID_FIELD = 'id' in DeleteDuplicateRows.py 13 | # $ python3 DeleteDuplicateRows.py ./AllTeamDrives.csv ./TeamDrives.csv 14 | # 4: Get ACLs for all Team Drives 15 | # $ gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls "~id" fields emailaddress,role,type 16 | # 5: From that list of ACLs, output a CSV file with headers "id,name,organizers" 17 | # that shows the organizers for each Team Drive 18 | # 6: Customize GetTeamDriveOrganizers.py 19 | # Set DOMAIN_LIST as desired 20 | # Set ONE_ORGANIZER = True 21 | # Set SHOW_GROUP_ORGANIZERS = False 22 | # Set SHOW_USER_ORGANIZERS = True 23 | # $ python3 GetTeamDriveOrganizers.py TeamDriveACLs.csv TeamDrives.csv TeamDriveOrganizers.csv 24 | # 7: Get Team Drive files 25 | # $ gam config csv_input_row_filter "organizers:regex:^.+$" redirect csv ./TeamDriveFileCountsSize.csv multiprocess csv ./TeamDriveOrganizers.csv gam user "~organizers" print filecounts select teamdriveid "~id" showsize 26 | # 8: Get Team Drive storage info 27 | # $ python3 GetTeamDriveStorageInfo.py TeamDriveFileCountsSize.csv TeamDriveStorageInfo.csv 28 | 29 | """ 30 | 31 | import csv 32 | import sys 33 | 34 | QUOTE_CHAR = '"' # Adjust as needed 35 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 36 | 37 | ONE_KILO_BYTES = 1024 38 | ONE_MEGA_BYTES = ONE_KILO_BYTES*ONE_KILO_BYTES 39 | ONE_GIGA_BYTES = ONE_KILO_BYTES*ONE_MEGA_BYTES 40 | ONE_TERA_BYTES = ONE_KILO_BYTES*ONE_GIGA_BYTES 41 | 42 | MAX_FILES_FOLDERS = 500000 43 | 44 | def formatSize(size): 45 | if size == 0: 46 | return '0 KB' 47 | if size < ONE_KILO_BYTES: 48 | return '1 KB' 49 | if size < ONE_MEGA_BYTES: 50 | return f'{size/ONE_KILO_BYTES:.2f} KB' 51 | if size < ONE_GIGA_BYTES: 52 | return f'{size/ONE_MEGA_BYTES:.2f} MB' 53 | if size < ONE_TERA_BYTES: 54 | return f'{size/ONE_GIGA_BYTES:.2f} GB' 55 | return f'{size/ONE_TERA_BYTES:.2f} TB' 56 | 57 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 58 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 59 | else: 60 | outputFile = sys.stdout 61 | outputCSV = csv.DictWriter(outputFile, ['id', 'name', 'size', 'Storage used', 'count', 'Item cap'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 62 | outputCSV.writeheader() 63 | 64 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 65 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 66 | else: 67 | inputFile = sys.stdin 68 | 69 | teamDriveInfo = {} 70 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 71 | outputCSV.writerow({'id': row['id'], 'name': row['name'], 'size': row['Size'], 'Storage used': formatSize(int(row['Size'])), 72 | 'count': row['Total'], 'Item cap': f"{int(row['Total'])/MAX_FILES_FOLDERS:.2%}"}) 73 | 74 | if inputFile != sys.stdin: 75 | inputFile.close() 76 | if outputFile != sys.stdout: 77 | outputFile.close() 78 | -------------------------------------------------------------------------------- /GetTeamDriveSuspendedUsersACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Get ACLs for Team Drives that reference suspended users 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get all Team Drives 9 | # $ gam redirect csv ./TeamDrives.csv print teamdrives fields id,name 10 | # 2: Get ACLs for all Team Drives 11 | # $ gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls "~id" fields id,emailaddress,role,type,deleted pm type user em pmfilter 12 | # 3: Get suspended users 13 | # $ gam redirect csv ./SuspendedUsers.csv print users query "isSuspended=True" 14 | # 4: From the list of ACLs, output a CSV file with headers "id,name,permissionId,role,emailAddress" 15 | # $ python3 GetTeamDriveSuspendedUsersACLs.py TeamDriveACLs.csv TeamDrives.csv SuspendedUsers.csv TeamDriveSuspendedUsersACLs.csv 16 | # 5: Inspect TeamDriveSuspendedUsersACLs.csv, verify that it makes sense and then proceed if desired 17 | # 4: If desired, delete the ACLs 18 | # $ gam redirect stdout ./DeleteTeamDriveSuspendedUsersACLs.log multiprocess redirect stderr stdout csv ./TeamDriveSuspendedUsersACLs.csv gam delete drivefileacl "~id" "~permissionId" 19 | """ 20 | 21 | import csv 22 | import re 23 | import sys 24 | 25 | QUOTE_CHAR = '"' # Adjust as needed 26 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 27 | 28 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 29 | 30 | teamDriveNames = {} 31 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 32 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 33 | teamDriveNames[row['id']] = row['name'] 34 | inputFile.close() 35 | 36 | userSet = set() 37 | inputFile = open(sys.argv[3], 'r', encoding='utf-8') 38 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 39 | userSet.add(row['primaryEmail'].lower()) 40 | inputFile.close() 41 | 42 | if sys.argv[1] != '-': 43 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 44 | else: 45 | inputFile = sys.stdin 46 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 47 | 48 | if (len(sys.argv) > 4) and (sys.argv[4] != '-'): 49 | outputFile = open(sys.argv[4], 'w', encoding='utf-8', newline='') 50 | else: 51 | outputFile = sys.stdout 52 | outputCSV = csv.DictWriter(outputFile, ['id', 'name', 'permissionId', 'role', 'emailAddress'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 53 | outputCSV.writeheader() 54 | 55 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 56 | for k, v in iter(row.items()): 57 | mg = PERMISSIONS_N_TYPE.match(k) 58 | if mg and v == 'user': 59 | permissions_N = mg.group(1) 60 | if row.get(f'permissions.{permissions_N}.deleted') == 'True': 61 | continue 62 | emailAddress = row[f'permissions.{permissions_N}.emailAddress'].lower() 63 | if emailAddress in userSet: 64 | outputCSV.writerow({'id': row['id'], 65 | 'name': teamDriveNames.get(row['id'], row['id']), 66 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 67 | 'role': row[f'permissions.{permissions_N}.role'], 68 | 'emailAddress': emailAddress}) 69 | 70 | if inputFile != sys.stdin: 71 | inputFile.close() 72 | if outputFile != sys.stdout: 73 | outputFile.close() 74 | -------------------------------------------------------------------------------- /GetTypeWithLinkDriveACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), show all drive file ACLs for files shared with the desired type and withLink values 4 | # Customize: Set LINK_FIELD, DESIRED_TYPE and LINK_VALUE. 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 10 | # $ gam config auto_batch_min 1 redirect csv ./filelistperms.csv multiprocess all users print filelist fields id,title,permissions,owners.emailaddress,mimetype 11 | # 2: From that list of ACLs, output a CSV file with headers "Owner,driveFileId,driveFileTitle,permissionId,role" 12 | # that lists the driveFileIds and permissionIds for all ACLs with the desired type and withLink values 13 | # $ python3 GetTypeWithLinkDriveACLs.py filelistperms.csv deleteperms.csv 14 | # 3: Inspect deleteperms.csv, verify that it makes sense and then proceed 15 | # 4: If desired, delete the ACLs 16 | # $ gam csv ./deleteperms.csv gam user "~Owner" delete drivefileacl "~driveFileId" "~permissionId" 17 | """ 18 | 19 | import csv 20 | import re 21 | import sys 22 | 23 | FILE_NAME = 'name' 24 | ALT_FILE_NAME = 'title' 25 | # For GAMADV-XTD3 with drive_v3_native_names = false 26 | #LINK_FIELD = 'withLink' 27 | # For GAMADV-XTD3 with drive_v3_native_names = true 28 | LINK_FIELD = 'allowFileDiscovery' 29 | 30 | DESIRED_TYPE = 'anyone' # anyone or domain 31 | # Remember: withLink True = allowFileDiscovery False 32 | LINK_VALUE = 'True' # 'True' or 'False' 33 | 34 | QUOTE_CHAR = '"' # Adjust as needed 35 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 36 | 37 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 38 | 39 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 40 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 41 | else: 42 | outputFile = sys.stdout 43 | outputCSV = csv.DictWriter(outputFile, ['Owner', 'driveFileId', 'driveFileTitle', 'mimeType', 'permissionId', 'role'], 44 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 45 | outputCSV.writeheader() 46 | 47 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 48 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 49 | else: 50 | inputFile = sys.stdin 51 | 52 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 53 | for k, v in iter(row.items()): 54 | mg = PERMISSIONS_N_TYPE.match(k) 55 | if mg and v: 56 | permissions_N = mg.group(1) 57 | if v == DESIRED_TYPE and row[f'permissions.{permissions_N}.{LINK_FIELD}'] == LINK_VALUE: 58 | outputCSV.writerow({'Owner': row['owners.0.emailAddress'], 59 | 'driveFileId': row['id'], 60 | 'driveFileTitle': row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown')), 61 | 'mimeType': row['mimeType'], 62 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 63 | 'role': row[f'permissions.{permissions_N}.role']}) 64 | 65 | if inputFile != sys.stdin: 66 | inputFile.close() 67 | if outputFile != sys.stdout: 68 | outputFile.close() 69 | -------------------------------------------------------------------------------- /GetUserCCOrgs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Move root users to an Org Unit based on their work address countryCode 4 | # Customize: Set ORG_UNIT_FORMAT_MAP or ORG_UNIT_DICT_MAP 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get primaryEmail, orgUnitPath, addresses for all root users 10 | # $ gam redirect csv ./UserAddresses.csv org "/" print users fields primaryemail,orgunitpath,addresses 11 | # 2: From that list of users, output a CSV file with headers "Org,primaryEmail" 12 | # that lists the Org Unit derived from the root user's work address and their primaryEmail 13 | # $ python3 GetUserCCOrgs.py ./UserAddresses.csv ./UserCCOrgs.csv 14 | # 3: Inspect UserCCOrgs.csv, verify that it makes sense and then proceed 15 | # 4: Move the users 16 | # $ gam update orgs csvkmd ./UserCCOrgs.csv keyfield Org datafield primaryEmail add csvdata primaryEmail 17 | """ 18 | 19 | import csv 20 | import re 21 | import sys 22 | 23 | # If you simply want to substitute the country code into a string that defines the OU, 24 | # use ORG_UNIT_FORMAT_MAP, Change the format as desired, {0} is replaced by countryCode 25 | # For example, ORG_UNIT_FORMAT_MAP = '/SalesByCountry/{0}' 26 | ORG_UNIT_FORMAT_MAP = '{0}' 27 | 28 | # If you want to map the country codes to OUs in a more cmplex manner, use ORG_UNIT_DICT_MAP. 29 | # For example, ORG_UNIT_DICT_MAP = {'US': '/SalesByCountry/UnitedStates', 'CA': '/SalesByCountry/Canada'} 30 | ORG_UNIT_DICT_MAP = {} 31 | 32 | QUOTE_CHAR = '"' # Adjust as needed 33 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 34 | 35 | ADDRESSES_N_TYPE = re.compile(r"addresses.(\d+).type") 36 | 37 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 38 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 39 | else: 40 | outputFile = sys.stdout 41 | outputCSV = csv.DictWriter(outputFile, ['Org', 'primaryEmail'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 42 | outputCSV.writeheader() 43 | 44 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 45 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 46 | else: 47 | inputFile = sys.stdin 48 | 49 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 50 | if row['orgUnitPath'] == '/': 51 | for k, v in iter(row.items()): 52 | mg = ADDRESSES_N_TYPE.match(k) 53 | if mg and v == 'work': 54 | addresses_N = mg.group(1) 55 | cc = row[f'addresses.{addresses_N}.countryCode'] 56 | if cc: 57 | if ORG_UNIT_DICT_MAP: 58 | org = ORG_UNIT_DICT_MAP.get(cc, None) 59 | if org: 60 | outputCSV.writerow({'Org': org, 'primaryEmail': row['primaryEmail']}) 61 | break 62 | sys.stderr.write(f'ERROR: No OU for country code {cc} for user {row["primaryEmail"]}\n') 63 | else: 64 | outputCSV.writerow({'Org': ORG_UNIT_FORMAT_MAP.format(cc), 'primaryEmail': row['primaryEmail']}) 65 | break 66 | 67 | if inputFile != sys.stdin: 68 | inputFile.close() 69 | if outputFile != sys.stdout: 70 | outputFile.close() 71 | -------------------------------------------------------------------------------- /GetUserGroupAccessCounts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), show user/group file access counts 4 | # Customize: GROUP_ROLES, USER_ROLES 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get ACLs for all files, if you don't want all users, replace all users with your user selection in the command below 10 | # $ gam config auto_batch_min 1 redirect csv ./filelistperms.csv multiprocess all users print filelist fields id,title,permissions 11 | # $ gam redirect csv ./filelistperms.csv user user@domain.com print filelist fields id,title,permissions 12 | # 2: From that list of ACLs, output a CSV file that lists the user/group file access counts 13 | # $ python3 GetUserGroupAccessCounts.py filelistperms.csv UserCounts.csv GroupCounts.csv 14 | """ 15 | 16 | import csv 17 | import re 18 | import sys 19 | 20 | GROUP_ROLES = ['commenter', 'reader', 'writer'] # Choose from: commenter|reader|writer 21 | USER_ROLES = ['owner', 'commenter', 'reader', 'writer'] # Choose from: owner|commenter|reader|writer 22 | 23 | DEFAULT_GROUP = {} 24 | for role in GROUP_ROLES: 25 | DEFAULT_GROUP[role] = 0 26 | 27 | DEFAULT_USER = {} 28 | for role in USER_ROLES: 29 | DEFAULT_USER[role] = 0 30 | 31 | FILE_NAME = 'name' 32 | ALT_FILE_NAME = 'title' 33 | 34 | QUOTE_CHAR = '"' # Adjust as needed 35 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 36 | 37 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 38 | 39 | Users = {} 40 | Groups = {} 41 | 42 | groupOutputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 43 | groupFieldNames = ['Group']+GROUP_ROLES 44 | groupOutputCSV = csv.DictWriter(groupOutputFile, groupFieldNames, 45 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 46 | groupOutputCSV.writeheader() 47 | 48 | userOutputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 49 | userFieldNames = ['User']+USER_ROLES 50 | userOutputCSV = csv.DictWriter(userOutputFile, userFieldNames, 51 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 52 | userOutputCSV.writeheader() 53 | 54 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 55 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 56 | for k, v in iter(row.items()): 57 | mg = PERMISSIONS_N_TYPE.match(k) 58 | if mg and v: 59 | permissions_N = mg.group(1) 60 | if row.get(f'permissions.{permissions_N}.deleted') == 'True': 61 | continue 62 | role = row[f'permissions.{permissions_N}.role'] 63 | if v == 'user': 64 | if role in USER_ROLES: 65 | emailAddress = row[f'permissions.{permissions_N}.emailAddress'].lower() 66 | Users.setdefault(emailAddress, DEFAULT_USER.copy()) 67 | Users[emailAddress][role] += 1 68 | elif v == 'group': 69 | if role in GROUP_ROLES: 70 | emailAddress = row[f'permissions.{permissions_N}.emailAddress'].lower() 71 | Groups.setdefault(emailAddress, DEFAULT_GROUP.copy()) 72 | Groups[emailAddress][role] += 1 73 | inputFile.close() 74 | 75 | for k, v in sorted(iter(Users.items())): 76 | row = {'User': k} 77 | row.update(v) 78 | userOutputCSV.writerow(row) 79 | userOutputFile.close() 80 | 81 | for k, v in sorted(iter(Groups.items())): 82 | row = {'Group': k} 83 | row.update(v) 84 | groupOutputCSV.writerow(row) 85 | groupOutputFile.close() 86 | -------------------------------------------------------------------------------- /GetUserNonOwnerDrivePermissions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User, get all drive file ACLs except those indicating the user as owner 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Use print filelist to get selected ACLs 9 | # Syntax: gam print filelist [anyowner|(showownedby any|me|others)] 10 | # [query ] [fullquery ] [select |orphans] [depth ] [showparent] 11 | # For a full description of print filelist, see: https://github.com/taers232c/GAMADV-XTD/wiki/Users-Drive-Files 12 | # Example: gam redirect csv ./filelistperms.csv user testuser@domain.com print filelist id title permissions owners.emailaddress,mimetype pm not role owner em pmfilter 13 | # 2: From that list of ACLs, output a CSV file with headers "Owner,driveFileId,driveFileTitle,permissionIds" 14 | # that lists the driveFileIds and permissionIds for all ACLs except those indicating the user as owner 15 | # (n.b., driveFileTitle is not used in the next step, it is included for documentation purposes) 16 | # $ python3 GetUserNonOwnerDrivePermissions.py filelistperms.csv deleteperms.csv 17 | # 3: Inspect deleteperms.csv, verify that it makes sense and then proceed 18 | # 4: If desired, delete the ACLs 19 | # Parallel, faster: 20 | # $ gam csv ./deleteperms.csv gam user "~Owner" delete permissions "~driveFileId" "~permissionIds" 21 | # Serial, cleaner output: 22 | # $ gam csvkmd users deleteperms.csv keyfield Owner subkeyfield driveFileId datafield permissionIds delimiter "," delete permissions csvsubkey driveFileId csvdata permissionIds 23 | """ 24 | 25 | import csv 26 | import re 27 | import sys 28 | 29 | FILE_NAME = 'name' 30 | ALT_FILE_NAME = 'title' 31 | 32 | QUOTE_CHAR = '"' # Adjust as needed 33 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 34 | 35 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 36 | 37 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 38 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 39 | else: 40 | outputFile = sys.stdout 41 | outputCSV = csv.DictWriter(outputFile, ['Owner', 'driveFileId', 'driveFileTitle', 'mimeType', 'permissionIds'], 42 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 43 | outputCSV.writeheader() 44 | 45 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 46 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 47 | else: 48 | inputFile = sys.stdin 49 | 50 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 51 | permissionIds = [] 52 | for k, v in iter(row.items()): 53 | mg = PERMISSIONS_N_TYPE.match(k) 54 | if mg and v: 55 | permissions_N = mg.group(1) 56 | if v != 'user' or row[f'permissions.{permissions_N}.role'] != 'owner' or row.get(f'permissions.{permissions_N}.emailAddress', '') != row['owners.0.emailAddress']: 57 | permissionIds.append(row[f'permissions.{permissions_N}.id']) 58 | if permissionIds: 59 | outputCSV.writerow({'Owner': row['owners.0.emailAddress'], 60 | 'driveFileId': row['id'], 61 | 'driveFileTitle': row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown')), 62 | 'mimeType': row['mimeType'], 63 | 'permissionIds': ','.join(permissionIds)}) 64 | 65 | if inputFile != sys.stdin: 66 | inputFile.close() 67 | if outputFile != sys.stdout: 68 | outputFile.close() 69 | -------------------------------------------------------------------------------- /GetUsersGroupCounts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Create a CSV file showing users that belong to a number of groups beyond a threshold 4 | # threshold not specified - output all users 5 | # threshold 0 - output all users belonging to at least 1 group 6 | # threshold N - output all users belonging to more than N groups 7 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 8 | # $ python -V or python3 -V 9 | # Python 3.x.y 10 | # Usage: 11 | # 1: Get list of users, group members 12 | # $ gam redirect csv ./Users.csv print users 13 | # $ gam redirect csv ./GroupMembers.csv print group-members 14 | # 2: From that list of users, output a CSV file with headers with the same headers as Users.csv plus GroupsCount 15 | # that shows the number of groups 16 | # $ python3 GetUsersGroupCounts.py ./Users.csv ./GroupMembers.csv ./UsersGroupsCounts.csv 17 | """ 18 | 19 | import csv 20 | import sys 21 | 22 | QUOTE_CHAR = '"' # Adjust as needed 23 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 24 | 25 | Users = {} 26 | 27 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 28 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 29 | fieldnames = inputCSV.fieldnames[:] 30 | fieldnames.insert(1, 'GroupsCount') 31 | for row in inputCSV: 32 | row['GroupsCount'] = 0 33 | Users[row['primaryEmail']] = row 34 | inputFile.close() 35 | 36 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 37 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 38 | if row['email'] in Users: 39 | Users[row['email']]['GroupsCount'] += 1 40 | inputFile.close() 41 | 42 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 43 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 44 | else: 45 | outputFile = sys.stdout 46 | outputCSV = csv.DictWriter(outputFile, fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 47 | outputCSV.writeheader() 48 | 49 | if len(sys.argv) > 4: 50 | threshold = int(sys.argv[4]) 51 | else: 52 | threshold = -1 53 | 54 | for _, v in sorted(Users.items()): 55 | if v['GroupsCount'] > threshold: 56 | outputCSV.writerow(v) 57 | 58 | if outputFile != sys.stdout: 59 | outputFile.close() 60 | -------------------------------------------------------------------------------- /GetUsersNoGroups.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Create a CSV file showing users that don't belong to any groups 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get list of users, group members 9 | # $ gam redirect csv ./Users.csv print users 10 | # $ gam redirect csv ./GroupMembers.csv print group-members 11 | # 2: From that list of users, output a CSV file with the same headers as Users.csv plus GroupsCount 12 | # that shows users that don't belong to any groups 13 | # $ python3 GetUsersNoGroups.py ./Users.csv ./GroupMembers.csv ./UsersNoGroups.csv 14 | """ 15 | 16 | import csv 17 | import sys 18 | 19 | QUOTE_CHAR = '"' # Adjust as needed 20 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 21 | 22 | Users = {} 23 | 24 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 25 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 26 | fieldnames = inputCSV.fieldnames[:] 27 | fieldnames.insert(1, 'GroupsCount') 28 | for row in inputCSV: 29 | row['GroupsCount'] = 0 30 | Users[row['primaryEmail']] = row 31 | inputFile.close() 32 | 33 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 34 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 35 | if row['email'] in Users: 36 | Users[row['email']]['GroupsCount'] += 1 37 | inputFile.close() 38 | 39 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 40 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 41 | else: 42 | outputFile = sys.stdout 43 | outputCSV = csv.DictWriter(outputFile, fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 44 | outputCSV.writeheader() 45 | 46 | for _, v in sorted(Users.items()): 47 | if v['GroupsCount'] == 0: 48 | outputCSV.writerow(v) 49 | 50 | if outputFile != sys.stdout: 51 | outputFile.close() 52 | -------------------------------------------------------------------------------- /MakeGroupMembersSyncs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Convert a CSV file showing group members to one that can be fed into Gam to sync members 4 | # Customize: Set INPUT and OUTPUT field names 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get group members 10 | # $ gam redirect csv ./GroupMembers.csv print group-members fields email,role 11 | # 2: From that list of group members, output a CSV file with headers group,role,mambers that can be used to sync groups 12 | # $ python3 MakeGroupMembersSyncs.py ./GroupMembers.csv ./GroupUpdates.csv 13 | # 3: Preview the changes if desired 14 | # $ gam csv ./GroupUpdates.csv gam update group "~group" sync "~role" preview "~members" 15 | # 3: Sync the groups 16 | # $ gam csv ./GroupUpdates.csv gam update group "~group" sync "~role" "~members" 17 | """ 18 | 19 | import csv 20 | import sys 21 | 22 | INPUT_GROUP = 'group' 23 | INPUT_ROLE = 'role' 24 | INPUT_EMAIL = 'email' 25 | 26 | OUTPUT_GROUP = 'group' 27 | OUTPUT_ROLE = 'role' 28 | OUTPUT_MEMBERS = 'members' 29 | 30 | DELIMITER = ' ' 31 | QUOTE_CHAR = '"' # Adjust as needed 32 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 33 | 34 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 35 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 36 | else: 37 | outputFile = sys.stdout 38 | outputCSV = csv.DictWriter(outputFile, [OUTPUT_GROUP, OUTPUT_ROLE, OUTPUT_MEMBERS], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 39 | outputCSV.writeheader() 40 | 41 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 42 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 43 | else: 44 | inputFile = sys.stdin 45 | 46 | Groups = {} 47 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 48 | group = row[INPUT_GROUP] 49 | role = row[INPUT_ROLE] 50 | Groups.setdefault(group, {}) 51 | Groups[group].setdefault(role, []) 52 | Groups[group][role].append(row[INPUT_EMAIL]) 53 | 54 | for group in sorted(Groups): 55 | for role in Groups[group]: 56 | outputCSV.writerow({OUTPUT_GROUP: group, 57 | OUTPUT_ROLE: role, 58 | OUTPUT_MEMBERS: DELIMITER.join(Groups[group][role])}) 59 | 60 | if inputFile != sys.stdin: 61 | inputFile.close() 62 | if outputFile != sys.stdout: 63 | outputFile.close() 64 | -------------------------------------------------------------------------------- /MakeOneParentPerRow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a print filelist file, write multiple parents into separate rows. 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get all files, if you don't want all users, replace all users with your user selection in the command below 9 | # $ gam config auto_batch_min 1 redirect csv ./filelist.csv multiprocess all users print filelist fields id,name,permissions,parents 10 | # $ gam redirect csv ./filelist.csv user user@domain.com print filelist fields id,name,permissions,parents ... 11 | # 3: From that list of files, output a CSV file with the same headers but just 'parents,parents.id,parents.isRoot' 12 | # $ python3 MakeOneParentPerRow.py filelist.csv filelistoppr.csv 13 | """ 14 | 15 | import csv 16 | import re 17 | import sys 18 | 19 | ONE_ACL_PER_ROW = False # Set True for one ACL per row 20 | 21 | QUOTE_CHAR = '"' # Adjust as needed 22 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 23 | 24 | PARENTS_N_FIELD = re.compile(r"parents.(\d+).(.*)") 25 | 26 | if sys.argv[1] != '-': 27 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 28 | else: 29 | inputFile = sys.stdin 30 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 31 | fieldnames = [] 32 | nonParentNFieldNames = [] 33 | parentFields = [] 34 | lastParent = -1 35 | for k in inputCSV.fieldnames: 36 | mg = PARENTS_N_FIELD.match(k) 37 | if mg: 38 | lastParent = int(mg.group(1)) 39 | sk = mg.group(2) 40 | if sk not in parentFields: 41 | parentFields.append(sk) 42 | fieldnames.append(f'parents.{sk}') 43 | else: 44 | fieldnames.append(k) 45 | nonParentNFieldNames.append(k) 46 | 47 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 48 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 49 | else: 50 | outputFile = sys.stdout 51 | outputCSV = csv.DictWriter(outputFile, fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 52 | outputCSV.writeheader() 53 | 54 | for row in inputCSV: 55 | if lastParent >= 0: 56 | orow = {} 57 | for k in nonParentNFieldNames: 58 | orow[k] = row[k] 59 | numParents = int(row['parents']) 60 | if numParents > 0: 61 | orow['parents'] = 1 62 | for n in range(0, numParents): 63 | for sk in parentFields: 64 | orow[f'parents.{sk}'] = row.get(f'parents.{n}.{sk}', '') 65 | outputCSV.writerow(orow) 66 | else: 67 | orow['parents'] = 0 68 | for sk in parentFields: 69 | orow[f'parents.{sk}'] = '' 70 | outputCSV.writerow(orow) 71 | else: 72 | outputCSV.writerow(row) 73 | 74 | if inputFile != sys.stdin: 75 | inputFile.close() 76 | if outputFile != sys.stdout: 77 | outputFile.close() 78 | -------------------------------------------------------------------------------- /MergeGroupInfoMembers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Merge data from print groups CSV file into print group-members CSV file 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Generate the two data files, e.g.: 9 | # $ gam redirect csv ./GroupInfo.csv print groups fields name,description 10 | # $ gam redirect csv ./GroupMembers.csv print group-members 11 | # 2: Merge the files 12 | # $ python3 MergeGroupInfoMembers.py ./GroupInfo.csv ./GroupMembers.csv ./GroupMerged.csv 13 | """ 14 | 15 | import csv 16 | import sys 17 | 18 | QUOTE_CHAR = '"' # Adjust as needed 19 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 20 | 21 | # Data from print groups 22 | groupInfo = {} 23 | infoFileName = sys.argv[1] 24 | infoFile = open(infoFileName, 'r', encoding='utf-8') 25 | infoCSV = csv.DictReader(infoFile, quotechar=QUOTE_CHAR) 26 | infoFieldNames = infoCSV.fieldnames[:] 27 | INFO_KEY_FIELD = 'email' 28 | if INFO_KEY_FIELD not in infoFieldNames: 29 | sys.stderr.write(f'Group Info key field "{INFO_KEY_FIELD}" is not in {infoFileName} headers: {",".join(infoFieldNames)}\n') 30 | sys.exit(1) 31 | infoFieldNames.remove(INFO_KEY_FIELD) 32 | if 'name' in infoFieldNames: 33 | infoFieldNames[infoFieldNames.index('name')] = 'groupName' 34 | for row in infoCSV: 35 | if 'name' in row: 36 | row['groupName'] = row.pop('name') 37 | groupInfo[row.pop(INFO_KEY_FIELD).lower()] = row 38 | infoFile.close() 39 | 40 | # Data from print group-members 41 | memberFileName = sys.argv[2] 42 | memberFile = open(memberFileName, 'r', encoding='utf-8') 43 | memberCSV = csv.DictReader(memberFile, quotechar=QUOTE_CHAR) 44 | memberFieldNames = memberCSV.fieldnames[:] 45 | MEMBER_KEY_FIELD = 'group' 46 | if MEMBER_KEY_FIELD not in memberFieldNames: 47 | sys.stderr.write(f'Group Member key field "{MEMBER_KEY_FIELD}" is not in {memberFileName} headers: {",".join(memberFieldNames)}\n') 48 | sys.exit(1) 49 | i = memberFieldNames.index(MEMBER_KEY_FIELD) 50 | mergedFieldNames = memberFieldNames[0:i+1]+infoFieldNames+memberFieldNames[i+1:] 51 | mergedFileName = sys.argv[3] 52 | mergedFile = open(mergedFileName, 'w', encoding='utf-8', newline='') 53 | mergedCSV = csv.DictWriter(mergedFile, mergedFieldNames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 54 | mergedCSV.writeheader() 55 | 56 | errors = 0 57 | for row in memberCSV: 58 | group= row[MEMBER_KEY_FIELD].lower() 59 | if group in groupInfo: 60 | row.update(groupInfo[group]) 61 | else: 62 | errors = 1 63 | sys.stderr.write(f'Group Member key value "{group}" in {memberFileName} does not occur in {infoFileName}\n') 64 | mergedCSV.writerow(row) 65 | 66 | memberFile.close() 67 | mergedFile.close() 68 | sys.exit(errors) 69 | -------------------------------------------------------------------------------- /MergeSendasUsers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Make a CSV file that merges sendas addresses with user data 4 | # Customize: Set INCLUDE_PRIMARY = True/False, OPTIONAL_MERGE_FIELDS 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get Users 10 | # Use one of the following to select a collection of users 11 | # ::= 12 | # (all users)| 13 | # (users )| 14 | # (group|group_ns )| 15 | # (groups|groups_ns )| 16 | # (ou|ou_ns )| 17 | # (ou_and_children|ou_and_children_ns )| 18 | # (ous|ous_ns )| 19 | # (ous_and_children|ous_and_children_ns )| 20 | # (courseparticipants )| 21 | # (students )| 22 | # (teachers )| 23 | # (file [charset ] [delimiter ])| 24 | # (csvfile (:)+ [charset ] [columndelimiter ] [quotechar ] 25 | # [fields ] (matchfield )* [delimiter ]) 26 | # $ gam print users fields primaryemail,... > ./Users.csv 27 | # 2: Get Sendas addresses 28 | # $ gam print sendas > ./Sendas.csv 29 | # 3: Output an updated version of Users.csv with one row for each address in Sendas.csv 30 | # $ python3 MergeSendasUsers.py ./Sendas.csv ./Users.csv ./UpdatedUsers.csv 31 | """ 32 | 33 | import csv 34 | import sys 35 | 36 | QUOTE_CHAR = '"' # Adjust as needed 37 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 38 | 39 | # Should primary email address be include? 40 | INCLUDE_PRIMARY = True 41 | # Select optional fields from Sendas.csv to merge with Users.csv 42 | # Choose from: 'replyToAddress','isPrimary','isDefault','treatAsAlias','verificationStatus','signature' 43 | OPTIONAL_MERGE_FIELDS = [] 44 | 45 | usersSendasAddresses = {} 46 | 47 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 48 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 49 | else: 50 | inputFile = sys.stdin 51 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 52 | usersSendasAddresses.setdefault(row['User'], []) 53 | if row['isPrimary'] == 'False' or INCLUDE_PRIMARY: 54 | usersSendasAddresses[row['User']].append(row) 55 | if inputFile != sys.stdin: 56 | inputFile.close() 57 | 58 | if len(sys.argv) > 2: 59 | inputFile = open(sys.argv[2], 'r', encoding='utf-8') 60 | else: 61 | sys.stderr.write('Error: Users file missing') 62 | sys.exit(1) 63 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 64 | 65 | if (len(sys.argv) > 3) and (sys.argv[3] != '-'): 66 | outputFile = open(sys.argv[3], 'w', encoding='utf-8', newline='') 67 | else: 68 | outputFile = sys.stdout 69 | outputFieldnames = inputCSV.fieldnames[:] 70 | outputFieldnames.append('sendAsEmail') 71 | outputFieldnames.extend(OPTIONAL_MERGE_FIELDS) 72 | outputCSV = csv.DictWriter(outputFile, outputFieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 73 | outputCSV.writeheader() 74 | 75 | for row in inputCSV: 76 | for sendAs in usersSendasAddresses.get(row['primaryEmail'], []): 77 | row['sendAsEmail'] = sendAs['sendAsEmail'] 78 | for field in OPTIONAL_MERGE_FIELDS: 79 | row[field] = sendAs[field] 80 | outputCSV.writerow(row) 81 | 82 | inputFile.close() 83 | if outputFile != sys.stdout: 84 | outputFile.close() 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GAM-Scripts3 2 | Scripts for use with GAM7 and GAMADV-XTD3 - Python 3.9+ 3 | -------------------------------------------------------------------------------- /SelectiveDelete.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User(s), delete all files except those in selected top level folders 4 | # Customize: Set PATHS_TO_SAVE 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get information for all files, if you don't want all users, replace all users with your user selection 10 | # in the command below, e.g., ou /Students/Classof2019 11 | # These fields are required: fields id,name,owners.emailaddress 12 | # You can add additional fields that will be preserved in the output. 13 | # $ gam config auto_batch_min 1 redirect csv ./UserFiles.csv multiprocess all users print filelist fields id,name,owners.emailaddress fullpath 14 | # 2: From that list of files, output a CSV file with the same headers as the input CSV file 15 | # that lists the drive file Ids that are not in the selected top level folders 16 | # $ python3 SelectiveDelete.py ./UserFiles.csv ./DeleteFiles.csv 17 | # 3: Inspect DeleteFiles.csv, verify that it makes sense and then proceed 18 | # 4: Delete the files 19 | # $ gam redirect stdout ./DeleteFiles.log multiprocess redirect stderr stdout csv ./DetelteFiles.csv gam user "~owners.0.emailAddress" delete drivefile "~id" 20 | """ 21 | 22 | import csv 23 | import sys 24 | 25 | QUOTE_CHAR = '"' # Adjust as needed 26 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 27 | 28 | # ['My Drive/xxx'] or ['My Drive/xxx', 'My Drive/yyy'] 29 | PATHS_TO_SAVE = [] 30 | 31 | def pathToSave(crow): 32 | for i in range(0, int(crow['paths'])): 33 | path = crow[f'path.{i}'] 34 | for p in PATHS_TO_SAVE: 35 | if path.startswith(p): 36 | return True 37 | return False 38 | 39 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 40 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 41 | else: 42 | outputFile = sys.stdout 43 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 44 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 45 | else: 46 | inputFile = sys.stdin 47 | 48 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 49 | outputCSV = csv.DictWriter(outputFile, inputCSV.fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 50 | outputCSV.writeheader() 51 | 52 | for row in inputCSV: 53 | if not pathToSave(row): 54 | outputCSV.writerow(row) 55 | if inputFile != sys.stdin: 56 | inputFile.close() 57 | if outputFile != sys.stdout: 58 | outputFile.close() 59 | -------------------------------------------------------------------------------- /ShowDelegators.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Make a CSV file showing delegators for delegates 4 | # Customize: Set SELECTED_DELEGATES, ONE_DELEGATOR_PER_ROW 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get delegates 10 | # $ gam redirect csv ./AllDelegates.csv all users print delegates shownames 11 | # 2: From that list of delegates, output a CSV file with headers "Delegate,Delegate Email,Delegators 12 | # $ python3 ShowDelegators.py ./AllDelegates.csv ./AllDelegators.csv 13 | # 3: With SELECTED_DELEGATES and ONE_DELEGATOR_PER_ROW = True, it's easy to delete a delegate from delegator(s) 14 | # Edit AllDelegators.csv and delete any rows for delegators that are to remain 15 | # $ gam csv ./AllDelegators.csv gam user "~Delegators" delete delegate "~Delegate Email" 16 | """ 17 | 18 | import csv 19 | import sys 20 | 21 | # If you are only interested for delegators for a select list of delegates, 22 | # add them to SELECTED_DELEGATES, e.g., SELECTED_DELEGATES = ['delegate1@domain.com',] SELECTED_DELEGATES = ['delegate1@domain.com', 'delegate2@domain.com',] 23 | SELECTED_DELEGATES = [] 24 | 25 | # For each delegate, you can show all delegators on one row or on separate rows 26 | # ONE_DELEGATOR_PER_ROW = False - All delegators for a delegate on one row 27 | # ONE_DELEGATOR_PER_ROW = True - Each delegator for a delegate on a separate row 28 | ONE_DELEGATOR_PER_ROW = False 29 | 30 | QUOTE_CHAR = '"' # Adjust as needed 31 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 32 | 33 | delegates = {} 34 | 35 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 36 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 37 | else: 38 | outputFile = sys.stdout 39 | outputCSV = csv.DictWriter(outputFile, ['Delegate', 'Delegate Email', 'Delegators'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 40 | outputCSV.writeheader() 41 | 42 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 43 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 44 | else: 45 | inputFile = sys.stdin 46 | 47 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 48 | delegate = row['delegateAddress'] 49 | if not SELECTED_DELEGATES or delegate in SELECTED_DELEGATES: 50 | delegates.setdefault(delegate, {'Delegate': row.get('delegateName', delegate), 'Delegators': []}) 51 | delegates[delegate]['Delegators'].append(row['User']) 52 | 53 | for delegate in sorted(delegates): 54 | if not ONE_DELEGATOR_PER_ROW: 55 | outputCSV.writerow({'Delegate': delegates[delegate]['Delegate'], 56 | 'Delegate Email': delegate, 57 | 'Delegators': ' '.join(delegates[delegate]['Delegators'])}) 58 | else: 59 | for delegator in delegates[delegate]['Delegators']: 60 | outputCSV.writerow({'Delegate': delegates[delegate]['Delegate'], 61 | 'Delegate Email': delegate, 62 | 'Delegators': delegator}) 63 | 64 | if inputFile != sys.stdin: 65 | inputFile.close() 66 | if outputFile != sys.stdout: 67 | outputFile.close() 68 | -------------------------------------------------------------------------------- /ShowGroupMemberTree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Produce a file to show hierarchial group membership 4 | # Customize: SHOW_ROLE 5 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 6 | # $ python -V or python3 -V 7 | # Python 3.x.y 8 | # Usage: 9 | # 1: Get group members 10 | # $ gam redirect csv ./GroupMembers.csv print group-members fields email,role recursive noduplicates 11 | # 2: From that list of group members, output a file showing hierarchial group membership 12 | # $ python3 ShowGroupMemberTree.py ./GroupMembers.csv ./GroupMemberTree.txt 13 | """ 14 | 15 | import csv 16 | import sys 17 | 18 | QUOTE_CHAR = '"' # Adjust as needed 19 | 20 | SHOW_ROLE = False # True - display role, False - don't display role 21 | 22 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 23 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 24 | else: 25 | outputFile = sys.stdout 26 | 27 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 28 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 29 | else: 30 | inputFile = sys.stdin 31 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 32 | 33 | Groups = {} 34 | for row in inputCSV: 35 | group = row['group'] 36 | Groups.setdefault(group, {}) 37 | Groups[group][row['email']] = row['role'] 38 | for group, members in sorted(iter(Groups.items())): 39 | outputFile.write(f'Group: {group}\n') 40 | for member, role in sorted(iter(members.items())): 41 | if SHOW_ROLE: 42 | outputFile.write(f" {member}:{role}\n") 43 | else: 44 | outputFile.write(f" {member}\n") 45 | if inputFile != sys.stdin: 46 | inputFile.close() 47 | if outputFile != sys.stdout: 48 | outputFile.close() 49 | -------------------------------------------------------------------------------- /ShowUserNonOwnerDriveACLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: For a Google Drive User, get all drive file ACLs for files except those indicating the user as owner 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Use print filelist to get selected ACLs 9 | # gam redirect csv ./filelistperms.csv user testuser@domain.com print filelist fields id,title,permissions,owners.emailaddress,mimetype 10 | # 2: From that list of ACLs, output a CSV file with headers "Owner,driveFileId,driveFileTitle,mimeType,emailAddress" 11 | # that lists the driveFileIds/Titles for all ACLs except those indicating the user as owner 12 | # $ python3 ShowUserNonOwnerDriveACLs.py filelistperms.csv localperms.csv 13 | """ 14 | 15 | import csv 16 | import re 17 | import sys 18 | 19 | FILE_NAME = 'name' 20 | ALT_FILE_NAME = 'title' 21 | 22 | QUOTE_CHAR = '"' # Adjust as needed 23 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 24 | 25 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 26 | 27 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 28 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 29 | else: 30 | outputFile = sys.stdout 31 | outputCSV = csv.DictWriter(outputFile, ['Owner', 'driveFileId', 'driveFileTitle', 'mimeType', 'emailAddress'], 32 | lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 33 | outputCSV.writeheader() 34 | 35 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 36 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 37 | else: 38 | inputFile = sys.stdin 39 | 40 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 41 | for k, v in iter(row.items()): 42 | mg = PERMISSIONS_N_TYPE.match(k) 43 | if mg and v: 44 | permissions_N = mg.group(1) 45 | emailAddress = row.get(f'permissions.{permissions_N}.emailAddress', '').lower() 46 | if v != 'user' or row[f'permissions.{permissions_N}.role'] != 'owner' or emailAddress != row['owners.0.emailAddress'].lower(): 47 | outputCSV.writerow({'Owner': row['owners.0.emailAddress'], 48 | 'driveFileId': row['id'], 49 | 'driveFileTitle': row.get(FILE_NAME, row.get(ALT_FILE_NAME, 'Unknown')), 50 | 'mimeType': row['mimeType'], 51 | 'emailAddress': emailAddress}) 52 | 53 | if inputFile != sys.stdin: 54 | inputFile.close() 55 | if outputFile != sys.stdout: 56 | outputFile.close() 57 | -------------------------------------------------------------------------------- /UpdateOwnerFromPermissions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Update Owner column from permissions.n.emailAddress column where permissions.n.role == owner 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # $ python3 UpdateOwnerFromPermissions.py filelist.csv updatedfilelist.csv 9 | """ 10 | 11 | import csv 12 | import re 13 | import sys 14 | 15 | QUOTE_CHAR = '"' # Adjust as needed 16 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 17 | 18 | PERMISSIONS_N_ROLE = re.compile(r"permissions.(\d+).role") 19 | 20 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 21 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 22 | else: 23 | outputFile = sys.stdout 24 | 25 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 26 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 27 | else: 28 | inputFile = sys.stdin 29 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 30 | 31 | outputCSV = csv.DictWriter(outputFile, inputCSV.fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 32 | outputCSV.writeheader() 33 | 34 | for row in inputCSV: 35 | for k, v in iter(row.items()): 36 | mg = PERMISSIONS_N_ROLE.match(k) 37 | if mg and v == 'owner': 38 | permissions_N = mg.group(1) 39 | row['Owner'] = row[f'permissions.{permissions_N}.emailAddress'] 40 | break 41 | outputCSV.writerow(row) 42 | 43 | if inputFile != sys.stdin: 44 | inputFile.close() 45 | if outputFile != sys.stdout: 46 | outputFile.close() 47 | -------------------------------------------------------------------------------- /UpdateVacationToHTML.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Convert non-HTML(RTF) vacation messages to HTML(RTF) 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get vacation 9 | # $ gam redirect csv Vacation.csv print vacation compact 10 | # 2: Output a CSV file for users with non-blank/non-HTML vacation messages converted to HTML 11 | # $ python3 ComvertVacationToHTML.py ./Vacation.csv ./HTMLVacation.csv 12 | # 3: Inspect HTMLVacation.csv, verify that it makes sense and then proceed 13 | # 4: Update vacation 14 | # $ gam csv HTMLVacation.csv gam user "~User" vacation "~enabled" htmlmessage "~message" 15 | """ 16 | 17 | import csv 18 | import sys 19 | 20 | QUOTE_CHAR = '"' # Adjust as needed 21 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 22 | 23 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 24 | inputCSV = csv.DictReader(inputFile, quotechar=QUOTE_CHAR) 25 | 26 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 27 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 28 | else: 29 | outputFile = sys.stdout 30 | outputCSV = csv.DictWriter(outputFile, inputCSV.fieldnames, lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 31 | outputCSV.writeheader() 32 | 33 | for row in inputCSV: 34 | if row['html'] != 'True' and row['message']: 35 | row['message'] = "
"+row['message'].replace('\\\\n', '
').replace('\\\\r', '')+"
" 36 | outputCSV.writerow(row) 37 | 38 | if outputFile != sys.stdout: 39 | outputFile.close() 40 | -------------------------------------------------------------------------------- /UpgradeWritersToContentManagers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | # Purpose: Upgrade users with writer access on Team Drives to fileOrganizer access (aka content manager) 4 | # Python: Use python or python3 below as appropriate to your system; verify that you have version 3 5 | # $ python -V or python3 -V 6 | # Python 3.x.y 7 | # Usage: 8 | # 1: Get all Team Drives 9 | # $ gam redirect csv ./TeamDrives.csv print teamdrives fields id 10 | # 2: Get ACLs for all Team Drives 11 | # $ gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls "~id" fields id,emailaddress,role,type,deleted 12 | # 3: From that list of ACLs, output a CSV file with headers "teamDriveId,permissionId,type,emailAddress" 13 | # that lists the teamDriveIds and permissionIds for all ACLs with role writer. 14 | # $ python3 UpgradeWritersToContentManagers.py ./TeamDriveACLs.csv UpgradedACLs.csv 15 | # 4: Inspect UpgradedACLs.csv, verify that it makes sense and then proceed 16 | # 5: Upgrade the ACLs 17 | # $ gam redirect stdout ./UpgradedACLs.log multiprocess redirect stderr stdout multiprocess csv UpgradetdACLs.csv gam update drivefileacl teamdriveid "~teamDriveId" "~permissionId" role fileOrganizer 18 | """ 19 | 20 | import csv 21 | import re 22 | import sys 23 | 24 | QUOTE_CHAR = '"' # Adjust as needed 25 | LINE_TERMINATOR = '\n' # On Windows, you probably want '\r\n' 26 | 27 | PERMISSIONS_N_TYPE = re.compile(r"permissions.(\d+).type") 28 | 29 | if (len(sys.argv) > 2) and (sys.argv[2] != '-'): 30 | outputFile = open(sys.argv[2], 'w', encoding='utf-8', newline='') 31 | else: 32 | outputFile = sys.stdout 33 | outputCSV = csv.DictWriter(outputFile, ['teamDriveId', 'permissionId', 'type', 'emailAddress'], lineterminator=LINE_TERMINATOR, quotechar=QUOTE_CHAR) 34 | outputCSV.writeheader() 35 | 36 | if (len(sys.argv) > 1) and (sys.argv[1] != '-'): 37 | inputFile = open(sys.argv[1], 'r', encoding='utf-8') 38 | else: 39 | inputFile = sys.stdin 40 | 41 | for row in csv.DictReader(inputFile, quotechar=QUOTE_CHAR): 42 | for k, v in iter(row.items()): 43 | mg = PERMISSIONS_N_TYPE.match(k) 44 | if mg and v: 45 | permissions_N = mg.group(1) 46 | role = row[f'permissions.permissions_N{}.role'] 47 | if role != 'writer' or v not in ['user', 'group']: 48 | continue 49 | if row.get(f'permissions.{permissions_N}.deleted')) == 'True': 50 | continue 51 | outputCSV.writerow({'teamDriveId': row['id'], 52 | 'permissionId': f'id:{row[f"permissions.{permissions_N}.id"]}', 53 | 'type': v, 54 | 'emailAddress': row[f'permissions.{permissions_N}.emailAddress']}) 55 | 56 | if inputFile != sys.stdin: 57 | inputFile.close() 58 | if outputFile != sys.stdout: 59 | outputFile.close() 60 | --------------------------------------------------------------------------------