Skip to content

Commit 4d92134

Browse files
committed
feat: Add cross-account deployment role module for SageMaker
Creates least-privilege IAM role in target accounts for cross-account SageMaker endpoint deployment. Eliminates need for wide-open CDK bootstrap trust relationships. Features: - Explicit trust to specific pipeline role only - Scoped permissions for SageMaker + CloudFormation - Independent of CDK bootstrap Resolves security issue where CDK bootstrap requires account-level trust.
1 parent 40098d6 commit 4d92134

5 files changed

Lines changed: 241 additions & 0 deletions

File tree

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# SageMaker Model Deploy Cross-Account Role
2+
3+
Creates an IAM role in a target account (pre-prod/prod) that allows a SageMaker model deployment pipeline in a source account (dev) to deploy endpoints cross-account.
4+
5+
## Features
6+
7+
- **Least Privilege**: Only grants permissions needed for SageMaker endpoint deployment
8+
- **Explicit Trust**: Trusts only the specific pipeline role, not the entire source account
9+
- **No CDK Bootstrap**: Independent of CDK bootstrap trust relationships
10+
11+
## Usage
12+
13+
Deploy this module once in each target account/region before deploying the model deployment pipeline.
14+
15+
### Example: Deploy to Pre-Prod Account
16+
17+
```yaml
18+
name: preprod-deploy-role
19+
path: git::https://github.com/awslabs/aiops-modules.git//modules/sagemaker/sagemaker-model-deploy-cross-account-role?ref=main
20+
targetAccount: preprod
21+
parameters:
22+
- name: trusted-account-id
23+
value: "111111111111" # Dev account ID
24+
- name: trusted-role-name
25+
value: "model-deploy-pipeline-role" # Pipeline role name
26+
```
27+
28+
### Example: Deploy to Prod Account
29+
30+
```yaml
31+
name: prod-deploy-role
32+
path: git::https://github.com/awslabs/aiops-modules.git//modules/sagemaker/sagemaker-model-deploy-cross-account-role?ref=main
33+
targetAccount: prod
34+
parameters:
35+
- name: trusted-account-id
36+
value: "111111111111" # Dev account ID
37+
- name: trusted-role-name
38+
value: "model-deploy-pipeline-role" # Pipeline role name
39+
```
40+
41+
## Deployment Order
42+
43+
1. Deploy this module to pre-prod account
44+
2. Deploy this module to prod account
45+
3. Deploy the model deployment pipeline in dev account
46+
- Pipeline will use the roles created by this module
47+
48+
## Permissions Granted
49+
50+
The role grants least-privilege permissions for:
51+
- SageMaker endpoint/model/config CRUD operations
52+
- IAM role management (for SageMaker execution roles)
53+
- CloudFormation stack deployment
54+
- S3 access to model artifacts
55+
- ECR access to container images
56+
57+
## Outputs
58+
59+
- `DeployRoleArn`: ARN of the created deployment role
60+
61+
## Security
62+
63+
This module eliminates the need for wide-open CDK bootstrap trust relationships by creating explicit, scoped cross-account roles.
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Cross-account deployment role for SageMaker model deployment pipeline.
4+
Creates IAM role that allows a pipeline in another account to deploy SageMaker endpoints.
5+
"""
6+
import aws_cdk as cdk
7+
from constructs import Construct
8+
from aws_cdk import (
9+
aws_iam as iam,
10+
Stack,
11+
)
12+
13+
14+
class CrossAccountDeployRoleStack(Stack):
15+
def __init__(
16+
self,
17+
scope: Construct,
18+
construct_id: str,
19+
*,
20+
trusted_account_id: str,
21+
trusted_role_name: str,
22+
**kwargs,
23+
) -> None:
24+
super().__init__(scope, construct_id, **kwargs)
25+
26+
# Create deployment role with trust to pipeline role in source account
27+
deploy_role = iam.Role(
28+
self,
29+
"DeployRole",
30+
role_name=f"sagemaker-model-deploy-role-{self.region}",
31+
assumed_by=iam.ArnPrincipal(
32+
f"arn:aws:iam::{trusted_account_id}:role/{trusted_role_name}"
33+
),
34+
description=f"Cross-account deployment role for SageMaker model deployment from account {trusted_account_id}",
35+
)
36+
37+
# Grant permissions for SageMaker endpoint deployment
38+
deploy_role.add_to_policy(
39+
iam.PolicyStatement(
40+
sid="SageMakerEndpointDeployment",
41+
actions=[
42+
"sagemaker:CreateEndpoint",
43+
"sagemaker:CreateEndpointConfig",
44+
"sagemaker:CreateModel",
45+
"sagemaker:DeleteEndpoint",
46+
"sagemaker:DeleteEndpointConfig",
47+
"sagemaker:DeleteModel",
48+
"sagemaker:DescribeEndpoint",
49+
"sagemaker:DescribeEndpointConfig",
50+
"sagemaker:DescribeModel",
51+
"sagemaker:UpdateEndpoint",
52+
"sagemaker:UpdateEndpointWeightsAndCapacities",
53+
"sagemaker:AddTags",
54+
"sagemaker:ListTags",
55+
],
56+
resources=[
57+
f"arn:aws:sagemaker:{self.region}:{self.account}:endpoint/*",
58+
f"arn:aws:sagemaker:{self.region}:{self.account}:endpoint-config/*",
59+
f"arn:aws:sagemaker:{self.region}:{self.account}:model/*",
60+
],
61+
)
62+
)
63+
64+
# Grant permissions for IAM role creation (for SageMaker execution role)
65+
deploy_role.add_to_policy(
66+
iam.PolicyStatement(
67+
sid="IAMRoleManagement",
68+
actions=[
69+
"iam:CreateRole",
70+
"iam:DeleteRole",
71+
"iam:GetRole",
72+
"iam:PassRole",
73+
"iam:AttachRolePolicy",
74+
"iam:DetachRolePolicy",
75+
"iam:PutRolePolicy",
76+
"iam:DeleteRolePolicy",
77+
"iam:GetRolePolicy",
78+
"iam:TagRole",
79+
"iam:UntagRole",
80+
],
81+
resources=[
82+
f"arn:aws:iam::{self.account}:role/sagemaker-*",
83+
],
84+
)
85+
)
86+
87+
# Grant CloudFormation permissions
88+
deploy_role.add_to_policy(
89+
iam.PolicyStatement(
90+
sid="CloudFormationDeployment",
91+
actions=[
92+
"cloudformation:CreateStack",
93+
"cloudformation:UpdateStack",
94+
"cloudformation:DeleteStack",
95+
"cloudformation:DescribeStacks",
96+
"cloudformation:DescribeStackEvents",
97+
"cloudformation:DescribeStackResources",
98+
"cloudformation:GetTemplate",
99+
"cloudformation:ValidateTemplate",
100+
],
101+
resources=[
102+
f"arn:aws:cloudformation:{self.region}:{self.account}:stack/*/*",
103+
],
104+
)
105+
)
106+
107+
# Grant S3 permissions for model artifacts
108+
deploy_role.add_to_policy(
109+
iam.PolicyStatement(
110+
sid="S3ModelArtifacts",
111+
actions=[
112+
"s3:GetObject",
113+
"s3:ListBucket",
114+
],
115+
resources=[
116+
"arn:aws:s3:::sagemaker-*",
117+
],
118+
)
119+
)
120+
121+
# Grant ECR permissions for container images
122+
deploy_role.add_to_policy(
123+
iam.PolicyStatement(
124+
sid="ECRImageAccess",
125+
actions=[
126+
"ecr:BatchCheckLayerAvailability",
127+
"ecr:GetDownloadUrlForLayer",
128+
"ecr:BatchGetImage",
129+
"ecr:GetAuthorizationToken",
130+
],
131+
resources=["*"],
132+
)
133+
)
134+
135+
# Output the role ARN
136+
cdk.CfnOutput(
137+
self,
138+
"DeployRoleArn",
139+
value=deploy_role.role_arn,
140+
description="ARN of the cross-account deployment role",
141+
)
142+
143+
144+
app = cdk.App()
145+
CrossAccountDeployRoleStack(
146+
app,
147+
"sagemaker-model-deploy-cross-account-role",
148+
trusted_account_id=app.node.try_get_context("trusted_account_id"),
149+
trusted_role_name=app.node.try_get_context("trusted_role_name"),
150+
)
151+
app.synth()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: SAGEMAKER_MODEL_DEPLOY_CROSS_ACCOUNT_ROLE
2+
path: modules/sagemaker/sagemaker-model-deploy-cross-account-role
3+
parameters:
4+
- name: trusted-account-id
5+
value_from:
6+
module_metadata:
7+
group: core
8+
name: optionals
9+
key: trusted_account_id
10+
- name: trusted-role-name
11+
value_from:
12+
module_metadata:
13+
group: core
14+
name: optionals
15+
key: trusted_role_name
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: sagemaker-model-deploy-cross-account-role
2+
path: modules/sagemaker/sagemaker-model-deploy-cross-account-role
3+
description: Creates cross-account IAM role for SageMaker model deployment pipeline
4+
parameters:
5+
- name: trusted-account-id
6+
description: AWS account ID that contains the deployment pipeline
7+
type: string
8+
- name: trusted-role-name
9+
description: Name of the pipeline role that will assume this role
10+
type: string
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
aws-cdk-lib>=2.100.0
2+
constructs>=10.0.0

0 commit comments

Comments
 (0)