Added preview to updateprimaryemail <RegularExpression> <EmailReplacement> [preview]

This commit is contained in:
Ross Scroggs
2026-04-03 11:34:29 -07:00
parent 28adb4b1ce
commit 9c44a11a6a
3 changed files with 40 additions and 21 deletions

View File

@@ -1,3 +1,14 @@
7.39.05
Added optional argument `preview` to `updateprimaryemail <RegularExpression> <EmailReplacement> [preview]`
for the following commands that causes GAM to preview, but not perform, primary email address changes.
This allows verification of the primary email address changes before commiting the changes.
```
gam update group <GroupEntity>
gam update cigroup <GroupEntity>
gam <UserTypeEntity> update user
```
7.39.04 7.39.04
Added `updateprimaryemail <RegularExpression> <EmailReplacement>` option to Added `updateprimaryemail <RegularExpression> <EmailReplacement>` option to

View File

@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
""" """
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>' __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
__version__ = '7.39.04' __version__ = '7.39.05'
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
# pylint: disable=wrong-import-position # pylint: disable=wrong-import-position
@@ -34102,7 +34102,7 @@ UPDATE_GROUP_SUBCMDS = ['add', 'create', 'delete', 'remove', 'clear', 'sync', 'u
GROUP_PREVIEW_TITLES = ['group', 'email', 'role', 'action', 'message'] GROUP_PREVIEW_TITLES = ['group', 'email', 'role', 'action', 'message']
# gam update groups <GroupEntity> [email <EmailAddress>] # gam update groups <GroupEntity> [email <EmailAddress>]
# [updateprimaryemail <RESEarchPattern> <RESubstitution>] # [updateprimaryemail <RESEarchPattern> <RESubstitution> [preview]]
# [copyfrom <GroupItem>] <GroupAttribute>* # [copyfrom <GroupItem>] <GroupAttribute>*
# [security|makesecuritygroup] # [security|makesecuritygroup]
# [admincreated <Boolean>] # [admincreated <Boolean>]
@@ -34532,13 +34532,14 @@ def doUpdateGroups():
gs_body = {} gs_body = {}
ci_body = {} ci_body = {}
verifyNotInvitable = False verifyNotInvitable = False
updatePrimaryEmail = {} updatePrimaryEmail = []
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if myarg == 'email': if myarg == 'email':
body['email'] = getEmailAddress(noUid=True) body['email'] = getEmailAddress(noUid=True)
elif myarg == 'updateprimaryemail': elif myarg == 'updateprimaryemail':
updatePrimaryEmail = getREPatternSubstitution(re.IGNORECASE) updatePrimaryEmail = list(getREPatternSubstitution(re.IGNORECASE))
updatePrimaryEmail.append(checkArgumentPresent(['preview']))
elif myarg == 'admincreated': elif myarg == 'admincreated':
body['adminCreated'] = getBoolean() body['adminCreated'] = getBoolean()
elif myarg == 'getbeforeupdate': elif myarg == 'getbeforeupdate':
@@ -34579,11 +34580,12 @@ def doUpdateGroups():
if updatePrimaryEmail: if updatePrimaryEmail:
if updatePrimaryEmail[0].search(group) is not None: if updatePrimaryEmail[0].search(group) is not None:
body['email'] = re.sub(updatePrimaryEmail[0], updatePrimaryEmail[1], group) body['email'] = re.sub(updatePrimaryEmail[0], updatePrimaryEmail[1], group)
else: if updatePrimaryEmail[2]:
body.pop('email', None) entityActionNotPerformedWarning([Ent.GROUP, group], Msg.UPDATE_PRIMARY_EMAIL_PREVIEW.format(body['email']), i, count)
if not body:
entityActionNotPerformedWarning([Ent.GROUP, group], Msg.PRIMARY_EMAIL_DID_NOT_MATCH_PATTERN.format(updatePrimaryEmail[0].pattern), i, count)
continue continue
else:
entityActionNotPerformedWarning([Ent.GROUP, group], Msg.PRIMARY_EMAIL_DID_NOT_MATCH_PATTERN.format(updatePrimaryEmail[0].pattern), i, count)
continue
if 'email' in body and verifyNotInvitable: if 'email' in body and verifyNotInvitable:
isInvitableUser, _ = _getIsInvitableUser(None, body['email']) isInvitableUser, _ = _getIsInvitableUser(None, body['email'])
if isInvitableUser: if isInvitableUser:
@@ -37050,7 +37052,7 @@ def doCreateCIGroup():
doCreateGroup(ciGroupsAPI=True) doCreateGroup(ciGroupsAPI=True)
# gam update cigroups <GroupEntity> [email <EmailAddress>] # gam update cigroups <GroupEntity> [email <EmailAddress>]
# [updateprimaryemail <RESEarchPattern> <RESubstitution>] # [updateprimaryemail <RESEarchPattern> <RESubstitution> [preview]]
# [copyfrom <GroupItem>] <GroupAttribute>* # [copyfrom <GroupItem>] <GroupAttribute>*
# [security|makesecuritygroup|dynamicsecurity|makedynamicsecuritygroup] # [security|makesecuritygroup|dynamicsecurity|makedynamicsecuritygroup]
# [dynamic <QueryDynamicGroup>] # [dynamic <QueryDynamicGroup>]
@@ -37239,13 +37241,14 @@ def doUpdateCIGroups():
gs_body = {} gs_body = {}
ci_body = {} ci_body = {}
se_body = {} se_body = {}
updatePrimaryEmail = {} updatePrimaryEmail = []
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if myarg == 'email': if myarg == 'email':
ci_body['groupKey'] = {'id': getEmailAddress(noUid=True)} ci_body['groupKey'] = {'id': getEmailAddress(noUid=True)}
elif myarg == 'updateprimaryemail': elif myarg == 'updateprimaryemail':
updatePrimaryEmail = getREPatternSubstitution(re.IGNORECASE) updatePrimaryEmail = list(getREPatternSubstitution(re.IGNORECASE))
updatePrimaryEmail.append(checkArgumentPresent(['preview']))
elif myarg == 'getbeforeupdate': elif myarg == 'getbeforeupdate':
getBeforeUpdate = True getBeforeUpdate = True
elif myarg == 'dynamic': elif myarg == 'dynamic':
@@ -37301,11 +37304,12 @@ def doUpdateCIGroups():
if updatePrimaryEmail: if updatePrimaryEmail:
if updatePrimaryEmail[0].search(group) is not None: if updatePrimaryEmail[0].search(group) is not None:
ci_body['groupKey'] = {'id': re.sub(updatePrimaryEmail[0], updatePrimaryEmail[1], group)} ci_body['groupKey'] = {'id': re.sub(updatePrimaryEmail[0], updatePrimaryEmail[1], group)}
else: if updatePrimaryEmail[2]:
ci_body.pop('groupKey', None) entityActionNotPerformedWarning([Ent.GROUP, group], Msg.UPDATE_PRIMARY_EMAIL_PREVIEW.format(ci_body['groupKey']['id']), i, count)
if not ci_body:
entityActionNotPerformedWarning([Ent.GROUP, group], Msg.PRIMARY_EMAIL_DID_NOT_MATCH_PATTERN.format(updatePrimaryEmail[0].pattern), i, count)
continue continue
else:
entityActionNotPerformedWarning([Ent.GROUP, group], Msg.PRIMARY_EMAIL_DID_NOT_MATCH_PATTERN.format(updatePrimaryEmail[0].pattern), i, count)
continue
if gs_body and not GroupIsAbuseOrPostmaster(group): if gs_body and not GroupIsAbuseOrPostmaster(group):
try: try:
if group.find('@') == -1: # group settings API won't take uid so we make sure cd API is used so that we can grab real email. if group.find('@') == -1: # group settings API won't take uid so we make sure cd API is used so that we can grab real email.
@@ -45794,7 +45798,7 @@ def getUserAttributes(cd, updateCmd, noUid=False):
notFoundBody = {} notFoundBody = {}
notify = {'recipients': [], 'subject': '', 'message': '', 'html': False, 'charset': UTF8, 'password': ''} notify = {'recipients': [], 'subject': '', 'message': '', 'html': False, 'charset': UTF8, 'password': ''}
primary = {} primary = {}
updatePrimaryEmail = {} updatePrimaryEmail = []
groupOrgUnitMap = None groupOrgUnitMap = None
tagReplacements = _initTagReplacements() tagReplacements = _initTagReplacements()
addGroups = {} addGroups = {}
@@ -45846,7 +45850,8 @@ def getUserAttributes(cd, updateCmd, noUid=False):
elif updateCmd and myarg == 'updateoufromgroup': elif updateCmd and myarg == 'updateoufromgroup':
groupOrgUnitMap = _getGroupOrgUnitMap() groupOrgUnitMap = _getGroupOrgUnitMap()
elif updateCmd and myarg == 'updateprimaryemail': elif updateCmd and myarg == 'updateprimaryemail':
updatePrimaryEmail = getREPatternSubstitution(re.IGNORECASE) updatePrimaryEmail = list(getREPatternSubstitution(re.IGNORECASE))
updatePrimaryEmail.append(checkArgumentPresent(['preview']))
elif myarg == 'json': elif myarg == 'json':
body.update(getJSON(USER_JSON_SKIP_FIELDS)) body.update(getJSON(USER_JSON_SKIP_FIELDS))
if 'name' in body and 'fullName' in body['name']: if 'name' in body and 'fullName' in body['name']:
@@ -46324,7 +46329,7 @@ def doCreateGuestUser():
# gam <UserTypeEntity> update user <UserAttribute>* # gam <UserTypeEntity> update user <UserAttribute>*
# [verifynotinvitable|alwaysevict] [noactionifalias] # [verifynotinvitable|alwaysevict] [noactionifalias]
# [updateprimaryemail <RESEarchPattern> <RESubstitution>] # [updateprimaryemail <RESEarchPattern> <RESubstitution> [preview]]
# [updateoufromgroup <CSVFileInput> [keyfield <FieldName>] [datafield <FieldName>]] # [updateoufromgroup <CSVFileInput> [keyfield <FieldName>] [datafield <FieldName>]]
# [immutableous <OrgUnitEntity>]| # [immutableous <OrgUnitEntity>]|
# [clearschema <SchemaName>|<SchemaNameField>] # [clearschema <SchemaName>|<SchemaNameField>]
@@ -46383,10 +46388,12 @@ def updateUsers(entityList):
elif updatePrimaryEmail: elif updatePrimaryEmail:
if updatePrimaryEmail[0].search(user) is not None: if updatePrimaryEmail[0].search(user) is not None:
body['primaryEmail'] = re.sub(updatePrimaryEmail[0], updatePrimaryEmail[1], user) body['primaryEmail'] = re.sub(updatePrimaryEmail[0], updatePrimaryEmail[1], user)
if updatePrimaryEmail[2]:
entityActionNotPerformedWarning([Ent.USER, user], Msg.UPDATE_PRIMARY_EMAIL_PREVIEW.format(body['primaryEmail']), i, count)
continue
else: else:
body.pop('primaryEmail', None) entityActionNotPerformedWarning([Ent.USER, user], Msg.PRIMARY_EMAIL_DID_NOT_MATCH_PATTERN.format(updatePrimaryEmail[0].pattern), i, count)
if not body: continue
entityActionNotPerformedWarning([Ent.USER, user], Msg.PRIMARY_EMAIL_DID_NOT_MATCH_PATTERN.format(updatePrimaryEmail[0].pattern), i, count)
if groupOrgUnitMap: if groupOrgUnitMap:
try: try:
groups = callGAPIpages(cd.groups(), 'list', 'groups', groups = callGAPIpages(cd.groups(), 'list', 'groups',

View File

@@ -534,6 +534,7 @@ UNKNOWN_API_OR_VERSION = 'Unknown Google API or version: ({0}), contact {1}'
UNRECOVERABLE_ERROR = 'Unrecoverable error' UNRECOVERABLE_ERROR = 'Unrecoverable error'
UPDATE_ATTENDEE_CHANGES = 'Update attendee changes' UPDATE_ATTENDEE_CHANGES = 'Update attendee changes'
UPDATE_GAM_TO_64BIT = "You're running a 32-bit version of GAM on a 64-bit version of Windows, upgrade to a windows-x86_64 version of GAM" UPDATE_GAM_TO_64BIT = "You're running a 32-bit version of GAM on a 64-bit version of Windows, upgrade to a windows-x86_64 version of GAM"
UPDATE_PRIMARY_EMAIL_PREVIEW = 'updateprimaryemail preview: {0}'
UPDATE_USER_PASSWORD_CHANGE_NOTIFY_MESSAGE = 'The account password for #givenname# #familyname#, #user# has been changed to: #password#\n' UPDATE_USER_PASSWORD_CHANGE_NOTIFY_MESSAGE = 'The account password for #givenname# #familyname#, #user# has been changed to: #password#\n'
UPDATE_USER_PASSWORD_CHANGE_NOTIFY_SUBJECT = 'Account #user# password has been changed' UPDATE_USER_PASSWORD_CHANGE_NOTIFY_SUBJECT = 'Account #user# password has been changed'
UPLOAD_CSV_FILE_INTERNAL_ERROR = 'Google reported "{0}" but the file was probably uploaded, check that it has {1} rows' UPLOAD_CSV_FILE_INTERNAL_ERROR = 'Google reported "{0}" but the file was probably uploaded, check that it has {1} rows'