# 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

![](attachments/686074283/686074278.png)

- Change the tenant to Inactive

Login to Suite Administration:

TENANTS>General>Inactive

![](attachments/686074283/686074279.png)

- 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

![](attachments/686074283/686074280.png)

### 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:

![](attachments/686074283/686074281.png)

## 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.

![](attachments/686074283/686074282.png)

More information will be added during next testing.

## Script Files

change

[change](attachments/686074283/686074285)

changeUserUpn.sh

[changeUserUpn.sh](attachments/686074283/686074286.sh)
