Skip to content

Commit f85ce9e

Browse files
svia3svia3
andauthored
fix: add multi-user-project byod scenario and updated ReadMe for potential errors + workarounds (#4790)
* fix: add multi-user-project byod scenario and updated ReadMe for potential errors + workarounds * fix: add multi-user-project byod scenario and updated ReadMe for potential errors + workarounds --------- Co-authored-by: svia3 <[email protected]>
1 parent bef4b8b commit f85ce9e

File tree

4 files changed

+126
-20
lines changed

4 files changed

+126
-20
lines changed

ml_ops/sm-datazone_import/README.md

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ aws configure add-model --service-model file://resources/datazone-linkedtypes-20
1313
2. Create a federation role
1414

1515
This role will be used by DataZone to launch the SageMaker Domain. See [BringYourOwnDomainResources.yml](.resources/BringYourOwnDomainResources.yml) for an example.
16+
If you are using a single SageMaker domain across multiple projects, you will need to create a separate SageMaker Execution Role and User Profile for each user in each project and a separate Federation Role per project.
1617

1718
### Prerequisites
1819

@@ -31,11 +32,43 @@ Run the script and follow the instructions.
3132
```bash
3233
python import-sagemaker-domain.py \
3334
--region REGION \
34-
--federation-role ARN_OF_FEDERATION_ROLE \
3535
--account-id ACCOUNTID
3636
```
3737

3838
### Additional Configuration
3939

4040
- SageMaker execution roles need DataZone API permissions in order for the Assets UI to function. See [DataZoneUserPolicy.json](./resources/DataZoneUserPolicy.json) for an example.
41-
- Ensure the DataZone Domain trusts SageMaker. In the AWS DataZone console navigate to Domain details and select the "Trusted services".
41+
- Ensure the DataZone Domain trusts SageMaker. In the AWS DataZone console navigate to Domain details and select the "Trusted services".
42+
43+
### Potential errors and workarounds
44+
45+
**Cannot view ML assets in SageMaker Studio, missing "Assets" tab**
46+
47+
Make sure that the execution role that is attached to the SageMaker User in the attached domain has ListTags attached as a permissions policy to the role. A simple workaround is to attach AmazonSageMakerCanvasFullAccess policy which contains this permission. Without it - you will not be able to view the Assets tab in the Studio UI. If you were to inspect the network UI, you would see the following error:
48+
```
49+
User: arn:aws:sts::789706018617:assumed-role/AmazonSageMaker-ExecutionRole-20241127T120959/SageMaker is not authorized to
50+
perform: sagemaker:ListTags on resource: arn:aws:sagemaker:us-east-1:789706018617:domain/d-qy9jzu4s7q0y because no
51+
identity-based policy allows the sagemaker:ListTags action
52+
```
53+
54+
**Able to view assets in sidebar, but page is not loading**
55+
56+
If you are able to view the assets - but are getting a `There was a problem when loading subscriptions` error in the page where your ML assets should be - ensure that the SageMaker Execution role tied to this SageMaker user has permissions. We can use the provided /resources/DatazoneUserPolicy.json or a more limited version of what is included in AmazonDataZoneFullUserAccess attached to it.
57+
58+
**DataZone portal is not showing a generated action-link for user**
59+
60+
If you are attempting to create ProjectB using a subset of users B under created environment B - make sure. that you use a separate federation role when the _associate_fed_role action is called. This is required or else the association will fail and thus the subsequent call to create_environment_action will fail with the following error.
61+
See `../resources` for sample permissions and trust policies for the federation role. Be sure to fill in your SageMaker Domain Id.
62+
63+
```
64+
An error occurred (ValidationException) when calling the AssociateEnvironmentRole operation: Role Arn
65+
arn:aws:iam::789706018617:role/svia-test-byod-fed-role already being used in a different project
66+
```
67+
68+
Successful association will return the following
69+
70+
```
71+
Federation role to federate into sagemaker studio from datazone portal: arn:aws:iam::789706018617:role/svia-test-byod-fed-role
72+
Associating Environment Role using Federation Role [arn:aws:iam::789706018617:role/svia-test-byod-fed-role] ...
73+
Associating Environment Role using Federation Role [arn:aws:iam::789706018617:role/svia-test-byod-fed-role] COMPLETE
74+
```

ml_ops/sm-datazone_import/import-sagemaker-domain.py

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import argparse
22
import boto3
33
import botocore
4+
import time
45
from botocore.exceptions import ClientError
56

67
"""
@@ -13,10 +14,9 @@
1314

1415

1516
class SageMakerDomainImporter:
16-
def __init__(self, region, stage, federation_role, account_id) -> None:
17+
def __init__(self, region, stage, account_id) -> None:
1718
self.region = region
1819
self.stage = stage
19-
self.federation_role = federation_role
2020
self.account_id = account_id
2121
# Setup client.
2222
sm_endpoint_url = "https://api.sagemaker." + region + ".amazonaws.com" # prod
@@ -169,7 +169,9 @@ def _map_users(self):
169169
exec_role = user_settings["ExecutionRole"]
170170

171171
if exec_role is None:
172-
print(f'User {sm_user_name} has no execution role set, using default from domain.')
172+
print(
173+
f"User {sm_user_name} has no execution role set, using default from domain."
174+
)
173175
exec_role = self.default_execution_role
174176

175177
self.sm_user_info["exec_role_arn"] = exec_role
@@ -233,6 +235,30 @@ def _map_users(self):
233235
break
234236
self.dz_users_id_list.append(dz_uzer)
235237

238+
def _link_multiple_users_and_projects(self):
239+
"""
240+
Add the option for the user to attach Users in subset B to Project B.
241+
"""
242+
print("--------------------------------------------------------------------")
243+
decision = input(
244+
"Would you like to onboard an additional subset of user profiles to another project? "
245+
"(This would require you to have another project created. In this new project, you will create"
246+
"a new environment if not already created, as well) [y/n]: "
247+
)
248+
if decision == "y":
249+
self._choose_dz_project()
250+
self._configure_blueprint()
251+
self._configure_environment()
252+
self._tag_sm_domain()
253+
self._map_users()
254+
self._associate_fed_role()
255+
self._add_environment_action()
256+
self._link_domain()
257+
self._link_users()
258+
self._debug_print_results()
259+
self._get_env_link()
260+
self._link_multiple_users_and_projects()
261+
236262
def _configure_blueprint(self):
237263
# [4] Create environment profile + environment and use new API BatchPutLinkedTypes to connect DataZone and SageMaker entities.
238264

@@ -266,7 +292,6 @@ def _configure_blueprint(self):
266292
return self.managed_blueprint_id
267293

268294
def _configure_environment(self):
269-
print("--------------------------------------------------------------------")
270295
decision_env = input(
271296
"Do you need to create a new DataZone environment? [y/n]: "
272297
)
@@ -346,6 +371,9 @@ def _add_environment_action(self):
346371
def _associate_fed_role(self):
347372
# Associate fed role
348373
print("--------------------------------------------------------------------")
374+
self.federation_role = input(
375+
"Federation Role Arn to federate into sagemaker studio from datazone portal: "
376+
)
349377
print(
350378
"Associating Environment Role using Federation Role [{}] ...".format(
351379
self.federation_role
@@ -367,6 +395,8 @@ def _associate_fed_role(self):
367395
print(
368396
"Environment has a role configured already. Skipping role association ..."
369397
)
398+
else:
399+
print(f"Caught error: {repr(e)}")
370400

371401
def _link_domain(self):
372402
# attach SAGEMAKER_DOMAIN
@@ -394,15 +424,15 @@ def _link_domain(self):
394424
)
395425
print("--------------------------------------------------------------------")
396426

397-
print("Linking SageMaker Domain")
427+
print(f"Linking SageMaker Domain using project id [{self.dz_project_id}]")
398428
link_domain_response = self.byod_client.batch_put_linked_types(
399429
domainIdentifier=self.dz_domain_id,
400430
projectIdentifier=self.dz_project_id,
401431
environmentIdentifier=self.env_id,
402432
items=linkedDomainItems,
403433
)
404434
print(link_domain_response)
405-
print("Linked SageMaker Domain")
435+
print("Linked SageMaker Domain.")
406436

407437
def _link_users(self):
408438
# attach SAGEMAKER_USER_PROFILE
@@ -429,15 +459,17 @@ def _link_users(self):
429459
linkedUserItems.append(linkedUserItem)
430460

431461
print("--------------------------------------------------------------------")
432-
print("Linking SageMaker User Profiles")
462+
print(
463+
f"Linking SageMaker User Profiles using project id [{self.dz_project_id}]"
464+
)
433465
link_users_response = self.byod_client.batch_put_linked_types(
434466
domainIdentifier=self.dz_domain_id,
435467
projectIdentifier=self.dz_project_id,
436468
environmentIdentifier=self.env_id,
437469
items=linkedUserItems,
438470
)
439471
print(link_users_response)
440-
print("Linked SageMaker User Profiles")
472+
print("Linked SageMaker User Profiles.")
441473
print("--------------------------------------------------------------------")
442474

443475
def _debug_print_results(self):
@@ -482,12 +514,13 @@ def import_interactive(self):
482514
self._configure_environment()
483515
self._tag_sm_domain()
484516
self._map_users()
485-
self._add_environment_action()
486517
self._associate_fed_role()
518+
self._add_environment_action()
487519
self._link_domain()
488520
self._link_users()
489521
self._debug_print_results()
490522
self._get_env_link()
523+
self._link_multiple_users_and_projects()
491524

492525

493526
if __name__ == "__main__":
@@ -506,13 +539,6 @@ def import_interactive(self):
506539
default="prod",
507540
help="Stage to test e2e BYOD. This impacts the endpoint targeted.",
508541
)
509-
parser.add_argument(
510-
"--federation-role",
511-
type=str,
512-
required=True,
513-
default="test",
514-
help="Role used to federate access into environment.",
515-
)
516542
parser.add_argument(
517543
"--account-id",
518544
type=str,
@@ -524,10 +550,9 @@ def import_interactive(self):
524550

525551
region = args.region
526552
stage = args.stage
527-
federation_role = args.federation_role
528553
account_id = args.account_id
529554

530555
print("--------------------------------------------------------------------")
531-
importer = SageMakerDomainImporter(region, stage, federation_role, account_id)
556+
importer = SageMakerDomainImporter(region, stage, account_id)
532557
importer.import_interactive()
533558
print("--------------------------------------------------------------------")
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"Version": "2012-10-17",
3+
"Statement": [
4+
{
5+
"Action": [
6+
"sagemaker:CreateUserProfile",
7+
"sagemaker:DescribeUserProfile",
8+
"sagemaker:CreatePresignedDomainUrl"
9+
],
10+
"Resource": [
11+
"arn:aws:sagemaker:*:789706018617:*/<YOUR-SM-DOMAIN-ID-HERE>/*"
12+
],
13+
"Effect": "Allow"
14+
},
15+
{
16+
"Sid": "Statement1",
17+
"Effect": "Allow",
18+
"Action": [
19+
"iam:ListRoleTags"
20+
],
21+
"Resource": [
22+
"*"
23+
]
24+
}
25+
]
26+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"Version": "2012-10-17",
3+
"Statement": [
4+
{
5+
"Effect": "Allow",
6+
"Principal": {
7+
"Service": [
8+
"datazone.amazonaws.com",
9+
"lakeformation.amazonaws.com",
10+
"glue.amazonaws.com",
11+
"auth.datazone.amazonaws.com"
12+
]
13+
},
14+
"Action": [
15+
"sts:AssumeRole",
16+
"sts:TagSession",
17+
"sts:SetContext",
18+
"sts:SetSourceIdentity"
19+
]
20+
}
21+
]
22+
}

0 commit comments

Comments
 (0)