mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-07 15:51:38 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3e23fabf1 | ||
|
|
95eb36c5c2 | ||
|
|
17105d51f1 | ||
|
|
8dbc455407 | ||
|
|
7e7b8416a4 | ||
|
|
ed81501bf2 | ||
|
|
428f8f5987 | ||
|
|
b9a489251b | ||
|
|
3b9a9ba224 | ||
|
|
0d63a85a45 | ||
|
|
a8ea30e19e | ||
|
|
660e9deb70 | ||
|
|
ad5ba656ee | ||
|
|
991b1aa33b | ||
|
|
51fafdb735 | ||
|
|
52f1e998fd | ||
|
|
0f144b24d0 | ||
|
|
52d0b0a84b | ||
|
|
28224045b2 | ||
|
|
3fbb261821 | ||
|
|
0b4a4a270e | ||
|
|
177f5f0792 | ||
|
|
57e0cc80af | ||
|
|
1e67310f3d | ||
|
|
e661641544 | ||
|
|
81aa9c6e16 | ||
|
|
8c3b49a967 | ||
|
|
13c6893b67 | ||
|
|
6702e4db30 | ||
|
|
2ed23c8f55 | ||
|
|
24a58c8edc | ||
|
|
0112264291 | ||
|
|
fa5af0ed1f | ||
|
|
15e3f50a3c | ||
|
|
24b7c65754 | ||
|
|
2429e99e18 | ||
|
|
ff41390c85 | ||
|
|
d8d68e8eb0 | ||
|
|
2196e70e7b | ||
|
|
4f79a29155 | ||
|
|
0290f0d0ce | ||
|
|
b7c96a8172 | ||
|
|
fed1662eb4 | ||
|
|
077747be99 | ||
|
|
9eee475b4a | ||
|
|
2636dd9827 | ||
|
|
e18a70e55b | ||
|
|
e11123fccd | ||
|
|
536ea84321 | ||
|
|
4d953ac09b | ||
|
|
d9ce0acd83 | ||
|
|
f735e5e268 | ||
|
|
97358d15ef | ||
|
|
ea0886229e | ||
|
|
4e5462c704 | ||
|
|
ed2291bedf | ||
|
|
44ff2eba62 | ||
|
|
77fb2d0693 | ||
|
|
cea5a33856 | ||
|
|
3a7f84e49d | ||
|
|
5136c0a268 | ||
|
|
8e877a938e | ||
|
|
ed695b234f | ||
|
|
83179be184 |
29
.github/workflows/build.yml
vendored
29
.github/workflows/build.yml
vendored
@@ -91,7 +91,7 @@ jobs:
|
||||
freethreaded: false
|
||||
goal: build
|
||||
name: Build Arm MacOS 26
|
||||
- os: windows-2025
|
||||
- os: windows-2025-vs2026
|
||||
jid: 12
|
||||
freethreaded: false
|
||||
goal: build
|
||||
@@ -153,7 +153,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
cache.tar.xz
|
||||
key: gam-${{ matrix.jid }}-20260129
|
||||
key: gam-${{ matrix.jid }}-20260224
|
||||
|
||||
- name: Untar Cache archive
|
||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
|
||||
@@ -507,6 +507,8 @@ jobs:
|
||||
"$PYTHON" -m pip list
|
||||
|
||||
- name: Install pip requirements
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
echo "before anything..."
|
||||
"$PYTHON" -m pip list
|
||||
@@ -517,6 +519,12 @@ jobs:
|
||||
echo "--pip debug verbose--"
|
||||
"$PYTHON" -m pip debug --verbose
|
||||
echo "--------"
|
||||
if ([ "$RUNNER_OS" == "Windows" ] && [ "$RUNNER_ARCH" == "ARM64" ]); then
|
||||
# custom cryptography wheel for win arm64 since the project doesn't provide one:
|
||||
# https://github.com/pyca/cryptography/issues/14293
|
||||
gh release download --repo "jay0lee/cryptography-wheels" --pattern "*win_arm64.whl" --clobber
|
||||
"$PYTHON" -m pip install cryptography-*.whl
|
||||
fi
|
||||
"$PYTHON" -m pip install -vvv --upgrade ..[yubikey]
|
||||
echo "after everything..."
|
||||
"$PYTHON" -m pip list
|
||||
@@ -625,13 +633,19 @@ jobs:
|
||||
- name: MacOS send GAM binary for Apple notarization
|
||||
if: runner.os == 'macOS'
|
||||
env:
|
||||
ASP_NOTARIZE: ${{ secrets.ASP_NOTARIZE }}
|
||||
APPLE_KEY: ${{ secrets.APPLE_KEY }}
|
||||
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
|
||||
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
|
||||
run: |
|
||||
# Apple wants some kind of "package" submitted so just add gam to a .zip
|
||||
# name it something we can track and link in Apple's notarize process
|
||||
zipfilename="./gam-${RUNNER_ARCH}-${GITHUB_RUN_ID}-${GITHUB_RUN_NUMBER}.zip"
|
||||
zip -r "$zipfilename" "$gampath"
|
||||
xcrun notarytool submit --apple-id "jay0lee@gmail.com" --password "$ASP_NOTARIZE" --team-id GZ85H2DRLM "$zipfilename"
|
||||
export KEY_FILE="$(mktemp).p8"
|
||||
trap 'rm -f "$KEY_FILE"' EXIT
|
||||
echo "$APPLE_KEY" > "$KEY_FILE"
|
||||
xcrun notarytool submit "$zipfilename" --key "$KEY_FILE" --key-id "$APPLE_KEY_ID" --issuer "$APPLE_ISSUER_ID"
|
||||
rm -v "$KEY_FILE"
|
||||
rm -v "$zipfilename"
|
||||
|
||||
- name: Basic Tests all jobs
|
||||
@@ -669,10 +683,9 @@ jobs:
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
#$url = "https://files.certum.eu/software/SimplySignDesktop/Windows/9.3.2.67/SimplySignDesktop-9.3.2.67-64-bit-en.msi"
|
||||
#$file = "SimplySignDesktop-9.3.2.67-64-bit-en.msi"
|
||||
$url = "https://files.certum.eu/software/SimplySignDesktop/Windows/9.3.4.72/SimplySignDesktop-9.3.4.72-64-bit-en.msi"
|
||||
$file = "SimplySignDesktop-9.3.4.72-64-bit-en.msi"
|
||||
#$url = "https://files.certum.eu/software/SimplySignDesktop/Windows/9.3.4.72/SimplySignDesktop-9.3.4.72-64-bit-en.msi"
|
||||
$url = "https://www.files.certum.eu/software/SimplySignDesktop/Windows/9.4.0.84/SimplySignDesktop-9.4.0.84-64-bit-en.msi"
|
||||
$file = "SimplySignDesktop.msi"
|
||||
Invoke-WebRequest $url -OutFile $file
|
||||
$log = "install.log"
|
||||
$procMain = Start-Process "msiexec" "/i `"$file`" /qn /l*! `"$log`"" -NoNewWindow -PassThru
|
||||
|
||||
@@ -11,8 +11,8 @@ authors = [
|
||||
#significant compile dependencies.
|
||||
dependencies = [
|
||||
"arrow>=1.3.0",
|
||||
"chardet>=5.2.0",
|
||||
"cryptography==46.0.3",
|
||||
"chardet==5.2.0",
|
||||
"cryptography>=46.0.5",
|
||||
"distro; sys_platform=='linux'",
|
||||
"filelock>=3.18.0",
|
||||
"google-api-python-client>=2.167.0",
|
||||
|
||||
@@ -2789,21 +2789,23 @@ gam create chromepolicyimage <ChromePolicyImageSchemaName> <FileName>
|
||||
|
||||
gam update chromepolicy [convertcrnl]
|
||||
(<SchemaName> ((<Field> <Value>)+ | <JSONData>))+
|
||||
((ou|orgunit <OrgUnitItem>)|(cigroup <GroupItem>))
|
||||
((ou|orgunit <OrgUnitItem>)|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
gam delete chromepolicy
|
||||
(<SchemaName> [<JSONData>])+
|
||||
((ou|orgunit <OrgUnitItem>)|(cigroup <GroupItem>))
|
||||
((ou|orgunit <OrgUnitItem>)|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
gam show chromepolicies
|
||||
((ou|orgunit <OrgUnitItem> [show all|direct|inherited])|(cigroup <GroupItem>))
|
||||
((ou|orgunit <OrgUnitItem> [show all|direct|inherited])|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
(filter <StringList>)* (namespace <NamespaceList>)*
|
||||
[show all|direct|inherited]
|
||||
[formatjson]
|
||||
gam print chromepolicies [todrive <ToDriveAttribute>*]
|
||||
((ou|orgunit <OrgUnitItem> [show all|direct|inherited])|(cigroup <GroupItem>))
|
||||
((ou|orgunit <OrgUnitItem> [show all|direct|inherited])|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
(filter <StringList>)* (namespace <NamespaceList>)*
|
||||
[show all|direct|inherited] [shownopolicy]
|
||||
[[formatjson [quotechar <Character>]]
|
||||
|
||||
<ChromePolicySchemaFieldName> ::=
|
||||
@@ -4660,6 +4662,7 @@ gam report <ActivityApplicationName> [todrive <ToDriveAttribute>*]
|
||||
[event|events <EventNameList>] [ip <String>]
|
||||
[gmaileventtypes <NumberRangeList>]
|
||||
[groupidfilter <String>] [resourcedetailsfilter <String>]
|
||||
[notimesort]
|
||||
[maxactivities <Number>] [maxevents <Number>] [maxresults <Number>]
|
||||
[countsonly [bydate|summary] [eventrowfilter]]
|
||||
(addcsvdata <FieldName> <String>)* [shownoactivities]
|
||||
@@ -5690,6 +5693,7 @@ gam download storagefile <StorageBucketObjectName>
|
||||
fullname|
|
||||
gender|
|
||||
givenname|firstname|
|
||||
guestaccountinfo|
|
||||
id|
|
||||
ims|im|
|
||||
includeinglobaladdresslist|gal|
|
||||
@@ -5697,6 +5701,7 @@ gam download storagefile <StorageBucketObjectName>
|
||||
isdelegatedadmin|admin|isadmin|
|
||||
isenforcedin2sv|is2svenforced|
|
||||
isenrolledin2sv|is2svenrolled|
|
||||
isguestuser|
|
||||
ismailboxsetup|
|
||||
keyword|keywords|
|
||||
language|languages|
|
||||
@@ -6166,6 +6171,7 @@ Display a users calendar settings
|
||||
defaulteventlength|
|
||||
format24hourtime|
|
||||
hideinvitations|
|
||||
hideinvitationssetting|
|
||||
hideweekends|
|
||||
locale|
|
||||
remindonrespondedeventsonly|
|
||||
@@ -6903,6 +6909,7 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
|
||||
[copyfilepermissions [<Boolean>]]
|
||||
[copyfileinheritedpermissions [<Boolean>]
|
||||
[copyfilenoninheritedpermissions [<Boolean>]
|
||||
[copyfolderpermissions [<Boolean>]]
|
||||
[copymergewithparentfolderpermissions [<Boolean>]]
|
||||
[copymergedtopfolderpermissions [<Boolean>]]
|
||||
[copytopfolderpermissions [<Boolean>]]
|
||||
@@ -6932,6 +6939,7 @@ gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileNam
|
||||
[createshortcutsfornonmovablefiles [<Boolean>]]
|
||||
[duplicatefiles overwriteolder|overwriteall|duplicatename|uniquename|skip]
|
||||
[duplicatefolders merge|duplicatename|uniquename|skip]
|
||||
[copyfolderpermissions [<Boolean>]]
|
||||
[copymergewithparentfolderpermissions [<Boolean>]]
|
||||
[copymergedtopfolderpermissions [<Boolean>]]
|
||||
[copytopfolderpermissions [<Boolean>]]
|
||||
@@ -6974,6 +6982,7 @@ gam <UserTypeEntity> info drivefile <DriveFileEntity>
|
||||
[returnidonly]
|
||||
[filepath|fullpath] [folderpathonly [<Boolean>]] [pathdelimiter <Character>]
|
||||
[allfields|<DriveFieldName>*|(fields <DriveFieldNameList>)]
|
||||
[includepermissionsforview published]
|
||||
(orderby <DriveFileOrderByFieldName> [ascending|descending])*
|
||||
[showdrivename] [showshareddrivepermissions]
|
||||
[(showlabels details|ids)|(includelabels <ClassificationLabelIDList>)]
|
||||
@@ -7457,6 +7466,7 @@ gam <UserTypeEntity> show fileinfo <DriveFileEntity>
|
||||
[returnidonly]
|
||||
[filepath|fullpath] [folderpathonly [<Boolean>]] [pathdelimiter <Character>]
|
||||
[allfields|<DriveFieldName>*|(fields <DriveFieldNameList>)]
|
||||
[includepermissionsforview published]
|
||||
(orderby <DriveFileOrderByFieldName> [ascending|descending])*
|
||||
[showdrivename] [showshareddrivepermissions]
|
||||
[(showlabels details|ids)|(includelabels <ClassificationLabelIDList>)]
|
||||
@@ -7570,6 +7580,7 @@ gam <UserTypeEntity> print filelist [todrive <ToDriveAttribute>*]
|
||||
[sizefield quotabytesused|size] [minimumfilesize <Integer>] [maximumfilesize <Integer>]
|
||||
[filenamematchpattern <REMatchPattern>]
|
||||
<PermissionMatch>* [<PermissionMatchMode>] [<PermissionMatchAction>] [pmfilter] [oneitemperrow]
|
||||
[includepermissionsforview published]
|
||||
[excludetrashed]
|
||||
[maxfiles <Integer>] [nodataheaders <String>]
|
||||
[countsonly [summary none|only|plus] [summaryuser <String>]
|
||||
|
||||
@@ -1,7 +1,162 @@
|
||||
7.34.07
|
||||
|
||||
Added the following command to create a guest user.
|
||||
* See: https://support.google.com/a/answer/16558545?hl=en
|
||||
```
|
||||
gam create guestuser <EmailAddress>
|
||||
```
|
||||
|
||||
Added the following items to `<UserFieldName>`:
|
||||
* `guestaccountinfo` - Additional guest-related metadata fields
|
||||
* `isguestuser` - Indicates if the inserted user is a guest
|
||||
|
||||
7.34.06
|
||||
|
||||
Added option `copyfolderpermissions [<Boolean>]` to `gam <UserTypeEntity> copy|move drivefile`.
|
||||
|
||||
When `copyfolderpermissions false` is specified, no folder permissions are copied; this simplifies
|
||||
disabling all folder permission copying.
|
||||
|
||||
When not specified or `copyfolderpermissions [true]` is specified, folder permissions are copied based on the following options:
|
||||
```
|
||||
copymergewithparentfolderpermissions [<Boolean>]
|
||||
copymergedtopfolderpermissions [<Boolean>]
|
||||
copytopfolderpermissions [<Boolean>]
|
||||
copytopfolderiheritedpermissions [<Boolean>]
|
||||
copytopfoldernoniheritedpermissions never|always|syncallfolders|syncupdatedfolders
|
||||
copymergedsubfolderpermissions [<Boolean>]
|
||||
copysubfolderpermissions [<Boolean>]
|
||||
copysubfolderinheritedpermissions [<Boolean>]
|
||||
copysubfoldernoniheritedpermissions never|always|syncallfolders|syncupdatedfolders
|
||||
```
|
||||
|
||||
7.34.05
|
||||
|
||||
Updated `gam report <ActivityApplictionName>` to perform a reverse chronological sort
|
||||
on all rows across multiple users and/or event names; this is consistent with the behavior
|
||||
in the Admin console. Use option `notimesort` to suppress this sort.
|
||||
|
||||
7.34.04
|
||||
|
||||
Updated `gam <UserTypeEntity> create drivefileacl <DriveFileEntity> user <UserItem> role owner` to better
|
||||
handle the case where the current owner of a file is suspended. Previously, the command was displayed as an error
|
||||
even though the ownership was changed.
|
||||
```
|
||||
gam user currentowner@domain.com add drivefileacl <DriveFileID> user newowner@domain.com role owner
|
||||
User: currentowner@domain.com, Add 1 Drive File/Folder ACL
|
||||
User: currentowner@domain.com, Drive File/Folder ID: <DriveFileID>, Permission ID: newowner@domain.com, Add Failed: Sorry, the items were successfully shared but emails could not be sent to newowner@domain.com.
|
||||
```
|
||||
|
||||
Now the command is displayed as a success with a note indicating that the ownership change email was not sent.
|
||||
```
|
||||
gam user currentowner@domain.com add drivefileacl <DriveFileID> user newowner@domain.com role owner
|
||||
User: currentowner@domain.com, Add 1 Drive File/Folder ACL
|
||||
User: currentowner@domain.com, Drive File/Folder ID: <DriveFileID>, Permission ID: newowner@domain.com, Added: Sorry, the items were successfully shared but emails could not be sent to newowner@domain.com.
|
||||
New Owner
|
||||
id: 10834698115409747890
|
||||
type: user
|
||||
emailAddress: newowner@domain.com
|
||||
domain: domain.com
|
||||
role: owner
|
||||
permissionDetails:
|
||||
role: writer
|
||||
type: file
|
||||
inherited: True
|
||||
inheritedFrom: Unknown
|
||||
role: owner
|
||||
type: file
|
||||
inherited: False
|
||||
deleted: False
|
||||
pendingOwner: False
|
||||
```
|
||||
|
||||
7.34.03
|
||||
|
||||
Updated to Python 3.14.3
|
||||
|
||||
Updated Cryptography to 46.0.5
|
||||
|
||||
Updated `gam course <CourseID> create student|teacher <EmailAddress>` error message when
|
||||
`<EmailAddress>` is not in a trusted domain to remove suggestion about creating an invitation.
|
||||
|
||||
7.34.02
|
||||
|
||||
Updated GAM to prevent errors like the following:
|
||||
```
|
||||
ERROR: Unable to find the server at oauth2.googleapis.com
|
||||
ERROR: Unable to find the server at gmail.googleapis.com
|
||||
```
|
||||
|
||||
If you experience any unexpected errors, post a message to:
|
||||
* The GAM Discussion Forum (google-apps-manager@googlegroups.com)
|
||||
* The GAM Public Chat Room (https://chat.google.com/app/chat/AAAA4BULhWo)
|
||||
|
||||
7.34.01
|
||||
|
||||
Updated `gam create|update adminrole` to handle the following errors:
|
||||
```
|
||||
ERROR: 400: invalid - Invalid Role privileges
|
||||
ERROR: 400: required - Required parameter: [resource.privileges[n].service_id]
|
||||
```
|
||||
|
||||
7.34.00
|
||||
|
||||
Added variable `csv_output_header_required` to `gam.cfg` that is a comma separated list of `<Strings>`
|
||||
that are required to be in the list of column headers in the CSV file written by a gam print command.
|
||||
This will typically be used to specify headers that are required in subsequent commands that process
|
||||
the CSV file even if the API didn't return any data for those columns.
|
||||
|
||||
Updated the following commands to not require the `Directory API - Domains` scope
|
||||
unless the `internal` or `external` options are used to request the member category.
|
||||
```
|
||||
gam info|print groups
|
||||
gam print|show group-members
|
||||
gam info|print cigroups
|
||||
gam print|show cigroup-members
|
||||
gam <UserTypeEntity> print|show filesharecounts
|
||||
```
|
||||
|
||||
7.33.03
|
||||
|
||||
Fixed bug in `gam [<UserTypeEntity>] sendemail ... from <EmailAddress> replyto <EmailAddress>`
|
||||
where an `<EmailAddress>` of the form `Text <user@domain.com>` had the `Text` removed.
|
||||
|
||||
7.33.02
|
||||
|
||||
Added `hideinvitationssetting` to `<UserCalendarSettingsField>` used by
|
||||
`gam <UserTypeEntity> print|show calsettings`.
|
||||
|
||||
7.33.01
|
||||
|
||||
Added option `shownopolicy` to `gam print chromepolicies` that will display output like the following
|
||||
if no policies apply to the selected OU or group.
|
||||
```
|
||||
gam print chromepolicies ou /Test appid chrome:emidddocikgklceeeifefomdnbkldhng namespace chrome.users.apps shownopolicy
|
||||
Getting all Chrome Policies that match query (chrome.users.apps.*) for /Test
|
||||
Got 0 Chrome Policies that matched query (chrome.users.apps.*) for /Test...
|
||||
name,orgUnitPath,parentOrgUnitPath,direct,appId
|
||||
noPolicy,/Test,/,False,chrome:emidddocikgklceeeifefomdnbkldhng
|
||||
```
|
||||
|
||||
7.33.00
|
||||
|
||||
Added variable `developer_preview_apis` to `gam.cfg` that is a comma separated list of APIs requiring a Developer Preview key.
|
||||
Currently, `chat` is the only API that requires a Developer Preview key; it is required for the User Sections commands.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#introduction
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#manage-chat-user-sections
|
||||
|
||||
7.32.07
|
||||
|
||||
Added option `includepermissionsforview published` to `gam <UserTypeEntity> print filelist` and
|
||||
`gam <UserTypeEntity> show fileinfo`. From the Drive API documentation:
|
||||
```
|
||||
Specifies which additional view's permissions to include in the response. Only published is supported.
|
||||
```
|
||||
|
||||
7.32.06
|
||||
|
||||
Added options to `gam <UserTypeEntity> copy drivefile ... copysubfiles` to limit copying
|
||||
of files whose `modifiedTime` meets specified requirements.
|
||||
to files whose `modifiedTime` meets specified requirements.
|
||||
* `start|starttime <Date>|<Time>` - If specified, `modifiedTime` must be >= the value
|
||||
* `end|endtime <Date>|<Time>` - If specified, `modifiedTime` must be <= the value
|
||||
* `range <Date>|<Time> <Date>|<Time>` - first value <= `modifiedTime` <= second value
|
||||
|
||||
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
|
||||
"""
|
||||
|
||||
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
||||
__version__ = '7.32.06'
|
||||
__version__ = '7.34.08'
|
||||
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
@@ -1331,6 +1331,8 @@ def validateEmailAddressOrUID(emailAddressOrUID, checkPeople=True, ciGroupsAPI=F
|
||||
return emailAddressOrUID
|
||||
return emailAddressOrUID.find('@') != 0 and emailAddressOrUID.count('@') <= 1
|
||||
|
||||
NAME_EMAIL_ADDRESS_PATTERN = re.compile(r'^(.*)<(.+)>$')
|
||||
|
||||
# Normalize user/group email address/uid
|
||||
# uid:12345abc -> 12345abc
|
||||
# foo -> foo@domain
|
||||
@@ -1349,6 +1351,10 @@ def normalizeEmailAddressOrUID(emailAddressOrUID, noUid=False, checkForCustomerI
|
||||
return cg.group(1)
|
||||
if ciGroupsAPI and emailAddressOrUID.startswith('groups/'):
|
||||
return emailAddressOrUID
|
||||
if emailAddressOrUID.find('<') >= 0:
|
||||
match = NAME_EMAIL_ADDRESS_PATTERN.match(emailAddressOrUID)
|
||||
if match:
|
||||
emailAddressOrUID = match.group(2)
|
||||
atLoc = emailAddressOrUID.find('@')
|
||||
if atLoc == 0:
|
||||
return emailAddressOrUID[1:].lower() if not noLower else emailAddressOrUID[1:]
|
||||
@@ -2308,10 +2314,10 @@ def getMatchSkipFields(fieldNames):
|
||||
|
||||
def checkMatchSkipFields(row, fieldnames, matchFields, skipFields):
|
||||
for matchField, matchPattern in matchFields.items():
|
||||
if (matchField not in row) or not matchPattern.search(row[matchField]):
|
||||
if (matchField not in row) or not matchPattern.search(str(row[matchField])):
|
||||
return False
|
||||
for skipField, matchPattern in skipFields.items():
|
||||
if (skipField in row) and matchPattern.search(row[skipField]):
|
||||
if (skipField in row) and matchPattern.search(str(row[skipField])):
|
||||
return False
|
||||
if fieldnames and (GC.Values[GC.CSV_INPUT_ROW_FILTER] or GC.Values[GC.CSV_INPUT_ROW_DROP_FILTER]):
|
||||
return RowFilterMatch(row, fieldnames,
|
||||
@@ -3752,6 +3758,15 @@ def SetGlobalVariables():
|
||||
if (productId, sku) not in GM.Globals[GM.LICENSE_SKUS]:
|
||||
GM.Globals[GM.LICENSE_SKUS].append((productId, sku))
|
||||
|
||||
def _validateDeveloperPreviewAPIs(sectionName, itemName, apiList):
|
||||
GM.Globals[GM.DEVELOPER_PREVIEW_APIS] = set()
|
||||
validAPIs = API.getAPIsList()
|
||||
for api in apiList.split(','):
|
||||
if api in validAPIs:
|
||||
GM.Globals[GM.DEVELOPER_PREVIEW_APIS].add(api)
|
||||
else:
|
||||
_printValueError(sectionName, itemName, api, f'{Msg.EXPECTED}: {",".join(sorted(validAPIs))}')
|
||||
|
||||
def _getCfgString(sectionName, itemName):
|
||||
value = _stripStringQuotes(GM.Globals[GM.PARSER].get(sectionName, itemName))
|
||||
if itemName == GC.DOMAIN:
|
||||
@@ -3760,6 +3775,8 @@ def SetGlobalVariables():
|
||||
if ((minLen is None) or (len(value) >= minLen)) and ((maxLen is None) or (len(value) <= maxLen)):
|
||||
if itemName == GC.LICENSE_SKUS and value:
|
||||
_validateLicenseSKUs(sectionName, itemName, value)
|
||||
elif itemName == GC.DEVELOPER_PREVIEW_APIS and value:
|
||||
_validateDeveloperPreviewAPIs(sectionName, itemName, value.lower())
|
||||
return value
|
||||
_printValueError(sectionName, itemName, f'"{value}"', f'{Msg.EXPECTED}: {integerLimits(minLen, maxLen, Msg.STRING_LENGTH)}')
|
||||
return ''
|
||||
@@ -4136,7 +4153,7 @@ def SetGlobalVariables():
|
||||
GC.Values[itemName] = _getCfgPassword(sectionName, itemName)
|
||||
elif varType == GC.TYPE_STRING:
|
||||
GC.Values[itemName] = _getCfgString(sectionName, itemName)
|
||||
elif varType in {GC.TYPE_STRINGLIST, GC.TYPE_HEADERFORCE, GC.TYPE_HEADERORDER}:
|
||||
elif varType in {GC.TYPE_STRINGLIST, GC.TYPE_HEADERFORCEREQUIRED, GC.TYPE_HEADERORDER}:
|
||||
GC.Values[itemName] = _getCfgStringList(sectionName, itemName)
|
||||
elif varType == GC.TYPE_FILE:
|
||||
GC.Values[itemName] = _getCfgFile(sectionName, itemName)
|
||||
@@ -4160,6 +4177,7 @@ def SetGlobalVariables():
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_FILTER] = _getCfgHeaderFilter(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_FILTER)
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_DROP_FILTER] = _getCfgHeaderFilter(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_DROP_FILTER)
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_ORDER] = _getCfgStringList(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_ORDER)
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED] = _getCfgStringList(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_REQUIRED)
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER] = _getCfgRowFilter(outputFilterSectionName, GC.CSV_OUTPUT_ROW_FILTER)
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE] = _getCfgChoice(outputFilterSectionName, GC.CSV_OUTPUT_ROW_FILTER_MODE)
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER] = _getCfgRowFilter(outputFilterSectionName, GC.CSV_OUTPUT_ROW_DROP_FILTER)
|
||||
@@ -4279,7 +4297,7 @@ def SetGlobalVariables():
|
||||
if GM.Globals[GM.PID] == 0:
|
||||
for itemName, itemEntry in sorted(GC.VAR_INFO.items()):
|
||||
varType = itemEntry[GC.VAR_TYPE]
|
||||
if varType in {GC.TYPE_HEADERFILTER, GC.TYPE_HEADERFORCE, GC.TYPE_HEADERORDER, GC.TYPE_ROWFILTER}:
|
||||
if varType in {GC.TYPE_HEADERFILTER, GC.TYPE_HEADERFORCEREQUIRED, GC.TYPE_HEADERORDER, GC.TYPE_ROWFILTER}:
|
||||
GM.Globals[GM.PARSER].set(sectionName, itemName, '')
|
||||
elif (varType == GC.TYPE_INTEGER) and itemName in {GC.CSV_INPUT_ROW_LIMIT, GC.CSV_OUTPUT_ROW_LIMIT}:
|
||||
GM.Globals[GM.PARSER].set(sectionName, itemName, '0')
|
||||
@@ -4294,6 +4312,8 @@ def SetGlobalVariables():
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_FORCE] = GM.Globals[GM.CSV_OUTPUT_HEADER_FORCE][:]
|
||||
if not GC.Values[GC.CSV_OUTPUT_HEADER_ORDER]:
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_ORDER] = GM.Globals[GM.CSV_OUTPUT_HEADER_ORDER][:]
|
||||
if not GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED]:
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED] = GM.Globals[GM.CSV_OUTPUT_HEADER_REQUIRED][:]
|
||||
if not GC.Values[GC.CSV_OUTPUT_ROW_FILTER]:
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER] = GM.Globals[GM.CSV_OUTPUT_ROW_FILTER][:]
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE] = GM.Globals[GM.CSV_OUTPUT_ROW_FILTER_MODE]
|
||||
@@ -4820,11 +4840,12 @@ def getService(api, httpObj):
|
||||
triesLimit = 3
|
||||
for n in range(1, triesLimit+1):
|
||||
try:
|
||||
if api not in {API.CHAT} or not GC.Values[GC.DEVELOPER_PREVIEW_API_KEY]:
|
||||
if api not in GM.Globals[GM.DEVELOPER_PREVIEW_APIS] or not GC.Values[GC.DEVELOPER_PREVIEW_API_KEY]:
|
||||
discoveryServiceUrl = DISCOVERY_URIS[v2discovery]
|
||||
developerKey = ''
|
||||
else:
|
||||
discoveryServiceUrl = DEVELOPER_PREVIEW_DISCOVERY_URI
|
||||
developerKey = GC.Values[GC.DEVELOPER_PREVIEW_API_KEY]
|
||||
developerKey = GC.Values[GC.DEVELOPER_PREVIEW_API_KEY]
|
||||
service = googleapiclient.discovery.build(api, version, http=httpObj, cache_discovery=False,
|
||||
discoveryServiceUrl=discoveryServiceUrl, developerKey=developerKey, static_discovery=False)
|
||||
GM.Globals[GM.CURRENT_API_SERVICES].setdefault(api, {})
|
||||
@@ -5692,7 +5713,9 @@ def chooseSaAPI(api1, api2):
|
||||
|
||||
def buildGAPIServiceObject(api, user, i=0, count=0, displayError=True):
|
||||
userEmail = getSaUser(user)
|
||||
httpObj = getHttpObj(cache=GM.Globals[GM.CACHE_DIR])
|
||||
if GM.Globals[GM.HTTP_OBJECT] is None:
|
||||
GM.Globals[GM.HTTP_OBJECT] = getHttpObj(cache=GM.Globals[GM.CACHE_DIR])
|
||||
httpObj = GM.Globals[GM.HTTP_OBJECT]
|
||||
service = getService(api, httpObj)
|
||||
credentials = getSvcAcctCredentials(api, userEmail)
|
||||
request = transportCreateRequest(httpObj)
|
||||
@@ -6066,7 +6089,7 @@ def _checkMemberCategory(member, memberDisplayOptions):
|
||||
if memberDisplayOptions['showCategory']:
|
||||
member['category'] = category
|
||||
if memberDisplayOptions['checkCategory']:
|
||||
return True if memberDisplayOptions[category] else False
|
||||
return bool(memberDisplayOptions[category])
|
||||
return True
|
||||
|
||||
def _checkCIMemberCategory(member, memberDisplayOptions):
|
||||
@@ -6079,7 +6102,7 @@ def _checkCIMemberCategory(member, memberDisplayOptions):
|
||||
if memberDisplayOptions['showCategory']:
|
||||
member['category'] = category
|
||||
if memberDisplayOptions['checkCategory']:
|
||||
return True if memberDisplayOptions[category] else False
|
||||
return bool(memberDisplayOptions[category])
|
||||
return True
|
||||
|
||||
def getCIGroupMemberRoleFixType(member):
|
||||
@@ -7316,8 +7339,6 @@ def _addEmbeddedImagesToMessage(message, embeddedImages):
|
||||
except (IOError, UnicodeDecodeError) as e:
|
||||
usageErrorExit(f'{imageFilename}: {str(e)}')
|
||||
|
||||
NAME_EMAIL_ADDRESS_PATTERN = re.compile(r'^.*<(.+)>$')
|
||||
|
||||
# Send an email
|
||||
def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msgFrom=None, msgReplyTo=None,
|
||||
html=False, charset=UTF8, attachments=None, embeddedImages=None,
|
||||
@@ -7339,8 +7360,11 @@ def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msg
|
||||
def cleanAddr(emailAddr):
|
||||
match = NAME_EMAIL_ADDRESS_PATTERN.match(emailAddr)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return emailAddr
|
||||
emailName = match.group(1)
|
||||
emailAddr = normalizeEmailAddressOrUID(match.group(2), noUid=True, noLower=True)
|
||||
return (f'{emailName} <{emailAddr}>', emailAddr)
|
||||
emailAddr = normalizeEmailAddressOrUID(emailAddr, noUid=True, noLower=True)
|
||||
return (emailAddr, emailAddr)
|
||||
|
||||
if msgFrom is None:
|
||||
msgFrom = _getAdminEmail()
|
||||
@@ -7360,9 +7384,9 @@ def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msg
|
||||
if embeddedImages:
|
||||
_addEmbeddedImagesToMessage(message, embeddedImages)
|
||||
message['Subject'] = msgSubject
|
||||
message['From'] = normalizeEmailAddressOrUID(cleanAddr(msgFrom), noUid=True, noLower=True)
|
||||
message['From'], msgFromAddr = cleanAddr(msgFrom)
|
||||
if msgReplyTo is not None:
|
||||
message['Reply-To'] = normalizeEmailAddressOrUID(cleanAddr(msgReplyTo), noUid=True, noLower=True)
|
||||
message['Reply-To'], _ = cleanAddr(msgReplyTo)
|
||||
if ccRecipients:
|
||||
message['Cc'] = ccRecipients.lower()
|
||||
if bccRecipients:
|
||||
@@ -7372,8 +7396,8 @@ def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msg
|
||||
if header not in {'Subject', 'From', 'To', 'Reply-To', 'Cc', 'Bcc'}:
|
||||
message[header] = value
|
||||
if mailBox is None:
|
||||
mailBox = msgFrom
|
||||
mailBoxAddr = normalizeEmailAddressOrUID(cleanAddr(mailBox), noUid=True, noLower=True)
|
||||
mailBox = msgFromAddr
|
||||
_, mailBoxAddr = cleanAddr(mailBox)
|
||||
action = Act.Get()
|
||||
Act.Set(Act.SENDEMAIL)
|
||||
if not GC.Values[GC.SMTP_HOST]:
|
||||
@@ -7903,6 +7927,7 @@ class CSVPrintFile():
|
||||
self.JSONtitlesList = []
|
||||
self.sortHeaders = []
|
||||
self.SetHeaderForce(GC.Values[GC.CSV_OUTPUT_HEADER_FORCE])
|
||||
self.SetHeaderRequired(GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED])
|
||||
if not self.headerForce and titles is not None:
|
||||
self.SetTitles(titles)
|
||||
self.SetJSONTitles(titles)
|
||||
@@ -8619,6 +8644,9 @@ class CSVPrintFile():
|
||||
self.SetTitles(headerForce)
|
||||
self.SetJSONTitles(headerForce)
|
||||
|
||||
def SetHeaderRequired(self, headerRequired):
|
||||
self.headerRequired = headerRequired
|
||||
|
||||
def SetHeaderOrder(self, headerOrder):
|
||||
self.headerOrder = headerOrder
|
||||
|
||||
@@ -9062,6 +9090,8 @@ class CSVPrintFile():
|
||||
extrasaction = 'raise'
|
||||
if not self.formatJSON:
|
||||
if not self.headerForce:
|
||||
if self.headerRequired:
|
||||
self.AddTitles(self.headerRequired)
|
||||
self.SortTitles()
|
||||
self.SortIndexedTitles(self.titlesList)
|
||||
if self.fixPaths:
|
||||
@@ -9079,6 +9109,17 @@ class CSVPrintFile():
|
||||
titlesList = self.titlesList
|
||||
else:
|
||||
if not self.headerForce:
|
||||
if self.headerRequired:
|
||||
for i, v in enumerate(self.JSONtitlesList):
|
||||
if v.startswith('JSON'):
|
||||
j = i
|
||||
for title in self.headerRequired:
|
||||
self.JSONtitlesList.insert(j, title)
|
||||
self.JSONtitlesSet.add(title)
|
||||
j += 1
|
||||
break
|
||||
else:
|
||||
self.AddJSONTitles(self.headerRequired)
|
||||
if self.fixPaths:
|
||||
self.FixPathsTitles(self.JSONtitlesList)
|
||||
if not self.rows and self.nodataFields is not None:
|
||||
@@ -9888,7 +9929,7 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
|
||||
csvColumnDelimiter, csvNoEscapeChar, csvQuoteChar,
|
||||
csvSortHeaders, csvTimestampColumn,
|
||||
csvHeaderFilter, csvHeaderDropFilter,
|
||||
csvHeaderForce, csvHeaderOrder,
|
||||
csvHeaderForce, csvHeaderOrder, csvHeaderRequired,
|
||||
csvRowFilter, csvRowFilterMode, csvRowDropFilter, csvRowDropFilterMode,
|
||||
csvRowLimit,
|
||||
showGettings, showGettingsGotNL,
|
||||
@@ -9913,6 +9954,7 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
|
||||
GM.Globals[GM.CSV_OUTPUT_HEADER_FILTER] = csvHeaderFilter[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_HEADER_FORCE] = csvHeaderForce[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_HEADER_ORDER] = csvHeaderOrder[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_HEADER_REQUIRED] = csvHeaderRequired[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_QUOTE_CHAR] = csvQuoteChar
|
||||
GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER] = csvRowDropFilter[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER_MODE] = csvRowDropFilterMode
|
||||
@@ -10137,6 +10179,7 @@ def MultiprocessGAMCommands(items, showCmds):
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_DROP_FILTER],
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_FORCE],
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_ORDER],
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED],
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER],
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE],
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER],
|
||||
@@ -13786,7 +13829,9 @@ REPORT_ACTIVITIES_TIME_OBJECTS = {'time'}
|
||||
# yesterday|today|thismonth|(previousmonths <Integer>)]
|
||||
# [filter <String> (filtertime<String> <Time>)*]
|
||||
# [event|events <EventNameList>] [ip <String>]
|
||||
# [groupidfilter <String>]
|
||||
# [gmaileventtypes <NumberRangeList>]
|
||||
# [groupidfilter <String>] [resourcedetailsfilter <String>]
|
||||
# [notimesort]
|
||||
# [maxactivities <Number>] [maxevents <Number>] [maxresults <Number>]
|
||||
# [countsonly [bydate|summary] [eventrowfilter]]
|
||||
# (addcsvdata <FieldName> <String>)* [shownoactivities]
|
||||
@@ -14101,6 +14146,7 @@ def doReport():
|
||||
maxResults = 1000
|
||||
aggregateByDate = aggregateByUser = convertMbToGb = countsOnly = countsByDate = countsSummary = \
|
||||
eventRowFilter = exitUserLoop = noAuthorizedApps = normalizeUsers = select = userCustomerRange = False
|
||||
sortAllTimes = True
|
||||
limitDateChanges = -1
|
||||
allVerifyUser = userKey = 'all'
|
||||
cd = orgUnit = orgUnitId = None
|
||||
@@ -14198,6 +14244,8 @@ def doReport():
|
||||
eventNames.append(event)
|
||||
elif activityReports and myarg == 'ip':
|
||||
actorIpAddress = getString(Cmd.OB_STRING)
|
||||
elif activityReports and myarg == 'notimesort':
|
||||
sortAllTimes = False
|
||||
elif activityReports and myarg == 'countsonly':
|
||||
countsOnly = True
|
||||
elif activityReports and myarg == 'bydate':
|
||||
@@ -14663,6 +14711,8 @@ def doReport():
|
||||
if addCSVData:
|
||||
row.update(addCSVData)
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif sortAllTimes:
|
||||
csvPF.SortRows('id.time', True)
|
||||
else:
|
||||
if eventRowFilter:
|
||||
csvPF.SetRowFilter([], GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE])
|
||||
@@ -16951,12 +17001,13 @@ def doCreateUpdateAdminRoles():
|
||||
if not updateCmd:
|
||||
result = callGAPI(cd.roles(), 'insert',
|
||||
throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED]+[GAPI.DUPLICATE],
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED]+[GAPI.DUPLICATE, GAPI.INVALID, GAPI.REQUIRED],
|
||||
customer=GC.Values[GC.CUSTOMER_ID], body=body, fields=fieldsList)
|
||||
else:
|
||||
result = callGAPI(cd.roles(), 'patch',
|
||||
throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED]+[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION, GAPI.CONFLICT],
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED]+[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION,
|
||||
GAPI.CONFLICT, GAPI.INVALID, GAPI.REQUIRED],
|
||||
customer=GC.Values[GC.CUSTOMER_ID], roleId=roleId, body=body, fields=fieldsList)
|
||||
if not csvPF:
|
||||
entityActionPerformed([Ent.ADMIN_ROLE, f"{result['roleName']}({result['roleId']})"])
|
||||
@@ -16974,7 +17025,7 @@ def doCreateUpdateAdminRoles():
|
||||
row.update(addCSVData)
|
||||
row['JSON'] = json.dumps(cleanJSON(result), ensure_ascii=False, sort_keys=True)
|
||||
csvPF.WriteRowNoFilter(row)
|
||||
except GAPI.duplicate as e:
|
||||
except (GAPI.duplicate, GAPI.invalid, GAPI.required) as e:
|
||||
entityActionFailedWarning([Ent.ADMIN_ROLE, f"{body['roleName']}"], str(e))
|
||||
except (GAPI.notFound, GAPI.failedPrecondition, GAPI.conflict) as e:
|
||||
entityActionFailedWarning([Ent.ADMIN_ROLE, roleId], str(e))
|
||||
@@ -27063,6 +27114,8 @@ def createUpdateChatSection(users):
|
||||
except GAPI.failedPrecondition:
|
||||
userChatServiceNotEnabledWarning(user, i, count)
|
||||
continue
|
||||
except AttributeError:
|
||||
systemErrorExit(GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
|
||||
|
||||
# gam <UserTypeEntity> delete chatsection <ChatSection>
|
||||
def deleteChatSection(users):
|
||||
@@ -27093,6 +27146,8 @@ def deleteChatSection(users):
|
||||
except GAPI.failedPrecondition:
|
||||
userChatServiceNotEnabledWarning(user, i, count)
|
||||
continue
|
||||
except AttributeError:
|
||||
systemErrorExit(GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
|
||||
|
||||
# gam <UserTypeEntity> show chatsections
|
||||
# [formatjson]
|
||||
@@ -27127,6 +27182,8 @@ def printShowChatSections(users):
|
||||
except GAPI.failedPrecondition:
|
||||
userChatServiceNotEnabledWarning(user, i, count)
|
||||
continue
|
||||
except AttributeError:
|
||||
systemErrorExit(GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
|
||||
jcount = len(sections)
|
||||
if jcount == 0:
|
||||
setSysExitRC(NO_ENTITIES_FOUND_RC)
|
||||
@@ -27181,6 +27238,8 @@ def moveShowChatSectionItem(users):
|
||||
except GAPI.failedPrecondition:
|
||||
userChatServiceNotEnabledWarning(user, i, count)
|
||||
continue
|
||||
except AttributeError:
|
||||
systemErrorExit(GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
|
||||
|
||||
# gam <UserTypeEntity> show chatsectionitems <ChatSection>
|
||||
# [space <ChatSpace>]
|
||||
@@ -27252,6 +27311,8 @@ def printShowChatSectionItems(users):
|
||||
except GAPI.failedPrecondition:
|
||||
userChatServiceNotEnabledWarning(user, i, count)
|
||||
continue
|
||||
except AttributeError:
|
||||
systemErrorExit(GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
|
||||
jcount = len(sectionItems)
|
||||
if jcount == 0:
|
||||
setSysExitRC(NO_ENTITIES_FOUND_RC)
|
||||
@@ -30067,7 +30128,7 @@ CHROME_POLICY_SHOW_CHOICE_MAP = {
|
||||
# ((ou|orgunit <OrgUnitItem>)|(group <GroupItem>))
|
||||
# [(printerid <PrinterID>)|(appid <AppID>)]
|
||||
# (filter <StringList>)* (namespace <NamespaceList>)*
|
||||
# [show all|direct|inherited]
|
||||
# [show all|direct|inherited] [shownopolicy]
|
||||
# [[formatjson [quotechar <Character>]]
|
||||
def doPrintShowChromePolicies():
|
||||
def normalizedPolicy(policy):
|
||||
@@ -30138,25 +30199,54 @@ def doPrintShowChromePolicies():
|
||||
showJSON(None, policy, sortDictKeys=False)
|
||||
Ind.Decrement()
|
||||
|
||||
def _printPolicyRow(policy):
|
||||
row = flattenJSON(policy)
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif (not csvPF.rowFilter and not csvPF.rowDropFilter) or csvPF.CheckRowTitles(row):
|
||||
if entityType == Ent.ORGANIZATIONAL_UNIT:
|
||||
csvPF.WriteRowNoFilter({'name': policy['name'],
|
||||
'orgUnitPath': policy['orgUnitPath'],
|
||||
'parentOrgUnitPath': policy['parentOrgUnitPath'],
|
||||
'direct': policy['direct'],
|
||||
'JSON': json.dumps(cleanJSON(policy),
|
||||
ensure_ascii=False, sort_keys=True)})
|
||||
else:
|
||||
csvPF.WriteRowNoFilter({'name': policy['name'],
|
||||
'group': policy['group'],
|
||||
'JSON': json.dumps(cleanJSON(policy),
|
||||
ensure_ascii=False, sort_keys=True)})
|
||||
|
||||
|
||||
def _printPolicy(policy):
|
||||
nonlocal policiesShown
|
||||
policy = normalizedPolicy(policy)
|
||||
if (entityType == Ent.GROUP) or showPolicies in (CHROME_POLICY_SHOW_ALL, policy['direct']):
|
||||
row = flattenJSON(policy)
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif (not csvPF.rowFilter and not csvPF.rowDropFilter) or csvPF.CheckRowTitles(row):
|
||||
if entityType == Ent.ORGANIZATIONAL_UNIT:
|
||||
csvPF.WriteRowNoFilter({'name': policy['name'],
|
||||
'orgUnitPath': policy['orgUnitPath'],
|
||||
'parentOrgUnitPath': policy['parentOrgUnitPath'],
|
||||
'direct': policy['direct'],
|
||||
'JSON': json.dumps(cleanJSON(policy),
|
||||
ensure_ascii=False, sort_keys=True)})
|
||||
else:
|
||||
csvPF.WriteRowNoFilter({'name': policy['name'],
|
||||
'group': policy['group'],
|
||||
'JSON': json.dumps(cleanJSON(policy),
|
||||
ensure_ascii=False, sort_keys=True)})
|
||||
policiesShown += 1
|
||||
_printPolicyRow(policy)
|
||||
|
||||
def _printNoPolicy():
|
||||
nonlocal targetName
|
||||
policy = {'name': 'noPolicy'}
|
||||
if app_id:
|
||||
policy['appId'] = app_id
|
||||
elif printer_id:
|
||||
policy['printerId'] = printer_id
|
||||
if entityType == Ent.ORGANIZATIONAL_UNIT:
|
||||
policy['orgUnitPath'] = targetName
|
||||
if targetName == '/':
|
||||
policy['parentOrgUnitPath'] = '/'
|
||||
else:
|
||||
targetName = makeOrgUnitPathRelative(targetName)
|
||||
policy['parentOrgUnitPath'] = callGAPI(cd.orgunits(), 'get',
|
||||
throwReasons=GAPI.ORGUNIT_GET_THROW_REASONS,
|
||||
customerId=GC.Values[GC.CUSTOMER_ID],
|
||||
orgUnitPath=encodeOrgUnitPath(targetName),
|
||||
fields='parentOrgUnitPath')['parentOrgUnitPath']
|
||||
policy['direct'] = False
|
||||
else:
|
||||
policy['group'] = targetName
|
||||
_printPolicyRow(policy)
|
||||
|
||||
cp = buildGAPIObject(API.CHROMEPOLICY)
|
||||
cd = buildGAPIObject(API.DIRECTORY)
|
||||
@@ -30168,6 +30258,8 @@ def doPrintShowChromePolicies():
|
||||
app_id = groupEmail = orgUnit = printer_id = targetResource = None
|
||||
showPolicies = CHROME_POLICY_SHOW_ALL
|
||||
psFilters = []
|
||||
showNoPolicy = False
|
||||
policiesShown = 0
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if csvPF and myarg == 'todrive':
|
||||
@@ -30191,6 +30283,8 @@ def doPrintShowChromePolicies():
|
||||
psFilters.append(f'{psFilter}.*')
|
||||
elif myarg == 'show':
|
||||
showPolicies = getChoice(CHROME_POLICY_SHOW_CHOICE_MAP, mapChoice=True)
|
||||
elif csvPF and myarg == 'shownopolicy':
|
||||
showNoPolicy = True
|
||||
else:
|
||||
FJQC.GetFormatJSONQuoteChar(myarg, False)
|
||||
checkPolicyArgs(targetResource, printer_id, app_id)
|
||||
@@ -30280,7 +30374,8 @@ def doPrintShowChromePolicies():
|
||||
else:
|
||||
for policy in policies:
|
||||
_printPolicy(policy)
|
||||
if csvPF:
|
||||
if showNoPolicy and policiesShown == 0:
|
||||
_printNoPolicy()
|
||||
csvPF.writeCSVfile(f'Chrome Policies - {targetName}')
|
||||
|
||||
CHROME_IMAGE_SCHEMAS_MAP = {
|
||||
@@ -34783,13 +34878,14 @@ def finalizeInternalDomains(cd, internalDomains):
|
||||
return internalDomains
|
||||
|
||||
def finalizeIPSGMGroupRolesMemberDisplayOptions(cd, memberDisplayOptions, verifyAllowExternal):
|
||||
memberDisplayOptions['internalDomains'] = finalizeInternalDomains(cd, memberDisplayOptions['internalDomains'])
|
||||
if verifyAllowExternal:
|
||||
memberDisplayOptions['external'] = memberDisplayOptions['checkCategory'] = memberDisplayOptions['showCategory'] = True
|
||||
memberDisplayOptions['internal'] = False
|
||||
if memberDisplayOptions['showCategory']:
|
||||
memberDisplayOptions['gs'] = buildGAPIObject(API.GROUPSSETTINGS)
|
||||
memberDisplayOptions['checkShowCategory'] = memberDisplayOptions['checkCategory'] or memberDisplayOptions['showCategory']
|
||||
if memberDisplayOptions['checkShowCategory']:
|
||||
memberDisplayOptions['internalDomains'] = finalizeInternalDomains(cd, memberDisplayOptions['internalDomains'])
|
||||
return memberDisplayOptions['showCategory'], memberDisplayOptions['checkShowCategory']
|
||||
|
||||
GROUP_FIELDS_CHOICE_MAP = {
|
||||
@@ -37735,7 +37831,7 @@ def doCreateUpdateCIPolicy():
|
||||
updateCmd = Act.Get() == Act.UPDATE
|
||||
groupEmail = orgUnit = None
|
||||
checkArgumentPresent('json', True)
|
||||
jsonData = getJSON(['type'])
|
||||
jsonData = getJSON(['customer', 'type'])
|
||||
if updateCmd:
|
||||
pname = jsonData.pop('name', None)
|
||||
else:
|
||||
@@ -45915,7 +46011,10 @@ def doCreateGuestUser():
|
||||
result = callGAPI(cd.users(), 'createGuest',
|
||||
throwReasons=[GAPI.FAILED_PRECONDITION],
|
||||
body=body)
|
||||
entityActionPerformed([Ent.GUEST_USER, result['primaryGuestEmail']])
|
||||
entityActionPerformed([Ent.GUEST_USER, body['primaryGuestEmail']])
|
||||
Ind.Increment()
|
||||
showJSON(None, result)
|
||||
Ind.Decrement()
|
||||
except (GAPI.failedPrecondition) as e:
|
||||
entityActionFailedExit([Ent.GUEST_USER, body['primaryGuestEmail']], str(e))
|
||||
|
||||
@@ -46528,8 +46627,9 @@ USER_FIELDS_CHOICE_MAP = {
|
||||
'firstname': 'name.givenName',
|
||||
'fullname': 'name.fullName',
|
||||
'gal': 'includeInGlobalAddressList',
|
||||
'givenname': 'name.givenName',
|
||||
'gender': ['gender.type', 'gender.customGender', 'gender.addressMeAs'],
|
||||
'givenname': 'name.givenName',
|
||||
'guestaccountinfo': 'guestAccountInfo',
|
||||
'id': 'id',
|
||||
'im': 'ims',
|
||||
'ims': 'ims',
|
||||
@@ -46539,6 +46639,7 @@ USER_FIELDS_CHOICE_MAP = {
|
||||
'isdelegatedadmin': ['isAdmin', 'isDelegatedAdmin'],
|
||||
'isenforcedin2sv': 'isEnforcedIn2Sv',
|
||||
'isenrolledin2sv': 'isEnrolledIn2Sv',
|
||||
'isguestuser': 'isGuestUser',
|
||||
'is2svenforced': 'isEnforcedIn2Sv',
|
||||
'is2svenrolled': 'isEnrolledIn2Sv',
|
||||
'ismailboxsetup': 'isMailboxSetup',
|
||||
@@ -51302,8 +51403,6 @@ def _batchAddItemsToCourse(croom, courseId, i, count, addItems, addType):
|
||||
errMsg = getPhraseDNEorSNA(riItem)
|
||||
else:
|
||||
errMsg = getHTTPError(_ADD_PART_REASON_TO_MESSAGE_MAP, http_status, reason, message)
|
||||
if (reason == GAPI.PERMISSION_DENIED) and (addType in {Ent.STUDENT, Ent.TEACHER}) and ('CannotDirectAddUser' in errMsg):
|
||||
errMsg += f' Add external user with: gam user {riItem} create classroominvitation courses {ri[RI_ENTITY]} addType {Ent.Singular(addType)}'
|
||||
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], addType, riItem], errMsg, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
||||
return
|
||||
waitOnFailure(1, 10, reason, message)
|
||||
@@ -54125,6 +54224,7 @@ USER_CALENDAR_SETTINGS_FIELDS_CHOICE_MAP = {
|
||||
'defaulteventlength': 'defaultEventLength',
|
||||
'format24hourtime': 'format24HourTime',
|
||||
'hideinvitations': 'hideInvitations',
|
||||
'hideinvitationssetting': 'hideInvitationsSetting',
|
||||
'hideweekends': 'hideWeekends',
|
||||
'locale': 'locale',
|
||||
'remindonrespondedeventsonly': 'remindOnRespondedEventsOnly',
|
||||
@@ -57294,6 +57394,31 @@ FILEPATH_FIELDS = ','.join(FILEPATH_FIELDS_TITLES)
|
||||
|
||||
DRIVE_TIME_OBJECTS = {'createdTime', 'viewedByMeTime', 'modifiedByMeTime', 'modifiedTime', 'restrictionTime', 'sharedWithMeTime', 'trashedTime'}
|
||||
|
||||
def _getIncludeLabels(includeLabels):
|
||||
labelIds = getEntityList(Cmd.OB_CLASSIFICATION_LABEL_ID, shlexSplit=True)
|
||||
for labelId in labelIds:
|
||||
includeLabels.add(normalizeDriveLabelID(labelId))
|
||||
|
||||
def _finalizeIncludeLabels(includeLabels):
|
||||
if includeLabels:
|
||||
return ','.join(includeLabels)
|
||||
return None
|
||||
|
||||
DRIVEFILE_PERMISSIONS_FOR_VIEW_CHOICES = ['published']
|
||||
|
||||
def _getIncludePermissionsForView(includePermissionsForView):
|
||||
ipfwList = getEntityList(Cmd.OB_STRING_LIST)
|
||||
for ipfw in ipfwList:
|
||||
if ipfw in DRIVEFILE_PERMISSIONS_FOR_VIEW_CHOICES:
|
||||
includePermissionsForView.add(ipfw)
|
||||
else:
|
||||
invalidChoiceExit(ipfw, DRIVEFILE_PERMISSIONS_FOR_VIEW_CHOICES, True)
|
||||
|
||||
def _finalizeIncludePermissionsForView(includePermissionsForView):
|
||||
if includePermissionsForView:
|
||||
return ','.join(includePermissionsForView)
|
||||
return None
|
||||
|
||||
def _getDriveFieldSubField(field, fieldsList, parentsSubFields):
|
||||
field, subField = field.split('.', 1)
|
||||
if field in DRIVE_SUBFIELDS_CHOICE_MAP:
|
||||
@@ -57320,7 +57445,8 @@ class DriveFileFields():
|
||||
self.allFields = False
|
||||
self.OBY = OrderBy(DRIVEFILE_ORDERBY_CHOICE_MAP)
|
||||
self.fieldsList = []
|
||||
self.includeLabels = []
|
||||
self.includeLabels = set()
|
||||
self.includePermissionsForView = set()
|
||||
self.parentsSubFields = {'id': False, 'isRoot': False, 'rootFolderId': None}
|
||||
|
||||
def SetAllParentsSubFields(self):
|
||||
@@ -57354,9 +57480,9 @@ class DriveFileFields():
|
||||
else:
|
||||
_getDriveFieldSubField(field, self.fieldsList, self.parentsSubFields)
|
||||
elif myarg == 'includelabels':
|
||||
labelIds = getEntityList(Cmd.OB_CLASSIFICATION_LABEL_ID, shlexSplit=True)
|
||||
for labelId in labelIds:
|
||||
self.includeLabels.append(normalizeDriveLabelID(labelId))
|
||||
_getIncludeLabels(self.includeLabels)
|
||||
elif myarg == 'includepermissionsforview':
|
||||
_getIncludePermissionsForView(self.includePermissionsForView)
|
||||
elif myarg.find('.') != -1:
|
||||
_getDriveFieldSubField(myarg, self.fieldsList, self.parentsSubFields)
|
||||
elif myarg == 'orderby':
|
||||
@@ -57416,6 +57542,7 @@ def _formatFileDriveLabels(showLabels, labels, result, printMode, delimiter):
|
||||
# (orderby <DriveFileOrderByFieldName> [ascending|descending])*
|
||||
# [showdrivename] [showshareddrivepermissions]
|
||||
# [(showlabels details|ids)|(includelabels <DriveLabelIDList>)]
|
||||
# [includepermissionsforview published]
|
||||
# [showparentsidsaslist] [followshortcuts [<Boolean>]]
|
||||
# [stripcrsfromname] [formatjson]
|
||||
# gam <UserTypeEntity> show fileinfo <DriveFileEntity>
|
||||
@@ -57425,6 +57552,7 @@ def _formatFileDriveLabels(showLabels, labels, result, printMode, delimiter):
|
||||
# (orderby <DriveFileOrderByFieldName> [ascending|descending])*
|
||||
# [showdrivename] [showshareddrivepermissions]
|
||||
# [(showlabels details|ids)|(includelabels <DriveLabelIDList>)]
|
||||
# [includepermissionsforview published]
|
||||
# [showparentsidsaslist] [followshortcuts [<Boolean>]]
|
||||
# [stripcrsfromname] [formatjson]
|
||||
def showFileInfo(users):
|
||||
@@ -57487,7 +57615,8 @@ def showFileInfo(users):
|
||||
DFF.SetAllParentsSubFields()
|
||||
skipObjects = skipObjects.union(DEFAULT_SKIP_OBJECTS)
|
||||
showNoParents = True
|
||||
includeLabels = ','.join(DFF.includeLabels)
|
||||
includeLabels = _finalizeIncludeLabels(DFF.includeLabels)
|
||||
includePermissionsForView = _finalizeIncludePermissionsForView(DFF.includePermissionsForView)
|
||||
pathFields = FILEPATH_FIELDS
|
||||
i, count, users = getEntityArgument(users)
|
||||
for user in users:
|
||||
@@ -57526,12 +57655,14 @@ def showFileInfo(users):
|
||||
try:
|
||||
result = callGAPI(drive.files(), 'get',
|
||||
throwReasons=GAPI.DRIVE_GET_THROW_REASONS+[GAPI.INVALID],
|
||||
fileId=fileId, includeLabels=includeLabels, fields=fields, supportsAllDrives=True)
|
||||
fileId=fileId, includeLabels=includeLabels, includePermissionsForView=includePermissionsForView,
|
||||
fields=fields, supportsAllDrives=True)
|
||||
if followShortcuts and result['mimeType'] == MIMETYPE_GA_SHORTCUT:
|
||||
fileId = result['shortcutDetails']['targetId']
|
||||
result = callGAPI(drive.files(), 'get',
|
||||
throwReasons=GAPI.DRIVE_GET_THROW_REASONS+[GAPI.INVALID],
|
||||
fileId=fileId, includeLabels=includeLabels, fields=fields, supportsAllDrives=True)
|
||||
fileId=fileId, includeLabels=includeLabels, includePermissionsForView=includePermissionsForView,
|
||||
fields=fields, supportsAllDrives=True)
|
||||
if stripCRsFromName:
|
||||
result['name'] = _stripControlCharsFromName(result['name'])
|
||||
driveId = result.get('driveId')
|
||||
@@ -58859,6 +58990,7 @@ SIZE_FIELD_CHOICE_MAP = {
|
||||
# [allfields|<DriveFieldName>*|(fields <DriveFieldNameList>)]
|
||||
# [showdrivename] [showshareddrivepermissions]
|
||||
# (showlabels details|ids)|(includelabels <DriveLabelIDList>)]
|
||||
# [includepermissionsforview published]
|
||||
# [showparentsidsaslist] [showpermissionslast]
|
||||
# (orderby <DriveFileOrderByFieldName> [ascending|descending])* [delimiter <Character>]
|
||||
# [stripcrsfromname]
|
||||
@@ -59039,7 +59171,8 @@ def printFileList(users):
|
||||
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID_QUERY, GAPI.INVALID,
|
||||
GAPI.BAD_REQUEST],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS+[GAPI.UNKNOWN_ERROR],
|
||||
q=q, orderBy=DFF.orderBy, includeLabels=includeLabels, fields=pagesFields,
|
||||
q=q, orderBy=DFF.orderBy, includeLabels=includeLabels, includePermissionsForView=includePermissionsForView,
|
||||
fields=pagesFields,
|
||||
pageSize=GC.Values[GC.DRIVE_MAX_RESULTS], includeItemsFromAllDrives=True, supportsAllDrives=True)
|
||||
for childEntryInfo in children:
|
||||
childFileId = childEntryInfo['id']
|
||||
@@ -59056,8 +59189,12 @@ def printFileList(users):
|
||||
_printFileInfo(drive, user, childEntryInfo.copy(), stripCRsFromName)
|
||||
if childEntryInfo['mimeType'] == MIMETYPE_GA_FOLDER and (maxdepth == -1 or depth < maxdepth):
|
||||
_printChildDriveFolderContents(drive, childEntryInfo, user, i, count, depth+1)
|
||||
except (GAPI.invalidQuery, GAPI.invalid, GAPI.badRequest):
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE, None], invalidQuery(selectSubQuery), i, count)
|
||||
except (GAPI.invalidQuery, GAPI.invalid, GAPI.badRequest) as e:
|
||||
errMsg = str(e)
|
||||
if 'Invalid field selection' in errMsg or "Only a 'published' value is supported." in errMsg:
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, None], errMsg, i, count)
|
||||
else:
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE, None], invalidQuery(selectSubQuery), i, count)
|
||||
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
||||
userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
||||
|
||||
@@ -59279,7 +59416,8 @@ def printFileList(users):
|
||||
if filepath and not countsOnly:
|
||||
csvPF.AddTitles('paths')
|
||||
csvPF.SetFixPaths(True)
|
||||
includeLabels = ','.join(DFF.includeLabels)
|
||||
includeLabels = _finalizeIncludeLabels(DFF.includeLabels)
|
||||
includePermissionsForView = _finalizeIncludePermissionsForView(DFF.includePermissionsForView)
|
||||
csvPF.RemoveTitles(['capabilities'])
|
||||
if DLP.queryTimes and selectSubQuery:
|
||||
for queryTimeName, queryTimeValue in DLP.queryTimes.items():
|
||||
@@ -59352,7 +59490,8 @@ def printFileList(users):
|
||||
GAPI.BAD_REQUEST, GAPI.FILE_NOT_FOUND,
|
||||
GAPI.NOT_FOUND, GAPI.TEAMDRIVE_MEMBERSHIP_REQUIRED],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS+[GAPI.UNKNOWN_ERROR],
|
||||
q=DLP.fileIdEntity['query'], orderBy=DFF.orderBy, includeLabels=includeLabels,
|
||||
q=DLP.fileIdEntity['query'], orderBy=DFF.orderBy,
|
||||
includeLabels=includeLabels, includePermissionsForView=includePermissionsForView,
|
||||
fields=pagesFields, pageSize=GC.Values[GC.DRIVE_MAX_RESULTS], **btkwargs)
|
||||
for files in feed:
|
||||
if showLabels is not None:
|
||||
@@ -59383,7 +59522,7 @@ def printFileList(users):
|
||||
DLP.GetLocationFileIdsFromTree(fileTree, fileIdEntity)
|
||||
except (GAPI.invalidQuery, GAPI.invalid, GAPI.badRequest) as e:
|
||||
errMsg = str(e)
|
||||
if 'Invalid field selection' in errMsg:
|
||||
if 'Invalid field selection' in errMsg or "Only a 'published' value is supported." in errMsg:
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, None], errMsg, i, count)
|
||||
break
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, None], invalidQuery(DLP.fileIdEntity['query']), i, count)
|
||||
@@ -59415,8 +59554,9 @@ def printFileList(users):
|
||||
else:
|
||||
try:
|
||||
fileEntryInfo = callGAPI(drive.files(), 'get',
|
||||
throwReasons=GAPI.DRIVE_GET_THROW_REASONS,
|
||||
fileId=fileId, includeLabels=includeLabels, fields=fields, supportsAllDrives=True)
|
||||
throwReasons=GAPI.DRIVE_GET_THROW_REASONS+[GAPI.INVALID],
|
||||
fileId=fileId, includeLabels=includeLabels, includePermissionsForView=includePermissionsForView,
|
||||
fields=fields, supportsAllDrives=True)
|
||||
if stripCRsFromName:
|
||||
fileEntryInfo['name'] = _stripControlCharsFromName(fileEntryInfo['name'])
|
||||
if showLabels is not None:
|
||||
@@ -59427,6 +59567,9 @@ def printFileList(users):
|
||||
_formatFileDriveLabels(showLabels, labels, fileEntryInfo, True, delimiter)
|
||||
if filepath:
|
||||
fileTree[fileId] = {'info': fileEntryInfo}
|
||||
except GAPI.invalid as e:
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, fileId], str(e), j, jcount)
|
||||
continue
|
||||
except GAPI.fileNotFound:
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, fileId], Msg.NOT_FOUND, j, jcount)
|
||||
continue
|
||||
@@ -61927,6 +62070,7 @@ def initCopyMoveOptions(copyCmd):
|
||||
'copyFilePermissions': False,
|
||||
'copyFileInheritedPermissions': True,
|
||||
'copyFileNonInheritedPermissions': COPY_NONINHERITED_PERMISSIONS_ALWAYS,
|
||||
'copyFolderPermissions': True,
|
||||
'copyMergeWithParentFolderPermissions': False,
|
||||
'copyMergedTopFolderPermissions': copyCmd,
|
||||
'copyMergedSubFolderPermissions': copyCmd,
|
||||
@@ -62004,6 +62148,8 @@ def getCopyMoveOptions(myarg, copyMoveOptions):
|
||||
copyMoveOptions['duplicateFiles'] = getChoice(DUPLICATE_FILE_CHOICES, mapChoice=True)
|
||||
elif myarg == 'duplicatefolders':
|
||||
copyMoveOptions['duplicateFolders'] = getChoice(DUPLICATE_FOLDER_CHOICES, mapChoice=True)
|
||||
elif myarg == 'copyfolderpermissions':
|
||||
copyMoveOptions['copyFolderPermissions'] = getBoolean()
|
||||
elif myarg == 'copymergewithparentfolderpermissions':
|
||||
copyMoveOptions['copyMergeWithParentFolderPermissions'] = getBoolean()
|
||||
elif myarg == 'copymergedtopfolderpermissions':
|
||||
@@ -62682,6 +62828,7 @@ copyReturnItemMap = {
|
||||
# [copyfilepermissions [<Boolean>]]
|
||||
# [copyfileinheritedpermissions [<Boolean>]
|
||||
# [copyfilenoninheritedpermissions [<Boolean>]
|
||||
# [copyfolderpermissions [<Boolean>]]
|
||||
# [copymergewithparentfolderpermissions [<Boolean>]]
|
||||
# [copymergedtopfolderpermissions [<Boolean>]]
|
||||
# [copytopfolderpermissions [<Boolean>]]
|
||||
@@ -62729,7 +62876,8 @@ def copyDriveFile(users):
|
||||
_writeCSVData(user, folderName, folderId, newParentName, newParentId, MIMETYPE_GA_FOLDER)
|
||||
Act.Set(action)
|
||||
_incrStatistic(statistics, STAT_FOLDER_MERGED)
|
||||
if (copyMoveOptions['copyMergeWithParentFolderPermissions'] and
|
||||
if (copyMoveOptions['copyFolderPermissions'] and
|
||||
copyMoveOptions['copyMergeWithParentFolderPermissions'] and
|
||||
copyMoveOptions['destParentType'] != DEST_PARENT_MYDRIVE_ROOT):
|
||||
copyFolderNonInheritedPermissions =\
|
||||
_getCopyFolderNonInheritedPermissions(copyMoveOptions,
|
||||
@@ -62759,7 +62907,8 @@ def copyDriveFile(users):
|
||||
_writeCSVData(user, folderName, folderId, newFolderName, newFolderId, MIMETYPE_GA_FOLDER)
|
||||
Act.Set(action)
|
||||
_incrStatistic(statistics, STAT_FOLDER_MERGED)
|
||||
if (copyMoveOptions[['copyMergedSubFolderPermissions', 'copyMergedTopFolderPermissions'][atTop]] and
|
||||
if (copyMoveOptions['copyFolderPermissions'] and
|
||||
copyMoveOptions[['copyMergedSubFolderPermissions', 'copyMergedTopFolderPermissions'][atTop]] and
|
||||
(not atTop or copyMoveOptions['destParentType'] != DEST_PARENT_MYDRIVE_ROOT)):
|
||||
copyFolderNonInheritedPermissions =\
|
||||
_getCopyFolderNonInheritedPermissions(copyMoveOptions,
|
||||
@@ -62811,7 +62960,8 @@ def copyDriveFile(users):
|
||||
else:
|
||||
_writeCSVData(user, folderName, folderId, newFolderName, newFolderId, body['mimeType'])
|
||||
_incrStatistic(statistics, STAT_FOLDER_COPIED_MOVED)
|
||||
if copyMoveOptions[['copySubFolderPermissions', 'copyTopFolderPermissions'][atTop]]:
|
||||
if (copyMoveOptions['copyFolderPermissions'] and
|
||||
copyMoveOptions[['copySubFolderPermissions', 'copyTopFolderPermissions'][atTop]]):
|
||||
_copyPermissions(drive, user, i, count, j, jcount,
|
||||
Ent.DRIVE_FOLDER, folderId, folderName, newFolderId, newFolderName,
|
||||
statistics, STAT_FOLDER_PERMISSIONS_FAILED,
|
||||
@@ -63566,6 +63716,7 @@ def _recursiveUpdateMovePermissions(drive, user, i, count,
|
||||
# [createshortcutsfornonmovablefiles [<Boolean>]]
|
||||
# [duplicatefiles overwriteolder|overwriteall|duplicatename|uniquename|skip]
|
||||
# [duplicatefolders merge|duplicatename|uniquename|skip]
|
||||
# [copyfolderpermissions [<Boolean>]]
|
||||
# [copymergewithparentfolderpermissions [<Boolean>]]
|
||||
# [copymergedtopfolderpermissions [<Boolean>]]
|
||||
# [copytopfolderpermissions [<Boolean>]]
|
||||
@@ -63602,7 +63753,8 @@ def moveDriveFile(users):
|
||||
entityPerformActionModifierItemValueList(kvList, Act.MODIFIER_CONTENTS_WITH, [Ent.DRIVE_FOLDER, newParentNameId], j, jcount)
|
||||
Act.Set(action)
|
||||
_incrStatistic(statistics, STAT_FOLDER_MERGED)
|
||||
if (copyMoveOptions['copyMergeWithParentFolderPermissions'] and
|
||||
if (copyMoveOptions['copyFolderPermissions'] and
|
||||
copyMoveOptions['copyMergeWithParentFolderPermissions'] and
|
||||
copyMoveOptions['destParentType'] != DEST_PARENT_MYDRIVE_ROOT):
|
||||
copyFolderNonInheritedPermissions =\
|
||||
_getCopyFolderNonInheritedPermissions(copyMoveOptions,
|
||||
@@ -63632,7 +63784,8 @@ def moveDriveFile(users):
|
||||
entityModifierItemValueListActionPerformed(kvList, Act.MODIFIER_CONTENTS_WITH, [Ent.DRIVE_FOLDER, f'{newFolderName}({newFolderId})'], j, jcount)
|
||||
Act.Set(action)
|
||||
_incrStatistic(statistics, STAT_FOLDER_MERGED)
|
||||
if (copyMoveOptions[['copyMergedSubFolderPermissions', 'copyMergedTopFolderPermissions'][atTop]] and
|
||||
if (copyMoveOptions['copyFolderPermissions'] and
|
||||
copyMoveOptions[['copyMergedSubFolderPermissions', 'copyMergedTopFolderPermissions'][atTop]] and
|
||||
(not atTop or copyMoveOptions['destParentType'] != DEST_PARENT_MYDRIVE_ROOT)):
|
||||
copyFolderNonInheritedPermissions =\
|
||||
_getCopyFolderNonInheritedPermissions(copyMoveOptions,
|
||||
@@ -63726,7 +63879,8 @@ def moveDriveFile(users):
|
||||
j, jcount)
|
||||
Act.Set(action)
|
||||
_incrStatistic(statistics, STAT_FOLDER_COPIED_MOVED)
|
||||
if copyMoveOptions[['copySubFolderPermissions', 'copyTopFolderPermissions'][atTop]]:
|
||||
if (copyMoveOptions['copyFolderPermissions'] and
|
||||
copyMoveOptions[['copySubFolderPermissions', 'copyTopFolderPermissions'][atTop]]):
|
||||
_copyPermissions(drive, user, i, count, j, jcount,
|
||||
Ent.DRIVE_FOLDER, folderId, folderName, newFolderId, newFolderName,
|
||||
statistics, STAT_FOLDER_PERMISSIONS_FAILED,
|
||||
@@ -66650,6 +66804,29 @@ def _checkFileIdEntityDomainAccess(fileIdEntity, useDomainAdminAccess):
|
||||
# [sendemail|sendnotification] [emailmessage <String>]
|
||||
# [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])]
|
||||
def createDriveFileACL(users, useDomainAdminAccess=False):
|
||||
def _showResult(permission, showAction):
|
||||
if updateSheetProtectedRanges and mimeType == MIMETYPE_GA_SPREADSHEET:
|
||||
_updateSheetProtectedRangesACLchange(sheet, user, i, count, j, jcount, fileId, fileName, True, permission)
|
||||
if csvPF:
|
||||
baserow = {'Owner': user, 'id': fileId}
|
||||
if showTitles:
|
||||
baserow['name'] = fileName
|
||||
row = baserow.copy()
|
||||
_mapDrivePermissionNames(permission)
|
||||
flattenJSON({'permission': permission}, flattened=row, timeObjects=timeObjects)
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif csvPF.CheckRowTitles(row):
|
||||
row = baserow.copy()
|
||||
row['JSON'] = json.dumps(cleanJSON({'permission': permission}, timeObjects=timeObjects),
|
||||
ensure_ascii=False, sort_keys=True)
|
||||
csvPF.WriteRowNoFilter(row)
|
||||
else:
|
||||
if showAction:
|
||||
entityActionPerformed([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], j, jcount)
|
||||
if showDetails:
|
||||
_showDriveFilePermission(permission, printKeys, timeObjects)
|
||||
|
||||
moveToNewOwnersRoot = False
|
||||
sendNotificationEmail = showTitles = _transferOwnership = updateSheetProtectedRanges = False
|
||||
roleLocation = withLinkLocation = expirationLocation = None
|
||||
@@ -66759,35 +66936,30 @@ def createDriveFileACL(users, useDomainAdminAccess=False):
|
||||
_, sheet = buildGAPIServiceObject(API.SHEETS, user, i, count)
|
||||
if not sheet:
|
||||
break
|
||||
permission = callGAPI(drive.permissions(), 'create',
|
||||
bailOnInternalError=True,
|
||||
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_CREATE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE],
|
||||
moveToNewOwnersRoot=moveToNewOwnersRoot,
|
||||
useDomainAdminAccess=useDomainAdminAccess,
|
||||
fileId=fileId, sendNotificationEmail=sendNotificationEmail, emailMessage=emailMessage,
|
||||
transferOwnership=_transferOwnership, body=body, fields='*', supportsAllDrives=True)
|
||||
if updateSheetProtectedRanges and mimeType == MIMETYPE_GA_SPREADSHEET:
|
||||
_updateSheetProtectedRangesACLchange(sheet, user, i, count, j, jcount, fileId, fileName, True, permission)
|
||||
if csvPF:
|
||||
baserow = {'Owner': user, 'id': fileId}
|
||||
if showTitles:
|
||||
baserow['name'] = fileName
|
||||
row = baserow.copy()
|
||||
_mapDrivePermissionNames(permission)
|
||||
flattenJSON({'permission': permission}, flattened=row, timeObjects=timeObjects)
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif csvPF.CheckRowTitles(row):
|
||||
row = baserow.copy()
|
||||
row['JSON'] = json.dumps(cleanJSON({'permission': permission}, timeObjects=timeObjects),
|
||||
ensure_ascii=False, sort_keys=True)
|
||||
csvPF.WriteRowNoFilter(row)
|
||||
else:
|
||||
entityActionPerformed([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], j, jcount)
|
||||
if showDetails:
|
||||
_showDriveFilePermission(permission, printKeys, timeObjects)
|
||||
try:
|
||||
permission = callGAPI(drive.permissions(), 'create',
|
||||
bailOnInternalError=True,
|
||||
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_CREATE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE],
|
||||
moveToNewOwnersRoot=moveToNewOwnersRoot,
|
||||
useDomainAdminAccess=useDomainAdminAccess,
|
||||
fileId=fileId, sendNotificationEmail=sendNotificationEmail, emailMessage=emailMessage,
|
||||
transferOwnership=_transferOwnership, body=body, fields='*', supportsAllDrives=True)
|
||||
_showResult(permission, True)
|
||||
except GAPI.invalidSharingRequest as e:
|
||||
errMsg = str(e)
|
||||
if ('successfully shared but emails could not be sent' not in errMsg) or ('emailAddress' not in body):
|
||||
entityActionFailedWarning([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], errMsg, j, jcount)
|
||||
else:
|
||||
if not csvPF:
|
||||
entityActionPerformedMessage([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], errMsg, j, jcount)
|
||||
tempPermId = getPermissionIdForEmail(user, i, count, body['emailAddress'])
|
||||
permission = callGAPI(drive.permissions(), 'get',
|
||||
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.BAD_REQUEST, GAPI.PERMISSION_NOT_FOUND, GAPI.INSUFFICIENT_ADMINISTRATOR_PRIVILEGES],
|
||||
useDomainAdminAccess=useDomainAdminAccess,
|
||||
fileId=fileId, permissionId=tempPermId, fields='*', supportsAllDrives=True)
|
||||
_showResult(permission, False)
|
||||
except (GAPI.badRequest, GAPI.invalid, GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError,
|
||||
GAPI.cannotSetExpiration, GAPI.cannotSetExpirationOnAnyoneOrDomain,
|
||||
GAPI.permissionNotFound, GAPI.cannotSetExpiration, GAPI.cannotSetExpirationOnAnyoneOrDomain,
|
||||
GAPI.expirationDateNotAllowedForSharedDriveMembers, GAPI.expirationDatesMustBeInTheFuture,
|
||||
GAPI.insufficientFilePermissions, GAPI.unknownError, GAPI.ownershipChangeAcrossDomainNotPermitted,
|
||||
GAPI.teamDriveDomainUsersOnlyRestriction, GAPI.teamDriveTeamMembersOnlyRestriction,
|
||||
@@ -66801,7 +66973,7 @@ def createDriveFileACL(users, useDomainAdminAccess=False):
|
||||
GAPI.fileOrganizerOnNonTeamDriveNotSupported,
|
||||
GAPI.cannotModifyInheritedPermission,
|
||||
GAPI.teamDrivesFolderSharingNotSupported, GAPI.invalidLinkVisibility,
|
||||
GAPI.invalidSharingRequest, GAPI.fileNeverWritable, GAPI.abusiveContentRestriction) as e:
|
||||
GAPI.fileNeverWritable, GAPI.abusiveContentRestriction) as e:
|
||||
entityActionFailedWarning([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], str(e), j, jcount)
|
||||
except GAPI.notFound as e:
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE, fileName], str(e), j, jcount)
|
||||
@@ -66820,6 +66992,29 @@ def doCreateDriveFileACL():
|
||||
# [updatesheetprotectedranges [<Boolean>]] [enforceexpansiveaccess [<Boolean>]]
|
||||
# [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])]
|
||||
def updateDriveFileACLs(users, useDomainAdminAccess=False):
|
||||
def _showResult(permission, showAction):
|
||||
if updateSheetProtectedRanges and mimeType == MIMETYPE_GA_SPREADSHEET:
|
||||
_updateSheetProtectedRangesACLchange(sheet, user, i, count, j, jcount, fileId, fileName, True, permission)
|
||||
if csvPF:
|
||||
baserow = {'Owner': user, 'id': fileId}
|
||||
if showTitles:
|
||||
baserow['name'] = fileName
|
||||
row = baserow.copy()
|
||||
_mapDrivePermissionNames(permission)
|
||||
flattenJSON({'permission': permission}, flattened=row, timeObjects=timeObjects)
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif csvPF.CheckRowTitles(row):
|
||||
row = baserow.copy()
|
||||
row['JSON'] = json.dumps(cleanJSON({'permission': permission}, timeObjects=timeObjects),
|
||||
ensure_ascii=False, sort_keys=True)
|
||||
csvPF.WriteRowNoFilter(row)
|
||||
else:
|
||||
if showAction:
|
||||
entityActionPerformed([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], j, jcount)
|
||||
if showDetails:
|
||||
_showDriveFilePermission(permission, printKeys, timeObjects)
|
||||
|
||||
fileIdEntity = getDriveFileEntity()
|
||||
isEmail, permissionId = getPermissionId()
|
||||
enforceExpansiveAccess = GC.Values[GC.ENFORCE_EXPANSIVE_ACCESS]
|
||||
@@ -66899,26 +67094,7 @@ def updateDriveFileACLs(users, useDomainAdminAccess=False):
|
||||
useDomainAdminAccess=useDomainAdminAccess, enforceExpansiveAccess=enforceExpansiveAccess,
|
||||
fileId=fileId, permissionId=permissionId, removeExpiration=removeExpiration,
|
||||
transferOwnership=body.get('role', '') == 'owner', body=body, fields='*', supportsAllDrives=True)
|
||||
if updateSheetProtectedRanges and mimeType == MIMETYPE_GA_SPREADSHEET:
|
||||
_updateSheetProtectedRangesACLchange(sheet, user, i, count, j, jcount, fileId, fileName, True, permission)
|
||||
if csvPF:
|
||||
baserow = {'Owner': user, 'id': fileId}
|
||||
if showTitles:
|
||||
baserow['name'] = fileName
|
||||
row = baserow.copy()
|
||||
_mapDrivePermissionNames(permission)
|
||||
flattenJSON({'permission': permission}, flattened=row, timeObjects=timeObjects)
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif csvPF.CheckRowTitles(row):
|
||||
row = baserow.copy()
|
||||
row['JSON'] = json.dumps(cleanJSON({'permission': permission}, timeObjects=timeObjects),
|
||||
ensure_ascii=False, sort_keys=True)
|
||||
csvPF.WriteRowNoFilter(row)
|
||||
else:
|
||||
entityActionPerformed([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], j, jcount)
|
||||
if showDetails:
|
||||
_showDriveFilePermission(permission, printKeys, timeObjects)
|
||||
_showResult(permission, True)
|
||||
except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError,
|
||||
GAPI.cannotSetExpiration, GAPI.cannotSetExpirationOnAnyoneOrDomain,
|
||||
GAPI.expirationDateNotAllowedForSharedDriveMembers, GAPI.expirationDatesMustBeInTheFuture,
|
||||
@@ -67410,8 +67586,6 @@ def infoDriveFileACLs(users, useDomainAdminAccess=False):
|
||||
def doInfoDriveFileACLs():
|
||||
infoDriveFileACLs([_getAdminEmail()], True)
|
||||
|
||||
DRIVEFILE_PERMISSIONS_FOR_VIEW_CHOICES = ['published']
|
||||
|
||||
def getDriveFilePermissionsFields(myarg, fieldsList):
|
||||
if myarg in DRIVE_PERMISSIONS_SUBFIELDS_CHOICE_MAP:
|
||||
fieldsList.append(DRIVE_PERMISSIONS_SUBFIELDS_CHOICE_MAP[myarg])
|
||||
@@ -67466,7 +67640,7 @@ def printShowDriveFileACLs(users, useDomainAdminAccess=False):
|
||||
addTitle = None
|
||||
roles = set()
|
||||
oneItemPerRow = pmselect = showTitles = False
|
||||
includePermissionsForView = None
|
||||
includePermissionsForView = set()
|
||||
fieldsList = []
|
||||
OBY = OrderBy(DRIVEFILE_ORDERBY_CHOICE_MAP)
|
||||
PM = PermissionMatch()
|
||||
@@ -67501,13 +67675,14 @@ def printShowDriveFileACLs(users, useDomainAdminAccess=False):
|
||||
elif PM.ProcessArgument(myarg):
|
||||
pass
|
||||
elif myarg == 'includepermissionsforview':
|
||||
includePermissionsForView = getChoice(DRIVEFILE_PERMISSIONS_FOR_VIEW_CHOICES)
|
||||
_getIncludePermissionsForView(includePermissionsForView)
|
||||
else:
|
||||
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
||||
_checkFileIdEntityDomainAccess(fileIdEntity, useDomainAdminAccess)
|
||||
if fieldsList:
|
||||
if roles:
|
||||
fieldsList.append('role')
|
||||
includePermissionsForView = _finalizeIncludePermissionsForView(includePermissionsForView)
|
||||
fields = getItemFieldsFromFieldsList('permissions', fieldsList, True)
|
||||
printKeys, timeObjects = _getDriveFileACLPrintKeysTimeObjects()
|
||||
i, count, users = getEntityArgument(users)
|
||||
|
||||
@@ -649,6 +649,16 @@ _SVCACCT_SCOPES = [
|
||||
'api': CLOUDIDENTITY_DEVICES,
|
||||
'subscopes': READONLY,
|
||||
'scope': 'https://www.googleapis.com/auth/cloud-identity.devices'},
|
||||
# {'name': 'Cloud Identity API - Policy',
|
||||
# 'api': CLOUDIDENTITY_POLICY,
|
||||
# 'subscopes': READONLY,
|
||||
# 'roByDefault': True,
|
||||
# 'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
|
||||
# {'name': 'Cloud Identity API - Policy Beta',
|
||||
# 'api': CLOUDIDENTITY_POLICY_BETA,
|
||||
# 'subscopes': [],
|
||||
# 'offByDefault': True,
|
||||
# 'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
|
||||
# {'name': 'Cloud Identity User Invitations API',
|
||||
# 'api': CLOUDIDENTITY_USERINVITATIONS,
|
||||
# 'subscopes': READONLY,
|
||||
@@ -812,6 +822,12 @@ def getVersion(api):
|
||||
api = _INFO[api].get('mappedAPI', api)
|
||||
return (api, version, v2discovery)
|
||||
|
||||
def getAPIsList():
|
||||
apisList = set()
|
||||
for api, value in _INFO.items():
|
||||
apisList.add(value.get('mappedAPI', api))
|
||||
return apisList
|
||||
|
||||
def getClientScopesSet(api):
|
||||
return {scope['scope'] for scope in _CLIENT_SCOPES if scope['api'] == api}
|
||||
|
||||
|
||||
@@ -123,6 +123,8 @@ CSV_OUTPUT_HEADER_DROP_FILTER = 'csv_output_header_drop_filter'
|
||||
CSV_OUTPUT_HEADER_FORCE = 'csv_output_header_force'
|
||||
# Orde output column headers
|
||||
CSV_OUTPUT_HEADER_ORDER = 'csv_output_header_order'
|
||||
# Required output column headers
|
||||
CSV_OUTPUT_HEADER_REQUIRED = 'csv_output_header_required'
|
||||
# Line terminator in CSV output file
|
||||
CSV_OUTPUT_LINE_TERMINATOR = 'csv_output_line_terminator'
|
||||
# Quote character in CSV output file
|
||||
@@ -151,6 +153,8 @@ CUSTOMER_ID = 'customer_id'
|
||||
DEBUG_LEVEL = 'debug_level'
|
||||
# redact sensitive credentials from debug output
|
||||
DEBUG_REDACTION = 'debug_redaction'
|
||||
# Developer Preview APIs
|
||||
DEVELOPER_PREVIEW_APIS = 'developer_preview_apis'
|
||||
# Developer Preview API Key
|
||||
DEVELOPER_PREVIEW_API_KEY = 'developer_preview_api_key'
|
||||
# When retrieving lists of ChromeOS devices from API, how many should be retrieved in each chunk
|
||||
@@ -327,6 +331,7 @@ CSV_INPUT_ROW_FILTER_ITEMS = {CSV_INPUT_ROW_FILTER, CSV_INPUT_ROW_FILTER_MODE,
|
||||
|
||||
CSV_OUTPUT_ROW_FILTER_ITEMS = {CSV_OUTPUT_HEADER_FILTER, CSV_OUTPUT_HEADER_DROP_FILTER,
|
||||
CSV_OUTPUT_HEADER_FORCE, CSV_OUTPUT_HEADER_ORDER,
|
||||
CSV_OUTPUT_HEADER_REQUIRED,
|
||||
CSV_OUTPUT_ROW_FILTER, CSV_OUTPUT_ROW_FILTER_MODE,
|
||||
CSV_OUTPUT_ROW_DROP_FILTER, CSV_OUTPUT_ROW_DROP_FILTER_MODE,
|
||||
CSV_OUTPUT_ROW_LIMIT}
|
||||
@@ -371,6 +376,7 @@ Defaults = {
|
||||
CSV_OUTPUT_HEADER_DROP_FILTER: '',
|
||||
CSV_OUTPUT_HEADER_FORCE: '',
|
||||
CSV_OUTPUT_HEADER_ORDER: '',
|
||||
CSV_OUTPUT_HEADER_REQUIRED: '',
|
||||
CSV_OUTPUT_LINE_TERMINATOR: 'lf',
|
||||
CSV_OUTPUT_QUOTE_CHAR: '\'"\'',
|
||||
CSV_OUTPUT_ROW_FILTER: '',
|
||||
@@ -385,6 +391,7 @@ Defaults = {
|
||||
CUSTOMER_ID: MY_CUSTOMER,
|
||||
DEBUG_LEVEL: '0',
|
||||
DEBUG_REDACTION: TRUE,
|
||||
DEVELOPER_PREVIEW_APIS: '',
|
||||
DEVELOPER_PREVIEW_API_KEY: '',
|
||||
DEVICE_MAX_RESULTS: '200',
|
||||
DOMAIN: '',
|
||||
@@ -484,7 +491,7 @@ TYPE_EMAIL_OPTIONAL = 'emao'
|
||||
TYPE_FILE = 'file'
|
||||
TYPE_FLOAT = 'floa'
|
||||
TYPE_HEADERFILTER = 'heaf'
|
||||
TYPE_HEADERFORCE = 'hefo'
|
||||
TYPE_HEADERFORCEREQUIRED = 'hefr'
|
||||
TYPE_HEADERORDER = 'heor'
|
||||
TYPE_INTEGER = 'inte'
|
||||
TYPE_LANGUAGE = 'lang'
|
||||
@@ -541,8 +548,9 @@ VAR_INFO = {
|
||||
CSV_OUTPUT_FIELD_DELIMITER: {VAR_TYPE: TYPE_CHARACTER},
|
||||
CSV_OUTPUT_HEADER_FILTER: {VAR_TYPE: TYPE_HEADERFILTER},
|
||||
CSV_OUTPUT_HEADER_DROP_FILTER: {VAR_TYPE: TYPE_HEADERFILTER},
|
||||
CSV_OUTPUT_HEADER_FORCE: {VAR_TYPE: TYPE_HEADERFORCE},
|
||||
CSV_OUTPUT_HEADER_FORCE: {VAR_TYPE: TYPE_HEADERFORCEREQUIRED},
|
||||
CSV_OUTPUT_HEADER_ORDER: {VAR_TYPE: TYPE_HEADERORDER},
|
||||
CSV_OUTPUT_HEADER_REQUIRED: {VAR_TYPE: TYPE_HEADERFORCEREQUIRED},
|
||||
CSV_OUTPUT_LINE_TERMINATOR: {VAR_TYPE: TYPE_CHOICE, VAR_CHOICES: {'cr': '\r', 'lf': '\n', 'crlf': '\r\n'}},
|
||||
CSV_OUTPUT_QUOTE_CHAR: {VAR_TYPE: TYPE_CHARACTER},
|
||||
CSV_OUTPUT_ROW_FILTER: {VAR_TYPE: TYPE_ROWFILTER},
|
||||
@@ -557,6 +565,7 @@ VAR_INFO = {
|
||||
CUSTOMER_ID: {VAR_TYPE: TYPE_STRING, VAR_ENVVAR: 'CUSTOMER_ID', VAR_LIMITS: (0, None)},
|
||||
DEBUG_LEVEL: {VAR_TYPE: TYPE_INTEGER, VAR_SIGFILE: 'debug.gam', VAR_LIMITS: (0, None), VAR_SFFT: ('0', '4')},
|
||||
DEBUG_REDACTION: {VAR_TYPE: TYPE_BOOLEAN},
|
||||
DEVELOPER_PREVIEW_APIS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
DEVELOPER_PREVIEW_API_KEY: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
DEVICE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 200)},
|
||||
DOMAIN: {VAR_TYPE: TYPE_STRING, VAR_ENVVAR: 'GA_DOMAIN', VAR_LIMITS: (0, None)},
|
||||
|
||||
@@ -69,6 +69,8 @@ CSV_OUTPUT_HEADER_FILTER = 'cohf'
|
||||
CSV_OUTPUT_HEADER_FORCE = 'cofh'
|
||||
# Order output column headers
|
||||
CSV_OUTPUT_HEADER_ORDER = 'coho'
|
||||
# Required output column headers
|
||||
CSV_OUTPUT_HEADER_REQUIRED = 'corh'
|
||||
# No escape character in CSV output file
|
||||
CSV_OUTPUT_NO_ESCAPE_CHAR = 'cone'
|
||||
# Quote character in CSV output file
|
||||
@@ -111,6 +113,8 @@ DEBUG_LEVEL = 'dbgl'
|
||||
DEBUG_REDACTION = 'dbrd'
|
||||
# Decoded ID token
|
||||
DECODED_ID_TOKEN = 'didt'
|
||||
# Developer Preview APIs
|
||||
DEVELOPER_PREVIEW_APIS = 'dapi'
|
||||
# Index of start of <UserTypeEntity> in command line
|
||||
ENTITY_CL_DELAY_START = 'ecld'
|
||||
ENTITY_CL_START = 'ecls'
|
||||
@@ -125,6 +129,8 @@ GAM_CFG_SECTION_NAME = 'gcsn'
|
||||
GAM_PATH = 'gpth'
|
||||
# Python source, PyInstaller or StaticX?
|
||||
GAM_TYPE = 'gtyp'
|
||||
# Shared Service Account HTTP Object
|
||||
HTTP_OBJECT = 'http'
|
||||
# Length of last Got message
|
||||
LAST_GOT_MSG_LEN = 'lgml'
|
||||
# License SKUs
|
||||
@@ -246,6 +252,7 @@ Globals = {
|
||||
CSV_OUTPUT_HEADER_FILTER: [],
|
||||
CSV_OUTPUT_HEADER_FORCE: [],
|
||||
CSV_OUTPUT_HEADER_ORDER: [],
|
||||
CSV_OUTPUT_HEADER_REQUIRED: [],
|
||||
CSV_OUTPUT_NO_ESCAPE_CHAR: None,
|
||||
CSV_OUTPUT_QUOTE_CHAR: None,
|
||||
CSV_OUTPUT_ROW_DROP_FILTER: [],
|
||||
@@ -267,6 +274,7 @@ Globals = {
|
||||
DEBUG_LEVEL: 0,
|
||||
DEBUG_REDACTION: True,
|
||||
DECODED_ID_TOKEN: None,
|
||||
DEVELOPER_PREVIEW_APIS: set(),
|
||||
ENTITY_CL_DELAY_START: 1,
|
||||
ENTITY_CL_START: 1,
|
||||
EXTRA_ARGS_LIST: [],
|
||||
@@ -276,6 +284,7 @@ Globals = {
|
||||
GAM_CFG_SECTION_NAME: '',
|
||||
GAM_PATH: '.',
|
||||
GAM_TYPE: '',
|
||||
HTTP_OBJECT: None,
|
||||
LAST_GOT_MSG_LEN: 0,
|
||||
LICENSE_SKUS: [],
|
||||
MAKE_BUILDING_ID_NAME_MAP: True,
|
||||
|
||||
@@ -236,6 +236,7 @@ DATA_TRANSFER_COMPLETED = 'Data Transfer completed: {0}\n'
|
||||
DATA_UPLOADED_TO_DRIVE_FILE = 'Data uploaded to Drive File'
|
||||
DEFAULT_SMIME = 'Default S/MIME'
|
||||
DELETED = 'Deleted'
|
||||
DEVELOPER_PREVIEW_REQUIRED = 'Developer Preview is required for this command\n'
|
||||
DEVICE_LIST_BUG = 'GAM hit Google internal bug 237397223. Please file a Google Support ticket stating that you are encountering this bug.'
|
||||
DEVICE_LIST_BUG_WORKAROUND_NOT_POSSIBLE = 'GAM workaround for this issue only works if orderby argument is not used and query does not contain \'register\'.'
|
||||
DEVICE_LIST_BUG_ATTEMPTING_WORKAROUND = 'GAM is attempting to work around the bug by filtering for devices created on or after the newest we\'ve seen ({0})...\n'
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
There are seven values in `gam.cfg` that can be used to filter the output from `gam print` commands.
|
||||
* `csv_output_header_filter` - A list of `<RegularExpressions>` used to select specific column headers to include
|
||||
* `csv_output_header_drop_filter` - A list of `<RegularExpressions>` used to select specific column headers to exclude
|
||||
* `csv_output_header_force` - A list of <Strings> used to specify the exact column headers to include
|
||||
* `csv_output_header_order` - A list of <Strings> used to specify the column header order; any headers in the file but not in the list will appear after the headers in the list.
|
||||
* `csv_output_header_force` - A list of `<Strings>` used to specify the exact column headers to include
|
||||
* `csv_output_header_order` - A list of `<Strings>` used to specify the column header order; any headers in the file but not in the list will appear after the header* `csv_output_header_required` - A list of `<Strings>` used to specify column headers that are included even if the print command doesn't return them
|
||||
s in the list.
|
||||
* `csv_output_row_filter` - A list or JSON dictionary used to include specific rows based on column values
|
||||
* `csv_output_row_drop_filter` - A list or JSON dictionary used to exclude specific rows based on column values
|
||||
* `csv_output_row_limit` - A limit on the number of rows written
|
||||
|
||||
@@ -282,10 +282,11 @@ gam show chromepolicies
|
||||
((ou|orgunit <OrgUnitItem> [show all|direct|inherited])|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
[filter <StringList>] [namespace <NamespaceList>]
|
||||
[show all|direct|inherited]
|
||||
[formatjson]
|
||||
```
|
||||
By default, all Chrome policies for the OU or group are displayed.
|
||||
* `filter <String>` - Display policies based on fields like its resource name, description and additionalTargetKeyNames.
|
||||
* `filter <StringList>` - Display policies based on fields like its resource name, description and additionalTargetKeyNames.
|
||||
* `show all` - For OUs, display policies regardless of where set; this is the default
|
||||
* `show direct` - For OUs, display policies set directly in the OU
|
||||
* `show inherited` - For OUs, display policies set in a parent OU
|
||||
@@ -329,15 +330,25 @@ By default, Gam displays the information as an indented list of keys and values.
|
||||
gam print chromepolicies [todrive <ToDriveAttribute>*]
|
||||
((ou|orgunit <OrgUnitItem> [show all|direct|inherited])|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
[filter <String>] [namespace <NamespaceList>]
|
||||
[filter <StringList>] [namespace <NamespaceList>]
|
||||
[show all|direct|inherited] [shownopolicy]
|
||||
[[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, all Chrome policies for the OU or group are displayed.
|
||||
* `filter <String>` - Display policies based on fields like its resource name, description and additionalTargetKeyNames.
|
||||
* `filter <StringList>` - Display policies based on fields like its resource name, description and additionalTargetKeyNames.
|
||||
* `show all` - For OUs, display policies regardless of where set; this is the default
|
||||
* `show direct` - For OUs, display policies set directly in the OU
|
||||
* `show inherited` - For OUs, display policies set in a parent OU
|
||||
|
||||
Use option `shownopolicy` to display output like the following if no policies apply to the OU or group.
|
||||
```
|
||||
gam print chromepolicies ou /Test appid chrome:emidddocikgklceeeifefomdnbkldhng namespace chrome.users.apps shownopolicy
|
||||
Getting all Chrome Policies that match query (chrome.users.apps.*) for /Test
|
||||
Got 0 Chrome Policies that matched query (chrome.users.apps.*) for /Test...
|
||||
name,orgUnitPath,parentOrgUnitPath,direct,appId
|
||||
noPolicy,/Test,/,False,chrome:emidddocikgklceeeifefomdnbkldhng
|
||||
```
|
||||
|
||||
These are the default namespaces; use `namespace <NamespaceList>` to override.
|
||||
* `default`
|
||||
* chrome.users
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
- [Display user group member options](#display-user-group-member-options)
|
||||
- [Display group membership in CSV format](#display-group-membership-in-csv-format)
|
||||
- [Display group membership in hierarchical format](#display-group-membership-in-hierarchical-format)
|
||||
- [Display external users in groups with allowExternalMembers False](#display-external-users-in-groups-with-allowexternalmembers-false)
|
||||
- [Manage external users in groups with allowExternalMembers False](#manage-external-users-in-groups-with-allowexternalmembers-false)
|
||||
## API documentation
|
||||
* [Cloud Identity Groups Overview](https://cloud.google.com/identity/docs/groups)
|
||||
* [Cloud Identity Groups API - Groups](https://cloud.google.com/identity/docs/reference/rest/v1/groups)
|
||||
@@ -534,6 +534,40 @@ To show the structure of all groups you can do the following; it will be time co
|
||||
gam redirect stdout ./groups.txt show cigroup-members types group
|
||||
```
|
||||
|
||||
## Display external users in groups with allowExternalMembers False
|
||||
When printing group membership, the option `verifyallowexternal` causes
|
||||
GAM to only display external users in groups with `allowExternalMembers=False`.
|
||||
## Manage external users in groups with allowExternalMembers False
|
||||
|
||||
* See: https://support.google.com/a/answer/16778447
|
||||
|
||||
Get external members of groups with allowExternalMembers = False
|
||||
```
|
||||
gam redirect csv ./ExternalMembersGroupsWithAEMFalse.csv print cigroup-members verifyallowexternal
|
||||
```
|
||||
|
||||
Update selected groups to allowExternalMembers = True
|
||||
* Add a column labelled `update` to ExternalMembersGroupsWithAEMFalse.csv
|
||||
* For groups to be updated, add an x to the `update` column for any member of the group to be updated
|
||||
```
|
||||
gam redirect stdout ./UpdateGroupsWithAEMFalseToAEMTrue.txt redirect stderr stdout update group csvkmd ExternalMembersGroupsWithAEMFalse.csv keyfield group matchfield update x allowexternalmembers true
|
||||
```
|
||||
|
||||
Update all groups to allowExternalMembers = True
|
||||
**Caution, be sure that this is what you want**
|
||||
```
|
||||
gam redirect stdout ./UpdateGroupsWithAEMFalseToEMTrue.txt redirect stderr stdout update group csvkmd ExternalMembersGroupsWithAEMFalse.csv keyfield group allowexternalmembers true
|
||||
```
|
||||
|
||||
Delete selected external members from groups with allowExternalMembers = False
|
||||
* Add a column labelled `delete` to ExternalMembersGroupsWithAEMFalse.csv
|
||||
* For exernal members to be deleted, add an x to the `delete` column
|
||||
* The `preview` option let's you verify what external members are to be deleted, remove it to do the deletions
|
||||
```
|
||||
gam redirect csv ./DeletedMembersFromGroupsWithAEMFalse.csv redirect stdout ./DeleteMembersFromGroupsWithAEMFalse.txt redirect stderr stdout update group csvkmd ExternalMembersGroupsWithAEMFalse.csv keyfield group matchfield delete x datafield email delete preview actioncsv csvdata email
|
||||
```
|
||||
|
||||
Delete all external members from groups with allowExternalMembers = False
|
||||
**Caution, be sure that this is what you want**
|
||||
* The `preview` option let's you verify what external members are to be deleted, remove it to do the deletions
|
||||
```
|
||||
gam redirect csv ./DeletedMembersFromGroupsWithAEMFalse.csv redirect stdout ./DeleteMembersFromGroupsWithAEMFalse.txt redirect stderr stdout update group csvkmd ExternalMembersGroupsWithAEMFalse.csv keyfield group datafield email delete preview actioncsv csvdata email
|
||||
```
|
||||
|
||||
|
||||
@@ -319,6 +319,8 @@ Data fields identified in a `csvkmd` argument.
|
||||
(select <ProjectIDList> | <FileSelector> | <CSVFileSelector>)
|
||||
<PrinterIDEntity> ::=
|
||||
<PrinterIDList> | <FileSelector> | <CSVFileSelector>
|
||||
<QueryDriveFile> :: = <String> See: https://developers.google.com/workspace/drive/api/guides/search-files
|
||||
<QuerySharedDrive> ::= <String> See: https://developers.google.com/workspace/drive/api/guides/search-shareddrives
|
||||
<RecipientEntity> ::=
|
||||
<EmailAddressEntity> | (select <UserTypeEntity>)
|
||||
<ResourceEntity> ::=
|
||||
@@ -329,22 +331,22 @@ Data fields identified in a `csvkmd` argument.
|
||||
<SerialNumberList> | <FileSelector> | <CSVFileSelector>
|
||||
<SharedDriveIDEntity> ::=
|
||||
<DriveFileItem> |
|
||||
(teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
(shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::=
|
||||
(teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
(shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
<SharedDriveNameEntity>
|
||||
<SharedDriveAdminQueryEntity> ::=
|
||||
(teamdriveadminquery <QueryTeamDrive>) | (teamdriveadminquery:<QueryTeamDrive>)
|
||||
(shareddriveadminquery <QuerySharedDrive>) | (shareddriveadminquery:<QuerySharedDrive>)
|
||||
<SharedDriveEntityAdmin> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
<SharedDriveNameEntity>|
|
||||
<SharedDriveAdminQueryEntity>
|
||||
<SharedDriveFileNameEntity> ::=
|
||||
(teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
(shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
<SharedDriveFileQueryEntity> ::=
|
||||
(teamdrivequery <QueryDriveFile>) | (teamdrivequery:<QueryDriveFile>)
|
||||
(shareddrivequery <QueryDriveFile>) | (shareddrivequery:<QueryDriveFile>)
|
||||
<SharedDriveFileQueryShortcut> ::=
|
||||
all_files | all_folders | all_google_files | all_non_google_files | all_items
|
||||
<SiteACLScopeEntity> ::=
|
||||
|
||||
@@ -114,9 +114,9 @@ ous_and_children_na_ns
|
||||
(anydrivefilename <DriveFileName>)|(anydrivefilename:<DriveFileName>)
|
||||
<SharedDriveID> ::= <String>
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveIDEntity> ::= (teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::= (teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
<SharedDriveIDEntity> ::= (shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::= (shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
<SharedDriveNameEntity>
|
||||
@@ -327,7 +327,7 @@ Use these options to select users for GAM commands.
|
||||
* `ou_arch` - Archived users
|
||||
* `ou_ns` - Non-suspended users
|
||||
* `ou_susp` - Suspended users
|
||||
* `ou_na_ns` - Non-archived and nn-suspended users
|
||||
* `ou_na_ns` - Non-archived and non-suspended users
|
||||
|
||||
## Users in the Organization Unit `<OrgUnitItem>` and all of its sub Organization Units
|
||||
* `ou_and_children|ou_and_children_na|ou_and_children_arch|ou_and_children_ns|ou_and_children_susp|ou_and_children_na_ns <OrgUnitItem>`
|
||||
@@ -336,7 +336,7 @@ Use these options to select users for GAM commands.
|
||||
* `ou_and_children_arch` - Archived users
|
||||
* `ou_and_children_ns` - Non-suspended users
|
||||
* `ou_and_children_susp` - Suspended users
|
||||
* `ou_and_children_na_ns` - Non-archived and nn-suspended users
|
||||
* `ou_and_children_na_ns` - Non-archived and non-suspended users
|
||||
|
||||
## Users directly in the Organization Units `<OrgUnitList>`
|
||||
* `ous|ous_na|ous_arch|ous_ns|ous_susp|ous_na_ns <OrgUnitList>` - Users directly in the Organization Units `<OrgUnitList>`
|
||||
@@ -345,7 +345,7 @@ Use these options to select users for GAM commands.
|
||||
* `ous_arch` - Archived users
|
||||
* `ous_ns` - Non-suspended users
|
||||
* `ous_susp` - Suspended users
|
||||
* `ous_na_ns` - Non-archived and nn-suspended users
|
||||
* `ous_na_ns` - Non-archived and non-suspended users
|
||||
|
||||
`<OrgUnitList>` may require special quoting based on whether the OUs contain spaces, commas or single quotes.
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
<DriveFolderID> ::= <String>
|
||||
<DriveFolderIDList> ::= "<DriveFolderID>(,<DriveFolderID>)*"
|
||||
<DriveFolderName> ::= <String>
|
||||
<QueryDriveFile> :: = <String> See: https://developers.google.com/drive/api/v3/search-files
|
||||
<QueryDriveFile> :: = <String> See: https://developers.google.com/workspace/drive/api/guides/search-files
|
||||
<QuerySharedDrive> ::= <String> See: https://developers.google.com/workspace/drive/api/guides/search-shareddrives
|
||||
<DriveFileQueryEntity> ::=
|
||||
(query <QueryDriveFile>) | (query:<QueryDriveFile>)
|
||||
<DriveFileQueryShortcut> ::=
|
||||
@@ -90,15 +91,15 @@
|
||||
|
||||
<SharedDriveID> ::= <String>
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveIDEntity> ::= (teamdriveid <SharedDriveID>) | (teamdriveid:<SharedDriveID>)
|
||||
<SharedDriveNameEntity> ::= (teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
<SharedDriveIDEntity> ::= (shareddriveid <SharedDriveID>) | (shareddriveid:<SharedDriveID>)
|
||||
<SharedDriveNameEntity> ::= (shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
<SharedDriveNameEntity>
|
||||
<SharedDriveAdminQueryEntity> ::=
|
||||
(teamdriveadminquery <QueryTeamDrive>) | (teamdriveadminquery:<QueryTeamDrive>)
|
||||
(shareddriveadminquery <QuerySharedDrive>) | (shareddriveadminquery:<QuerySharedDrive>)
|
||||
<SharedDriveFileQueryEntity> ::=
|
||||
(query <QueryDriveFile>) | (query:<QueryDriveFile>)
|
||||
<SharedDriveFileQueryShortcut> ::=
|
||||
@@ -335,13 +336,13 @@ Select a Shared Drive file by giving its unique ID.
|
||||
```
|
||||
<SharedDriveIDEntity> ::=
|
||||
<DriveFileItem> |
|
||||
(teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
(shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
```
|
||||
### Examples
|
||||
```
|
||||
gam user testuser show fileinfo 1234ABCD
|
||||
gam user testuser show fileinfo id 1234ABCD
|
||||
gam user testuser show fileinfo teamdriveid 1234ABCD
|
||||
gam user testuser show fileinfo shareddriveid 1234ABCD
|
||||
```
|
||||
## Select Shared Drive file by name
|
||||
If you have the name, a search must be performed to find the ID that matches the name.
|
||||
@@ -350,16 +351,16 @@ You must specify the Shared Drive, either by ID or name, and the name of the fil
|
||||
Remember, searching for a file by name may return several file IDs if you have multiple files with the same name.
|
||||
```
|
||||
<SharedDriveIDEntity> ::=
|
||||
(teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
(shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::=
|
||||
(teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
(shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::=
|
||||
(teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
(shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
```
|
||||
### Examples
|
||||
```
|
||||
gam user testuser show fileinfo teamdriveid 1234ABCD teamdrivefilename "Test File"
|
||||
gam user testuser show fileinfo teamdrive "Shared Drive 1" teamdrivefilename "Test File"
|
||||
gam user testuser show fileinfo shareddriveid 1234ABCD shareddrivefilename "Test File"
|
||||
gam user testuser show fileinfo shareddrive "Shared Drive 1" shareddrivefilename "Test File"
|
||||
```
|
||||
## Select Shared Drive file by query
|
||||
You can use a query to find a file ID. You perform the query on all Shared Drives or a specific Shared Drive.
|
||||
@@ -367,7 +368,7 @@ You can use a query to find a file ID. You perform the query on all Shared Drive
|
||||
See: [Drive Query](https://developers.google.com/drive/api/v3/search-files)
|
||||
```
|
||||
<SharedDriveFileQueryEntity> ::=
|
||||
(teamdrivequery <QueryDriveFile>) | (teamdrivequery:<QueryDriveFile>)
|
||||
(shareddrivequery <QueryDriveFile>) | (shareddrivequery:<QueryDriveFile>)
|
||||
<SharedDriveFileQueryShortcut> ::=
|
||||
all_files | all_folders | all_google_files | all_non_google_files | all_items
|
||||
```
|
||||
@@ -380,32 +381,32 @@ Keyword to query mappings for `<DriveFileQueryShortcut>`:
|
||||
|
||||
### Examples
|
||||
```
|
||||
gam user testuser show fileinfo teamdrivequery "name='Test File'"
|
||||
gam user testuser show fileinfo teamdriveid 1234ABCD teamdrivequery "name='Test File'"
|
||||
gam user testuser show fileinfo teamdrive teamdrive "Shared Drive 1" teamdrivequery "name='Test File'"
|
||||
gam user testuser show fileinfo teamdriveid 1234ABCD all_non_google_files
|
||||
gam user testuser show fileinfo shareddrivequery "name='Test File'"
|
||||
gam user testuser show fileinfo shareddriveid 1234ABCD shareddrivequery "name='Test File'"
|
||||
gam user testuser show fileinfo shareddrive shareddrive "Shared Drive 1" shareddrivequery "name='Test File'"
|
||||
gam user testuser show fileinfo shareddriveid 1234ABCD all_non_google_files
|
||||
```
|
||||
## Select root folder of a Shared Drive by ID
|
||||
The root folder of a Shared Drive is a folder, you select it by giving its unique ID.
|
||||
```
|
||||
<SharedDriveIDEntity> ::=
|
||||
<DriveFileItem> |
|
||||
(teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
(shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
```
|
||||
### Examples
|
||||
```
|
||||
gam user testuser show fileinfo 1234ABCD
|
||||
gam user testuser show fileinfo teamdriveid 1234ABCD
|
||||
gam user testuser show fileinfo shareddriveid 1234ABCD
|
||||
|
||||
```
|
||||
## Select root folder of a Shared Drive by name
|
||||
If you have a Shared Drive name, a search must be performed to find the ID that matches the name.
|
||||
```
|
||||
<SharedDriveNameEntity> ::=
|
||||
(teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
(shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
```
|
||||
### Examples
|
||||
```
|
||||
gam user testuser show fileinfo teamdrive "Shared Drive 1"
|
||||
gam user testuser show fileinfo shareddrive "Shared Drive 1"
|
||||
|
||||
```
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
(anydrivefilename <DriveFileName>) | (anydrivefilename:<DriveFileName>)
|
||||
<SharedDriveIDEntity> ::=
|
||||
<DriveFileItem> |
|
||||
(teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
(shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveNameEntity> ::=
|
||||
(teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
(shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
<SharedDriveNameEntity>
|
||||
<SharedDriveFileNameEntity> ::=
|
||||
(teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
(shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
```
|
||||
|
||||
@@ -10,6 +10,169 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
|
||||
|
||||
See [Downloads-Installs-GAM7](https://github.com/GAM-team/GAM/wiki/Downloads-Installs) for Windows or other options, including manual installation
|
||||
|
||||
### 7.34.07
|
||||
|
||||
Added the following command to create a guest user.
|
||||
* See: https://support.google.com/a/answer/16558545
|
||||
```
|
||||
gam create guestuser <EmailAddress>
|
||||
```
|
||||
|
||||
Added the following items to `<UserFieldName>`:
|
||||
* `guestaccountinfo` - Additional guest-related metadata fields
|
||||
* `isguestuser` - Indicates if the inserted user is a guest
|
||||
|
||||
### 7.34.06
|
||||
|
||||
Added option `copyfolderpermissions [<Boolean>]` to `gam <UserTypeEntity> copy|move drivefile`.
|
||||
|
||||
When `copyfolderpermissions false` is specified, no folder permissions are copied; this simplifies
|
||||
disabling all folder permission copying.
|
||||
|
||||
When not specified or `copyfolderpermissions [true]` is specified, folder permissions are copied based on the following options:
|
||||
```
|
||||
copymergewithparentfolderpermissions [<Boolean>]
|
||||
copymergedtopfolderpermissions [<Boolean>]
|
||||
copytopfolderpermissions [<Boolean>]
|
||||
copytopfolderiheritedpermissions [<Boolean>]
|
||||
copytopfoldernoniheritedpermissions never|always|syncallfolders|syncupdatedfolders
|
||||
copymergedsubfolderpermissions [<Boolean>]
|
||||
copysubfolderpermissions [<Boolean>]
|
||||
copysubfolderinheritedpermissions [<Boolean>]
|
||||
copysubfoldernoniheritedpermissions never|always|syncallfolders|syncupdatedfolders
|
||||
```
|
||||
|
||||
### 7.34.05
|
||||
|
||||
Updated `gam report <ActivityApplictionName>` to perform a reverse chronological sort
|
||||
on all rows across multiple users and/or event names; this is consistent with the behavior
|
||||
in the Admin console. Use option `notimesort` to suppress this sort.
|
||||
|
||||
### 7.34.04
|
||||
|
||||
Updated `gam <UserTypeEntity> create drivefileacl <DriveFileEntity> user <UserItem> role owner` to better
|
||||
handle the case where the current owner of a file is suspended. Previously, the command was displayed as an error
|
||||
even though the ownership was changed.
|
||||
```
|
||||
gam user currentowner@domain.com add drivefileacl <DriveFileID> user newowner@domain.com role owner
|
||||
User: currentowner@domain.com, Add 1 Drive File/Folder ACL
|
||||
User: currentowner@domain.com, Drive File/Folder ID: <DriveFileID>, Permission ID: newowner@domain.com, Add Failed: Sorry, the items were successfully shared but emails could not be sent to newowner@domain.com.
|
||||
```
|
||||
|
||||
Now the command is displayed as a success with a note indicating that the ownership change email was not sent.
|
||||
```
|
||||
gam user currentowner@domain.com add drivefileacl <DriveFileID> user newowner@domain.com role owner
|
||||
User: currentowner@domain.com, Add 1 Drive File/Folder ACL
|
||||
User: currentowner@domain.com, Drive File/Folder ID: <DriveFileID>, Permission ID: newowner@domain.com, Added: Sorry, the items were successfully shared but emails could not be sent to newowner@domain.com.
|
||||
New Owner
|
||||
id: 10834698115409747890
|
||||
type: user
|
||||
emailAddress: newowner@domain.com
|
||||
domain: domain.com
|
||||
role: owner
|
||||
permissionDetails:
|
||||
role: writer
|
||||
type: file
|
||||
inherited: True
|
||||
inheritedFrom: Unknown
|
||||
role: owner
|
||||
type: file
|
||||
inherited: False
|
||||
deleted: False
|
||||
pendingOwner: False
|
||||
```
|
||||
|
||||
### 7.34.03
|
||||
|
||||
Updated to Python 3.14.3
|
||||
|
||||
Updated Cryptography to 46.0.5
|
||||
|
||||
Updated `gam course <CourseID> create student|teacher <EmailAddress>` error message when
|
||||
`<EmailAddress>` is not in a trusted domain to remove suggestion about creating an invitation.
|
||||
|
||||
|
||||
### 7.34.02
|
||||
|
||||
Updated GAM to prevent errors like the following:
|
||||
```
|
||||
ERROR: Unable to find the server at oauth2.googleapis.com
|
||||
ERROR: Unable to find the server at gmail.googleapis.com
|
||||
```
|
||||
If you experience any unexpected errors, post a message to:
|
||||
* The GAM Discussion Forum (google-apps-manager@googlegroups.com)
|
||||
* The GAM Public Chat Room (https://chat.google.com/app/chat/AAAA4BULhWo)
|
||||
|
||||
### 7.34.01
|
||||
|
||||
Updated `gam create|update adminrole` to handle the following errors:
|
||||
```
|
||||
ERROR: 400: invalid - Invalid Role privileges
|
||||
ERROR: 400: required - Required parameter: [resource.privileges[n].service_id]
|
||||
```
|
||||
|
||||
### 7.34.00
|
||||
|
||||
Added variable `csv_output_header_required` to `gam.cfg` that is a comma separated list of `<Strings>`
|
||||
that are required to be in the list of column headers in the CSV file written by a gam print command.
|
||||
This will typically be used to specify headers that are required in subsequent commands that process
|
||||
the CSV file even if the API didn't return any data for those columns.
|
||||
|
||||
Updated the following commands to not require the `Directory API - Domains` scope
|
||||
unless the `internal` or `external` options are used to request the member category.
|
||||
```
|
||||
gam info|print groups
|
||||
gam print|show group-members
|
||||
gam info|print cigroups
|
||||
gam print|show cigroup-members
|
||||
gam <UserTypeEntity> print|show filesharecounts
|
||||
```
|
||||
|
||||
### 7.33.03
|
||||
|
||||
Fixed bug in `gam [<UserTypeEntity>] sendemail ... from <EmailAddress> replyto <EmailAddress>`
|
||||
where an `<EmailAddress>` of the form `Text <user@domain.com>` had the `Text` removed.
|
||||
|
||||
### 7.33.02
|
||||
|
||||
Added `hideinvitationssetting` to `<UserCalendarSettingsField>` used by
|
||||
`gam <UserTypeEntity> print|show calsettings`.
|
||||
|
||||
### 7.33.01
|
||||
|
||||
Added option `shownopolicy` to `gam print chromepolicies` that will display output like the following
|
||||
if no policies apply to the selected OU or group.
|
||||
```
|
||||
gam print chromepolicies ou /Test appid chrome:emidddocikgklceeeifefomdnbkldhng namespace chrome.users.apps shownopolicy
|
||||
Getting all Chrome Policies that match query (chrome.users.apps.*) for /Test
|
||||
Got 0 Chrome Policies that matched query (chrome.users.apps.*) for /Test...
|
||||
name,orgUnitPath,parentOrgUnitPath,direct,appId
|
||||
noPolicy,/Test,/,False,chrome:emidddocikgklceeeifefomdnbkldhng
|
||||
```
|
||||
|
||||
### 7.33.00
|
||||
|
||||
Added variable `developer_preview_apis` to `gam.cfg` that is a comma separated list of APIs requiring a Developer Preview key.
|
||||
Currently, `chat` is the only API that requires a Developer Preview key; it is required for the User Sections commands.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#introduction
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#manage-chat-user-sections
|
||||
|
||||
### 7.32.07
|
||||
|
||||
Added option `includepermissionsforview published` to `gam <UserTypeEntity> print filelist` and
|
||||
`gam <UserTypeEntity> show fileinfo`. From the Drive API documentation:
|
||||
```
|
||||
Specifies which additional view's permissions to include in the response. Only published is supported.
|
||||
```
|
||||
|
||||
### 7.32.06
|
||||
|
||||
Added options to `gam <UserTypeEntity> copy drivefile ... copysubfiles` to limit copying
|
||||
to files whose `modifiedTime` meets specified requirements.
|
||||
* `start|starttime <Date>|<Time>` - If specified, `modifiedTime` must be >= the value
|
||||
* `end|endtime <Date>|<Time>` - If specified, `modifiedTime` must be <= the value
|
||||
* `range <Date>|<Time> <Date>|<Time>` - first value <= `modifiedTime` <= second value
|
||||
|
||||
### 7.32.05
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> print messages|threads ... headers <SMTPHeaderList>` where
|
||||
|
||||
@@ -9,30 +9,30 @@ and all necessary authentications.
|
||||
|
||||
## Linux and MacOS and Google Cloud Shell
|
||||
|
||||
In these examples, your Google Super admin is shown as admin@domain.com; replace with the
|
||||
In these examples, your Google Super admin is shown as gamteam@domain.com; replace with the
|
||||
actual email adddress.
|
||||
|
||||
In these examples, the user home folder is shown as /Users/admin; adjust according to your
|
||||
In these examples, the user home folder is shown as /Users/gamteam; adjust according to your
|
||||
specific situation; e.g., /home/administrator.
|
||||
|
||||
This example assumes that GAM7 has been installed in /Users/admin/bin/gam7.
|
||||
This example assumes that GAM7 has been installed in /Users/gamteam/bin/gam7.
|
||||
If you've installed GAM7 in another directory, substitute that value in the directions.
|
||||
|
||||
### Set a configuration directory
|
||||
|
||||
The default GAM configuration directory is /Users/admin/.gam; for more flexibility you
|
||||
The default GAM configuration directory is /Users/gamteam/.gam; for more flexibility you
|
||||
probably want to select a non-hidden location. This example assumes that the GAM
|
||||
configuration directory will be /Users/admin/GAMConfig; If you've chosen another directory,
|
||||
configuration directory will be /Users/gamteam/GAMConfig; If you've chosen another directory,
|
||||
substitute that value in the directions.
|
||||
|
||||
Make the directory:
|
||||
```
|
||||
admin@server:/Users/admin$ mkdir -p /Users/admin/GAMConfig
|
||||
gamteam@server:/Users/gamteam$ mkdir -p /Users/gamteam/GAMConfig
|
||||
```
|
||||
|
||||
Add the following line:
|
||||
```
|
||||
export GAMCFGDIR="/Users/admin/GAMConfig"
|
||||
export GAMCFGDIR="/Users/gamteam/GAMConfig"
|
||||
```
|
||||
to one of these files based on your shell:
|
||||
```
|
||||
@@ -44,34 +44,34 @@ to one of these files based on your shell:
|
||||
|
||||
Issue the following command replacing `<Filename>` with the name of the file you edited:
|
||||
```
|
||||
admin@server:/Users/admin$ source <Filename>
|
||||
gamteam@server:/Users/gamteam$ source <Filename>
|
||||
```
|
||||
|
||||
You need to make sure the GAM configuration directory actually exists. Test that like this:
|
||||
```
|
||||
admin@server:/Users/admin$ ls -l $GAMCFGDIR
|
||||
gamteam@server:/Users/gamteam$ ls -l $GAMCFGDIR
|
||||
```
|
||||
|
||||
### Set a working directory
|
||||
|
||||
You should establish a GAM working directory; you will store your GAM related
|
||||
data in this folder and execute GAM commands from this folder. You should not use
|
||||
/Users/admin/bin/gam7 or /Users/admin/GAMConfig for this purpose.
|
||||
This example assumes that the GAM working directory will be /Users/admin/GAMWork; If you've chosen
|
||||
/Users/gamteam/bin/gam7 or /Users/gamteam/GAMConfig for this purpose.
|
||||
This example assumes that the GAM working directory will be /Users/gamteam/GAMWork; If you've chosen
|
||||
another directory, substitute that value in the directions.
|
||||
|
||||
Make the directory:
|
||||
```
|
||||
admin@server:/Users/admin$ mkdir -p /Users/admin/GAMWork
|
||||
gamteam@server:/Users/gamteam$ mkdir -p /Users/gamteam/GAMWork
|
||||
```
|
||||
|
||||
### Set an alias
|
||||
You should set an alias to point to /Users/admin/bin/gam7/gam so you can operate from the /Users/admin/GAMWork directory.
|
||||
You should set an alias to point to /Users/gamteam/bin/gam7/gam so you can operate from the /Users/gamteam/GAMWork directory.
|
||||
Aliases aren't available in scripts, so you may want to set a symlink instead, see below.
|
||||
|
||||
Add the following line:
|
||||
```
|
||||
alias gam="/Users/admin/bin/gam7/gam"
|
||||
alias gam="/Users/gamteam/bin/gam7/gam"
|
||||
```
|
||||
to one of these files based on your shell:
|
||||
```
|
||||
@@ -84,48 +84,48 @@ to one of these files based on your shell:
|
||||
|
||||
Issue the following command replacing `<Filename>` with the name of the file you edited:
|
||||
```
|
||||
admin@server:/Users/admin$ source <Filename>
|
||||
gamteam@server:/Users/gamteam$ source <Filename>
|
||||
```
|
||||
|
||||
### Set a symlink
|
||||
Set a symlink in `/usr/local/bin` (or some other location on $PATH) to point to GAM.
|
||||
```
|
||||
admin@server:/Users/admin$ ln -s "/Users/admin/bin/gam7/gam" /usr/local/bin/gam
|
||||
gamteam@server:/Users/gamteam$ ln -s "/Users/gamteam/bin/gam7/gam" /usr/local/bin/gam
|
||||
```
|
||||
|
||||
### Initialize GAM7; this should be the first GAM7 command executed.
|
||||
```
|
||||
admin@server:/Users/admin$ gam config drive_dir /Users/admin/GAMWork verify
|
||||
Created: /Users/admin/GAMConfig
|
||||
Created: /Users/admin/GAMConfig/gamcache
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Initialized
|
||||
gamteam@server:/Users/gamteam$ gam config drive_dir /Users/gamteam/GAMWork verify
|
||||
Created: /Users/gamteam/GAMConfig
|
||||
Created: /Users/gamteam/GAMConfig/gamcache
|
||||
Config File: /Users/gamteam/GAMConfig/gam.cfg, Initialized
|
||||
Section: DEFAULT
|
||||
...
|
||||
cache_dir = /Users/admin/GAMConfig/gamcache
|
||||
cache_dir = /Users/gamteam/GAMConfig/gamcache
|
||||
...
|
||||
config_dir = /Users/admin/GAMConfig
|
||||
config_dir = /Users/gamteam/GAMConfig
|
||||
...
|
||||
drive_dir = /Users/admin/GAMWork
|
||||
drive_dir = /Users/gamteam/GAMWork
|
||||
...
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
### Verify initialization, this was a successful installation.
|
||||
```
|
||||
admin@server:/Users/admin$ ls -l $GAMCFGDIR
|
||||
gamteam@server:/Users/gamteam$ ls -l $GAMCFGDIR
|
||||
total 48
|
||||
-rw-r-----+ 1 admin staff 1069 Mar 3 09:23 gam.cfg
|
||||
drwxr-x---+ 2 admin staff 68 Mar 3 09:23 gamcache
|
||||
-rw-rw-rw-+ 1 admin staff 0 Mar 3 09:23 oauth2.txt.lock
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
### Create your project with local browser
|
||||
```
|
||||
admin@server:/Users/admin$ gam create project
|
||||
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Item: client_secrets_json, Value: /Users/admin/GAMConfig/client_secrets.json, Not Found
|
||||
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Item: oauth2service_json, Value: /Users/admin/GAMConfig/oauth2service.json, Not Found
|
||||
gamteam@server:/Users/gamteam$ gam create project
|
||||
WARNING: Config File: /Users/gamteam/GAMConfig/gam.cfg, Item: client_secrets_json, Value: /Users/gamteam/GAMConfig/client_secrets.json, Not Found
|
||||
WARNING: Config File: /Users/gamteam/GAMConfig/gam.cfg, Item: oauth2service_json, Value: /Users/gamteam/GAMConfig/oauth2service.json, Not Found
|
||||
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) admin@domain.com
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) gamteam@domain.com
|
||||
|
||||
Your browser has been opened to visit:
|
||||
|
||||
@@ -167,7 +167,7 @@ Project: gam-project-abc-def-ghi, Service Account: gam-project-abc-def-ghi@gam-p
|
||||
Project: gam-project-abc-def-ghi, Service Account: gam-project-abc-def-ghi@gam-project-abc-def-ghi.iam.gserviceaccount.com, Extracting public certificate
|
||||
Project: gam-project-abc-def-ghi, Service Account: gam-project-abc-def-ghi@gam-project-abc-def-ghi.iam.gserviceaccount.com, Done generating private key and public certificate
|
||||
Project: gam-project-abc-def-ghi, Service Account: gam-project-abc-def-ghi@gam-project-abc-def-ghi.iam.gserviceaccount.com, Service Account Key: SVCACCTKEY, Uploaded
|
||||
Service Account OAuth2 File: /Users/admin/GAMConfig/oauth2service.json, Service Account Key: SVCACCTKEY, Updated
|
||||
Service Account OAuth2 File: /Users/gamteam/GAMConfig/oauth2service.json, Service Account Key: SVCACCTKEY, Updated
|
||||
Project: gam-project-abc-def-ghi, Service Account: gam-project-abc-def-ghi@gam-project-abc-def-ghi.iam.gserviceaccount.com, Has rights to rotate own private key
|
||||
Please go to:
|
||||
|
||||
@@ -185,16 +185,16 @@ Enter your Client Secret: CLIENTSECRET
|
||||
6. Go back to your browser and click OK to close the "OAuth client" popup if it's still open.
|
||||
That's it! Your GAM Project is created and ready to use.
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
### Create your project without local browser (Google Cloud Shell for instance)
|
||||
```
|
||||
admin@server:/Users/admin$ gam config no_browser true save
|
||||
admin@server:/Users/admin$ gam create project
|
||||
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Item: client_secrets_json, Value: /Users/admin/GAMConfig/client_secrets.json, Not Found
|
||||
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Item: oauth2service_json, Value: /Users/admin/GAMConfig/oauth2service.json, Not Found
|
||||
gamteam@server:/Users/gamteam$ gam config no_browser true save
|
||||
gamteam@server:/Users/gamteam$ gam create project
|
||||
WARNING: Config File: /Users/gamteam/GAMConfig/gam.cfg, Item: client_secrets_json, Value: /Users/gamteam/GAMConfig/client_secrets.json, Not Found
|
||||
WARNING: Config File: /Users/gamteam/GAMConfig/gam.cfg, Item: oauth2service_json, Value: /Users/gamteam/GAMConfig/oauth2service.json, Not Found
|
||||
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) admin@domain.com
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) gamteam@domain.com
|
||||
|
||||
Go to the following link in a browser on other computer:
|
||||
|
||||
@@ -235,7 +235,7 @@ Project: gam-project-abc-def-ghi, Service Account: gam-project-abc-def-ghi@gam-p
|
||||
Project: gam-project-abc-def-ghi, Service Account: gam-project-abc-def-ghi@gam-project-abc-def-ghi.iam.gserviceaccount.com, Extracting public certificate
|
||||
Project: gam-project-abc-def-ghi, Service Account: gam-project-abc-def-ghi@gam-project-abc-def-ghi.iam.gserviceaccount.com, Done generating private key and public certificate
|
||||
Project: gam-project-abc-def-ghi, Service Account: gam-project-abc-def-ghi@gam-project-abc-def-ghi.iam.gserviceaccount.com, Service Account Key: SVCACCTKEY, Uploaded
|
||||
Service Account OAuth2 File: /Users/admin/GAMConfig/oauth2service.json, Service Account Key: SVCACCTKEY, Updated
|
||||
Service Account OAuth2 File: /Users/gamteam/GAMConfig/oauth2service.json, Service Account Key: SVCACCTKEY, Updated
|
||||
Project: gam-project-abc-def-ghi, Service Account: gam-project-abc-def-ghi@gam-project-abc-def-ghi.iam.gserviceaccount.com, Has rights to rotate own private key
|
||||
Please go to:
|
||||
|
||||
@@ -253,7 +253,7 @@ Enter your Client Secret: CLIENTSECRET
|
||||
6. Go back to your browser and click OK to close the "OAuth client" popup if it's still open.
|
||||
That's it! Your GAM Project is created and ready to use.
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
### Enable GAM7 client access
|
||||
|
||||
@@ -261,7 +261,7 @@ You select a list of scopes, GAM uses a browser to get final authorization from
|
||||
writes the credentials into the file oauth2.txt.
|
||||
|
||||
```
|
||||
admin@server:/Users/admin$ gam oauth create
|
||||
gamteam@server:/Users/gamteam$ gam oauth create
|
||||
|
||||
[*] 0) Calendar API (supports readonly)
|
||||
[*] 1) Chrome Browser Cloud Management API (supports readonly)
|
||||
@@ -328,7 +328,7 @@ Continue to authorization by entering a 'c'
|
||||
|
||||
Please enter 0-50[a|r] or s|u|e|c: c
|
||||
|
||||
Enter your Google Workspace admin email address? admin@domain.com
|
||||
Enter your Google Workspace admin email address? gamteam@domain.com
|
||||
|
||||
Go to the following link in a browser on this computer or on another computer:
|
||||
|
||||
@@ -340,16 +340,16 @@ click the Allow button, paste "Unable to connect" URL from other computer (only
|
||||
Enter verification code or paste "Unable to connect" URL from other computer (only URL data up to &scope required):
|
||||
|
||||
The authentication flow has completed.
|
||||
Client OAuth2 File: /Users/admin/GAMConfig/oauth2.txt, Created
|
||||
Client OAuth2 File: /Users/gamteam/GAMConfig/oauth2.txt, Created
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
|
||||
If clicking on the link in the instructions does not work (i.e. you get a 404 or 400 error message, instead of something about 'unable to connect') the URL in the link is too long. Most likely, you have selected all scopes. Try again with fewer scopes until it works. (there is no harm in repeatedly trying)
|
||||
|
||||
### Enable GAM7 service account access.
|
||||
```
|
||||
admin@server:/Users/admin$ gam user admin@domain.com update serviceaccount
|
||||
gamteam@server:/Users/gamteam$ gam user gamteam@domain.com update serviceaccount
|
||||
[*] 0) AlertCenter API
|
||||
[*] 1) Analytics API - read only
|
||||
[*] 2) Analytics Admin API - read only
|
||||
@@ -413,7 +413,7 @@ Service Account Private Key Authentication
|
||||
Authentication PASS
|
||||
Service Account Private Key age; Google recommends rotating keys on a routine basis
|
||||
Service Account Private Key age: 1 day WARN
|
||||
Domain-wide Delegation authentication:, User: admin@domain.com, Scopes: 38
|
||||
Domain-wide Delegation authentication:, User: gamteam@domain.com, Scopes: 38
|
||||
https://mail.google.com/ FAIL (1/38)
|
||||
https://sites.google.com/feeds FAIL (2/38)
|
||||
https://www.googleapis.com/auth/analytics.readonly FAIL (3/38)
|
||||
@@ -464,7 +464,7 @@ Click AUTHORIZE
|
||||
When the box closes you're done
|
||||
After authorizing it may take some time for this test to pass so wait a few moments and then try this command again.
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
The link shown in the error message should take you directly to the authorization screen.
|
||||
If not, make sure that you are logged in as a domain admin, then re-enter the link.
|
||||
@@ -474,14 +474,14 @@ If not, make sure that you are logged in as a domain admin, then re-enter the li
|
||||
Wait a moment and then perform the following command; it it still fails, wait a bit longer, it can sometimes take serveral minutes
|
||||
for the authorization to complete.
|
||||
```
|
||||
admin@server:/Users/admin$ gam user admin@domain.com check serviceaccount
|
||||
gamteam@server:/Users/gamteam$ gam user gamteam@domain.com check serviceaccount
|
||||
System time status
|
||||
Your system time differs from admin.googleapis.com by less than 1 second PASS
|
||||
Service Account Private Key Authentication
|
||||
Authentication PASS
|
||||
Service Account Private Key age; Google recommends rotating keys on a routine basis
|
||||
Service Account Private Key age: 1 day WARN
|
||||
Domain-wide Delegation authentication:, User: admin@domain.com, Scopes: 38
|
||||
Domain-wide Delegation authentication:, User: gamteam@domain.com, Scopes: 38
|
||||
https://mail.google.com/ PASS (1/38)
|
||||
https://sites.google.com/feeds PASS (2/38)
|
||||
https://www.googleapis.com/auth/analytics.readonly PASS (3/38)
|
||||
@@ -524,14 +524,14 @@ All scopes PASSED!
|
||||
|
||||
Service Account Client name: SVCACCTID is fully authorized.
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
### Update gam.cfg with some basic values
|
||||
* `customer_id` - Having this data keeps Gam from having to make extra API calls
|
||||
* `domain` - This allows you to omit the domain portion of email addresses
|
||||
* `timezone local` - Gam will convert all UTC times to your local timezone
|
||||
```
|
||||
admin@server:/Users/admin$ gam info domain
|
||||
gamteam@server:/Users/gamteam$ gam info domain
|
||||
Customer ID: C01234567
|
||||
Primary Domain: domain.com
|
||||
Customer Creation Time: 2007-06-06T15:47:55.444Z
|
||||
@@ -539,8 +539,8 @@ Primary Domain Verified: True
|
||||
Default Language: en
|
||||
...
|
||||
|
||||
admin@server:/Users/admin$ gam config customer_id C01234567 domain domain.com timezone local save verify
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Saved
|
||||
gamteam@server:/Users/gamteam$ gam config customer_id C01234567 domain domain.com timezone local save verify
|
||||
Config File: /Users/gamteam/GAMConfig/gam.cfg, Saved
|
||||
Section: DEFAULT
|
||||
...
|
||||
customer_id = C01234567
|
||||
@@ -550,12 +550,12 @@ Section: DEFAULT
|
||||
timezone = local
|
||||
...
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
|
||||
## Windows
|
||||
|
||||
In these examples, your Google Super admin is shown as admin@domain.com; replace with the
|
||||
In these examples, your Google Super admin is shown as gamteam@domain.com; replace with the
|
||||
actual email adddress.
|
||||
|
||||
This example assumes that GAM7 has been installed in C:\GAM7; if you've installed
|
||||
@@ -645,7 +645,7 @@ C:\>gam create project
|
||||
WARNING: Config File: C:\GAMConfig\gam.cfg, Item: client_secrets_json, Value: C:\GAMConfig\client_secrets.json, Not Found
|
||||
WARNING: Config File: C:\GAMConfig\gam.cfg, Item: oauth2service_json, Value: C:\GAMConfig\oauth2service.json, Not Found
|
||||
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) admin@domain.com
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) gamteam@domain.com
|
||||
|
||||
Your browser has been opened to visit:
|
||||
|
||||
@@ -714,7 +714,7 @@ C:\>gam create project
|
||||
WARNING: Config File: C:\GAMConfig\gam.cfg, Item: client_secrets_json, Value: C:\GAMConfig\client_secrets.json, Not Found
|
||||
WARNING: Config File: C:\GAMConfig\gam.cfg, Item: oauth2service_json, Value: C:\GAMConfig\oauth2service.json, Not Found
|
||||
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) admin@domain.com
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) gamteam@domain.com
|
||||
|
||||
Go to the following link in a browser on other computer:
|
||||
|
||||
@@ -848,7 +848,7 @@ Continue to authorization by entering a 'c'
|
||||
|
||||
Please enter 0-50[a|r] or s|u|e|c: c
|
||||
|
||||
Enter your Google Workspace admin email address? admin@domain.com
|
||||
Enter your Google Workspace admin email address? gamteam@domain.com
|
||||
|
||||
Go to the following link in a browser on this computer or on another computer:
|
||||
|
||||
@@ -866,7 +866,7 @@ C:\>
|
||||
```
|
||||
### Enable GAM7 service account access.
|
||||
```
|
||||
C:\>gam user admin@domain.com update serviceaccount
|
||||
C:\>gam user gamteam@domain.com update serviceaccount
|
||||
[*] 0) AlertCenter API
|
||||
[*] 1) Analytics API - read only
|
||||
[*] 2) Analytics Admin API - read only
|
||||
@@ -930,7 +930,7 @@ Service Account Private Key Authentication
|
||||
Authentication PASS
|
||||
Service Account Private Key age; Google recommends rotating keys on a routine basis
|
||||
Service Account Private Key age: 1 day WARN
|
||||
Domain-wide Delegation authentication:, User: admin@domain.com, Scopes: 38
|
||||
Domain-wide Delegation authentication:, User: gamteam@domain.com, Scopes: 38
|
||||
https://mail.google.com/ FAIL (1/38)
|
||||
https://sites.google.com/feeds FAIL (2/38)
|
||||
https://www.googleapis.com/auth/analytics.readonly FAIL (3/38)
|
||||
@@ -991,14 +991,14 @@ If not, make sure that you are logged in as a domain admin, then re-enter the li
|
||||
Wait a moment and then perform the following command; it it still fails, wait a bit longer, it can sometimes take serveral minutes
|
||||
for the authorization to complete.
|
||||
```
|
||||
C:\>gam user admin@domain.com check serviceaccount
|
||||
C:\>gam user gamteam@domain.com check serviceaccount
|
||||
System time status
|
||||
Your system time differs from admin.googleapis.com by less than 1 second PASS
|
||||
Service Account Private Key Authentication
|
||||
Authentication PASS
|
||||
Service Account Private Key age; Google recommends rotating keys on a routine basis
|
||||
Service Account Private Key age: 1 day WARN
|
||||
Domain-wide Delegation authentication:, User: admin@domain.com, Scopes: 38
|
||||
Domain-wide Delegation authentication:, User: gamteam@domain.com, Scopes: 38
|
||||
https://mail.google.com/ PASS (1/38)
|
||||
https://sites.google.com/feeds PASS (2/38)
|
||||
https://www.googleapis.com/auth/analytics.readonly PASS (3/38)
|
||||
|
||||
@@ -10,30 +10,30 @@ and all necessary authentications.
|
||||
|
||||
## Linux and MacOS and Google Cloud Shell
|
||||
|
||||
In these examples, your Google Super admin is shown as admin@domain.com; replace with the
|
||||
In these examples, your Google Super admin is shown as gamteam@domain.com; replace with the
|
||||
actual email adddress.
|
||||
|
||||
In these examples, the user home folder is shown as /Users/admin; adjust according to your
|
||||
In these examples, the user home folder is shown as /Users/gamteam; adjust according to your
|
||||
specific situation; e.g., /home/administrator.
|
||||
|
||||
This example assumes that GAM7 has been installed in /Users/admin/bin/gam7.
|
||||
This example assumes that GAM7 has been installed in /Users/gamteam/bin/gam7.
|
||||
If you've installed GAM7 in another directory, substitute that value in the directions.
|
||||
|
||||
### Set a configuration directory
|
||||
|
||||
The default GAM configuration directory is /Users/admin/.gam; for more flexibility you
|
||||
The default GAM configuration directory is /Users/gamteam/.gam; for more flexibility you
|
||||
probably want to select a non-hidden location. This example assumes that the GAM
|
||||
configuration directory will be /Users/admin/GAMConfig; If you've chosen another directory,
|
||||
configuration directory will be /Users/gamteam/GAMConfig; If you've chosen another directory,
|
||||
substitute that value in the directions.
|
||||
|
||||
Make the directory:
|
||||
```
|
||||
admin@server:/Users/admin$ mkdir -p /Users/admin/GAMConfig
|
||||
gamteam@server:/Users/gamteam$ mkdir -p /Users/gamteam/GAMConfig
|
||||
```
|
||||
|
||||
Add the following line:
|
||||
```
|
||||
export GAMCFGDIR="/Users/admin/GAMConfig"
|
||||
export GAMCFGDIR="/Users/gamteam/GAMConfig"
|
||||
```
|
||||
to one of these files based on your shell:
|
||||
```
|
||||
@@ -45,34 +45,34 @@ to one of these files based on your shell:
|
||||
|
||||
Issue the following command replacing `<Filename>` with the name of the file you edited:
|
||||
```
|
||||
admin@server:/Users/admin$ source <Filename>
|
||||
gamteam@server:/Users/gamteam$ source <Filename>
|
||||
```
|
||||
|
||||
You need to make sure the GAM configuration directory actually exists. Test that like this:
|
||||
```
|
||||
admin@server:/Users/admin$ ls -l $GAMCFGDIR
|
||||
gamteam@server:/Users/gamteam$ ls -l $GAMCFGDIR
|
||||
```
|
||||
|
||||
### Set a working directory
|
||||
|
||||
You should establish a GAM working directory; you will store your GAM related
|
||||
data in this folder and execute GAM commands from this folder. You should not use
|
||||
/Users/admin/bin/gam7 or /Users/admin/GAMConfig for this purpose.
|
||||
This example assumes that the GAM working directory will be /Users/admin/GAMWork; If you've chosen
|
||||
/Users/gamteam/bin/gam7 or /Users/gamteam/GAMConfig for this purpose.
|
||||
This example assumes that the GAM working directory will be /Users/gamteam/GAMWork; If you've chosen
|
||||
another directory, substitute that value in the directions.
|
||||
|
||||
Make the directory:
|
||||
```
|
||||
admin@server:/Users/admin$ mkdir -p /Users/admin/GAMWork
|
||||
gamteam@server:/Users/gamteam$ mkdir -p /Users/gamteam/GAMWork
|
||||
```
|
||||
|
||||
### Set an alias
|
||||
You should set an alias to point to /Users/admin/bin/gam7/gam so you can operate from the /Users/admin/GAMWork directory.
|
||||
You should set an alias to point to /Users/gamteam/bin/gam7/gam so you can operate from the /Users/gamteam/GAMWork directory.
|
||||
Aliases aren't available in scripts, so you may want to set a symlink instead, see below.
|
||||
|
||||
Add the following line:
|
||||
```
|
||||
alias gam="/Users/admin/bin/gam7/gam"
|
||||
alias gam="/Users/gamteam/bin/gam7/gam"
|
||||
```
|
||||
to one of these files based on your shell:
|
||||
```
|
||||
@@ -85,62 +85,62 @@ to one of these files based on your shell:
|
||||
|
||||
If you already have an alias for legacy GAM but are no longer going to run it, delete these lines:
|
||||
```
|
||||
function gam() { "/Users/admin/bin/gam/gam" "$@" ; }"
|
||||
alias gam="/Users/admin/bin/gam/gam"
|
||||
function gam() { "/Users/gamteam/bin/gam/gam" "$@" ; }"
|
||||
alias gam="/Users/gamteam/bin/gam/gam"
|
||||
```
|
||||
|
||||
If you already have an alias for legacy GAM and want to run it and GAM7, give your old alias a different name:
|
||||
```
|
||||
function gamstd() { "/Users/admin/bin/gam/gam" "$@" ; }"
|
||||
alias gamstd="/Users/admin/bin/gam/gam"
|
||||
function gamstd() { "/Users/gamteam/bin/gam/gam" "$@" ; }"
|
||||
alias gamstd="/Users/gamteam/bin/gam/gam"
|
||||
```
|
||||
|
||||
Issue the following command replacing `<Filename>` with the name of the file you edited:
|
||||
```
|
||||
admin@server:/Users/admin$ source <Filename>
|
||||
gamteam@server:/Users/gamteam$ source <Filename>
|
||||
```
|
||||
|
||||
### Set a symlink
|
||||
Set a symlink in `/usr/local/bin` (or some other location on $PATH) to point to GAM.
|
||||
```
|
||||
admin@server:/Users/admin$ ln -s "/Users/admin/bin/gam7/gam" /usr/local/bin/gam
|
||||
gamteam@server:/Users/gamteam$ ln -s "/Users/gamteam/bin/gam7/gam" /usr/local/bin/gam
|
||||
```
|
||||
|
||||
Set environment variable OLDGAMPATH to point to the existing Gam directory; /Users/admin/bin/gam will be used in this example.
|
||||
Set environment variable OLDGAMPATH to point to the existing Gam directory; /Users/gamteam/bin/gam will be used in this example.
|
||||
If your existing Gam is in another directory, substitute that value in the directions.
|
||||
```
|
||||
admin@server:/Users/admin$ export OLDGAMPATH=/Users/admin/bin/gam
|
||||
gamteam@server:/Users/gamteam$ export OLDGAMPATH=/Users/gamteam/bin/gam
|
||||
```
|
||||
Verify that OLDGAMPATH points to the correct location.
|
||||
```
|
||||
admin@server:/Users/admin$ ls -l $OLDGAMPATH/*.json
|
||||
-rw-r-----@ 1 admin staff 553 Feb 26 10:39 /Users/admin/bin/gam/client_secrets.json
|
||||
-rw-r-----@ 1 admin staff 2377 Feb 26 10:39 /Users/admin/bin/gam/oauth2service.json
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$ ls -l $OLDGAMPATH/*.json
|
||||
-rw-r-----@ 1 admin staff 553 Feb 26 10:39 /Users/gamteam/bin/gam/client_secrets.json
|
||||
-rw-r-----@ 1 admin staff 2377 Feb 26 10:39 /Users/gamteam/bin/gam/oauth2service.json
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
### Initialize GAM7; this should be the first GAM7 command executed.
|
||||
```
|
||||
admin@server:/Users/admin$ gam config drive_dir /Users/admin/GAMWork verify
|
||||
Created: /Users/admin/GAMConfig
|
||||
Created: /Users/admin/GAMConfig/gamcache
|
||||
Copied: /Users/admin/bin/gam/oauth2service.json, To: /Users/admin/GAMConfig/oauth2service.json
|
||||
Copied: /Users/admin/bin/gam/oauth2.txt, To: /Users/admin/GAMConfig/oauth2.txt
|
||||
Copied: /Users/admin/bin/gam/client_secrets.json, To: /Users/admin/GAMConfig/client_secrets.json
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Initialized
|
||||
gamteam@server:/Users/gamteam$ gam config drive_dir /Users/gamteam/GAMWork verify
|
||||
Created: /Users/gamteam/GAMConfig
|
||||
Created: /Users/gamteam/GAMConfig/gamcache
|
||||
Copied: /Users/gamteam/bin/gam/oauth2service.json, To: /Users/gamteam/GAMConfig/oauth2service.json
|
||||
Copied: /Users/gamteam/bin/gam/oauth2.txt, To: /Users/gamteam/GAMConfig/oauth2.txt
|
||||
Copied: /Users/gamteam/bin/gam/client_secrets.json, To: /Users/gamteam/GAMConfig/client_secrets.json
|
||||
Config File: /Users/gamteam/GAMConfig/gam.cfg, Initialized
|
||||
Section: DEFAULT
|
||||
...
|
||||
cache_dir = /Users/admin/GAMConfig/gamcache
|
||||
cache_dir = /Users/gamteam/GAMConfig/gamcache
|
||||
...
|
||||
config_dir = /Users/admin/GAMConfig
|
||||
config_dir = /Users/gamteam/GAMConfig
|
||||
...
|
||||
drive_dir = /Users/admin/GAMWork
|
||||
drive_dir = /Users/gamteam/GAMWork
|
||||
...
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
### Verify initialization, this was a successful installation.
|
||||
```
|
||||
admin@server:/Users/admin$ ls -l $GAMCFGDIR
|
||||
gamteam@server:/Users/gamteam$ ls -l $GAMCFGDIR
|
||||
total 48
|
||||
-rw-r-----+ 1 admin staff 553 Mar 3 09:23 client_secrets.json
|
||||
-rw-r-----+ 1 admin staff 1069 Mar 3 09:23 gam.cfg
|
||||
@@ -149,21 +149,21 @@ drwxr-x---+ 2 admin staff 68 Mar 3 09:23 gamcache
|
||||
-rw-r-----+ 1 admin staff 5104 Mar 3 09:23 oauth2.txt
|
||||
-rw-rw-rw-+ 1 admin staff 0 Mar 3 09:23 oauth2.txt.lock
|
||||
-rw-r-----+ 1 admin staff 2377 Mar 3 09:23 oauth2service.json
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
If the verification looks like this, then you'll have to copy client_secrets.json and oauth2service.json manually.
|
||||
```
|
||||
admin@server:/Users/admin$ ls -l $GAMCFGDIR
|
||||
gamteam@server:/Users/gamteam$ ls -l $GAMCFGDIR
|
||||
total 40
|
||||
-rw-r-----+ 1 admin admin 1427 Nov 1 11:38 gam.cfg
|
||||
drwxr-x---+ 16 admin admin 544 Nov 2 07:25 gamcache
|
||||
-rw-r--r--+ 1 admin admin 10 Nov 2 15:31 lastupdatecheck.txt
|
||||
-rw-rw-rw-+ 1 admin admin 0 Sep 19 17:28 oauth2.txt.lock
|
||||
|
||||
admin@server:/Users/admin$ cp -p $OLDGAMPATH/client_secrets.json $GAMCFGDIR/
|
||||
admin@server:/Users/admin$ cp -p $OLDGAMPATH/oauth2service.json $GAMCFGDIR/
|
||||
admin@server:/Users/admin$ cp -p $OLDGAMPATH/oauth2.txt $GAMCFGDIR/
|
||||
admin@server:/Users/admin$ ls -l $GAMCFGDIR
|
||||
gamteam@server:/Users/gamteam$ cp -p $OLDGAMPATH/client_secrets.json $GAMCFGDIR/
|
||||
gamteam@server:/Users/gamteam$ cp -p $OLDGAMPATH/oauth2service.json $GAMCFGDIR/
|
||||
gamteam@server:/Users/gamteam$ cp -p $OLDGAMPATH/oauth2.txt $GAMCFGDIR/
|
||||
gamteam@server:/Users/gamteam$ ls -l $GAMCFGDIR
|
||||
total 40
|
||||
-rw-r-----+ 1 admin staff 553 Mar 3 09:23 client_secrets.json
|
||||
-rw-r-----+ 1 admin staff 1069 Mar 3 09:23 gam.cfg
|
||||
@@ -175,9 +175,9 @@ drwxr-x---+ 2 admin staff 68 Mar 3 09:23 gamcache
|
||||
```
|
||||
### Update your project with local browser to include the additional APIs that GAM7 uses.
|
||||
```
|
||||
admin@server:/Users/admin$ gam update project
|
||||
gamteam@server:/Users/gamteam$ gam update project
|
||||
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) gam-project-abc-123-xyz? admin@domain.com
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) gam-project-abc-123-xyz? gamteam@domain.com
|
||||
|
||||
Your browser has been opened to visit:
|
||||
|
||||
@@ -205,14 +205,14 @@ Enable 3 APIs
|
||||
API: groupsmigration.googleapis.com, Enabled (2/3)
|
||||
API: sheets.googleapis.com, Enabled (3/3)
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
### Update your project without local browser (Google Cloud Shell for instance) to include the additional APIs that GAM7 uses
|
||||
```
|
||||
admin@server:/Users/admin$ gam config no_browser true save
|
||||
admin@server:/Users/admin$ gam update project
|
||||
gamteam@server:/Users/gamteam$ gam config no_browser true save
|
||||
gamteam@server:/Users/gamteam$ gam update project
|
||||
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) gam-project-abc-123-xyz? admin@domain.com
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) gam-project-abc-123-xyz? gamteam@domain.com
|
||||
|
||||
Go to the following link in a browser on other computer:
|
||||
|
||||
@@ -239,7 +239,7 @@ Enable 3 APIs
|
||||
API: groupsmigration.googleapis.com, Enabled (2/3)
|
||||
API: sheets.googleapis.com, Enabled (3/3)
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
### Enable GAM7 client access
|
||||
|
||||
@@ -249,17 +249,17 @@ You select a list of scopes, GAM uses a browser to get final authorization from
|
||||
writes the credentials into the file oauth2.txt.
|
||||
|
||||
```
|
||||
admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt
|
||||
admin@server:/Users/admin$ gam version
|
||||
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
|
||||
GAM 7.32.05 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
gamteam@server:/Users/gamteam$ rm -f /Users/gamteam/GAMConfig/oauth2.txt
|
||||
gamteam@server:/Users/gamteam$ gam version
|
||||
WARNING: Config File: /Users/gamteam/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/gamteam/GAMConfig/oauth2.txt, Not Found
|
||||
GAM 7.34.07 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.2 64-bit final
|
||||
macOS Tahoe 26.2 x86_64
|
||||
Path: /Users/admin/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Python 3.14.3 64-bit final
|
||||
macOS Tahoe 26.3 arm64
|
||||
Path: /Users/gamteam/bin/gam7
|
||||
Config File: /Users/gamteam/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
|
||||
admin@server:/Users/admin$ gam oauth create
|
||||
gamteam@server:/Users/gamteam$ gam oauth create
|
||||
|
||||
[*] 0) Calendar API (supports readonly)
|
||||
[*] 1) Chrome Browser Cloud Management API (supports readonly)
|
||||
@@ -326,7 +326,7 @@ Continue to authorization by entering a 'c'
|
||||
|
||||
Please enter 0-50[a|r] or s|u|e|c: c
|
||||
|
||||
Enter your Google Workspace admin email address? admin@domain.com
|
||||
Enter your Google Workspace admin email address? gamteam@domain.com
|
||||
|
||||
Go to the following link in a browser on this computer or on another computer:
|
||||
|
||||
@@ -338,13 +338,13 @@ click the Allow button, paste "Unable to connect" URL from other computer (only
|
||||
Enter verification code or paste "Unable to connect" URL from other computer (only URL data up to &scope required):
|
||||
|
||||
The authentication flow has completed.
|
||||
Client OAuth2 File: /Users/admin/GAMConfig/oauth2.txt, Created
|
||||
Client OAuth2 File: /Users/gamteam/GAMConfig/oauth2.txt, Created
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
### Enable GAM7 service account access.
|
||||
```
|
||||
admin@server:/Users/admin$ gam user admin@domain.com update serviceaccount
|
||||
gamteam@server:/Users/gamteam$ gam user gamteam@domain.com update serviceaccount
|
||||
[*] 0) AlertCenter API
|
||||
[*] 1) Analytics API - read only
|
||||
[*] 2) Analytics Admin API - read only
|
||||
@@ -408,7 +408,7 @@ Service Account Private Key Authentication
|
||||
Authentication PASS
|
||||
Service Account Private Key age; Google recommends rotating keys on a routine basis
|
||||
Service Account Private Key age: 1 day WARN
|
||||
Domain-wide Delegation authentication:, User: admin@domain.com, Scopes: 38
|
||||
Domain-wide Delegation authentication:, User: gamteam@domain.com, Scopes: 38
|
||||
https://mail.google.com/ PASS (1/38)
|
||||
https://sites.google.com/feeds FAIL (2/38)
|
||||
https://www.googleapis.com/auth/analytics.readonly FAIL (3/38)
|
||||
@@ -459,7 +459,7 @@ Click AUTHORIZE
|
||||
When the box closes you're done
|
||||
After authorizing it may take some time for this test to pass so wait a few moments and then try this command again.
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
The link shown in the error message should take you directly to the authorization screen.
|
||||
If not, make sure that you are logged in as a domain admin, then re-enter the link.
|
||||
@@ -469,7 +469,7 @@ If not, make sure that you are logged in as a domain admin, then re-enter the li
|
||||
Wait a moment and then perform the following command; it it still fails, wait a bit longer, it can sometimes take serveral minutes
|
||||
for the authorization to complete.
|
||||
```
|
||||
admin@server:/Users/admin$ gam user admin@domain.com check serviceaccount
|
||||
gamteam@server:/Users/gamteam$ gam user gamteam@domain.com check serviceaccount
|
||||
|
||||
System time status
|
||||
Your system time differs from admin.googleapis.com by less than 1 second PASS
|
||||
@@ -477,7 +477,7 @@ Service Account Private Key Authentication
|
||||
Authentication PASS
|
||||
Service Account Private Key age; Google recommends rotating keys on a routine basis
|
||||
Service Account Private Key age: 1 day WARN
|
||||
Domain-wide Delegation authentication:, User: admin@domain.com, Scopes: 38
|
||||
Domain-wide Delegation authentication:, User: gamteam@domain.com, Scopes: 38
|
||||
https://mail.google.com/ PASS (1/38)
|
||||
https://sites.google.com/feeds PASS (2/38)
|
||||
https://www.googleapis.com/auth/analytics.readonly PASS (3/38)
|
||||
@@ -519,14 +519,14 @@ Domain-wide Delegation authentication:, User: admin@domain.com, Scopes: 38
|
||||
All scopes PASSED!
|
||||
Service Account Client name: SVCACCTID is fully authorized.
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
### Update gam.cfg with some basic values
|
||||
* `customer_id` - Having this data keeps Gam from having to make extra API calls
|
||||
* `domain` - This allows you to omit the domain portion of email addresses
|
||||
* `timezone local` - Gam will convert all UTC times to your local timezone
|
||||
```
|
||||
admin@server:/Users/admin$ gam info domain
|
||||
gamteam@server:/Users/gamteam$ gam info domain
|
||||
Customer ID: C01234567
|
||||
Primary Domain: domain.com
|
||||
Customer Creation Time: 2007-06-06T15:47:55.444Z
|
||||
@@ -534,8 +534,8 @@ Primary Domain Verified: True
|
||||
Default Language: en
|
||||
...
|
||||
|
||||
admin@server:/Users/admin$ gam config customer_id C01234567 domain domain.com timezone local save verify
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Saved
|
||||
gamteam@server:/Users/gamteam$ gam config customer_id C01234567 domain domain.com timezone local save verify
|
||||
Config File: /Users/gamteam/GAMConfig/gam.cfg, Saved
|
||||
Section: DEFAULT
|
||||
activity_max_results = 100
|
||||
admin_email = ''
|
||||
@@ -546,19 +546,21 @@ Section: DEFAULT
|
||||
bail_on_internal_error_tries = 2
|
||||
batch_size = 50
|
||||
cacerts_pem = ''
|
||||
cache_dir = /Users/admin/GAMConfig/gamcache
|
||||
cache_dir = /Users/gamteam/GAMConfig/gamcache
|
||||
cache_discovery_only = true
|
||||
channel_customer_id = ''
|
||||
charset = utf-8
|
||||
chat_max_results = 100
|
||||
classroom_max_results = 0
|
||||
client_secrets_json = client_secrets.json ; /Users/admin/GAMConfig/client_secrets.json
|
||||
client_secrets_json = client_secrets.json ; /Users/gamteam/GAMConfig/client_secrets.json
|
||||
clock_skew_in_seconds = 10
|
||||
cmdlog = ''
|
||||
cmdlog_max_backups = 5
|
||||
cmdlog_max_kilo_bytes = 1000
|
||||
config_dir = /Users/admin/GAMConfig
|
||||
config_dir = /Users/gamteam/GAMConfig
|
||||
contact_max_results = 100
|
||||
csv_input_column_delimiter = ,
|
||||
csv_input_no_escape_char = true
|
||||
csv_input_quote_char = '"'
|
||||
csv_input_row_drop_filter = ''
|
||||
csv_input_row_drop_filter_mode = anymatch
|
||||
@@ -571,32 +573,48 @@ Section: DEFAULT
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_header_required = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
csv_output_row_drop_filter = ''
|
||||
csv_output_row_drop_filter_mode = anymatch
|
||||
csv_output_row_filter = ''
|
||||
csv_output_row_filter_mode = allmatch
|
||||
csv_output_row_limit = 0
|
||||
csv_output_sort_headers = ''
|
||||
csv_output_subfield_delimiter = '.'
|
||||
csv_output_timestamp_column = ''
|
||||
csv_output_users_audit = false
|
||||
customer_id = C01234567
|
||||
debug_level = 0
|
||||
debug_redaction = true
|
||||
developer_preview_api_key = ''
|
||||
developer_preview_apis = ''
|
||||
device_max_results = 200
|
||||
domain = domain.com
|
||||
drive_dir = /Users/admin/GAMWork
|
||||
drive_dir = /Users/gamteam/GAMWork
|
||||
drive_max_results = 1000
|
||||
drive_v3_native_names = true
|
||||
email_batch_size = 50
|
||||
enable_dasa = false
|
||||
enable_gcloud_reauth = false
|
||||
enforce_expansive_access = true
|
||||
event_max_results = 250
|
||||
extra_args = ''
|
||||
gmail_cse_incert_dir = ''
|
||||
gmail_cse_inkey_dir = ''
|
||||
input_dir = .
|
||||
inter_batch_wait = 0
|
||||
license_max_results = 100
|
||||
license_skus = ''
|
||||
member_max_results = 200
|
||||
member_max_results_ci_basic = 1000
|
||||
member_max_results_ci_full = 500
|
||||
message_batch_size = 50
|
||||
message_max_results = 500
|
||||
mobile_max_results = 100
|
||||
multiprocess_pool_limit = 0
|
||||
never_time = Never
|
||||
no_browser = false
|
||||
no_cache = false
|
||||
@@ -604,15 +622,17 @@ Section: DEFAULT
|
||||
no_verify_ssl = false
|
||||
num_tbatch_threads = 2
|
||||
num_threads = 5
|
||||
oauth2_txt = oauth2.txt ; /Users/admin/GAMConfig/oauth2.txt
|
||||
oauth2service_json = oauth2service.json ; /Users/admin/GAMConfig/oauth2service.json
|
||||
oauth2_txt = oauth2.txt ; /Users/gamteam/GAMConfig/oauth2.txt
|
||||
oauth2service_json = oauth2service.json ; /Users/gamteam/GAMConfig/oauth2service.json
|
||||
output_dateformat = ''
|
||||
output_timeformat = ''
|
||||
people_max_results = 100
|
||||
print_agu_domains = ''
|
||||
print_cros_ous = ''
|
||||
print_cros_ous_and_children = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = False
|
||||
quick_info_user = false
|
||||
reseller_id = ''
|
||||
retry_api_service_not_available = false
|
||||
section = ''
|
||||
@@ -629,12 +649,13 @@ Section: DEFAULT
|
||||
smtp_username = ''
|
||||
timezone = local
|
||||
tls_max_version = ''
|
||||
tls_min_version = 'TLSv1_2'
|
||||
tls_min_version = 'TLSv1_3'
|
||||
todrive_clearfilter = false
|
||||
todrive_clientaccess = false
|
||||
todrive_conversion = true
|
||||
todrive_localcopy = false
|
||||
todrive_locale = ''
|
||||
todrive_no_escape_char = true
|
||||
todrive_nobrowser = false
|
||||
todrive_noemail = true
|
||||
todrive_parent = root
|
||||
@@ -647,16 +668,18 @@ Section: DEFAULT
|
||||
todrive_user = ''
|
||||
truncate_client_id = false
|
||||
update_cros_ou_with_id = false
|
||||
use_chat_admin_access = false
|
||||
use_course_owner_access = false
|
||||
use_projectid_as_name = false
|
||||
user_max_results = 500
|
||||
user_service_account_access_only = false
|
||||
|
||||
admin@server:/Users/admin$
|
||||
gamteam@server:/Users/gamteam$
|
||||
```
|
||||
|
||||
## Windows
|
||||
|
||||
In these examples, your Google Super admin is shown as admin@domain.com; replace with the
|
||||
In these examples, your Google Super admin is shown as gamteam@domain.com; replace with the
|
||||
actual email adddress.
|
||||
|
||||
This example assumes that GAM7 has been installed in C:\GAM7; if you've installed
|
||||
@@ -751,6 +774,7 @@ Section: DEFAULT
|
||||
cache_discovery_only = true
|
||||
channel_customer_id = ''
|
||||
charset = utf-8
|
||||
chat_max_results = 100
|
||||
classroom_max_results = 0
|
||||
client_secrets_json = client_secrets.json ; C:\GAMConfig\client_secrets.json
|
||||
clock_skew_in_seconds = 10
|
||||
@@ -760,6 +784,7 @@ Section: DEFAULT
|
||||
config_dir = C:\GAMConfig
|
||||
contact_max_results = 100
|
||||
csv_input_column_delimiter = ,
|
||||
csv_input_no_escape_char = true
|
||||
csv_input_quote_char = '"'
|
||||
csv_input_row_drop_filter = ''
|
||||
csv_input_row_drop_filter_mode = anymatch
|
||||
@@ -772,32 +797,48 @@ Section: DEFAULT
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_header_required = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
csv_output_row_drop_filter = ''
|
||||
csv_output_row_drop_filter_mode = anymatch
|
||||
csv_output_row_filter = ''
|
||||
csv_output_row_filter_mode = allmatch
|
||||
csv_output_row_limit = 0
|
||||
csv_output_sort_headers = ''
|
||||
csv_output_subfield_delimiter = '.'
|
||||
csv_output_timestamp_column = ''
|
||||
csv_output_users_audit = false
|
||||
customer_id = my_customer
|
||||
debug_level = 0
|
||||
debug_redaction = true
|
||||
developer_preview_api_key = ''
|
||||
developer_preview_apis = ''
|
||||
device_max_results = 200
|
||||
domain = ''
|
||||
drive_dir = C:\GAMWork
|
||||
drive_max_results = 1000
|
||||
drive_v3_native_names = true
|
||||
email_batch_size = 50
|
||||
enable_dasa = false
|
||||
enable_gcloud_reauth = false
|
||||
enforce_expansive_access = true
|
||||
event_max_results = 250
|
||||
extra_args = ''
|
||||
gmail_cse_incert_dir = ''
|
||||
gmail_cse_inkey_dir = ''
|
||||
input_dir = .
|
||||
inter_batch_wait = 0
|
||||
license_max_results = 100
|
||||
license_skus = ''
|
||||
member_max_results = 200
|
||||
member_max_results_ci_basic = 1000
|
||||
member_max_results_ci_full = 500
|
||||
message_batch_size = 50
|
||||
message_max_results = 500
|
||||
mobile_max_results = 100
|
||||
multiprocess_pool_limit = 0
|
||||
never_time = Never
|
||||
no_browser = false
|
||||
no_cache = false
|
||||
@@ -807,13 +848,15 @@ Section: DEFAULT
|
||||
num_threads = 5
|
||||
oauth2_txt = oauth2.txt ; C:\GAMConfig\oauth2.txt
|
||||
oauth2service_json = oauth2service.json ; C:\GAMConfig\oauth2service.json
|
||||
output_dateformat = ''
|
||||
output_timeformat = ''
|
||||
people_max_results = 100
|
||||
print_agu_domains = ''
|
||||
print_cros_ous = ''
|
||||
print_cros_ous_and_children = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = False
|
||||
quick_info_user = false
|
||||
reseller_id = ''
|
||||
retry_api_service_not_available = false
|
||||
section = ''
|
||||
@@ -830,12 +873,13 @@ Section: DEFAULT
|
||||
smtp_username = ''
|
||||
timezone = utc
|
||||
tls_max_version = ''
|
||||
tls_min_version = 'TLSv1_2'
|
||||
tls_min_version = 'TLSv1_3'
|
||||
todrive_clearfilter = false
|
||||
todrive_clientaccess = false
|
||||
todrive_conversion = true
|
||||
todrive_localcopy = false
|
||||
todrive_locale = ''
|
||||
todrive_no_escape_char = true
|
||||
todrive_nobrowser = false
|
||||
todrive_noemail = true
|
||||
todrive_parent = root
|
||||
@@ -848,6 +892,8 @@ Section: DEFAULT
|
||||
todrive_user = ''
|
||||
truncate_client_id = false
|
||||
update_cros_ou_with_id = false
|
||||
use_chat_admin_access = false
|
||||
use_course_owner_access = false
|
||||
use_projectid_as_name = false
|
||||
user_max_results = 500
|
||||
user_service_account_access_only = false
|
||||
@@ -919,7 +965,7 @@ C:\>dir %GAMCFGDIR%
|
||||
```
|
||||
C:\>gam update project
|
||||
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) gam-project-abc-123-xyz? admin@domain.com
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) gam-project-abc-123-xyz? gamteam@domain.com
|
||||
|
||||
Your browser has been opened to visit:
|
||||
|
||||
@@ -951,7 +997,7 @@ C:\>
|
||||
C:\>gam config no_browser true save
|
||||
C:\>gam update project
|
||||
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) gam-project-abc-123-xyz? admin@domain.com
|
||||
Enter your Google Workspace admin or GCP project manager email address authorized to manage project(s) gam-project-abc-123-xyz? gamteam@domain.com
|
||||
|
||||
Go to the following link in a browser on other computer:
|
||||
|
||||
@@ -990,9 +1036,9 @@ writes the credentials into the file oauth2.txt.
|
||||
C:\>del C:\GAMConfig\oauth2.txt
|
||||
C:\>gam version
|
||||
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
|
||||
GAM 7.32.05 - https://github.com/GAM-team/GAM - pythonsource
|
||||
GAM 7.34.07 - https://github.com/GAM-team/GAM - pythonsource
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.2 64-bit final
|
||||
Python 3.14.3 64-bit final
|
||||
Windows 11 10.0.26200 AMD64
|
||||
Path: C:\GAM7
|
||||
Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
@@ -1064,7 +1110,7 @@ Continue to authorization by entering a 'c'
|
||||
|
||||
Please enter 0-50[a|r] or s|u|e|c: c
|
||||
|
||||
Enter your Google Workspace admin email address? admin@domain.com
|
||||
Enter your Google Workspace admin email address? gamteam@domain.com
|
||||
|
||||
Go to the following link in a browser on this computer or on another computer:
|
||||
|
||||
@@ -1083,7 +1129,7 @@ C:\>
|
||||
|
||||
### Enable GAM7 service account access.
|
||||
```
|
||||
C:\>gam user admin@domain.com update serviceaccount
|
||||
C:\>gam user gamteam@domain.com update serviceaccount
|
||||
[*] 0) AlertCenter API
|
||||
[*] 1) Analytics API - read only
|
||||
[*] 2) Analytics Admin API - read only
|
||||
@@ -1147,7 +1193,7 @@ Service Account Private Key Authentication
|
||||
Authentication PASS
|
||||
Service Account Private Key age; Google recommends rotating keys on a routine basis
|
||||
Service Account Private Key age: 1 day WARN
|
||||
Domain-wide Delegation authentication:, User: admin@domain.com, Scopes: 38
|
||||
Domain-wide Delegation authentication:, User: gamteam@domain.com, Scopes: 38
|
||||
https://mail.google.com/ PASS (1/38)
|
||||
https://sites.google.com/feeds FAIL (2/38)
|
||||
https://www.googleapis.com/auth/analytics.readonly FAIL (3/38)
|
||||
@@ -1208,14 +1254,14 @@ If not, make sure that you are logged in as a domain admin, then re-enter the li
|
||||
Wait a moment and then perform the following command; it it still fails, wait a bit longer, it can sometimes take serveral minutes
|
||||
for the authorization to complete.
|
||||
```
|
||||
C:\>gam user admin@domain.com check serviceaccount
|
||||
C:\>gam user gamteam@domain.com check serviceaccount
|
||||
System time status
|
||||
Your system time differs from admin.googleapis.com by less than 1 second PASS
|
||||
Service Account Private Key Authentication
|
||||
Authentication PASS
|
||||
Service Account Private Key age; Google recommends rotating keys on a routine basis
|
||||
Service Account Private Key age: 1 day WARN
|
||||
Domain-wide Delegation authentication:, User: admin@domain.com, Scopes: 38
|
||||
Domain-wide Delegation authentication:, User: gamteam@domain.com, Scopes: 38
|
||||
https://mail.google.com/ PASS (1/38)
|
||||
https://sites.google.com/feeds PASS (2/38)
|
||||
https://www.googleapis.com/auth/analytics.readonly PASS (3/38)
|
||||
@@ -1289,6 +1335,7 @@ Section: DEFAULT
|
||||
cache_discovery_only = true
|
||||
channel_customer_id = ''
|
||||
charset = utf-8
|
||||
chat_max_results = 100
|
||||
classroom_max_results = 0
|
||||
client_secrets_json = client_secrets.json ; C:\GAMConfig\client_secrets.json
|
||||
clock_skew_in_seconds = 10
|
||||
@@ -1298,6 +1345,7 @@ Section: DEFAULT
|
||||
config_dir = C:\GAMConfig
|
||||
contact_max_results = 100
|
||||
csv_input_column_delimiter = ,
|
||||
csv_input_no_escape_char = true
|
||||
csv_input_quote_char = '"'
|
||||
csv_input_row_drop_filter = ''
|
||||
csv_input_row_drop_filter_mode = anymatch
|
||||
@@ -1310,32 +1358,48 @@ Section: DEFAULT
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_header_required = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
csv_output_row_drop_filter = ''
|
||||
csv_output_row_drop_filter_mode = anymatch
|
||||
csv_output_row_filter = ''
|
||||
csv_output_row_filter_mode = allmatch
|
||||
csv_output_row_limit = 0
|
||||
csv_output_sort_headers = ''
|
||||
csv_output_subfield_delimiter = '.'
|
||||
csv_output_timestamp_column = ''
|
||||
csv_output_users_audit = false
|
||||
customer_id = C01234567
|
||||
debug_level = 0
|
||||
debug_redaction = true
|
||||
developer_preview_api_key = ''
|
||||
developer_preview_apis = ''
|
||||
device_max_results = 200
|
||||
domain = domain.com
|
||||
drive_dir = C:\GAMWork
|
||||
drive_max_results = 1000
|
||||
drive_v3_native_names = true
|
||||
email_batch_size = 50
|
||||
enable_dasa = false
|
||||
enable_gcloud_reauth = false
|
||||
enforce_expansive_access = true
|
||||
event_max_results = 250
|
||||
extra_args = ''
|
||||
gmail_cse_incert_dir = ''
|
||||
gmail_cse_inkey_dir = ''
|
||||
input_dir = .
|
||||
inter_batch_wait = 0
|
||||
license_max_results = 100
|
||||
license_skus = ''
|
||||
member_max_results = 200
|
||||
member_max_results_ci_basic = 1000
|
||||
member_max_results_ci_full = 500
|
||||
message_batch_size = 50
|
||||
message_max_results = 500
|
||||
mobile_max_results = 100
|
||||
multiprocess_pool_limit = 0
|
||||
never_time = Never
|
||||
no_browser = false
|
||||
no_cache = false
|
||||
@@ -1353,7 +1417,7 @@ Section: DEFAULT
|
||||
print_cros_ous_and_children = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = False
|
||||
quick_info_user = false
|
||||
reseller_id = ''
|
||||
retry_api_service_not_available = false
|
||||
section = ''
|
||||
@@ -1370,12 +1434,13 @@ Section: DEFAULT
|
||||
smtp_username = ''
|
||||
timezone = local
|
||||
tls_max_version = ''
|
||||
tls_min_version = 'TLSv1_2'
|
||||
tls_min_version = 'TLSv1_3'
|
||||
todrive_clearfilter = false
|
||||
todrive_clientaccess = false
|
||||
todrive_conversion = true
|
||||
todrive_localcopy = false
|
||||
todrive_locale = ''
|
||||
todrive_no_escape_char = true
|
||||
todrive_nobrowser = false
|
||||
todrive_noemail = true
|
||||
todrive_parent = root
|
||||
@@ -1388,6 +1453,8 @@ Section: DEFAULT
|
||||
todrive_user = ''
|
||||
truncate_client_id = false
|
||||
update_cros_ou_with_id = false
|
||||
use_chat_admin_access = false
|
||||
use_course_owner_access = false
|
||||
use_projectid_as_name = false
|
||||
user_max_results = 500
|
||||
user_service_account_access_only = false
|
||||
|
||||
@@ -56,6 +56,7 @@ The only `<VariableNames>` recognized in this `<Section>` are:
|
||||
* `csv_output_header_drop_filter`
|
||||
* `csv_output_header_force`
|
||||
* `csv_output_header_order`
|
||||
* `csv_output_header_required`
|
||||
* `csv_output_row_filter`
|
||||
* `csv_output_row_filter_mode`
|
||||
* `csv_output_row_drop_filter`
|
||||
|
||||
34
wiki/No-Owner-Secondary-Calendars.md
Normal file
34
wiki/No-Owner-Secondary-Calendars.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Secondary Calendars with no Owner
|
||||
|
||||
Here's a start on how to get information for non-owned secondary calendars
|
||||
|
||||
Save the CSV file that Google sent you as NoOwnerSecCals.csv
|
||||
|
||||
Get calendar description and summary for non-owned secondary calendars.
|
||||
* There will be one row per calendar.
|
||||
```
|
||||
gam config num_threads 10 csv_output_header_force "calendarId,description,summary" redirect csv ./NOSC_Details.csv multiprocess redirect stderr - multiprocess csv NoOwnerSecCals.csv gam calendar "~Secondary calendar email" print settings fields description,summary
|
||||
```
|
||||
|
||||
Get event counts for non-owned secondary calendars.
|
||||
* There will be one row per calendar.
|
||||
```
|
||||
gam config num_threads 10 redirect csv ./NOSC_EventCounts.csv multiprocess redirect stderr - multiprocess csv NOSC_Details.csv gam calendar "~calendarId" print events countsonly addcsvdata description "~description" addcsvdata summary "~summary"
|
||||
```
|
||||
|
||||
Get summary for non-owned secondary calendars - Contains details, counts, ACLs
|
||||
* There will be one row per calendar/ACL combination
|
||||
```
|
||||
gam config num_threads 10 redirect csv ./NOSC_Summary.csv multiprocess redirect stderr - multiprocess csv NOSC_EventCounts.csv gam calendar "~calendarId" print acls noselfowner addcsvdata description "~description" addcsvdata summary "~summary" addcsvdata events "~events"
|
||||
```
|
||||
|
||||
You can add an owner.
|
||||
* Replace `admin@domain.com` with a super admin other than the one from: `gam oauth info`
|
||||
```
|
||||
gam config num_threads 10 redirect stdout ./Add_NOSC_Owner.txt multiprocess redirect stderr stdout csv NoOwnerSecCals.csv gam calendar "~Secondary calendar email" add acls owner admin@domain.com sendnotifications false
|
||||
```
|
||||
After inspecting NOSC_Summary.csv, you can delete the calendars if desired.
|
||||
* Replace `admin@domain.com` with the super admin specified in the previous step
|
||||
```
|
||||
gam config num_threads 10 redirect stdout ./Delete_NOSC.txt multiprocess redirect stderr stdout csv NoOwnerSecCals.csv gam user admin@domain.com remove calendar "~Secondary calendar email"
|
||||
``
|
||||
@@ -14,17 +14,17 @@
|
||||
## API documentation
|
||||
* [Activity Data Sources](https://support.google.com/a/answer/11482175)
|
||||
|
||||
Changes starting 2025-10-29.
|
||||
* [Reports API - Admin log event changes](https://support.google.com/a/answer/16601511)
|
||||
|
||||
Changes starting 2025-12-20
|
||||
* [Reports API - Admin log enhancements](https://workspaceupdates.googleblog.com/2025/12/google-workspace-audit-log-api.html)
|
||||
|
||||
These pages show event/parameter names; scroll down in the left column to: Reports.
|
||||
Changes starting 2026-02-17.
|
||||
* [Reports API - Admin log event changes](https://support.google.com/a/answer/16601511)
|
||||
|
||||
* [Reports API - Activities](https://developers.google.com/admin-sdk/reports/v1/reference/activities)
|
||||
* [Reports API - Customer Usage](https://developers.google.com/admin-sdk/reports/v1/reference/customerUsageReports)
|
||||
* [Reports API - User Usage](https://developers.google.com/admin-sdk/reports/v1/reference/userUsageReport)
|
||||
These pages show event/parameter names:
|
||||
* [Reports API - Admin Activities](https://developers.google.com/workspace/admin/reports/v1/appendix/activity/admin-event-names)
|
||||
* [Reports API - Activities](https://developers.google.com/workspace/admin/reports/v1/appendix/activity/access-transparency)
|
||||
* [Reports API - Customer Usage](https://developers.google.com/workspace/admin/reports/v1/appendix/usage/customer)
|
||||
* [Reports API - User Usage](https://developers.google.com/workspace/admin/reports/v1/appendix/usage/user)
|
||||
|
||||
## Definitions
|
||||
```
|
||||
@@ -99,6 +99,7 @@ gam report <ActivityApplicationName> [todrive <ToDriveAttribute>*]
|
||||
[event|events <EventNameList>] [ip <String>]
|
||||
[gmaileventtypes <NumberRangeList>]
|
||||
[groupidfilter <String>] [resourcedetailsfilter <String>]
|
||||
[notimesort]
|
||||
[maxactivities <Number>] [maxevents <Number>] [maxresults <Number>]
|
||||
[countsonly [bydate|summary] [eventrowfilter]]
|
||||
(addcsvdata <FieldName> <String>)* [shownoactivities]
|
||||
@@ -176,6 +177,9 @@ show the most recent activity/event; this can be useful when reporting drive act
|
||||
Add additional columns of data from the command line to the output.
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
By default, a reverse chronological sort is performed on all rows across multiple users and/or event names;
|
||||
this is consistent with the behavior in the Admin console. Use option `notimesort` to suppress this sort.
|
||||
|
||||
Display a row with a key value of `NoActivities` when there are no activities to report.
|
||||
* `shownoactivities`
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
withlink
|
||||
<DrivePermissionsFieldNameList> ::= "<DrivePermissionsFieldName>(,<DrivePermissionsFieldName>)*"
|
||||
|
||||
<QueryTeamDrive> ::= <String> See: https://developers.google.com/drive/api/v3/search-parameters
|
||||
<QuerySharedDrive> ::= <String> See: https://developers.google.com/workspace/drive/api/guides/search-shareddrives
|
||||
<SharedDriveACLRole> ::=
|
||||
manager|organizer|owner|
|
||||
contentmanager|fileorganizer|
|
||||
@@ -183,8 +183,8 @@
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveID>|
|
||||
(teamdriveid <SharedDriveID>)|(teamdriveid:<SharedDriveID>)|
|
||||
(teamdrive <SharedDriveName>)|(teamdrive:<SharedDriveName>)
|
||||
(shareddriveid <SharedDriveID>)|(shareddriveid:<SharedDriveID>)|
|
||||
(shareddrive <SharedDriveName>)|(shareddrive:<SharedDriveName>)
|
||||
|
||||
<SharedDriveFieldName> ::=
|
||||
backgroundimagefile|
|
||||
@@ -199,11 +199,11 @@
|
||||
<SharedDriveFieldNameList> ::= "<SharedDriveFieldName>(,<SharedDriveFieldName>)*"
|
||||
|
||||
<SharedDriveIDEntity> ::=
|
||||
<DriveFileItem>|(teamdriveid <DriveFileItem>)|(teamdriveid:<DriveFileItem>)
|
||||
<DriveFileItem>|(shareddriveid <DriveFileItem>)|(shareddriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::=
|
||||
(teamdrive <SharedDriveName>)|(teamdrive:<SharedDriveName>)
|
||||
(shareddrive <SharedDriveName>)|(shareddrive:<SharedDriveName>)
|
||||
<SharedDriveAdminQueryEntity> ::=
|
||||
(teamdriveadminquery <QueryTeamDrive>)|(teamdriveadminquery:<QueryTeamDrive>)
|
||||
(shareddriveadminquery <QuerySharedDrive>)|(shareddriveadminquery:<QuerySharedDrive>)
|
||||
|
||||
<SharedDriveEntityAdmin> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
@@ -327,11 +327,11 @@ When either of these options is chosen, no infomation about Shared Drive restric
|
||||
To retrieve the Shared Drive ID with `returnidonly`:
|
||||
```
|
||||
Linux/MacOS
|
||||
teamDriveId=$(gam create shareddrive ... returnidonly)
|
||||
shareddriveId=$(gam create shareddrive ... returnidonly)
|
||||
Windows PowerShell
|
||||
$teamDriveId = & gam create shareddrive ... returnidonly
|
||||
$shareddriveId = & gam create shareddrive ... returnidonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam create shareddrive ... returnidonly') do set teamDriveId=%a
|
||||
for /f "delims=" %a in ('gam create shareddrive ... returnidonly') do set shareddriveId=%a
|
||||
```
|
||||
|
||||
## Bulk Create Shared Drives
|
||||
@@ -422,14 +422,14 @@ By default, Gam displays the information as an indented list of keys and values.
|
||||
* `formatjson` - Display the fields in JSON format.
|
||||
```
|
||||
gam [<UserTypeEntity>] show shareddrives
|
||||
[adminaccess|asadmin] [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
[adminaccess|asadmin] [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
[fields <SharedDriveFieldNameList>]
|
||||
[showwebviewlink text|hyperlink]
|
||||
[formatjson]
|
||||
```
|
||||
By default, all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
|
||||
@@ -441,14 +441,14 @@ By default, Gam displays the information as an indented list of keys and values.
|
||||
* `formatjson` - Display the fields in JSON format.
|
||||
```
|
||||
gam [<UserTypeEntity>] print shareddrives [todrive <ToDriveAttribute>*]
|
||||
[adminaccess|asadmin] [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
[adminaccess|asadmin] [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
[fields <SharedDriveFieldNameList>]
|
||||
[showwebviewlink text|hyperlink]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
|
||||
@@ -498,7 +498,7 @@ Options `shareddriveadminquery|query` and `shareddrives|teamdrives` are mutually
|
||||
Options `shareddriveadminquery|query` and `orgunit|org|ou` require `adminaccess|asadmin`.
|
||||
|
||||
By default, organizers for all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `shareddrives|teamdrives <SharedDriveIDList>` - Select the Shared Drive IDs specified in `<SharedDriveIDList>`
|
||||
* `shareddrives|teamdrives select <FileSelector>|<CSVFileSelector>` - Select the Shared Drive IDs specified in `<FileSelector>|<CSVFileSelector>`
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
@@ -535,12 +535,12 @@ gam print shareddrives query "organizerCount = 0"
|
||||
Display the number of Shared Drives.
|
||||
```
|
||||
gam [<UserTypeEntity>] show|print shareddrives
|
||||
[adminaccess|asadmin] [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
[adminaccess|asadmin] [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
showitemcountonly
|
||||
```
|
||||
By default, all Shared Drives are counted; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
|
||||
@@ -758,7 +758,7 @@ gam config csv_output_header_drop_filter "User,createdTime,permission.photoLink,
|
||||
## Display Shared Drive access for selected Shared Drives
|
||||
```
|
||||
gam [<UserTypeEntity>] show shareddriveacls
|
||||
[adminaccess|asadmin] [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
[adminaccess|asadmin] [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
[user|group <EmailAddress> [checkgroups]] (role|roles <SharedDriveACLRoleList>)*
|
||||
<PermissionMatch>* [<PermissionMatchAction>] [pmselect]
|
||||
@@ -767,7 +767,7 @@ gam [<UserTypeEntity>] show shareddriveacls
|
||||
[formatjson]
|
||||
|
||||
gam [<UserTypeEntity>] print shareddriveacls [todrive <ToDriveAttribute>*]
|
||||
[adminaccess|asadmin] [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
[adminaccess|asadmin] [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
[user|group <EmailAddress> [checkgroups]] (role|roles <SharedDriveACLRoleList>)*
|
||||
<PermissionMatch>* [<PermissionMatchAction>] [pmselect]
|
||||
@@ -777,7 +777,7 @@ gam [<UserTypeEntity>] print shareddriveacls [todrive <ToDriveAttribute>*]
|
||||
|
||||
```
|
||||
By default, all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
* `<PermissionMatch>* [<PermissionMatchAction>] pmselect` - Use permission matching to select Shared Drives; all ACLs are displayed for the selected Shared Drives
|
||||
@@ -927,12 +927,12 @@ gam redirect stdout ./DeleteSharedDrives.txt multiprocess redirect stderr stdout
|
||||
## Delete old empty Shared Drives
|
||||
```
|
||||
# Get a list of Shared Drives organizers for Shared Drives created before one year ago; alter date<-1y as required.
|
||||
gam config csv_output_row_filter "createdTime:date<-1y" redirect csv ./TeamDriveOrganizers.csv print shareddriveorganizers domainlist mydomain.com includetypes user oneorganizer shownoorganizerdrives
|
||||
gam config csv_output_row_filter "createdTime:date<-1y" redirect csv ./ShareddriveOrganizers.csv print shareddriveorganizers domainlist mydomain.com includetypes user oneorganizer shownoorganizerdrives
|
||||
|
||||
# Inspect shareddriveOrganizers.csv, you'll have to deal with Shared Drives with no organizer/manager
|
||||
|
||||
# Get old empty Shared Drives
|
||||
gam config num_threads 10 csv_input_row_filter "organizers:regex:^.+$" csv_output_row_filter "Total:count=0" redirect csv ./OldEmptySharedDrives.csv multiprocess redirect stderr - multiprocess csv ./TeamDriveOrganizers.csv gam user "~organizers" print filecounts select shareddriveid "~id" showsize
|
||||
gam config num_threads 10 csv_input_row_filter "organizers:regex:^.+$" csv_output_row_filter "Total:count=0" redirect csv ./OldEmptySharedDrives.csv multiprocess redirect stderr - multiprocess csv ./ShareddriveOrganizers.csv gam user "~organizers" print filecounts select shareddriveid "~id" showsize
|
||||
|
||||
# Inspect OldEmptySharedDrives.csv, if you're confident of the results, proceed
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
defaulteventlength|
|
||||
format24hourtime|
|
||||
hideinvitations|
|
||||
hideinvitationssetting|
|
||||
hideweekends|
|
||||
locale|
|
||||
remindonrespondedeventsonly|
|
||||
|
||||
@@ -37,7 +37,9 @@ gam user user@domain.com update serviceaccount
|
||||
[*] 11) Chat API - User Sections (supports readonly)
|
||||
|
||||
```
|
||||
`Chat API - User Sections` is in Developer Preview; you must have a `developer_preview_api_key` in `gam.cfg` to use these commands.
|
||||
`Chat API - User Sections` is in Developer Preview; you must have a the following variables set in `gam.cfg` to use these commands.
|
||||
* `developer_preview_apis = chat`
|
||||
* `developer_preview_api_key = <String>`
|
||||
|
||||
Added `use_chat_admin_access` Boolean variable to `gam.cfg`.
|
||||
```
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
<SharedDriveID> ::= <String>
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveID>|(teamdriveid <SharedDriveID>)|(teamdriveid:<SharedDriveID>)|
|
||||
(teamdrive <SharedDriveName>)|(teamdrive:<SharedDriveName>)
|
||||
<SharedDriveID>|(shareddriveid <SharedDriveID>)|(shareddriveid:<SharedDriveID>)|
|
||||
(shareddrive <SharedDriveName>)|(shareddrive:<SharedDriveName>)
|
||||
```
|
||||
## Display empty folders
|
||||
```
|
||||
|
||||
@@ -68,10 +68,10 @@
|
||||
(parentid <DriveFolderID>)|
|
||||
(parentname <DriveFolderName>)|
|
||||
(anyownerparentname <DriveFolderName>)|
|
||||
(teamdriveparentid <DriveFolderID>)|
|
||||
(teamdriveparent <SharedDriveName>)|
|
||||
(teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>)|
|
||||
(teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>)
|
||||
(shareddriveparentid <DriveFolderID>)|
|
||||
(shareddriveparent <SharedDriveName>)|
|
||||
(shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>)|
|
||||
(shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>)
|
||||
|
||||
<DriveFileCopyAttribute> ::=
|
||||
(contentrestrictions readonly false)|
|
||||
@@ -111,6 +111,7 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
|
||||
notusers <EmailAddressList>|
|
||||
regex <REMatchPattern>|
|
||||
notregex <REMatchPattern>]
|
||||
[([start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>])|(range <Date>|<Time> <Date>|<Time>)]|
|
||||
[copysubfolders [<Boolean>]] [foldernamematchpattern <REMatchPattern>]
|
||||
[copysubshortcuts [<Boolean>]] [shortcutnamematchpattern <REMatchPattern>]
|
||||
[duplicatefiles overwriteolder|overwriteall|duplicatename|uniquename|skip]
|
||||
@@ -119,6 +120,7 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
|
||||
[copyfilepermissions [<Boolean>]]
|
||||
[copyfileinheritedpermissions [<Boolean>]
|
||||
[copyfilenoninheritedpermissions [<Boolean>]
|
||||
[copyfolderpermissions [<Boolean>]]
|
||||
[copymergewithparentfolderpermissions [<Boolean>]]
|
||||
[copymergedtopfolderpermissions [<Boolean>]]
|
||||
[copytopfolderpermissions [<Boolean>]]
|
||||
@@ -182,16 +184,18 @@ You can specify whether sub files, folders and shortcuts are copied. If sub fold
|
||||
* `copysubshortcuts false` - Sub shortcuts are not copied
|
||||
* `copysubshortcuts [true]` - Sub shortcuts are copied; this is the default
|
||||
|
||||
By default, GAM displays a message referencing files and folders not selected for copying by the options above.
|
||||
* `suppressnotselectedmessages false` - Do not suppress these messages; this is the default
|
||||
* `suppressnotselectedmessages [true]` - Suppress these messages
|
||||
|
||||
### By default, when copying sub files, all files, regardless of MIME type, are copied.
|
||||
You can specify restrictions on the MIME types to be copied.
|
||||
* `filemimetypes <MimeTypeList>` - Copy sub files with the specified MIME types
|
||||
* `filemimetypes not <MimeTypeList>` - Copy sub files with MIME types other than those specified
|
||||
* `filemimetypes category <MimeTypeNameList>` - Copy sub files with the specified MIME type categories
|
||||
|
||||
### By default, when copying sub files, all files, regardless of their `modifiedTime`, are copied.
|
||||
You can specify restrictions on the `modifiedTime` to be copied.
|
||||
* `start|starttime <Date>|<Time>` - If specified, `modifiedTime` must be >= the value
|
||||
* `end|endtime <Date>|<Time>` - If specified, `modifiedTime` must be <= the value
|
||||
* `range <Date>|<Time> <Date>|<Time>` - first value <= `modifiedTime` <= second value
|
||||
|
||||
### By default, when copying sub files, folders and shortcuts, all are copied.
|
||||
You can specify `<REMatchPattern>` patterns that limit the items copied based on their name.
|
||||
* `filenamematchpattern <REMatchPattern>` - Only files whose name matches `<REMatchPattern>` are copied
|
||||
@@ -207,6 +211,10 @@ You can specify `<REMatchPattern>` patterns that limit the items copied based on
|
||||
* `copysubfilesownedby regex <REMatchPattern>` - Only files owned by users whose email addresses match `<REMatchPattern>` are copied.
|
||||
* `copysubfilesownedby notregex <REMatchPattern>` - Only files owned by users whose email addresses do not match `<REMatchPattern>` are copied.
|
||||
|
||||
### By default, GAM displays a message referencing files and folders not selected for copying by the options above.
|
||||
* `suppressnotselectedmessages false` - Do not suppress these messages; this is the default
|
||||
* `suppressnotselectedmessages [true]` - Suppress these messages
|
||||
|
||||
### Specify a new name for the file/folder
|
||||
* `newfilename <DriveFileName>` - The copied file/folder will be named `<DriveFileName>`
|
||||
* If `stripnameprefix <String>` is specified, `<String>` will be stripped from the front of `<DriveFileName>`
|
||||
@@ -224,10 +232,10 @@ and "Template" is replaced by "NewCustomer" in all copied sub files and folders
|
||||
* `parentid <DriveFolderID>` - The target folder is identified by `<DriveFolderID>` which must be writable by `<UserTypeEntity>`.
|
||||
* `parentname <DriveFolderName>` - A search is performed for a folder named `<DriveFolderName>` owned by `<UserTypeEntity>`.
|
||||
* `anyownerparentname <DriveFolderName>` - A search is performed for a folder named `<DriveFolderName>` owned by any user but must be writable by `<UserTypeEntity>`.
|
||||
* `teamdriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specific Shared Drive folder.
|
||||
* `teamdriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* `shareddriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specific Shared Drive folder.
|
||||
* `shareddriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* If none of the parent options are specified, the copied file/folder will be located in the source folder.
|
||||
|
||||
### Duplicate files
|
||||
@@ -287,6 +295,8 @@ When a folder is copied, its permissions are not copied; these options control c
|
||||
of the form `option [<Boolean>]`; if `<Boolean>` is omitted, `true` is assumed.
|
||||
|
||||
When copied, a target folder inherits the permissions of its parent folder; these options control whether/how GAM copies the existing source folder permissions.
|
||||
* `copyfolderpermissions false` - The permissions of the source folders are not copied to the target folder.
|
||||
* `copyfolderpermissions true` - The permissions of the source folders are copied to the target folder based on the following options; this is the default action.
|
||||
|
||||
When `mergewithparent` is `true`:
|
||||
* `copymergewithparentfolderpermissions false` - The permissions of the source top folder are not not copied to the target folder; this is the default action.
|
||||
@@ -407,15 +417,15 @@ Specify the target location on the Shared Drive, either the ID of the Shared Dri
|
||||
|
||||
Files/folders in root of My Drive will be merged into `<DriveFolderID>`
|
||||
```
|
||||
gam user user@domain.com copy drivefile root recursive teamdriveparentid <DriveFolderID> mergewithparent true
|
||||
gam user user@domain.com copy drivefile root recursive shareddriveparentid <DriveFolderID> mergewithparent true
|
||||
```
|
||||
Files/folders in root of My Drive will be in a new folder named `My Drive` created in `<DriveFolderID>`
|
||||
```
|
||||
gam user user@domain.com copy drivefile root recursive teamdriveparentid <DriveFolderID> mergewithparent false
|
||||
gam user user@domain.com copy drivefile root recursive shareddriveparentid <DriveFolderID> mergewithparent false
|
||||
```
|
||||
Files/folders in root of My Drive will be in a new folder named `<String>` created in `<DriveFolderID>`
|
||||
```
|
||||
gam user user@domain.com copy drivefile root recursive teamdriveparentid <SharedDriveID> mergewithparent false newfilename <String>
|
||||
gam user user@domain.com copy drivefile root recursive shareddriveparentid <SharedDriveID> mergewithparent false newfilename <String>
|
||||
```
|
||||
|
||||
### Copy content of a Shared Drive to another Shared Drive
|
||||
@@ -431,7 +441,7 @@ The example is assuming that the target drive is empty.
|
||||
* Non-inherited sub folder permissions are copied.
|
||||
* Non-inherited file permissions are copied.
|
||||
```
|
||||
gam user user@domain.com copy drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX mergewithparent recursive
|
||||
gam user user@domain.com copy drivefile shareddriveid 0AC_1AB shareddriveparentid 0AE_9ZX mergewithparent recursive
|
||||
copymergewithparentfolderpermissions true
|
||||
copytopfolderinheritedpermissions false
|
||||
copytopfoldernoninheritedpermissions always
|
||||
@@ -451,7 +461,7 @@ Suppose that the source drive has been updated and you want to refresh the targe
|
||||
* Non-inherited file permissions are copied.
|
||||
* Files and folders that have been deleted from the source drive will remain on the target drive
|
||||
```
|
||||
gam user user@domain.com copy drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX mergewithparent recursive
|
||||
gam user user@domain.com copy drivefile shareddriveid 0AC_1AB shareddriveparentid 0AE_9ZX mergewithparent recursive
|
||||
copymergewithparentfolderpermissions true
|
||||
copytopfolderinheritedpermissions false
|
||||
copytopfoldernoninheritedpermissions syncallfolders
|
||||
@@ -471,7 +481,7 @@ gam redirect csv ./TopSDItems.csv user user@domain.com print filelist select 0AC
|
||||
```
|
||||
Copy the top level items to target Shared Drive; append desired permission options
|
||||
```
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive teamdriveparentid 0AE_9ZX
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive shareddriveparentid 0AE_9ZX
|
||||
```
|
||||
|
||||
### Copy content of a source Shared Drive folder to a target Shared Drive with parallel Processing
|
||||
@@ -481,31 +491,31 @@ gam redirect csv ./TopSDItems.csv user user@domain.com print filelist select 1Bx
|
||||
```
|
||||
Create a folder on target Shared Drive with ID 0AE_9ZX, replace "New Folder Name" as desired.
|
||||
```
|
||||
gam user user@domain.com create drivefile mimetype gfolder teamdriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly
|
||||
gam user user@domain.com create drivefile mimetype gfolder shareddriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly
|
||||
```
|
||||
Copy the folder top level items to target Shared Drive folder, assume ID 2CY-45G was returned in previous step
|
||||
```
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive teamdriveparentid 2CY-45G
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive shareddriveparentid 2CY-45G
|
||||
```
|
||||
You can script the steps:
|
||||
|
||||
Linux/MacOS
|
||||
```
|
||||
gam redirect csv ./TopSDItems.csv user user@domain.com print filelist select 1Bx-8W3 fields id,name,mimetype depth 0
|
||||
targetFolderId=$(gam user user@domain.com create drivefile mimetype gfolder teamdriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly)
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive teamdriveparentid $targetFolderId
|
||||
targetFolderId=$(gam user user@domain.com create drivefile mimetype gfolder shareddriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly)
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive shareddriveparentid $targetFolderId
|
||||
```
|
||||
Windows PowerShell
|
||||
```
|
||||
gam redirect csv ./TopSDItems.csv user user@domain.com print filelist select 1Bx-8W3 fields id,name,mimetype depth 0
|
||||
$targetFolderId = & gam user user@domain.com create drivefile mimetype gfolder teamdriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive teamdriveparentid $targetFolderId
|
||||
$targetFolderId = & gam user user@domain.com create drivefile mimetype gfolder shareddriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive shareddriveparentid $targetFolderId
|
||||
```
|
||||
Windows Command Prompt
|
||||
```
|
||||
gam redirect csv ./TopSDItems.csv user user@domain.com print filelist select 1Bx-8W3 fields id,name,mimetype depth 0
|
||||
for /f "delims=" %a in ('gam user user@domain.com create drivefile mimetype gfolder teamdriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly') do set taregtFolderId=%a
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive teamdriveparentid %targetFolderId%
|
||||
for /f "delims=" %a in ('gam user user@domain.com create drivefile mimetype gfolder shareddriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly') do set taregtFolderId=%a
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive shareddriveparentid %targetFolderId%
|
||||
```
|
||||
|
||||
## Move files and folders
|
||||
@@ -564,6 +574,7 @@ gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileNam
|
||||
[createshortcutsfornonmovablefiles [<Boolean>]]
|
||||
[duplicatefiles overwriteolder|overwriteall|duplicatename|uniquename|skip]
|
||||
[duplicatefolders merge|duplicatename|uniquename|skip]
|
||||
[copyfolderpermissions [<Boolean>]]
|
||||
[copymergewithparentfolderpermissions [<Boolean>]]
|
||||
[copymergedtopfolderpermissions [<Boolean>]]
|
||||
[copytopfolderpermissions [<Boolean>]]
|
||||
@@ -607,10 +618,10 @@ This is the default mode.
|
||||
* `parentid <DriveFolderID>` - The target folder is identified by `<DriveFolderID>` which must be writable by `<UserTypeEntity>`.
|
||||
* `parentname <DriveFolderName>` - A search is performed for a folder named `<DriveFolderName>` owned by `<UserTypeEntity>`.
|
||||
* `anyownerparentname <DriveFolderName>` - A search is performed for a folder named `<DriveFolderName>` owned by any user but must be writable by `<UserTypeEntity>`.
|
||||
* `teamdriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specific Shared Drive folder.
|
||||
* `teamdriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* `shareddriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specific Shared Drive folder.
|
||||
* `shareddriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* If none of the parent options are specified, the moved file/folder will be located in the source folder.
|
||||
|
||||
### Duplicate files
|
||||
@@ -653,6 +664,8 @@ When a folder is moved by recreating it, its permissions are not copied by the D
|
||||
For options of the form `option [<Boolean>]`; if `<Boolean>` is omitted, `true` is assumed.
|
||||
|
||||
When recreated, a target folder inherits the permissions of its parent folder; these options control whether/how GAM copies the existing source folder permissions;
|
||||
* `copyfolderpermissions false` - The permissions of the source folders are not copied to the target folder.
|
||||
* `copyfolderpermissions true` - The permissions of the source folders are copied to the target folder based on the following options; this is the default action.
|
||||
|
||||
When `mergewithparent` is `true`:
|
||||
* `copymergewithparentfolderpermissions false` - The permissions of the source top folder are not not copied to the target folder; this is the default action.
|
||||
@@ -754,14 +767,14 @@ The following command will change the parents of the top level files and folders
|
||||
|
||||
* No permissions are processed.
|
||||
```
|
||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX mergewithparent
|
||||
gam user user@domain.com move drivefile shareddriveid 0AC_1AB shareddriveparentid 0AE_9ZX mergewithparent
|
||||
```
|
||||
|
||||
If you want the source Shared Drive with ID 0AC_1AB to be contained in a top level folder of the target Shared Drive with ID 0AE_9ZX, omit the `mergewithparent` argument.
|
||||
The folder on the target Shared Drive will have the same name as the name of the source Shared Drive; use the `newfilename <DriveFileName>` to use a different name.
|
||||
```
|
||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX
|
||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX newfilename "Copy of source Shared Drive"
|
||||
gam user user@domain.com move drivefile shareddriveid 0AC_1AB shareddriveparentid 0AE_9ZX
|
||||
gam user user@domain.com move drivefile shareddriveid 0AC_1AB shareddriveparentid 0AE_9ZX newfilename "Copy of source Shared Drive"
|
||||
```
|
||||
|
||||
### Inter-workspace moves
|
||||
@@ -771,7 +784,7 @@ Due to a restructuring, you want to move data from Shared Drive A in domaina.com
|
||||
* `user@domaina.com` is a manager of both Shared Drives.
|
||||
|
||||
```
|
||||
$ gam user user@domaina move drivefile teamdriveid <SharedDriveAID> teamdriveparentid <SharedDriveBID> mergewithparent
|
||||
$ gam user user@domaina move drivefile shareddriveid <SharedDriveAID> shareddriveparentid <SharedDriveBID> mergewithparent
|
||||
User: user@domaina.com, Move 1 Drive File/Folder
|
||||
User: user@domaina.com, Drive Folder: Shared Drive A(<SharedDriveAID>), Move(Merge) contents with Drive Folder: Shared Drive B(<SharedDriveBID>)
|
||||
User: user@domaina.com, Drive File: Filename(<FileID>), Move Failed: Bad Request. User message: "shareOutNotPermitted"
|
||||
@@ -787,13 +800,13 @@ The following command will change the parents of the top level files and folders
|
||||
|
||||
* No permissions are processed.
|
||||
```
|
||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB parentid root mergewithparent
|
||||
gam user user@domain.com move drivefile shareddriveid 0AC_1AB parentid root mergewithparent
|
||||
```
|
||||
|
||||
If you want the contents of Shared Drive with ID 0AC_1AB to be contained in a top level folder of the My Drive, omit the `mergewithparent` argument.
|
||||
The folder on the My Drive will have the same name as the name of the Shared Drive; use the `newfilename <DriveFileName>` to use a different name.
|
||||
```
|
||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB parentid root
|
||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB parentid root newfilename "Copy of Shared Drive"
|
||||
gam user user@domain.com move drivefile shareddriveid 0AC_1AB parentid root
|
||||
gam user user@domain.com move drivefile shareddriveid 0AC_1AB parentid root newfilename "Copy of Shared Drive"
|
||||
```
|
||||
|
||||
|
||||
@@ -65,9 +65,9 @@
|
||||
|
||||
<SharedDriveID> ::= <String>
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveIDEntity> ::= (teamdriveid <SharedDriveID>) | (teamdriveid:<SharedDriveID>)
|
||||
<SharedDriveNameEntity> ::= (teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
<SharedDriveIDEntity> ::= (shareddriveid <SharedDriveID>) | (shareddriveid:<SharedDriveID>)
|
||||
<SharedDriveNameEntity> ::= (shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
@@ -315,8 +315,8 @@
|
||||
size|
|
||||
spaces|
|
||||
starred|
|
||||
teamdriveid|
|
||||
teamdrivename|
|
||||
shareddriveid|
|
||||
shareddrivename|
|
||||
thumbnaillink|
|
||||
thumbnailversion|
|
||||
title|
|
||||
@@ -430,6 +430,7 @@ gam <UserTypeEntity> show fileinfo <DriveFileEntity>
|
||||
(orderby <DriveFileOrderByFieldName> [ascending|descending])*
|
||||
[showdrivename] [showshareddrivepermissions]
|
||||
[(showlabels details|ids)|(includelabels <ClassificationLabelIDList>)]
|
||||
[includepermissionsforview published]
|
||||
[showparentsidsaslist] [followshortcuts [<Boolean>]]
|
||||
[stripcrsfromname]
|
||||
[formatjson]
|
||||
@@ -440,6 +441,7 @@ gam <UserTypeEntity> info drivefile <DriveFileEntity>
|
||||
(orderby <DriveFileOrderByFieldName> [ascending|descending])*
|
||||
[showdrivename] [showshareddrivepermissions]
|
||||
[(showlabels details|ids)|(includelabels <ClassificationLabelIDList>)]
|
||||
[includepermissionsforview published]
|
||||
[showparentsidsaslist] [followshortcuts [<Boolean>]]
|
||||
[stripcrsfromname]
|
||||
[formatjson]
|
||||
@@ -617,8 +619,10 @@ This option is not available for `print|show filetree`.
|
||||
```
|
||||
((query <QueryDriveFile>) | (fullquery <QueryDriveFile>) | <DriveFileQueryShortcut>) (querytime<String> <Time>)*
|
||||
```
|
||||
GAM initializes the query to `'me' in owners`.
|
||||
|
||||
* `query "xxx"` - ` and xxx` is appended to the current query; you can repeat the query argument to build up a longer query.
|
||||
* `fullquery "xxx"` - The query is set to `xxx` eliminating the initial `'me' in owners`.
|
||||
* `fullquery "xxx"` - The query is set to `xxx` eliminating the initial `'me' in owners`. You must also use `showownedby any|others` as desired.
|
||||
* `<DriveFileQueryShortcut>` - Predefined queries
|
||||
|
||||
Use the `querytime<String> <Time>` option to allow times, usually relative, to be substituted into the `query <QueryDriveFile>` option.
|
||||
@@ -1102,6 +1106,7 @@ gam <UserTypeEntity> print|show filelist [todrive <ToDriveAttribute>*]
|
||||
[allfields|<DriveFieldName>*|(fields <DriveFieldNameList>)]
|
||||
[showdrivename] [showshareddrivepermissions]
|
||||
[(showlabels details|ids)|(includelabels <ClassificationLabelIDList>)]
|
||||
[includepermissionsforview published]
|
||||
[showparentsidsaslist] [showpermissionslast]
|
||||
(orderby <DriveFileOrderByFieldName> [ascending|descending])* [delimiter <Character>]
|
||||
[stripcrsfromname]
|
||||
|
||||
@@ -149,10 +149,10 @@
|
||||
(parentid <DriveFolderID>)|
|
||||
(parentname <DriveFolderName>)|
|
||||
(anyownerparentname <DriveFolderName>)|
|
||||
(teamdriveparentid <DriveFolderID>)|
|
||||
(teamdriveparent <SharedDriveName>)|
|
||||
(teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>)|
|
||||
(teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>)
|
||||
(shareddriveparentid <DriveFolderID>)|
|
||||
(shareddriveparent <SharedDriveName>)|
|
||||
(shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>)|
|
||||
(shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>)
|
||||
|
||||
<DriveFileCreateAttribute> ::=
|
||||
<DriveFileAttribute>|
|
||||
@@ -196,10 +196,10 @@ You can specify where the new file is to be located:
|
||||
* `parentid <DriveFolderID>` - Folder ID.
|
||||
* `parentname <DriveFolderName>` - Folder name; the folder must be owned by `<UserTypeEntity>`.
|
||||
* `anyownerparentname <DriveFolderName>` - Folder name; the folder can be owned by any user, `<UserTypeEntity>` must be able to write to the folder.
|
||||
* `teamdriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specfic Shared Drive folder.
|
||||
* `teamdriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* `shareddriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specfic Shared Drive folder.
|
||||
* `shareddriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* If none of the parent options are specified, the parent folder is the root folder.
|
||||
|
||||
By default, Google assigns the current time to the attributes `createdTime` and `modifiedTime`; you can assign your own values
|
||||
@@ -290,7 +290,7 @@ This will create a three column CSV file SharedDriveNamesIDs.csv with columns: U
|
||||
You are building student folders on a Shared Drive as an admin and want to add ACLs to the folders
|
||||
allowing the student write access and you want a shortcut on the student's My Drive pointing to the folder.
|
||||
By adding the student's primary email address to the CSV output, it can be used in subsequent commands.
|
||||
Sustitute for admin@domain.com and `<TeamDriveID>`.
|
||||
Sustitute for admin@domain.com and `<SharedDriveID>`.
|
||||
```
|
||||
Students.csv
|
||||
primaryEmail,Name
|
||||
@@ -299,7 +299,7 @@ mary@domain.com, Mary Smith
|
||||
...
|
||||
|
||||
# Create the student folders on the Shared Drive
|
||||
gam redirect csv ./StudentFolders.csv multiprocess csv Students.csv gam user admin@domain.com create drivefile mimetype gfolder drivefilename "~~Name~~ Digital Portfolio" parentid <TeamDriveID> csv addcsvdata primaryEmail "~primaryEmail"
|
||||
gam redirect csv ./StudentFolders.csv multiprocess csv Students.csv gam user admin@domain.com create drivefile mimetype gfolder drivefilename "~~Name~~ Digital Portfolio" parentid <SharedDriveID> csv addcsvdata primaryEmail "~primaryEmail"
|
||||
# Add ACLs granting the students write access to their folders; "~User" refers to admin@domain.com
|
||||
gam csv StudentFolders.csv gam user "~User" add drivefileacl "~id" user "~primaryEmail" role fileorganizer
|
||||
# Add a shortcut to the folder on the student's My Drive
|
||||
@@ -389,7 +389,7 @@ User: user@domain.com, Drive Folder Path:, Create
|
||||
|
||||
Build in a Shared Drive Folder
|
||||
```
|
||||
gam user user@domain.com create drivefolderpath path "Top Folder/Middle Folder/Bottom Folder/Sub Folder" teamdriveparent "TS Shared Drive" teamdriveparentname "TS SD6 Folder"
|
||||
gam user user@domain.com create drivefolderpath path "Top Folder/Middle Folder/Bottom Folder/Sub Folder" shareddriveparent "TS Shared Drive" shareddriveparentname "TS SD6 Folder"
|
||||
Getting all Drive Files/Folders that match query (mimeType = 'application/vnd.google-apps.folder' and name = 'TS SD6 Folder' and trashed = false) for user@domain.com
|
||||
Got 1 Drive File/Folder that matched query (mimeType = 'application/vnd.google-apps.folder' and name = 'TS SD6 Folder' and trashed = false) for user@domain.com...
|
||||
User: user@domain.com, Drive Folder Path:, Create
|
||||
@@ -495,10 +495,10 @@ You can change where the new file is to be located; this removes all other paren
|
||||
* `parentid <DriveFolderID>` - Folder ID.
|
||||
* `parentname <DriveFolderName>` - Folder name; the folder must be owned by `<UserTypeEntity>`.
|
||||
* `anyownerparentname <DriveFolderName>` - Folder name; the folder can be owned by any user, `<UserTypeEntity>` must be able to write to the folder.
|
||||
* `teamdriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specfic Shared Drive folder.
|
||||
* `teamdriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* `shareddriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specfic Shared Drive folder.
|
||||
* `shareddriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
|
||||
You can add/remove parent folders without affecting other parent folders.
|
||||
* `addparents|removeparents <DriveFolderIDList>` - Specify the parent folders by ID.
|
||||
|
||||
@@ -30,13 +30,13 @@
|
||||
(parentid <DriveFolderID>)|
|
||||
(parentname <DriveFolderName>)|
|
||||
(anyownerparentname <DriveFolderName>)|
|
||||
(teamdriveparentid <DriveFolderID>)|
|
||||
(teamdriveparent <SharedDriveName>)|
|
||||
(teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>)|
|
||||
(teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>))|
|
||||
(teamdriveparentid <DriveFolderID>)|(teamdriveparent <SharedDriveName>)|
|
||||
(teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>)|
|
||||
(teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>)
|
||||
(shareddriveparentid <DriveFolderID>)|
|
||||
(shareddriveparent <SharedDriveName>)|
|
||||
(shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>)|
|
||||
(shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>))|
|
||||
(shareddriveparentid <DriveFolderID>)|(shareddriveparent <SharedDriveName>)|
|
||||
(shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>)|
|
||||
(shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>)
|
||||
|
||||
<DriveOrderByFieldName> ::=
|
||||
createddate|createdtime|
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
- [Remove domainCanFind-domainWithLink ACLs for internal domain](#remove-domaincanfind-domainwithlink-acls-for-internal-domain)
|
||||
- [Remove My Drive ACLs for external domains](#remove-my-drive-acls-for-external-domains)
|
||||
- [Remove anyoneCanFind-anyoneWithLink ACLs](#remove-anyonecanfind-anyonewithlink-acls)
|
||||
- [Target Audiences](#target-audiences)
|
||||
|
||||
## API documentation
|
||||
* [Drive API - Permissions](https://developers.google.com/drive/api/v3/reference/permissions)
|
||||
@@ -143,6 +144,7 @@ specify `basicpermissions` and additional permission fields, e.g., `permissions.
|
||||
<FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVDataSelector>
|
||||
See: https://github.com/GAM-team/GAM/wiki/Collections-of-Items
|
||||
```
|
||||
|
||||
## GUI API permission name mapping
|
||||
|
||||
| GUI setting | API setting |
|
||||
@@ -357,8 +359,8 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
|
||||
|
||||
For example, to get the ACLs for your Team Drives with the Team Drive name included in the output:
|
||||
```
|
||||
gam redirect csv ./TeamDrives.csv print teamdrives
|
||||
gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls teamdriveid "~id" addtitle "~name" fields id,domain,emailaddress,role,type,deleted
|
||||
gam redirect csv ./SharedDrives.csv print shareddrives
|
||||
gam redirect csv ./SharedDriveACLs.csv multiprocess csv ./SharedDrives.csv gam print drivefileacls shareddriveid "~id" addtitle "~name" fields id,domain,emailaddress,role,type,deleted
|
||||
```
|
||||
|
||||
## Delete all ACLs except owner from a file
|
||||
@@ -616,3 +618,51 @@ Delete those Shared Drive ACLs.
|
||||
```
|
||||
gam config num_threads 20 redirect stdout ./DeleteSharedDriveShares.txt multiprocess redirect stderr stdout csv SharedDriveShares.csv gam user "~Owner" delete drivefileacl "~id" "id:~~permission.id~~"
|
||||
```
|
||||
|
||||
## Target Audiences
|
||||
|
||||
* See: https://support.google.com/a/answer/9934697
|
||||
|
||||
You can manage target audiences in the admin console at Directory/Target audiences.
|
||||
If you click on a target audience the URL will look like this: `https://admin.google.com/ac/targetaudiences/02xcytpi0xrdqxi`
|
||||
|
||||
You can add this target audience to a file with:
|
||||
```
|
||||
gam user user@domain.com create drivefileacl <DriveFileID> domain 02xcytpi0xrdqxi.audience.googledomains.com role reader
|
||||
User: user@domain.com, Add 1 Drive File/Folder ACL
|
||||
User: user@domain.com, Drive File/Folder ID: <DriveFileID>, Permission ID: 02xcytpi0xrdqxi.audience.googledomains.com, Added
|
||||
Test Audience
|
||||
id: 02897912034288871303
|
||||
type: domain
|
||||
domain: 02xcytpi0xrdqxi.audience.googledomains.com
|
||||
role: reader
|
||||
permissionDetails:
|
||||
role: reader
|
||||
type: file
|
||||
inherited: False
|
||||
allowFileDiscovery: False
|
||||
```
|
||||
|
||||
You can update the target audience role with:
|
||||
```
|
||||
gam user user@domain.com update drivefileacl <DriveFileID> id:02897912034288871303 role writer
|
||||
User: user@domain.com, Update 1 Drive File/Folder ACL
|
||||
User: user@domain.com, Drive File/Folder ID: <DriveFileID>, Permission ID: 02897912034288871303, Updated
|
||||
Test Audience
|
||||
id: 02897912034288871303
|
||||
type: domain
|
||||
domain: 02xcytpi0xrdqxi.audience.googledomains.com
|
||||
role: writer
|
||||
permissionDetails:
|
||||
role: writer
|
||||
type: file
|
||||
inherited: False
|
||||
allowFileDiscovery: False
|
||||
```
|
||||
|
||||
You can delete the target audience from a file with:
|
||||
```
|
||||
gam user user@domain.com delete drivefileacl <DriveFileID> id:02897912034288871303
|
||||
User: user@domain.com, Delete 1 Drive File/Folder ACL
|
||||
User: user@domain.com, Drive File/Folder ID: <DriveFileID>, Permission ID: 02897912034288871303, Deleted
|
||||
```
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
(parentid <DriveFolderID>)|
|
||||
(parentname <DriveFolderName>)|
|
||||
(anyownerparentname <DriveFolderName>)|
|
||||
(teamdriveparentid <DriveFolderID>)|
|
||||
(teamdriveparent <SharedDriveName>)|
|
||||
(teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>)|
|
||||
(teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>)
|
||||
(shareddriveparentid <DriveFolderID>)|
|
||||
(shareddriveparent <SharedDriveName>)|
|
||||
(shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>)|
|
||||
(shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>)
|
||||
|
||||
```
|
||||
## Create shortcuts
|
||||
@@ -48,10 +48,10 @@ There are two modes of operaton:
|
||||
* `parentid <DriveFolderID>` - Folder ID.
|
||||
* `parentname <DriveFolderName>` - Folder name; the folder must be owned by `<UserTypeEntity>`.
|
||||
* `anyownerparentname <DriveFolderName>` - Folder name; the folder can be owned by any user, `<UserTypeEntity>` must be able to write to the folder.
|
||||
* `teamdriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specfic Shared Drive folder.
|
||||
* `teamdriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* `shareddriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specfic Shared Drive folder.
|
||||
* `shareddriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* `convertparents` - Convert all but the last parent reference in `<DriveFileEntity>` to shortcuts. My testing shows that as parents are added to a file, they are added to the front of the parents list; thus, the last parent is the original parent.
|
||||
|
||||
If neither `<DriveFileParentAttribute>` nor `convertparents` are specified, the shortcut is placed in the root folder (My Drive).
|
||||
@@ -142,6 +142,6 @@ gam csv Shortcuts.csv matchfield code 4 gam user "~owner" create drivefileshortc
|
||||
|
||||
## Check shortcut validity on Shared Drives
|
||||
```
|
||||
gam redirect csv ./TDShortcuts.csv user organizer@domain.com print filelist select teamdriveid <SharedDriveID> showmimetype gshortcut fields id
|
||||
gam redirect csv ./TDShortcuts.csv user organizer@domain.com print filelist select shareddriveid <SharedDriveID> showmimetype gshortcut fields id
|
||||
gam redirect csv ./Shortcuts.csv user organizer@domain.com check drivefileshortcut csvfile TDShortcuts.csv:id csv
|
||||
```
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
- [API documentation](#api-documentation)
|
||||
- [Definitions](#definitions)
|
||||
- [Aliases](#aliases)
|
||||
- [Delegation Notification](#delegation-notification)
|
||||
- [Delegator Banner](#delegator-banner)
|
||||
- [Delegate Notification](#delegate-notification)
|
||||
- [Create Gmail delegates](#create-gmail-delegates)
|
||||
- [Delete Gmail delegates](#delete-gmail-delegates)
|
||||
- [Update Gmail delegates](#update-gmail-delegates)
|
||||
@@ -57,7 +58,13 @@ The `convertalias` option causes GAM to make an extra API call per user in `<Use
|
||||
to convert aliases to primary email addresses. If you know that all of the email addresses
|
||||
in `<UserEntity>` are primary, you can omit `convertalias` and avoid the extra API calls.
|
||||
|
||||
## Delegation Notification
|
||||
## Delegator Banner
|
||||
When creating a delegate, the following banner is displayed in the delegator's Gmail inbox, it can not be suppressed.
|
||||
```
|
||||
<Delegate> now has delegated access to your account. This notice will end in 7 days.
|
||||
```
|
||||
|
||||
## Delegate Notification
|
||||
When creating a delegate, you can send a message to the delegate.
|
||||
```
|
||||
[notify [<Boolean>]
|
||||
|
||||
@@ -404,7 +404,7 @@ Messages are archived to the group specified by `<GroupItem>`.
|
||||
### Archive a selected set of messages
|
||||
* `((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])+` - Criteria to select messages
|
||||
* `labelids <LabelIDList>` - Select messages with labels that match all of the specified label IDs.
|
||||
* `max_to_archive` - Limit the number of messages that will be archived; use a value of 0 for no limit
|
||||
* `max_to_archive <Number>` - Limit the number of messages that will be archived; use a value of 0 for no limit
|
||||
* `doit` - No messages are archived unless you specify `doit`. By not specifying `doit`, you can preview the messages selected to verify that the results match your expectations.
|
||||
|
||||
When `matchlabel <LabelName>` is specified, the following characters are replaced with a `-` in the generated query.
|
||||
@@ -412,8 +412,8 @@ When `matchlabel <LabelName>` is specified, the following characters are replace
|
||||
&()"|{}/
|
||||
```
|
||||
|
||||
By default, Gam fetches all matching messages from Google and then processes only `max_to_archive` of them.
|
||||
To speed up fetching, specify `quick` and only `max_to_archive` of the matching messages will be fetched.
|
||||
By default, Gam fetches all matching messages from Google and then archives only `max_to_archive <Number>` of them.
|
||||
To speed up fetching, specify `quick` and only `max_to_archive <Number>` of the matching messages will be fetched.
|
||||
You must still specify `doit` to perform the operation.
|
||||
|
||||
By default, the command results are displayed as indented keys and values. Use the `csv` option
|
||||
@@ -430,8 +430,6 @@ user@domain.com,18e9fc6581b9acab,Archived,
|
||||
user@domain.com,18e9fc58c5491f4c,Archived,
|
||||
```
|
||||
|
||||
See below for message selection.
|
||||
|
||||
## Export messages/threads
|
||||
Export messages in EML format.
|
||||
```
|
||||
@@ -447,6 +445,9 @@ gam <UserTypeEntity> export thread|threads
|
||||
[targetfolder <FilePath>] [targetname <FileName>] [overwrite [<Boolean>]]
|
||||
```
|
||||
|
||||
By default, Gam fetches all matching messages from Google and then exports only `max_to_export <Number>` of them.
|
||||
To speed up fetching, specify `quick` and only `max_to_export <Number>` of the matching messages will be fetched.
|
||||
|
||||
By default, when exporting a message, it is downloaded to the directory specified in `gam.cfg/drive_dir`.
|
||||
* `targetfolder <FilePath>` - Specify an alternate location for the downloaded file.
|
||||
|
||||
@@ -461,7 +462,18 @@ By default, when exporting a message, an existing local file will not be overwri
|
||||
* `overwrite true` - Overwite an existing file
|
||||
* `overwrite false` - Do not overwite an existing file; add a numeric prefix and create a new file
|
||||
|
||||
See below for message selection.
|
||||
### Export a specific set of messages
|
||||
* `ids <MessageIDEntity>` - A list of message ids
|
||||
|
||||
### Export a selected set of messages
|
||||
* `((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])+` - Criteria to select messages
|
||||
* `labelids <LabelIDList>` - Select messages with labels that match all of the specified label IDs.
|
||||
* `max_to_export <Number>` - Limit the number of messages that will be exported; use a value of 0 for no limit
|
||||
|
||||
When `matchlabel <LabelName>` is specified, the following characters are replaced with a `-` in the generated query.
|
||||
```
|
||||
&()"|{}/
|
||||
```
|
||||
|
||||
## Forward messages/threads
|
||||
```
|
||||
@@ -477,6 +489,9 @@ gam <UserTypeEntity> forward thread|threads recipient|to <RecipientEntity>
|
||||
[subject <String>] [addorigfieldstosubject]
|
||||
```
|
||||
|
||||
By default, Gam fetches all matching messages from Google and then forwards only `max_to_forward <Number>` of them.
|
||||
To speed up fetching, specify `quick` and only `max_to_forwrd <Number>` of the matching messages will be fetched.
|
||||
|
||||
By default, the message subject has `Fwd: ` prepended; use `subject <String>` to specify a new subject.
|
||||
|
||||
All `Cc` addresses are removed from the forwarded message.
|
||||
@@ -486,7 +501,19 @@ If `addorigfieldstosubject` is specified, GAM appends the original `from`, `to`
|
||||
Fwd: Ross to TestUser (Original From: Ross Scroggs <ross.scroggs@gmail.com> To: testuser@domain.com Date: Thu, 23 Nov 2023 07:01:59 -0800)
|
||||
```
|
||||
|
||||
See below for message selection.
|
||||
### Forward a specific set of messages
|
||||
* `ids <MessageIDEntity>` - A list of message ids
|
||||
|
||||
### Forward a selected set of messages
|
||||
* `((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])+` - Criteria to select messages
|
||||
* `labelids <LabelIDList>` - Select messages with labels that match all of the specified label IDs.
|
||||
* `max_to_forward <Number>` - Limit the number of messages that will be forwarded; use a value of 0 for no limit
|
||||
* `doit` - No messages are processed unless you specify `doit`. By not specifying `doit`, you can preview the messages selected to verify that the results match your expectations.
|
||||
|
||||
When `matchlabel <LabelName>` is specified, the following characters are replaced with a `-` in the generated query.
|
||||
```
|
||||
&()"|{}/
|
||||
```
|
||||
|
||||
## Manage messages/threads
|
||||
```
|
||||
@@ -538,7 +565,7 @@ user@domain.com,18e9fc58c5491f4c,Deleted,
|
||||
### Manage a selected set of messages
|
||||
* `((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])+` - Criteria to select messages
|
||||
* `labelids <LabelIDList>` - Select messages with labels that match all of the specified label IDs.
|
||||
* `max_to_xxx` - Limit the number of messages that will be processed; use a value of 0 for no limit
|
||||
* `max_to_xxx <Number>` - Limit the number of messages that will be processed; use a value of 0 for no limit
|
||||
* `doit` - No messages are processed unless you specify `doit`. By not specifying `doit`, you can preview the messages selected to verify that the results match your expectations.
|
||||
|
||||
When `matchlabel <LabelName>` is specified, the following characters are replaced with a `-` in the generated query.
|
||||
@@ -546,8 +573,8 @@ When `matchlabel <LabelName>` is specified, the following characters are replace
|
||||
&()"|{}/
|
||||
```
|
||||
|
||||
By default, Gam fetches all matching messages from Google and then processes only `max_to_process` of them.
|
||||
To speed up fetching, specify `quick` and only `max_to_process` of the matching messages will be fetched.
|
||||
By default, Gam fetches all matching messages from Google and then processes only `max_to_process <Number>` of them.
|
||||
To speed up fetching, specify `quick` and only `max_to_process <Number>` of the matching messages will be fetched.
|
||||
You must still specify `doit` to perform the operation.
|
||||
|
||||
## Delete messages by Message-Id
|
||||
@@ -611,7 +638,7 @@ gam <UserTypeEntity> print messages|threads [todrive <ToDriveAttribute>*]
|
||||
```
|
||||
## Display all messages
|
||||
By default, Gam displays all messages.
|
||||
* `max_to_xxx` - Limit the number of messages that will be displayed
|
||||
* `max_to_print|max_to_show <Number>` - Limit the number of messages that will be displayed
|
||||
* `includespamtrash` - Include messages in the Spam and Trash folders
|
||||
|
||||
By default, all messages in a thread are displayed with `print|show threads`.
|
||||
@@ -627,7 +654,7 @@ gam user user@domain.com print|show threads maxmessagesperthread 1
|
||||
## Display a selected set of messages
|
||||
* `((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])+` - Criteria to select messages
|
||||
* `labelids <LabelIDList>` - Select messages with labels that match all of the specified label IDs.
|
||||
* `max_to_xxx` - Limit the number of messages that will be displayed
|
||||
* `max_to_print|max_to_show <Number>` - Limit the number of messages that will be displayed
|
||||
* `includespamtrash` - Include messages in the Spam and Trash folders
|
||||
* `labelmatchpattern <REMatchPattern>` - Only display messages with some label that matches `<REMatchPattern>`
|
||||
* `labelmatchpattern xyz` - Label must start with xyz
|
||||
@@ -641,8 +668,8 @@ When `matchlabel <LabelName>` is specified, the following characters are replace
|
||||
&()"|{}/
|
||||
```
|
||||
|
||||
By default, Gam fetches only `max_to_process` matching messages from Google and then displays them.
|
||||
To see how many messages actually match, specify `notquick` and all matching messages will be fetched; only `max_to_process` of them will be displayed.
|
||||
By default, Gam fetches only `max_to_process <Number>` matching messages from Google and then displays them.
|
||||
To see how many messages actually match, specify `notquick` and all matching messages will be fetched; only `max_to_process <Number>` of them will be displayed.
|
||||
|
||||
### Difference between `From` and `Sender` headers
|
||||
The `From` header specifies the author of the message, that is,
|
||||
|
||||
@@ -586,6 +586,9 @@ Getting all Groups for testuser1@domain.com (1/2)
|
||||
Getting all Groups for testuser2@domain.com (2/2)
|
||||
User,Groups,GroupsList
|
||||
testuser2@domain.com,0,
|
||||
|
||||
# Get group membership for all users
|
||||
$ gam config auto_batch_min 1 num_threads 10 redirect csv ./UsersGroups.csv multiprocess sortheaders User,Group redirect stderr - multiprocess all users print groups
|
||||
```
|
||||
### Display groups and their parents
|
||||
Display a user's groups and their parents as an indented list.
|
||||
|
||||
@@ -148,6 +148,8 @@
|
||||
<CSVkmdSelector> |
|
||||
<CSVDataSelector>
|
||||
|
||||
<QuerySharedDrive> ::= <String> See: https://developers.google.com/workspace/drive/api/guides/search-shareddrives
|
||||
|
||||
<SharedDriveACLRole> ::=
|
||||
manager|organizer|owner|
|
||||
contentmanager|fileorganizer|
|
||||
@@ -159,8 +161,8 @@
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveID>|
|
||||
(teamdriveid <SharedDriveID>)|(teamdriveid:<SharedDriveID>)|
|
||||
(teamdrive <SharedDriveName>)|(teamdrive:<SharedDriveName>)
|
||||
(shareddriveid <SharedDriveID>)|(shareddriveid:<SharedDriveID>)|
|
||||
(shareddrive <SharedDriveName>)|(shareddrive:<SharedDriveName>)
|
||||
|
||||
<SharedDriveFieldName> ::=
|
||||
backgroundimagefile|
|
||||
@@ -174,10 +176,10 @@
|
||||
themeid
|
||||
<SharedDriveFieldNameList> ::= "<SharedDriveFieldName>(,<SharedDriveFieldName>)*"
|
||||
|
||||
<SharedDriveIDEntity> ::= (teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::= (teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
<SharedDriveFileQueryEntity> ::= (teamdrivequery <QueryDriveFile>) | (teamdrivequery:<QueryDriveFile>)
|
||||
<SharedDriveIDEntity> ::= (shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::= (shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
<SharedDriveFileQueryEntity> ::= (shareddrivequery <QueryDriveFile>) | (shareddrivequery:<QueryDriveFile>)
|
||||
<SharedDriveFileQueryShortcut> ::=
|
||||
all_files | all_folders | all_google_files | all_non_google_files | all_items
|
||||
|
||||
@@ -291,11 +293,11 @@ When either of these options is chosen, no infomation about Shared Drive restric
|
||||
To retrieve the Shared Drive ID with `returnidonly`:
|
||||
```
|
||||
Linux/MacOS
|
||||
teamDriveId=$(gam user user@domain.com create shareddrive ... returnidonly)
|
||||
shareddriveId=$(gam user user@domain.com create shareddrive ... returnidonly)
|
||||
Windows PowerShell
|
||||
$teamDriveId = & gam user user@domain.com create shareddrive ... returnidonly
|
||||
$shareddriveId = & gam user user@domain.com create shareddrive ... returnidonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam user user@domain.com create shareddrive ... returnidonly') do set teamDriveId=%a
|
||||
for /f "delims=" %a in ('gam user user@domain.com create shareddrive ... returnidonly') do set shareddriveId=%a
|
||||
```
|
||||
|
||||
## Bulk Create Shared Drives
|
||||
@@ -417,12 +419,12 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
|
||||
Display the number of Shared Drives.
|
||||
```
|
||||
gam <UserTypeEntity> show|print shareddrives
|
||||
[teamdriveadminquery|query <QueryTeamDrive>]
|
||||
[shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
showitemcountonly
|
||||
```
|
||||
By default, all Shared Drives are counted; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
|
||||
@@ -466,7 +468,7 @@ Options `shareddriveadminquery|query` and `shareddrives|teamdrives` are mutually
|
||||
Options `shareddriveadminquery|query` and `orgunit|org|ou` require `adminaccess|asadmin`.
|
||||
|
||||
By default, organizers for all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddrives|teamdrives <SharedDriveIDList>` - Select the Shared Drive IDs specified in `<SharedDriveIDList>`
|
||||
* `shareddrives|teamdrives select <FileSelector>|<CSVFileSelector>` - Select the Shared Drive IDs specified in `<FileSelector>|<CSVFileSelector>`
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
@@ -486,7 +488,7 @@ To select organizers from any domain, use: `domainlist ""`
|
||||
|
||||
For example, to get a single user organizer from your domain for all Shared Drives including no organizer drives:
|
||||
```
|
||||
gam redirect csv ./TeamDriveOrganizers.csv print shareddriveorganizers
|
||||
gam redirect csv ./ShareddriveOrganizers.csv print shareddriveorganizers
|
||||
```
|
||||
|
||||
## Manage Shared Drive access
|
||||
@@ -596,14 +598,14 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
|
||||
## Display Shared Drive access for selected Shared Drives
|
||||
```
|
||||
gam <UserTypeEntity> show shareddriveacls
|
||||
adminaccess [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
adminaccess [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
[user|group <EmailAddress> [checkgroups]] (role|roles <SharedDriveACLRoleList>)*
|
||||
<PermissionMatch>* [<PermissionMatchAction>] [pmselect]
|
||||
[oneitemperrow] [<DrivePermissionsFieldName>*|(fields <DrivePermissionsFieldNameList>)]
|
||||
[formatjson [quotechar <Character>]]
|
||||
gam <UserTypeEntity> print shareddriveacls [todrive <ToDriveAttribute>*]
|
||||
adminaccess [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
adminaccess [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
[user|group <EmailAddress> [checkgroups]] (role|roles <SharedDriveACLRoleList>)*
|
||||
<PermissionMatch>* [<PermissionMatchAction>] [pmselect]
|
||||
@@ -615,7 +617,7 @@ Shared Drives in the workspace, `<UserTypeEntity>` should specify a super admin
|
||||
option shoud be used.
|
||||
|
||||
By default, all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
* `<PermissionMatch>* [<PermissionMatchAction>] pmselect` - Use permission matching to select Shared Drives
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
- [Print user list](#print-user-list)
|
||||
- [Display user counts](#display-user-counts)
|
||||
- [Verify domain membership](#verify-domain-membership)
|
||||
- [Guest Users](#guest-users)
|
||||
|
||||
## API documentation
|
||||
* [Directory API - Users](https://developers.google.com/admin-sdk/directory/reference/rest/v1/users)
|
||||
@@ -169,6 +170,7 @@ queries "`"orgUnitPath=\'/Students/Lower\ School/2027\'`",`"orgUnitPath=\'/Stude
|
||||
fullname|
|
||||
gender|
|
||||
givenname|firstname|
|
||||
guestaccountinfo|
|
||||
id|
|
||||
ims|im|
|
||||
includeinglobaladdresslist|gal|
|
||||
@@ -176,6 +178,7 @@ queries "`"orgUnitPath=\'/Students/Lower\ School/2027\'`",`"orgUnitPath=\'/Stude
|
||||
isdelegatedadmin|admin|isadmin|
|
||||
isenforcedin2sv|is2svenforced|
|
||||
isenrolledin2sv|is2svenrolled|
|
||||
isguestuser|
|
||||
ismailboxsetup|
|
||||
keyword|keywords|
|
||||
language|languages|
|
||||
@@ -1384,3 +1387,9 @@ testuser1@domain.com,118080758787650801331,True,Test User 1
|
||||
testuserxxx@domain.com,,False,Test User XXX
|
||||
testuser2@domain.com,107344800159717682514,True,Test User 2
|
||||
```
|
||||
|
||||
## Guest Users
|
||||
* See: https://support.google.com/a/answer/16558545
|
||||
```
|
||||
gam create guestuser <EmailAddress>
|
||||
```
|
||||
|
||||
@@ -3,23 +3,23 @@
|
||||
Print the current version of Gam with details
|
||||
```
|
||||
gam version
|
||||
GAM 7.32.05 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.34.07 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.2 64-bit final
|
||||
macOS Tahoe 26.2 x86_64
|
||||
Path: /Users/Admin/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2025-12-23T13:57:00-08:00
|
||||
Python 3.14.3 64-bit final
|
||||
macOS Tahoe 26.3 arm64
|
||||
Path: /Users/gamteam/bin/gam7
|
||||
Config File: /Users/gamteam/GamConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2026-02-15T07:51:00-08:00
|
||||
```
|
||||
|
||||
Print the current version of Gam with details and time offset information
|
||||
```
|
||||
gam version timeoffset
|
||||
GAM 7.32.05 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.34.07 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.2 64-bit final
|
||||
macOS Tahoe 26.2 x86_64
|
||||
Path: /Users/Admin/bin/gam7
|
||||
Python 3.14.3 64-bit final
|
||||
macOS Tahoe 26.3 arm64
|
||||
Path: /Users/gamteam/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Your system time differs from www.googleapis.com by less than 1 second
|
||||
```
|
||||
@@ -27,29 +27,29 @@ Your system time differs from www.googleapis.com by less than 1 second
|
||||
Print the current version of Gam with extended details and SSL information
|
||||
```
|
||||
gam version extended
|
||||
GAM 7.32.05 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.34.07 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.2 64-bit final
|
||||
macOS Tahoe 26.2 x86_64
|
||||
Path: /Users/Admin/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2025-12-23T13:57:00-08:00
|
||||
Python 3.14.3 64-bit final
|
||||
macOS Tahoe 26.3 arm64
|
||||
Path: /Users/gamteam/bin/gam7
|
||||
Config File: /Users/gamteam/GamConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2026-02-15T07:51:00-08:00
|
||||
Your system time differs from admin.googleapis.com by less than 1 second
|
||||
OpenSSL 3.5.3 16 Sep 2025
|
||||
arrow 1.3.0
|
||||
OpenSSL 3.6.1 27 Jan 2026
|
||||
arrow 1.4.0
|
||||
chardet 5.2.0
|
||||
cryptography 46.0.1
|
||||
filelock 3.19.1
|
||||
google-api-python-client 2.182.0
|
||||
google-auth-httplib2 0.2.0
|
||||
google-auth-oauthlib 1.2.2
|
||||
google-auth 2.40.3
|
||||
lxml 6.0.1
|
||||
httplib2 0.31.0
|
||||
cryptography 46.0.5
|
||||
filelock 3.21.2
|
||||
google-api-python-client 2.190.0
|
||||
google-auth-httplib2 0.3.0
|
||||
google-auth-oauthlib 1.2.4
|
||||
google-auth 2.48.0
|
||||
lxml 6.0.2
|
||||
httplib2 0.31.2
|
||||
passlib 1.7.4
|
||||
pathvalidate 3.3.1
|
||||
pyscard 2.3.0
|
||||
yubikey-manager 5.8.0
|
||||
pyscard 2.3.1
|
||||
yubikey-manager 5.9.0
|
||||
admin.googleapis.com connects using TLSv1.3 TLS_AES_256_GCM_SHA384
|
||||
```
|
||||
|
||||
@@ -65,10 +65,10 @@ google-api-python-client 2.77.0
|
||||
httplib2 0.16.0
|
||||
oauth2client 4.1.3
|
||||
MacOS High Sierra 10.13.6 x86_64
|
||||
Path: /Users/Admin/bin/gam7
|
||||
Path: /Users/gamteam/bin/gam7
|
||||
Version Check:
|
||||
Current: 5.35.08
|
||||
Latest: 7.32.05
|
||||
Latest: 7.34.07
|
||||
echo $?
|
||||
1
|
||||
```
|
||||
@@ -76,7 +76,7 @@ echo $?
|
||||
Print the current version number without details
|
||||
```
|
||||
gam version simple
|
||||
7.32.05
|
||||
7.34.07
|
||||
```
|
||||
In Linux/MacOS you can do:
|
||||
```
|
||||
@@ -86,13 +86,13 @@ echo $VER
|
||||
Print the current version of Gam and address of this Wiki
|
||||
```
|
||||
gam help
|
||||
GAM 7.32.05 - https://github.com/GAM-team/GAM
|
||||
GAM 7.34.07 - https://github.com/GAM-team/GAM
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.2 64-bit final
|
||||
macOS Tahoe 26.2 x86_64
|
||||
Path: /Users/Admin/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2025-12-23T13:57:00-08:00
|
||||
Help: Syntax in file /Users/Admin/bin/gam7/GamCommands.txt
|
||||
Python 3.14.3 64-bit final
|
||||
macOS Tahoe 26.3 arm64
|
||||
Path: /Users/gamteam/bin/gam7
|
||||
Config File: /Users/gamteam/GamConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2026-02-15T07:51:00-08:00
|
||||
Help: Syntax in file /Users/gamteam/bin/gam7/GamCommands.txt
|
||||
Help: Documentation is at https://github.com/GAM-team/GAM/wiki
|
||||
```
|
||||
|
||||
@@ -74,6 +74,7 @@ Client Access
|
||||
* [Calendars](Calendars)
|
||||
* [Calendars - Access](Calendars-Access)
|
||||
* [Calendars - Events](Calendars-Events)
|
||||
* [Calendars - Secondary Calendars with no Owner](No-Owner-Secondary-Calendars)
|
||||
* [Chrome Auto Update Expiration Counts](Chrome-AUE-Counts)
|
||||
* [Chrome Browser Cloud Management](Chrome-Browser-Cloud-Management)
|
||||
* [Chrome Device Counts](Chrome-Device-Counts)
|
||||
|
||||
@@ -232,6 +232,11 @@ csv_output_header_order
|
||||
Any headers in the file but not in the list will appear after
|
||||
the headers in the list
|
||||
Default: ''
|
||||
csv_output_header_required
|
||||
A list of <Strings> used to specify column headers
|
||||
for inclusion in the CSV file written by a gam print command
|
||||
even if the API didn't return any data for those columns.
|
||||
Default: ''
|
||||
csv_output_line_terminator
|
||||
Allowed values: cr, lf, crlf
|
||||
Designates character(s) used to terminate the lines of a CSV file.
|
||||
@@ -307,6 +312,21 @@ debug_level
|
||||
debug_redaction
|
||||
Enable/disable redaction of sensitive data from API debugging output
|
||||
Default: True
|
||||
developer_preview_apis
|
||||
A comma separated list of APIs requiring a Developer Preview key.
|
||||
Default: Blank
|
||||
Valid values:
|
||||
accesscontextmanager,admin,alertcenter,analyticsadmin,calendar-json,cbcm,chat,
|
||||
chromemanagement,chromepolicy,classroom,cloudchannel,cloudidentity,
|
||||
cloudresourcemanager,contactdelegation,contacts,datastudio,docs,drive,
|
||||
driveactivity,drivelabels,email-audit,forms,gmail,groupsmigration,groupssettings,
|
||||
iam,iamcredentials,keep,licensing,meet,mybusinessaccountmanagement,oauth2,
|
||||
orgpolicy,people,pubsub,reseller,searchconsole,serviceaccountlookup,
|
||||
servicemanagement,serviceusage,sheets,siteVerification,storage,tagmanager,tasks,
|
||||
vault,versionhistory,youtube
|
||||
developer_preview_api_key
|
||||
A Developer Preview API key that is passed to all API calls for APIs in developer_preview_apis
|
||||
Default: Blank
|
||||
device_max_results
|
||||
When retrieving lists of ChromeOS devices from API,
|
||||
how many should be retrieved in each API call
|
||||
@@ -325,13 +345,6 @@ drive_max_results
|
||||
how many should be retrieved in each API call
|
||||
Default: 1000
|
||||
Range: 1 - 1000
|
||||
drive_v3_beta
|
||||
Enable/disable use of Drive API v3 beta for Limited Folder Access testing
|
||||
Default: False
|
||||
drive_v3_native_names
|
||||
Enable/disable use of Drive API v3 native column names
|
||||
in all gam print/show commands related to Google Drive
|
||||
Default: True
|
||||
email_batch_size
|
||||
When archiving, printing, showing, trashing, untrashing, marking as spam Gmail messages.
|
||||
how many should be processed in each batch
|
||||
@@ -371,7 +384,7 @@ gmail_cse_inkey_dir
|
||||
Default: Blank
|
||||
input_dir
|
||||
Input directory for files with non-absolute file names.
|
||||
The default
|
||||
The default is the current working directory.
|
||||
Default: .
|
||||
inter_batch_wait
|
||||
When processing items in batches, how many seconds should GAM wait between batches
|
||||
@@ -387,9 +400,6 @@ license_skus
|
||||
Each item in the list can be a <SKUID> which will be validated or
|
||||
a <ProductID>/<SKUID> which will not be validated.
|
||||
Default: Blank
|
||||
meet_v2_beta
|
||||
Enable/disable use of Meet API v2 beta for additional Chat Space parameters.
|
||||
Default: False
|
||||
member_max_results
|
||||
When retrieving lists of Google Group members from API,
|
||||
how many should be retrieved in each API call
|
||||
@@ -404,7 +414,7 @@ member_max_results_ci_basic
|
||||
member_max_results_ci_full
|
||||
When retrieving lists of Cloud Identity Group members from API
|
||||
with either the basic or full options,
|
||||
how many should be retrieved in each API call
|
||||
how many should be retrieved in each API call
|
||||
Default: 500
|
||||
Range: 1 - 500
|
||||
message_batch_size
|
||||
@@ -577,7 +587,7 @@ timezone
|
||||
to your local timezone. If you are running GAM on a remote computer or on a
|
||||
cloud shell, "local" will mean the time at the remote/cloud shell computer,
|
||||
not your location, Use "+|-hh:mm" to specify the timezone at your location.
|
||||
Starting with version 7.21.00 you can use a timezone name; the names are case sensitive.
|
||||
Starting with version 7.21.00 you can use a timezone name; the names are case sensitive.
|
||||
See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
Default: utc
|
||||
Range: utc|z|local|(+|-hh:mm)|<ValidTimezoneName>
|
||||
@@ -725,6 +735,8 @@ Section: DEFAULT
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_header_required = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
@@ -937,7 +949,7 @@ Edit gam.cfg to set up additional clients; it should look like this when complet
|
||||
activity_max_results = 100
|
||||
admin_email = ''
|
||||
api_calls_rate_check = false
|
||||
api_calls_rate_limit = 1000
|
||||
api_calls_rate_limit = 100
|
||||
api_calls_tries_limit = 10
|
||||
auto_batch_min = 0
|
||||
bail_on_internal_error_tries = 2
|
||||
@@ -947,55 +959,75 @@ cache_dir = /Users/admin/.gam/gamcache
|
||||
cache_discovery_only = true
|
||||
channel_customer_id = ''
|
||||
charset = utf-8
|
||||
cmdlog = ''
|
||||
cmdlog_max_backups = 5
|
||||
cmdlog_max_kilo_bytes = 1000
|
||||
chat_max_results = 100
|
||||
classroom_max_results = 0
|
||||
client_secrets_json = client_secrets.json
|
||||
clock_skew_in_seconds = 10
|
||||
cmdlog = ''
|
||||
cmdlog_max_backups = 5
|
||||
cmdlog_max_kilo_bytes = 1000
|
||||
config_dir = /Users/admin/.gam
|
||||
contact_max_results = 100
|
||||
csv_input_column_delimiter = ,
|
||||
csv_input_no_escape_char = true
|
||||
csv_input_quote_char = '"'
|
||||
csv_input_row_drop_filter = ''
|
||||
csv_input_row_drop_filter_mode = anymatch
|
||||
csv_input_row_filter = ''
|
||||
csv_input_row_filter_mode = allmatch
|
||||
csv_input_row_limit = 0
|
||||
csv_output_column_delimiter = ,
|
||||
csv_output_convert_cr_nl = false
|
||||
csv_output_field_delimiter = ' '
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_header_required = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
csv_output_row_drop_filter =
|
||||
csv_output_row_drop_filter = ''
|
||||
csv_output_row_drop_filter_mode = anymatch
|
||||
csv_output_row_filter = ''
|
||||
csv_output_row_filter_mode = allmatch
|
||||
csv_output_row_limit = 0
|
||||
csv_output_sort_headers = ''
|
||||
csv_output_subfield_delimiter = '.'
|
||||
csv_output_timestamp_column = ''
|
||||
csv_output_users_audit = false
|
||||
customer_id = my_customer
|
||||
debug_level = 0
|
||||
debug_redaction = true
|
||||
developer_preview_api_key = ''
|
||||
developer_preview_apis = ''
|
||||
device_max_results = 200
|
||||
domain =
|
||||
domain = ''
|
||||
drive_dir = /Users/admin/Downloads
|
||||
drive_max_results = 1000
|
||||
drive_v3_native_names = true
|
||||
email_batch_size = 100
|
||||
email_batch_size = 50
|
||||
enable_dasa = false
|
||||
enable_gcloud_reauth = false
|
||||
enforce_expansive_access = true
|
||||
event_max_results = 250
|
||||
extra_args =
|
||||
extra_args = ''
|
||||
gmail_cse_incert_dir = ''
|
||||
gmail_cse_inkey_dir = ''
|
||||
input_dir = .
|
||||
inter_batch_wait = 0
|
||||
license_max_results = 100
|
||||
license_sku = ''
|
||||
license_skus = ''
|
||||
member_max_results = 200
|
||||
member_max_results_ci_basic = 1000
|
||||
member_max_results_ci_full = 500
|
||||
message_batch_size = 50
|
||||
message_max_results = 1000
|
||||
message_max_results = 500
|
||||
mobile_max_results = 100
|
||||
multiprocess_pool_limit = 0
|
||||
never_time = Never
|
||||
no_browser = false
|
||||
no_cache = false
|
||||
no_short_urls = true
|
||||
no_update_check = true
|
||||
no_verify_ssl = false
|
||||
num_tbatch_threads = 2
|
||||
@@ -1009,9 +1041,10 @@ print_agu_domains = ''
|
||||
print_cros_ous = ''
|
||||
print_cros_ous_and_children = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = False
|
||||
quick_info_user = False
|
||||
quick_cros_move = false
|
||||
quick_info_user = false
|
||||
reseller_id = ''
|
||||
retry_api_service_not_available = false
|
||||
section =
|
||||
show_api_calls_retry_data = false
|
||||
show_commands = false
|
||||
@@ -1026,26 +1059,28 @@ smtp_password = ''
|
||||
smtp_username = ''
|
||||
timezone = utc
|
||||
tls_max_version = ''
|
||||
tls_min_version = 'TLSv1_2'
|
||||
tls_min_version = 'TLSv1_3'
|
||||
todrive_clearfilter = false
|
||||
todrive_clientaccess = false
|
||||
todrive_conversion = true
|
||||
todrive_localcopy = false
|
||||
todrive_locale = ''
|
||||
todrive_no_escape_char = true
|
||||
todrive_nobrowser = false
|
||||
todrive_noemail = true
|
||||
todrive_no_escape_char = true
|
||||
todrive_parent = root
|
||||
todrive_sheet_timeformat = ''
|
||||
todrive_sheet_timestamp = false
|
||||
todrive_timeformat = ''
|
||||
todrive_timestamp = false
|
||||
todrive_timezone = ''
|
||||
todrive_upload_nodata = true
|
||||
todrive_user = ''
|
||||
truncate_client_id = false
|
||||
update_cros_ou_with_id = false
|
||||
use_chat_admin_access = false
|
||||
use_course_owner_access = false
|
||||
use_projectid_as_name = false
|
||||
|
||||
user_max_results = 500
|
||||
user_service_account_access_only = false
|
||||
|
||||
|
||||
Reference in New Issue
Block a user