Compare commits

...

2 Commits

Author SHA1 Message Date
Ross Scroggs
f777ec177c Updated code to work around a Cryptography library change 2024-07-22 08:37:33 -07:00
Ross Scroggs
19304f95e8 Added command to check if an OU contains items 2024-07-21 13:14:15 -07:00
9 changed files with 301 additions and 35 deletions

View File

@@ -32,6 +32,7 @@ ENTITY_IS_A_USER_ALIAS_RC = 21
ENTITY_IS_A_GROUP_RC = 22
ENTITY_IS_A_GROUP_ALIAS_RC = 23
ENTITY_IS_AN_UNMANAGED_ACCOUNT_RC = 24
ORGUNIT_NOT_EMPTY_RC = 25
CHECK_USER_GROUPS_ERROR_RC = 29
ORPHANS_COLLECTED_RC = 30
# Warnings/Errors
@@ -61,4 +62,14 @@ TARGET_DRIVE_SPACE_ERROR_RC = 74
USER_REQUIRED_TO_CHANGE_PASSWORD_ERROR_RC = 75
USER_SUSPENDED_ERROR_RC = 76
NO_CSV_DATA_TO_UPLOAD_RC = 77
NO_SA_ACCESS_CONTEXT_MANAGER_EDITOR_ROLE_RC = 78
ACCESS_POLICY_ERROR_RC = 79
YUBIKEY_CONNECTION_ERROR_RC = 80
YUBIKEY_INVALID_KEY_TYPE_RC = 81
YUBIKEY_INVALID_SLOT_RC = 82
YUBIKEY_INVALID_PIN_RC = 83
YUBIKEY_APDU_ERROR_RC = 84
YUBIKEY_VALUE_ERROR_RC = 85
YUBIKEY_MULTIPLE_CONNECTED_RC = 86
YUBIKEY_NOT_FOUND_RC = 87
```

View File

@@ -10,9 +10,20 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
See [Downloads-Installs](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads-Installs) for Windows or other options, including manual installation
### 6.79.00
Updated code to work around a Cryptography library change that caused service account private key creation to fail.
### 6.78.00
Added command to check if an OU contains items; this is useful when tryng to delete an OU
as it must not contain any items in order to be deleted.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Organizational-Units#check-organizational-unit-for-contained-items
### 6.77.18
Added option `showitemcountonly` to `gam print domainaliasess` that causes GAM to display the
Added option `showitemcountonly` to `gam print domainaliases` that causes GAM to display the
number of domain aliasess on stdout; no CSV file is written.
### 6.77.17

View File

@@ -251,7 +251,7 @@ 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
GAMADV-XTD3 6.77.18 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.79.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64
@@ -923,7 +923,7 @@ 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
GAMADV-XTD3 6.77.18 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.79.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
Windows-10-10.0.17134 AMD64

View File

@@ -15,6 +15,7 @@
- [Print organizational units](#print-organizational-units)
- [Display organizational unit counts](#display-organizational-unit-counts)
- [Display indented organizational unit tree](#display-indented-organizational-unit-tree)
- [Check organizational unit for contained items](#check-organizational-unit-for-contained-items)
- [Special case handling for large number of organizational units](#special-case-handling-for-large-number-of-organizational-units)
## API documentation
@@ -270,6 +271,44 @@ gam show orgtree [fromparent <OrgUnitItem>] [batchsuborgs [<Boolean>]]
By default, Gam displays the organizational unit tree starting at /.
* `fromparent <OrgUnitItem>` - Display the organizational unit tree starting at `<OrgUnitItem>`.
## Check organizational unit for contained items
An organizational unit can be deleted only when it contains no items:
* Chrome Browsers
* ChromeOS Devices
* Shared Drives
* Sub Org Units
* Users
This command counts those items and displays a CSV file with the item counts.
* All counts are zero - A return code of 0 is returned and the `empty` column is `True`
* Some count is greater than 0 - A return code of 25 is returned and the `empty` column is `False`
Only items directly within the OU are counted, items in sub-OUs are not counted.
```
<OrgUnitCheckName> ::=
browsers|
devices|
shareddrives|
subous|
users
<OrgUnitCheckNameList> ::= "<OrgUnitCheckName>(,<OrgUnitCheckName>)*"
gam check org|ou <OrgUnitItem> [todrive <ToDriveAttribute>*]
[<OrgUnitCheckName>*|(fields <OrgUnitCheckNameList>)]
[formatjson [quotechar <Character>]]
```
By default, GAM checks each of the five items; you can select specfic fields
with `<OrgUnitCheckName>*` or `fields <OrgUnitCheckNameList>`.
By default, GAM displays the information as columns of fields; the following option causes the output to be in JSON format:
* `formatjson` - Display the fields in JSON format.
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
## Special case handling for large number of organizational units
By default, the `print orgs` and `show orgtree` commands issue a single API call to get the

View File

@@ -3,7 +3,7 @@
Print the current version of Gam with details
```
gam version
GAMADV-XTD3 6.77.18 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.79.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64
@@ -15,7 +15,7 @@ Time: 2023-06-02T21:10:00-07:00
Print the current version of Gam with details and time offset information
```
gam version timeoffset
GAMADV-XTD3 6.77.18 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.79.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64
@@ -27,7 +27,7 @@ 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
GAMADV-XTD3 6.77.18 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.79.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64
@@ -64,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64
Path: /Users/Admin/bin/gamadv-xtd3
Version Check:
Current: 5.35.08
Latest: 6.77.18
Latest: 6.79.00
echo $?
1
```
@@ -72,7 +72,7 @@ echo $?
Print the current version number without details
```
gam version simple
6.77.18
6.79.00
```
In Linux/MacOS you can do:
```
@@ -82,7 +82,7 @@ echo $VER
Print the current version of Gam and address of this Wiki
```
gam help
GAM 6.77.18 - https://github.com/taers232c/GAMADV-XTD3
GAM 6.79.00 - https://github.com/taers232c/GAMADV-XTD3
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64

View File

@@ -3275,8 +3275,7 @@ gam delete domain <DomainName>
gam info domain [<DomainName>]
[formatjson]
gam print domains
[todrive <ToDriveAttribute>*]
gam print domains [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]]
[showitemcountonly]
gam show domains
@@ -3286,10 +3285,14 @@ gam show domains
gam create|add domainalias|aliasdomain <DomainAlias> <DomainName>
gam delete domainalias|aliasdomain <DomainAlias>
gam info domainalias|aliasdomain <DomainAlias> [formatjson]
gam info domainalias|aliasdomain <DomainAlias>
[formatjson]
gam print domainaliases|aliasdomains [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]]
gam show domainaliases|aliasdomains [formatjson]
[showitemcountonly]
gam show domainaliases|aliasdomains
[formatjson]
[showitemcountonly]
# Domain - Contacts and Global Address List
@@ -4214,6 +4217,18 @@ gam print orgs|ous [todrive <ToDriveAttribute>*]
[showitemcountonly]
gam show orgtree [fromparent <OrgUnitItem>] [batchsuborgs [<Boolean>]]
<OrgUnitCheckName> ::=
browsers|
devices|
shareddrives|
subous|
users
<OrgUnitCheckNameList> ::= "<OrgUnitCheckName>(,<OrgUnitCheckName>)*"
gam check org|ou <OrgUnitItem> [todrive <ToDriveAttribute>*]
[<OrgUnitCheckName>*|(fields <OrgUnitCheckNameList>)]
[formatjson [quotechar <Character>]]
# Printers
<PrinterAttribute> ::=

View File

@@ -1,7 +1,23 @@
6.79.00
Updated code to work around a Cryptography library change that caused service account private key creation to fail.
7.00.00
Merged GAM-Team version
6.78.00
Added command to check if an OU contains items; this is useful when tryng to delete an OU
as it must not contain any items in order to be deleted.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Organizational-Units#check-organizational-unit-for-contained-items
6.77.18
Added option `showitemcountonly` to `gam print domainaliases` that causes GAM to display the
number of domain aliasess on stdout; no CSV file is written.
6.77.17
Added option `showitemcountonly` to `gam print domains` that causes GAM to display the

View File

@@ -332,6 +332,7 @@ ENTITY_IS_A_USER_ALIAS_RC = 21
ENTITY_IS_A_GROUP_RC = 22
ENTITY_IS_A_GROUP_ALIAS_RC = 23
ENTITY_IS_AN_UNMANAGED_ACCOUNT_RC = 24
ORGUNIT_NOT_EMPTY_RC = 25
CHECK_USER_GROUPS_ERROR_RC = 29
ORPHANS_COLLECTED_RC = 30
# Warnings/Errors
@@ -8890,14 +8891,6 @@ def getTodriveOnly(csvPF):
else:
unknownArgumentExit()
def getTodriveFJQCOnly(csvPF, FJQC, addTitle=False, noExit=False):
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters()
else:
FJQC.GetFormatJSONQuoteChar(myarg, addTitle, noExit)
DEFAULT_SKIP_OBJECTS = {'kind', 'etag', 'etags', '@type'}
# Clean a JSON object
@@ -12355,8 +12348,8 @@ def _generatePrivateKeyAndPublicCert(projectId, clientEmail, name, key_size, b64
writeStdout(Msg.EXTRACTING_PUBLIC_CERTIFICATE+'\n')
public_key = private_key.public_key()
builder = x509.CertificateBuilder()
builder = builder.subject_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, name)]))
builder = builder.issuer_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, name)]))
builder = builder.subject_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, name, _validate=False)]))
builder = builder.issuer_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, name, _validate=False)]))
# Gooogle seems to enforce the not before date strictly. Set the not before
# date to be UTC two minutes ago which should cover any clock skew.
now = datetime.datetime.utcnow()
@@ -15763,18 +15756,33 @@ def _printDomain(domain, csvPF):
DOMAIN_ALIAS_SORT_TITLES = ['domainAliasName', 'parentDomainName', 'creationTime', 'verified']
# gam print domainaliases [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]]
# gam show domainaliases [formatjson]
# gam print domainaliases [todrive <ToDriveAttribute>*]
# [formatjson [quotechar <Character>]]
# [showitemcountonly]
# gam show domainaliases
# [formatjson]
# [showitemcountonly]
def doPrintShowDomainAliases():
cd = buildGAPIObject(API.DIRECTORY)
csvPF = CSVPrintFile(['domainAliasName'], DOMAIN_ALIAS_SORT_TITLES) if Act.csvFormat() else None
FJQC = FormatJSONQuoteChar(csvPF)
getTodriveFJQCOnly(csvPF, FJQC, True)
showItemCountOnly = False
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters()
elif myarg == 'showitemcountonly':
showItemCountOnly = True
else:
FJQC.GetFormatJSONQuoteChar(myarg, True)
try:
domainAliases = callGAPIitems(cd.domainAliases(), 'list', 'domainAliases',
throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
customer=GC.Values[GC.CUSTOMER_ID])
count = len(domainAliases)
if showItemCountOnly:
writeStdout(f'{count}\n')
return
i = 0
for domainAlias in domainAliases:
i += 1
@@ -17477,7 +17485,11 @@ def _getOrgUnits(cd, orgUnitPath, fieldsList, listType, showParent, batchSubOrgs
else:
fields = ','.join(set(fieldsList))
listfields = f'organizationUnits({fields})'
printGettingAllAccountEntities(Ent.ORGANIZATIONAL_UNIT)
if listType == 'all' and orgUnitPath == '/':
printGettingAllAccountEntities(Ent.ORGANIZATIONAL_UNIT)
else:
printGettingAllEntityItemsForWhom(Ent.CHILD_ORGANIZATIONAL_UNIT, orgUnitPath,
qualifier=' (Direct Children)' if listType == 'children' else '', entityType=Ent.ORGANIZATIONAL_UNIT)
if listType == 'children':
batchSubOrgs = False
try:
@@ -17515,7 +17527,10 @@ def _getOrgUnits(cd, orgUnitPath, fieldsList, listType, showParent, batchSubOrgs
except (GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.backendError,
GAPI.badRequest, GAPI.invalidCustomerId, GAPI.loginRequired):
pass
printGotAccountEntities(len(orgUnits))
if listType == 'all' and orgUnitPath == '/':
printGotAccountEntities(len(orgUnits))
else:
printGotEntityItemsForWhom(len(orgUnits))
if childSelector is not None:
for orgUnit in orgUnits:
orgUnit[ORG_UNIT_SELECTOR_FIELD] = childSelector if orgUnit['orgUnitPath'] != orgUnitPath else parentSelector
@@ -17748,6 +17763,157 @@ def doShowOrgTree():
for org in sorted(orgTree):
printOrgUnit(org, orgTree)
ORG_ITEMS_FIELD_MAP = {
'browsers': 'browsers',
'devices': 'devices',
'shareddrives': 'sharedDrives',
'subous': 'subOus',
'users': 'users',
}
# gam check org|ou <OrgUnitItem> [todrive <ToDriveAttribute>*]
# [<OrgUnitCheckName>*|(fields <OrgUnitCheckNameList>)]
# [formatjson [quotechar <Character>]]
def doCheckOrgUnit():
cd = buildGAPIObject(API.DIRECTORY)
csvPF = CSVPrintFile(['orgUnitPath', 'orgUnitId', 'empty'])
FJQC = FormatJSONQuoteChar(csvPF)
orgUnitPath = None
fieldsList = []
titlesList = []
status, orgUnitPath, orgUnitId = checkOrgUnitPathExists(cd, getOrgUnitItem())
orgUnitPathLower = orgUnitPath.lower()
if not status:
entityDoesNotExistExit(Ent.ORGANIZATIONAL_UNIT, orgUnitPath)
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters()
elif myarg in ORG_ITEMS_FIELD_MAP:
fieldsList.append(myarg)
elif myarg == 'fields':
for field in _getFieldsList():
if field in ORG_ITEMS_FIELD_MAP:
fieldsList.append(field)
else:
invalidChoiceExit(field, list(ORG_ITEMS_FIELD_MAP), True)
else:
FJQC.GetFormatJSONQuoteChar(myarg, True)
if orgUnitPath is None:
missingArgumentExit('orgunit <OrgUnitItem>')
if not fieldsList:
fieldsList = ORG_ITEMS_FIELD_MAP.keys()
orgUnitItemCounts = {}
for field in sorted(fieldsList):
title = ORG_ITEMS_FIELD_MAP[field]
orgUnitItemCounts[title] = 0
if not FJQC.formatJSON:
titlesList.append(title)
if 'browsers' in fieldsList:
cbcm = buildGAPIObject(API.CBCM)
customerId = _getCustomerIdNoC()
printGettingAllEntityItemsForWhom(Ent.CHROME_BROWSER, orgUnitPath, entityType=Ent.ORGANIZATIONAL_UNIT)
pageMessage = getPageMessage()
try:
feed = yieldGAPIpages(cbcm.chromebrowsers(), 'list', 'browsers',
pageMessage=pageMessage, messageAttribute='deviceId',
throwReasons=[GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.INVALID_ORGUNIT, GAPI.FORBIDDEN],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
customer=customerId, orgUnitPath=orgUnitPath, projection='BASIC',
fields='nextPageToken,browsers(deviceId)')
for browsers in feed:
orgUnitItemCounts['browsers'] += len(browsers)
except (GAPI.invalidInput, GAPI.forbidden) as e:
entityActionFailedWarning([Ent.CHROME_BROWSER, None], str(e))
except GAPI.invalidOrgunit as e:
entityActionFailedExit([Ent.CHROME_BROWSER, None], str(e))
except (GAPI.badRequest, GAPI.resourceNotFound):
accessErrorExit(None)
if 'devices' in fieldsList:
printGettingAllEntityItemsForWhom(Ent.CROS_DEVICE, orgUnitPath, entityType=Ent.ORGANIZATIONAL_UNIT)
pageMessage = getPageMessageForWhom()
pageToken = None
totalItems = 0
tokenRetries = 0
while True:
try:
feed = callGAPI(cd.chromeosdevices(), 'list',
throwReasons=[GAPI.INVALID_INPUT, GAPI.INVALID_ORGUNIT,
GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
pageToken=pageToken, customerId=GC.Values[GC.CUSTOMER_ID],
orgUnitPath=orgUnitPath, fields='nextPageToken,chromeosdevices(deviceId)', maxResults=GC.Values[GC.DEVICE_MAX_RESULTS])
tokenRetries = 0
pageToken, totalItems = _processGAPIpagesResult(feed, 'chromeosdevices', None, totalItems, pageMessage, None, Ent.CROS_DEVICE)
if feed:
orgUnitItemCounts['devices'] += len(feed.get('chromeosdevices', []))
del feed
if not pageToken:
_finalizeGAPIpagesResult(pageMessage)
printGotAccountEntities(totalItems)
break
except GAPI.invalidInput as e:
message = str(e)
# Invalid Input: xyz - Check for invalid pageToken!!
# 0123456789012345
if message[15:] == pageToken:
tokenRetries += 1
if tokenRetries <= 2:
writeStderr(f'{WARNING_PREFIX}{Msg.LIST_CHROMEOS_INVALID_INPUT_PAGE_TOKEN_RETRY}')
time.sleep(tokenRetries*5)
continue
entityActionFailedWarning([Ent.CROS_DEVICE, None], message)
break
entityActionFailedWarning([Ent.CROS_DEVICE, None], message)
break
except GAPI.invalidOrgunit as e:
entityActionFailedExit([Ent.CROS_DEVICE, None], str(e))
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
accessErrorExit(cd)
if 'shareddrives' in fieldsList:
ci = buildGAPIObject(API.CLOUDIDENTITY_ORGUNITS_BETA)
printGettingAllEntityItemsForWhom(Ent.SHAREDDRIVE, orgUnitPath, entityType=Ent.ORGANIZATIONAL_UNIT)
sds = callGAPIpages(ci.orgUnits().memberships(), 'list', 'orgMemberships',
pageMessage=getPageMessageForWhom(),
parent=f'orgUnits/{orgUnitId[3:]}',
customer=_getCustomersCustomerIdWithC(),
filter="type == 'shared_drive'")
orgUnitItemCounts['sharedDrives'] = len(sds)
if 'subous' in fieldsList:
orgUnitItemCounts['subOus'] = len(_getOrgUnits(cd, orgUnitPath, ['orgUnitPath'], 'children', False, False, None, None))
if 'users' in fieldsList:
printGettingAllEntityItemsForWhom(Ent.USER, orgUnitPath, entityType=Ent.ORGANIZATIONAL_UNIT)
pageMessage = getPageMessageForWhom()
try:
feed = yieldGAPIpages(cd.users(), 'list', 'users',
pageMessage=pageMessage,
throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND,
GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None),
fields='nextPageToken,users(orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
for users in feed:
for user in users:
if orgUnitPathLower == user.get('orgUnitPath', '').lower():
orgUnitItemCounts['users'] += 1
except (GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.invalidInput, GAPI.badRequest, GAPI.backendError,
GAPI.invalidCustomerId, GAPI.loginRequired, GAPI.resourceNotFound, GAPI.forbidden):
checkEntityDNEorAccessErrorExit(cd, Ent.ORGANIZATIONAL_UNIT, orgUnitPath)
empty = True
for count in orgUnitItemCounts.values():
if count > 0:
empty = False
break
baseRow = {'orgUnitPath': orgUnitPath, 'orgUnitId': orgUnitId, 'empty': empty}
row = flattenJSON(orgUnitItemCounts, baseRow.copy())
if not FJQC.formatJSON:
csvPF.WriteRowTitles(row)
elif csvPF.CheckRowTitles(row):
baseRow['JSON'] = json.dumps(cleanJSON(orgUnitItemCounts), ensure_ascii=False, sort_keys=True)
csvPF.WriteRowNoFilter(baseRow)
csvPF.writeCSVfile(f'OrgUnit {orgUnitPath} Item Counts')
if not empty and GM.Globals[GM.SYSEXITRC] == 0:
setSysExitRC(ORGUNIT_NOT_EMPTY_RC)
ALIAS_TARGET_TYPES = ['user', 'group', 'target']
# gam create aliases|nicknames <EmailAddressEntity> user|group|target <UniqueID>|<EmailAddress>
@@ -25050,10 +25216,10 @@ def doPrintShowBrowsers():
else:
entityActionFailedWarning([Ent.CHROME_BROWSER, None], str(e))
return
except GAPI.invalidOrgunit as e:
except (GAPI.invalidOrgunit, GAPI.forbidden) as e:
entityActionFailedWarning([Ent.CHROME_BROWSER, None], str(e))
return
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
except (GAPI.badRequest, GAPI.resourceNotFound):
accessErrorExit(None)
else:
sortRows = True
@@ -43806,18 +43972,21 @@ def doPrintUsers(entityList=None):
projection=schemaParms['projection'], customFieldMask=schemaParms['customFieldMask'],
maxResults=maxResults, **kwargs)
for users in feed:
if showItemCountOnly:
itemCount += len(users)
continue
if orgUnitPath is None:
if not printOptions['countOnly']:
if showItemCountOnly:
itemCount += len(users)
elif not printOptions['countOnly']:
for user in users:
_printUser(user, 0, 0)
else:
for user in users:
_updateDomainCounts(user['primaryEmail'])
else:
if not printOptions['countOnly']:
if showItemCountOnly:
for user in users:
if orgUnitPathLower == user.get('orgUnitPath', '').lower():
itemCount += 1
elif not printOptions['countOnly']:
for user in users:
if orgUnitPathLower == user.get('orgUnitPath', '').lower():
_printUser(user, 0, 0)
@@ -63853,7 +64022,9 @@ def doPrintShowOrgunitSharedDrives():
if csvPF and FJQC.formatJSON:
csvPF.SetJSONTitles(['name', 'JSON'])
_, orgUnitId = getOrgUnitId(None, orgUnitPath)
printGettingAllEntityItemsForWhom(Ent.SHAREDDRIVE, orgUnitPath, entityType=Ent.ORGANIZATIONAL_UNIT)
sds = callGAPIpages(ci.orgUnits().memberships(), 'list', 'orgMemberships',
pageMessage=getPageMessageForWhom(),
parent=f'orgUnits/{orgUnitId[3:]}',
customer=_getCustomersCustomerIdWithC(),
filter="type == 'shared_drive'")
@@ -73666,6 +73837,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
{Cmd.ARG_SVCACCT: doCheckUpdateSvcAcct,
Cmd.ARG_USERINVITATION: doCheckCIUserInvitations,
Cmd.ARG_ISINVITABLE: doCheckCIUserInvitations,
Cmd.ARG_ORG: doCheckOrgUnit,
}
),
'clear':

View File

@@ -94,6 +94,7 @@ class GamEntity():
CHAT_MESSAGE_ID = 'chmi'
CHAT_SPACE = 'chsp'
CHAT_THREAD = 'chth'
CHILD_ORGANIZATIONAL_UNIT = 'corg'
CHROME_APP = 'capp'
CHROME_APP_DEVICE = 'capd'
CHROME_BROWSER = 'chbr'
@@ -435,6 +436,7 @@ class GamEntity():
CHAT_MEMBER_USER: ['Chat User Members', 'Chat User Member'],
CHAT_SPACE: ['Chat Spaces', 'Chat Space'],
CHAT_THREAD: ['Chat Threads', 'Chat Thread'],
CHILD_ORGANIZATIONAL_UNIT: ['Child Organizational Units', 'Child Organizational Unit'],
CHROME_APP: ['Chrome Applications', 'Chrome Application'],
CHROME_APP_DEVICE: ['Chrome Application Devices', 'Chrome Application Device'],
CHROME_BROWSER: ['Chrome Browsers', 'Chrome Browser'],