471 lines
17 KiB
Markdown
471 lines
17 KiB
Markdown
# SaaS-Change-UPN-Script-Runbook_686074283
|
||
## Introduction
|
||
|
||
There are many scenarios where customers may need to update UPNs, such as domain migrations or company mergers. This runbook will guide the Operations team to migrate customers' UPNs using a defined mapping table.
|
||
|
||
## Preparation
|
||
|
||
- Tenant admin should not be included
|
||
- User migration mapping file (CSV), including old UPN, old Email, new UPN, below is the template:
|
||
- Sample CSV format:
|
||
| oldUpn | newUpn | oldEmail | newEmail |
|
||
| --- | --- | --- | --- |
|
||
| xiao1 | xiao1\_New | [xiao1\_New@microfocus.com](mailto:xiao1_New@opentext.com) | [xiao1\_New@opentext.com](mailto:xiao1_New@opentext.com) |
|
||
| xiao2 | xiao2\_New | [xiao2\_New@](mailto:xiao2_New@opentext.com) [microfocus.com](mailto:xiao1_New@opentext.com) | [xiao2\_New@opentext.com](mailto:xiao2_New@opentext.com) |
|
||
| userTKit05 | userTKit055 | [userTKit055@](mailto:userTKit055@opentext.com) [microfocus.com](mailto:xiao1_New@opentext.com) | [userTKit055@opentext.com](mailto:userTKit055@opentext.com) |
|
||
- Make sure no duplicate users in mapping file
|
||
- Make sure the migrated users should not exist in SMAX
|
||
|
||
## Check PAT accounts
|
||
|
||
Go into IdM's database.
|
||
|
||
1. Get the uuid for target tenant.
|
||
|
||
```
|
||
select uuid,name from <idm_schema>.organizations where name =<tenant_id>
|
||
```
|
||
|
||
2\. List PAT users by uuid
|
||
|
||
```
|
||
select au.name, au.organization, at2.uuid from <idm_schema>.api_token at2, <idm_schema>.abstract_user au where at2.user_id=au.uuid and au.organization=<uuid>;
|
||
```
|
||
|
||
3.Inform the above users that they need to use a new UPN to obtain token after migrate if they use the UPN to get the PAT token before.
|
||
|
||
The admin user may use an api call(api/scim/organizations/<org-id>/users/<user\_id>/access-tokens) to get other ordinary user's token. The <user\_id> may be uuid or UPN, if it's UPN, it needs to be switched to a new one.
|
||
|
||
### Find out the user set that user's OT UPN are already existed in SMAX
|
||
|
||
#### BO
|
||
|
||
Go to BO's DB
|
||
|
||
a. Create a temp mapping table
|
||
|
||
```
|
||
create table <bo_user_schema>.mf_ot_user_mapping
|
||
(
|
||
old_upn varchar(512) not null
|
||
constraint unique_old_upn
|
||
unique,
|
||
new_upn varchar(512) not null
|
||
constraint unique_new_upn
|
||
unique,
|
||
old_email varchar(512) not null,
|
||
new_email varchar(512) not null
|
||
);
|
||
```
|
||
|
||
b. Copy csv data
|
||
|
||
```
|
||
\copy <bo_user_schema>.mf_ot_user_mapping(old_upn,new_upn,old_email,new_email) FROM '<csv_path>' DELIMITER ',' CSV HEADER;
|
||
```
|
||
|
||
c. Find out users
|
||
|
||
```
|
||
select distinct des.* from <bo_user_schema>.user_entity nu inner join
|
||
(select mp.* from <bo_user_schema>.mf_ot_user_mapping mp inner join <bo_user_schema>.user_entity u on lower(mp.old_upn) = lower(u.name) and lower(mp.old_upn) !=lower(mp.new_upn) and u.is_deleted=false where u.idm_organization='<tenant_id>') des
|
||
on lower(nu.name)=lower(des.new_upn) and nu.is_deleted=false and nu.idm_organization='<tenant_id>';
|
||
```
|
||
|
||
Collect the results
|
||
|
||
d. drop the temp table
|
||
|
||
```
|
||
drop table <bo_user_schema>.mf_ot_user_mapping
|
||
```
|
||
|
||
#### IdM
|
||
|
||
Go to IdM's DB
|
||
|
||
a. Create a temp mapping table
|
||
|
||
```
|
||
create table <idm_schema>.mf_ot_user_mapping
|
||
(
|
||
old_upn varchar(512) not null
|
||
constraint unique_old_upn
|
||
unique,
|
||
new_upn varchar(512) not null
|
||
constraint unique_new_upn
|
||
unique,
|
||
old_email varchar(512) not null,
|
||
new_email varchar(512) not null
|
||
);
|
||
```
|
||
|
||
b. Copy csv data
|
||
|
||
```
|
||
\copy <idm_schema>.mf_ot_user_mapping(old_upn,new_upn,old_email,new_email) FROM '<csv_path>' DELIMITER ',' CSV HEADER;
|
||
```
|
||
|
||
c. Find out users
|
||
|
||
```
|
||
select distinct des.* from <idm_schema>.abstract_user nu inner join
|
||
(select mp.* from <idm_schema>.mf_ot_user_mapping mp inner join <idm_schema>.abstract_user u on lower(mp.old_upn)=lower(u.name) and lower(mp.old_upn) !=lower(mp.new_upn) where u.organization=(select uuid from <idm_schema>.organizations where name='<tenant_id>')) as des
|
||
on lower(nu.name)=lower(des.new_upn) and nu.organization=(select uuid from <idm_schema>.organizations where name='<tenant_id>');
|
||
```
|
||
|
||
Collect the resluts
|
||
|
||
d. drop the temp table
|
||
|
||
```
|
||
drop table <idm_schema>.mf_ot_user_mapping
|
||
```
|
||
|
||
#### EMS
|
||
|
||
Since we need to select Person's upn from entities\_<tenant\_id>, we need to get Tenant version from RMS's DB first.
|
||
|
||
Go to RMS's DB
|
||
|
||
- Get tenant version
|
||
|
||
```
|
||
select body -> 'tenant' ->> 'version' from <rms_schema>."TenantData_857561481" where body -> 'tenant' ->> 'tenantId' = '<tenant_id>';
|
||
```
|
||
|
||
Go to EMS's DB
|
||
|
||
a. Create a temp mapping table
|
||
|
||
```
|
||
create table <ems_schema>.mf_ot_user_mapping
|
||
(
|
||
old_upn varchar(512) not null
|
||
constraint unique_old_upn
|
||
unique,
|
||
new_upn varchar(512) not null
|
||
constraint unique_new_upn
|
||
unique,
|
||
old_email varchar(512) not null,
|
||
new_email varchar(512) not null
|
||
);
|
||
```
|
||
|
||
b. Copy csv data
|
||
|
||
```
|
||
\copy <ems_schema>.mf_ot_user_mapping(old_upn,new_upn,old_email,new_email) FROM '<csv_path>' DELIMITER ',' CSV HEADER;
|
||
```
|
||
|
||
c. Find out users for tenant
|
||
|
||
- Get physical\_type\_name(Replace <tenant\_version> with the tenant version that get from RMS)
|
||
|
||
```
|
||
select physical_type_name from <ems_schema>.entityDescriptor_mapping where entity_type = 'Person' AND tenant_id = '<tenant_version>' AND logical_type_name = 'Upn';
|
||
```
|
||
|
||
- Get entity\_type\_id(Replace <tenant\_version> with the tenant version that get from RMS)
|
||
|
||
```
|
||
select id from "entity_descriptor" where tenant_id = '<tenant_version>' and name = 'Person';
|
||
```
|
||
|
||
Find out users(Replace <physical\_type\_name>,<entity\_type\_id> with the physical\_type\_name, entity\_type\_id that get from above)
|
||
|
||
```
|
||
select distinct des.* from <ems_schema>.entities_<tenant_id> nu inner join
|
||
(select mp.* from <ems_schema>.mf_ot_user_mapping mp inner join <ems_schema>.entities_<tenant_id> u on lower(mp.old_upn) = lower(u.<physical_type_name>) and lower(mp.old_upn) !=lower(mp.new_upn) and u.is_deleted=false and u.entity_type_id=<entity_type_id> ) des
|
||
on lower(nu.<physical_type_name>)=lower(des.new_upn) and nu.is_deleted=false and nu.entity_type_id=<entity_type_id>;
|
||
```
|
||
|
||
Collect the results.
|
||
|
||
d. drop the temp table
|
||
|
||
```
|
||
drop table <ems_schema>.mf_ot_user_mapping
|
||
```
|
||
|
||
#### RMS
|
||
|
||
Go to RMS's DB
|
||
|
||
a. Create a temp mapping table
|
||
|
||
```
|
||
create table <rms_schema>.mf_ot_user_mapping
|
||
(
|
||
old_upn varchar(512) not null
|
||
constraint unique_old_upn
|
||
unique,
|
||
new_upn varchar(512) not null
|
||
constraint unique_new_upn
|
||
unique,
|
||
old_email varchar(512) not null,
|
||
new_email varchar(512) not null
|
||
);
|
||
```
|
||
|
||
b. Copy csv data
|
||
|
||
```
|
||
\copy <rms_schema>.mf_ot_user_mapping(old_upn,new_upn,old_email,new_email) FROM '<csv_path>' DELIMITER ',' CSV HEADER;
|
||
```
|
||
|
||
c. Find out users for tenant
|
||
|
||
```
|
||
select distinct des.* from <rms_schema>."AuthorizationPrincipalResourceJSON_<tenant_id>" nu inner join
|
||
(select mp.* from <rms_schema>.mf_ot_user_mapping mp inner join <rms_schema>."AuthorizationPrincipalResourceJSON_<tenant_id>" u on lower(mp.old_upn) = lower(u.body ->> 'UserId') and lower(mp.old_upn) !=lower(mp.new_upn)) des
|
||
on lower(nu.body ->> 'UserId')=lower(des.new_upn);
|
||
```
|
||
|
||
Collect the results.
|
||
|
||
d. drop the temp table
|
||
|
||
```
|
||
drop table <rms_schema>.mf_ot_user_mapping
|
||
```
|
||
|
||
**In summary, we need to get the union of all the records from BO, IdM, EMS and RMS, Then cut this part of the data from the csv file.**
|
||
|
||
### 1.Actions in Suite
|
||
|
||
- Disable User sync in the account
|
||
|
||
Login to Suite Administration:
|
||
|
||
ACCOUNTS>General>User auto-sync
|
||
|
||

|
||
|
||
- Change the tenant to Inactive
|
||
|
||
Login to Suite Administration:
|
||
|
||
TENANTS>General>Inactive
|
||
|
||

|
||
|
||
- Stop the external integration in the tenant
|
||
- Do NOT do any write action(Create/Update/Delete) on Users
|
||
- Delete the SAML authentication config in IdM
|
||
|
||
Login to Suite Administration:
|
||
|
||
TENANTS>IdM settings>Authentication
|
||
|
||

|
||
|
||
### 2.Backup Databases:
|
||
|
||
Ensure backups of the following databases are taken before initiating changes:
|
||
|
||
- **Suite Administration** service database:
|
||
- `bo_user`
|
||
- `user_entity`
|
||
|
||
```
|
||
create table <schema>.user_entity_v0 as select * from <schema>.user_entity;
|
||
```
|
||
|
||
- **IDM** service database:
|
||
- `idm`
|
||
- `abstract_user`
|
||
- `abstract_user_metadata`
|
||
- `abstract_user_profile`
|
||
|
||
```
|
||
create table <schema>.abstract_user_v0 as select * from <schema>.abstract_user;
|
||
create table <schema>.abstract_user_metadata_v0 as select * from <schema>.abstract_user_metadata;
|
||
create table <schema>.abstract_user_profile_v0 as select * from <schema>.abstract_user_profile;
|
||
```
|
||
|
||
- XServices databases:
|
||
- `xservices_ems`
|
||
- entities\_<Target\_tenant\_Id> `eg:entities_140038523`
|
||
|
||
```
|
||
create table <schema>.entities_<tenant_id>_v0 as select * from <schema>.entities_<tenant_id>;
|
||
```
|
||
|
||
- - `xservices_rms`
|
||
- AuthorizationPrincipalResourceJSON\_<Target\_tenant\_Id> `eg:AuthorizationPrincipalResourceJSON_140038523`
|
||
|
||
```
|
||
create table <schema>."AuthorizationPrincipalResourceJSON_<tenant_id>_v0" as select * FROM <schema>."AuthorizationPrincipalResourceJSON_<tenant_id>";
|
||
```
|
||
|
||
Please note the single and double **quotes**
|
||
|
||
**Note: Please replace the value in <> according to the actual environment**
|
||
|
||
### 3.Prepare user Data
|
||
|
||
Provide a CSV file containing the new UPNs and corresponding old UPNs and new Emails.
|
||
|
||
Sample CSV format:
|
||
|
||
| oldUpn | newUpn | oldEmail | newEmail |
|
||
| --- | --- | --- | --- |
|
||
| xiao1 | xiao1\_New | [xiao1\_New@microfocus.com](mailto:xiao1_New@opentext.com) | [xiao1\_New@opentext.com](mailto:xiao1_New@opentext.com) |
|
||
| xiao2 | xiao2\_New | [xiao2\_New@](mailto:xiao2_New@opentext.com) [microfocus.com](mailto:xiao1_New@opentext.com) | [xiao2\_New@opentext.com](mailto:xiao2_New@opentext.com) |
|
||
| userTKit05 | userTKit055 | [userTKit055@](mailto:userTKit055@opentext.com) [microfocus.com](mailto:xiao1_New@opentext.com) | [userTKit055@opentext.com](mailto:userTKit055@opentext.com) |
|
||
|
||
**We can choose a small data set to test firstly, validate these users. If everything goes fine, we can do with the rest files.**
|
||
|
||
## Run Script
|
||
|
||
#### Notes:
|
||
|
||
1. This script needs to provide the old and new upn, as well as the email, the file format should be.csv.
|
||
2. In addition, you need to specify the column number of the column corresponding to the new/old UPN and old/new Email of the corresponding file and the row number starting from the line.
|
||
3. You must have the "root" privilege to execute the script.
|
||
4. Script package should include two files: **changeUserUpn.sh** and **change**, and put these two files in the same level directory.
|
||
5. The account for updating UPN should be of 3in1 type, which means that the account id and tenant id should be the same.
|
||
6. In IdM, this script is only update for SAML users, which means that if you are using non-SAML integrated authentication, this script is not suitable
|
||
7. Before the script runs, you need to back up the database
|
||
8. Before the script runs, you need to turn off the user sync of the account, and you need to set the tenant to " **Inactive** ", and stop the external integration on the tenant
|
||
|
||
#### Follow these steps:
|
||
|
||
1. Upload the scripts file and User data csv file to the bastion/master node of the suite
|
||
2. Log in to the bastion/master node as a root user, copy the scripts and data file to the toolkit pod, grant executable permissions to scripts and file.
|
||
3. Go into the toolkit pod and run the following command in the directory where the script is located: `sh changeUserUpn.sh -a <account_id>` Where <account\_id> is the ID of the account which you want to change these users under the account. For example, `sh changeUserUpn.sh -a 123456789`
|
||
4. Follow the prompts step by step to continue executing the script
|
||
|
||
**The script will be executed after you put the correct line number in mapping file.**And you can see following messages after the script is successful executed:
|
||
|
||

|
||
|
||
## Validation
|
||
|
||
Firstly, You can check whether the user's upn and email are updated correctly in IdM/BO and SAW. If it's OK, you need to
|
||
|
||
- configure OT's SAML in IdM
|
||
- Active Tenant and turn on User auto-sync in BO.
|
||
|
||
Then you can do some regular validation such as:
|
||
|
||
- The user account's UPN doesn't change before/after migration
|
||
To verify after SAML login the user can match to the original user account and role/permission settings aren’t changed
|
||
- The user account's UPN has been changed after migration
|
||
To verify after SAML login the user can match to the original user account and role/permission settings aren’t changed
|
||
To verify after SAML login the user information can be updated to new IDP information, such as email can be updated to OT email
|
||
- The user account never login SMAX tenant
|
||
To verify the new SAML user account can be created properly with new UPN in both BO and Tenant
|
||
|
||
## Rollback
|
||
|
||
If you need to rollback, just rollback the database that has been backed up.
|
||
|
||
### Primary rollback:
|
||
|
||
You can re-run the change UPN script and specify the old UPN and new UPN with reversed column numbers.
|
||
|
||
### Secondary rollback:
|
||
|
||
- **Suite Administration** service database:
|
||
- `bo_user`
|
||
|
||
```
|
||
create table <schema>.user_entity_v1 as select * from <schema>.user_entity;
|
||
|
||
update <schema>.user_entity as S set name = B.name, email = B.email
|
||
from <schema>.user_entity_v0 as B
|
||
where S.id = B.id
|
||
and S.account_id = '<tenant_id>';
|
||
```
|
||
|
||
IDM:
|
||
|
||
- IDM service database:
|
||
- `idm`
|
||
|
||
```
|
||
create table <schema>.abstract_user_v1 as select * from <schema>.abstract_user;
|
||
create table <schema>.abstract_user_metadata_v1 as select * from <schema>.abstract_user_metadata;
|
||
create table <schema>.abstract_user_profile_v1 as select * from <schema>.abstract_user_profile;
|
||
|
||
update <schema>.abstract_user as au set name=bak.name, display_name=bak.display_name, name_lower=bak.name_lower from <schema>.abstract_user_v0 bak where au.uuid=bak.uuid and au.organization='<tenant_uuid>';
|
||
|
||
update <schema>.abstract_user_metadata as am set field_value=bak.field_value, field_value_lower=bak.field_value_lower from <schema>.abstract_user au, abstract_user_metadata_v0 bak where am.field_key='username' and am.uuid=bak.uuid and bak.uuid=au.uuid and au.organization='<tenant_uuid>';
|
||
|
||
update <schema>.abstract_user_metadata as am set field_value=bak.field_value, field_value_lower=bak.field_value_lower from <schema>.abstract_user au, abstract_user_metadata_v0 bak where am.field_key='email' and am.uuid=bak.uuid and bak.uuid=au.uuid and au.organization='<tenant_uuid>';
|
||
|
||
update <schema>.abstract_user_profile as ap set user_name=bak.user_name, email=bak.email from <schema>.abstract_user au, abstract_user_profile_v0 bak where ap.uuid=bak.uuid and ap.user_id=au.uuid and au.organization='<tenant_uuid>';
|
||
```
|
||
|
||
- XServices databases:
|
||
- `xservices_ems`
|
||
|
||
```
|
||
create table <schema>.entities_<tenant_id>_v1 as select * from <schema>.entities_<tenant_id>;
|
||
|
||
-- get entity type id:
|
||
SELECT DISTINCT id, name FROM <schema>.entity_descriptor WHERE name in ('Person');
|
||
|
||
-- get tenant_version such as v27.
|
||
|
||
-- get UPN field name: (such as schar1)
|
||
select physical_type_name from <schema>.entityDescriptor_mapping where entity_type = 'Person' AND tenant_id = '<tenant_version>' AND logical_type_name = 'Upn';
|
||
|
||
-- get email field name: (such as schar2)
|
||
select physical_type_name from <schema>.entityDescriptor_mapping where entity_type = 'Person' AND tenant_id = '<tenant_version>' AND logical_type_name = 'Email';
|
||
|
||
update <schema>.entities_<tenant_id> as S set <UPN_field_name> = B.<UPN_field_name>, <email_field_name> = B.<email_field_name>
|
||
from <schema>.entities_<tenant_id>_v0 as B
|
||
where S.entity_id = B.entity_id
|
||
and S.entity_type_id = '<entity_type_id>';
|
||
```
|
||
|
||
- - `xservices_rms`
|
||
|
||
```
|
||
alter table <schema>."AuthorizationPrincipalResourceJSON_<tenant_id>" rename to "AuthorizationPrincipalResourceJSON_<tenant_id>_v1";
|
||
|
||
create table <schema>."AuthorizationPrincipalResourceJSON_<tenant_id>" as select * FROM <schema>."AuthorizationPrincipalResourceJSON_<tenant_id>_v0";
|
||
```
|
||
|
||
## Troubleshooting
|
||
|
||
### Empty mapping file
|
||
|
||
If you see this msg that means the.csv file is empty.
|
||
|
||
```
|
||
Try to read the file that mappings of user UPN and Email.
|
||
time="2024-03-11 16:21:54" level=error msg="The user mappings file is empty."
|
||
The user mappings file is empty.
|
||
```
|
||
|
||
### Empty parameter
|
||
|
||
```
|
||
time="2024-03-11 17:50:02" level=error msg="Required param is EMPTY,OriginName:test1,NewName:,OriginEmail:test1,NewEmail:test1"
|
||
Required param is EMPTY,OriginName:test1,NewName:,OriginEmail:test1,NewEmail:test1
|
||
```
|
||
|
||
If you see this msg that means the NewName is empty.
|
||
|
||
### Duplicated user
|
||
|
||
If you get this error, go to the Preparation and follow actions to find out the duplicated users.
|
||
|
||

|
||
|
||
More information will be added during next testing.
|
||
|
||
## Script Files
|
||
|
||
change
|
||
|
||
[change](attachments/686074283/686074285)
|
||
|
||
changeUserUpn.sh
|
||
|
||
[changeUserUpn.sh](attachments/686074283/686074286.sh)
|