Added option onelicenseperrow|onelicenceperrow to gam print users ... licenses

This commit is contained in:
Ross Scroggs
2024-01-17 20:14:18 -08:00
parent 647da9f980
commit 432ef09129
7 changed files with 103 additions and 25 deletions

View File

@@ -11,6 +11,24 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
See [Downloads](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads) for Windows or other options, including manual installation
### 6.67.20
Added option `onelicenseperrow|onelicenceperrow` to `gam print users ... licenses` that causes GAM to print
a seperate user information row for each license a user is assigned. This makes processing
the licenses in a script possible and allows better sorting in a CSV File.
By default, all licenses for a user are displayed in a list on one row:
```
primaryEmail,LicensesCount,Licenses,LicensesDisplay
user@domain.com,2,1010020020 1010330004,Google Workspace Enterprise Plus Google Voice Standard
```
With `onelicenseperrow|onelicenceperrow`, each license is on a separate row:
```
primaryEmail,License,LicenseDisplay
user@domain.com,1010020020,Google Workspace Enterprise Plus
user@domain.com 1010330004,Google Voice Standard
```
### 6.67.19
Updated `gam create|update user ... notify` to encode the characters `<>&` in the password

View File

@@ -334,7 +334,7 @@ writes the credentials into the file oauth2.txt.
admin@server:/Users/admin/bin/gamadv-xtd3$ rm -f /Users/admin/GAMConfig/oauth2.txt
admin@server:/Users/admin/bin/gamadv-xtd3$ ./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.17.19 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.67.20 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.1 64-bit final
MacOS Sonoma 14.2.1 x86_64
@@ -1002,7 +1002,7 @@ writes the credentials into the file oauth2.txt.
C:\GAMADV-XTD3>del C:\GAMConfig\oauth2.txt
C:\GAMADV-XTD3>gam version
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
GAMADV-XTD3 6.17.19 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.67.20 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.1 64-bit final
Windows-10-10.0.17134 AMD64

View File

@@ -980,7 +980,9 @@ gam print users [todrive <ToDriveAttribute>*]
([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitItem>] [deleted_only|only_deleted])
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns] [license|licenses|licence|licences]
[groups|groupsincolumns]
[license|licenses|licence|licences]
[onelicenseperrow|onelicenceperrow]
[schemas|custom|customschemas all|<SchemaNameList>]
[emailpart|emailparts|username]
[userview] [allfields|basic|full|(<UserFieldName>*|fields <UserFieldNameList>)]
@@ -1003,6 +1005,7 @@ gam print users [todrive <ToDriveAttribute>*] select <UserTypeEntity>
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns]
[license|licenses|licence|licences|licensebyuser|licensesbyuser|licencebyuser|licencesbyuser]
[onelicenseperrow|onelicenceperrow]
[(products|product <ProductIDList>)|(skus|sku <SKUIDList>)]
[schemas|custom|customschemas all|<SchemaNameList>]
[emailpart|emailparts|username][schemas|custom all|<SchemaNameList>]
@@ -1014,6 +1017,7 @@ gam <UserTypeEntity> print users [todrive <ToDriveAttribute>*]
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns]
[license|licenses|licence|licences|licensebyuser|licensesbyuser|licencebyuser|licencesbyuser]
[onelicenseperrow|onelicenceperrow]
[(products|product <ProductIDList>)|(skus|sku <SKUIDList>)]
[schemas|custom|customschemas all|<SchemaNameList>]
[emailpart|emailparts|username]
@@ -1058,6 +1062,17 @@ to limit the display of aliases to those that match `<RegularExpression>`.
By default, the entries in lists of groups and licenses are separated by the `csv_output_field_delimiter` from `gam.cfg`.
* `delimiter <Character>` - Separate list items with `<Character>`
By default, all licenses for a user are displayed in a list on one row:
```
primaryEmail,LicensesCount,Licenses,LicensesDisplay
user@domain.com,2,1010020020 1010330004,Google Workspace Enterprise Plus Google Voice Standard
```
With `onelicenseperrow|onelicenceperrow`, each license is on a separate row:
```
primaryEmail,License,LicenseDisplay
user@domain.com,1010020020,Google Workspace Enterprise Plus
user@domain.com 1010330004,Google Voice Standard
```
In the output, primaryEmail is the always the first column; these options control the sorting of the remaining columns.
* `allfields|basic|full` - All other columns are sorted by name.
* `sortheaders [true]` - All other columns are sorted by name.

View File

@@ -4,7 +4,7 @@
Print the current version of Gam with details
```
gam version
GAMADV-XTD3 6.17.19 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.67.20 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.1 64-bit final
MacOS Sonoma 14.2.1 x86_64
@@ -16,7 +16,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.17.19 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.67.20 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.1 64-bit final
MacOS Sonoma 14.2.1 x86_64
@@ -28,7 +28,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.17.19 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.67.20 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.1 64-bit final
MacOS Sonoma 14.2.1 x86_64
@@ -65,7 +65,7 @@ MacOS High Sierra 10.13.6 x86_64
Path: /Users/Admin/bin/gamadv-xtd3
Version Check:
Current: 5.35.08
Latest: 6.17.19
Latest: 6.67.20
echo $?
1
```
@@ -73,7 +73,7 @@ echo $?
Print the current version number without details
```
gam version simple
6.17.19
6.67.20
```
In Linux/MacOS you can do:
```
@@ -83,7 +83,7 @@ echo $VER
Print the current version of Gam and address of this Wiki
```
gam help
GAM 6.17.19 - https://github.com/taers232c/GAMADV-XTD3
GAM 6.67.20 - https://github.com/taers232c/GAMADV-XTD3
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.1 64-bit final
MacOS Sonoma 14.2.1 x86_64

View File

@@ -5348,6 +5348,7 @@ gam print users [todrive <ToDriveAttribute>*]
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns]
[license|licenses|licence|licences|licensebyuser|licensesbyuser|licencebyuser|licencesbyuser]
[onelicenseperrow|onelicenceperrow]
[(products|product <ProductIDList>)|(skus|sku <SKUIDList>)]
[schemas|custom|customschemas all|<SchemaNameList>]
[emailpart|emailparts|username]
@@ -5363,6 +5364,7 @@ gam print users [todrive <ToDriveAttribute>*] select <UserTypeEntity>
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns]
[license|licenses|licence|licences|licensebyuser|licensesbyuser|licencebyuser|licencesbyuser]
[onelicenseperrow|onelicenceperrow]
[(products|product <ProductIDList>)|(skus|sku <SKUIDList>)]
[schemas|custom|customschemas all|<SchemaNameList>]
[emailpart|emailparts|username]
@@ -5376,6 +5378,7 @@ gam <UserTypeEntity> print users [todrive <ToDriveAttribute>*]
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns]
[license|licenses|licence|licences|licensebyuser|licensesbyuser|licencebyuser|licencesbyuser]
[onelicenseperrow|onelicenceperrow]
[(products|product <ProductIDList>)|(skus|sku <SKUIDList>)]
[schemas|custom|customschemas all|<SchemaNameList>]
[emailpart|emailparts|username]
@@ -5778,7 +5781,7 @@ gam <UserTypeEntity> create workinglocation
(home|
(custom <String>)|
(office <String> [building|buildingid <String>] [floor|floorname <String>]
[section|floorsection <String>] [desk|deskcode <String>]))
[section|floorsection <String>] [desk|deskcode <String>]))
((date yyyy-mm-dd)|
(range yyyy-mm-dd yyyy-mm-dd)|
(daily yyyy-mm-dd N)|

View File

@@ -2,6 +2,24 @@
Merged GAM-Team version
6.67.20
Added option `onelicenseperrow|onelicenceperrow` to `gam print users ... licenses` that causes GAM to print
a seperate user information row for each license a user is assigned. This makes processing
the licenses in a script possible and allows better sorting in a CSV File.
By default, all licenses for a user are displayed in a list on one row:
```
primaryEmail,LicensesCount,Licenses,LicensesDisplay
user@domain.com,2,1010020020 1010330004,Google Workspace Enterprise Plus Google Voice Standard
```
With `onelicenseperrow|onelicenceperrow`, each license is on a separate row:
```
primaryEmail,License,LicenseDisplay
user@domain.com,1010020020,Google Workspace Enterprise Plus
user@domain.com 1010330004,Google Voice Standard
```
6.67.19
Updated `gam create|update user ... notify` to encode the characters `<>&` in the password

View File

@@ -42598,6 +42598,15 @@ USERS_INDEXED_TITLES = ['addresses', 'aliases', 'nonEditableAliases', 'emails',
# [issuspended <Boolean>]
# [showitemcountonly]
def doPrintUsers(entityList=None):
def _writeUserEntity(userEntity):
row = flattenJSON(userEntity, skipObjects=USER_SKIP_OBJECTS, timeObjects=USER_TIME_OBJECTS)
if not FJQC.formatJSON:
csvPF.WriteRowTitles(row)
elif csvPF.CheckRowTitles(row):
csvPF.WriteRowNoFilter({'primaryEmail': userEntity['primaryEmail'],
'JSON': json.dumps(cleanJSON(userEntity, skipObjects=USER_SKIP_OBJECTS, timeObjects=USER_TIME_OBJECTS),
ensure_ascii=False, sort_keys=True)})
def _printUser(userEntity, i, count):
if isSuspended is None or isSuspended == userEntity.get('suspended', isSuspended):
userEmail = userEntity['primaryEmail']
@@ -42635,24 +42644,31 @@ def doPrintUsers(entityList=None):
badRequestWarning(Ent.GROUP, Ent.MEMBER, userEmail)
except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
accessErrorExit(cd)
if aliasMatchPattern and 'aliases' in userEntity:
userEntity['aliases'] = [alias for alias in userEntity['aliases'] if aliasMatchPattern.match(alias)]
if printOptions['getLicenseFeed'] or printOptions['getLicenseFeedByUser']:
if printOptions['getLicenseFeed']:
u_licenses = licenses.get(userEmail.lower(), [])
else:
u_licenses = getUserLicenses(lic, userEntity, skus)
userEntity['LicensesCount'] = len(u_licenses)
if not oneLicensePerRow:
userEntity['LicensesCount'] = len(u_licenses)
if u_licenses:
userEntity['Licenses'] = delimiter.join(u_licenses)
userEntity['LicensesDisplay'] = delimiter.join([SKU.skuIdToDisplayName(skuId) for skuId in u_licenses])
else:
u_licenses = []
if not oneLicensePerRow:
_writeUserEntity(userEntity)
else:
if u_licenses:
userEntity['Licenses'] = delimiter.join(u_licenses)
userEntity['LicensesDisplay'] = delimiter.join([SKU.skuIdToDisplayName(skuId) for skuId in u_licenses])
if aliasMatchPattern and 'aliases' in userEntity:
userEntity['aliases'] = [alias for alias in userEntity['aliases'] if aliasMatchPattern.match(alias)]
row = flattenJSON(userEntity, skipObjects=USER_SKIP_OBJECTS, timeObjects=USER_TIME_OBJECTS)
if not FJQC.formatJSON:
csvPF.WriteRowTitles(row)
elif csvPF.CheckRowTitles(row):
csvPF.WriteRowNoFilter({'primaryEmail': userEmail,
'JSON': json.dumps(cleanJSON(userEntity, skipObjects=USER_SKIP_OBJECTS, timeObjects=USER_TIME_OBJECTS),
ensure_ascii=False, sort_keys=True)})
for skuId in u_licenses:
userEntity['License'] = skuId
userEntity['LicenseDisplay'] = SKU.skuIdToDisplayName(skuId)
_writeUserEntity(userEntity)
else:
userEntity['License'] = userEntity['LicenseDisplay'] = ''
_writeUserEntity(userEntity)
def _updateDomainCounts(emailAddress):
atLoc = emailAddress.find('@')
@@ -42718,7 +42734,7 @@ def doPrintUsers(entityList=None):
projection = 'basic'
projectionSet = False
customFieldMask = None
quotePlusPhoneNumbers = showDeleted = False
oneLicensePerRow = quotePlusPhoneNumbers = showDeleted = False
aliasMatchPattern = isSuspended = orgUnitPath = orgUnitPathLower = orderBy = sortOrder = None
viewType = 'admin_view'
delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
@@ -42781,6 +42797,8 @@ def doPrintUsers(entityList=None):
elif myarg in {'licensebyuser', 'licensesbyuser', 'licencebyuser', 'licencesbyuser'}:
printOptions['getLicenseFeedByUser'] = True
printOptions['getLicenseFeed'] = False
elif myarg in {'onelicenseperrow', 'onelicenceperrow'}:
oneLicensePerRow = True
elif myarg in {'products', 'product'}:
skus = SKU.convertProductListToSKUList(getGoogleProductList())
elif myarg in {'sku', 'skus'}:
@@ -42818,7 +42836,10 @@ def doPrintUsers(entityList=None):
else:
csvPF.AddTitles(['Groups'])
if printOptions['getLicenseFeed'] or printOptions['getLicenseFeedByUser']:
csvPF.AddTitles(['LicensesCount', 'Licenses', 'LicensesDisplay'])
if not oneLicensePerRow:
csvPF.AddTitles(['LicensesCount', 'Licenses', 'LicensesDisplay'])
else:
csvPF.AddTitles(['License', 'LicenseDisplay'])
if printOptions['getLicenseFeed']:
if skus is None and GM.Globals[GM.LICENSE_SKUS]:
skus = GM.Globals[GM.LICENSE_SKUS]
@@ -42958,7 +42979,10 @@ def doPrintUsers(entityList=None):
else:
csvPF.MoveTitlesToEnd(['Groups']+[f'Groups{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{j}' for j in range(printOptions['maxGroups'])])
if printOptions['getLicenseFeed'] or printOptions['getLicenseFeedByUser']:
csvPF.MoveTitlesToEnd(['LicensesCount', 'Licenses', 'LicensesDisplay'])
if not oneLicensePerRow:
csvPF.MoveTitlesToEnd(['LicensesCount', 'Licenses', 'LicensesDisplay'])
else:
csvPF.MoveTitlesToEnd(['License', 'LicenseDisplay'])
elif not FJQC.formatJSON:
for domain, count in sorted(iter(domainCounts.items())):
csvPF.WriteRowNoFilter({'domain': domain, 'count': count})