# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 AWSTemplateFormatVersion: '2010-09-09' Description: | Create the Data Science environment Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Data Science Environment Parameters: - EnvName - EnvType - StartKernelGatewayApps - EnvTypeStagingName - EnvTypeProdName - Label: default: Deployment Options Parameters: - CreateEnvironmentIAMRoles - CreateEnvironmentS3Buckets - UseSharedServicesPyPiMirror - Label: default: Environment IAM Roles ARNs (only needed if created outside of this stack) Parameters: - DSTeamAdministratorRoleArn - DataScientistRoleArn - SageMakerExecutionRoleArn - SageMakerPipelineExecutionRoleArn - SageMakerModelExecutionRoleName - SetupLambdaExecutionRoleArn - SCProjectLaunchRoleArn - StackSetAdministrationRoleArn - StackSetExecutionRoleName - Label: default: S3 Buckets (only needed if created outside of this stack) Parameters: - DataBucketName - ModelBucketName - Label: default: S3 Bucket Name with MLOps Seed Code Parameters: - SeedCodeS3BucketName - Label: default: Multi-account model deployment setup (provide OU ids or account lists) Parameters: - OrganizationalUnitStagingId - OrganizationalUnitProdId - StagingAccountList - ProductionAccountList - Label: default: Infrastructure setup in the target accounts in staging and production OUs (multi-account only) Parameters: - SetupStackSetExecutionRoleName - CreateTargetAccountNetworkInfra - Label: default: Availability Zone Configuration Parameters: - AvailabilityZones - NumberOfAZs - Label: default: VPC Endpoint Creation Parameters: - CreateS3VPCEndpoint - CreateSSMVPCEndpoint - CreateCWVPCEndpoint - CreateCWLVPCEndpoint - CreateSageMakerAPIVPCEndpoint - CreateSageMakerRuntimeVPCEndpoint - CreateSageMakerNotebookVPCEndpoint - CreateSTSVPCEndpoint - CreateServiceCatalogVPCEndpoint - CreateCodeCommitVPCEndpoint - CreateCodeCommitAPIVPCEndpoint - CreateCodePipelineVPCEndpoint - CreateCodeBuildVPCEndpoint - CreateECRAPIVPCEndpoint - CreateECRVPCEndpoint - CreateKMSVPCEndpoint - Label: default: Network Configuration Parameters: - CreateVPC - CreateNATGateways - ExistingVPCId - ExistingS3VPCEndpointId - VPCCIDR - CreatePrivateSubnets - PrivateSubnet1ACIDR - PrivateSubnet2ACIDR - PrivateSubnet3ACIDR - PrivateSubnet4ACIDR - PublicSubnet1CIDR - PublicSubnet2CIDR - PublicSubnet3CIDR - PublicSubnet4CIDR - Label: default: VPC Flow Logs Configuration Parameters: - CreateVPCFlowLogsToCloudWatch - CreateVPCFlowLogsRole - VPCFlowLogsRoleArn - VPCFlowLogsLogFormat - VPCFlowLogsLogGroupRetention - VPCFlowLogsMaxAggregationInterval - VPCFlowLogsTrafficType - VPCFlowLogsCloudWatchKMSKey ParameterLabels: EnvName: default: Environment name EnvType: default: Environment stage name (dev, test, prod) EnvTypeStagingName: default: Environment name for staging environment EnvTypeProdName: default: Environment name for production environment StartKernelGatewayApps: default: Select YES to start the Data Science and Data Wrangler kernels on KernelGateway DataBucketName: default: Existing S3 bucket name to store ML data (only if created outside of this stack, otherwise leave empty) ModelBucketName: default: Existing S3 bucket name to store ML models (only if created outside of this stack, otherwise leave empty) SeedCodeS3BucketName: default: Existing S3 bucket name where MLOps seed code will be stored CreateEnvironmentIAMRoles: default: Create environment IAM roles CreateEnvironmentS3Buckets: default: Create environment S3 buckets for models and data CreateS3VPCEndpoint: default: Create S3 VPC endpoint CreateSSMVPCEndpoint: default: Create SSM VPC endpoint CreateCWVPCEndpoint: default: Create CloudWatch VPC endpoint CreateCWLVPCEndpoint: default: Create CloudWatch Logs VPC endpoint CreateSageMakerAPIVPCEndpoint: default: Create SageMaker API VPC endpoint CreateSageMakerRuntimeVPCEndpoint: default: Create SageMaker Runtime VPC endpoint CreateSageMakerNotebookVPCEndpoint: default: Create SageMaker Notebook VPC endpoint CreateSTSVPCEndpoint: default: Create STS VPC endpoint CreateServiceCatalogVPCEndpoint: default: Create Service Catalog endpoint CreateCodeCommitVPCEndpoint: default: Create CodeCommit VPC endpoint CreateCodeCommitAPIVPCEndpoint: default: Create CodeCommit API VPC endpoint CreateCodePipelineVPCEndpoint: default: Create CodePipeline VPC endpoint CreateCodeBuildVPCEndpoint: default: Create CodeBuild VPC endpoint CreateECRAPIVPCEndpoint: default: Create ECR API VPC endpoint CreateECRVPCEndpoint: default: Create ECR VPC endpoint CreateKMSVPCEndpoint: default: Create KMS VPC endpoint UseSharedServicesPyPiMirror: default: Use shared services PyPI mirror DSTeamAdministratorRoleArn: default: Data Science Team Administrator role ARN DataScientistRoleArn: default: Data Scientist role ARN SageMakerExecutionRoleArn: default: SageMaker and SageMaker Notebook execution role ARN SageMakerPipelineExecutionRoleArn: default: SageMaker pipeline execution role ARN SageMakerModelExecutionRoleName: default: SageMaker model endpoint execution role name StackSetAdministrationRoleArn: default: CloudFormation StackSet administration role ARN StackSetExecutionRoleName: default: CloudFormation StackSet execution role name SetupLambdaExecutionRoleArn: default: Execution role ARN for Lambda function for setup script SCProjectLaunchRoleArn: default: Service Catalog Project Launch role ARN OrganizationalUnitStagingId: default: OU Id for the organizational unit with data science staging account. Leave empty for single-account deployment OrganizationalUnitProdId: default: OU Id for the organizational unit with data science production account. Leave empty for single-account deployment StagingAccountList: default: Comma-delimited list of the staging accounts for multi-account model deployment. Leave empty for single-account deployment ProductionAccountList: default: Comma-delimited list of the production accounts for multi-account model deployment. Leave empty for single-account deployment SetupStackSetExecutionRoleName: default: Stack set execution role name for initial infrastructure setup in the target accounts in the staging and production OUs CreateTargetAccountNetworkInfra: default: Create a private VPC and VPC endpoints for model hosting in the target accounts in the staging and production OUs AvailabilityZones: default: Availability Zones NumberOfAZs: default: Number of Availability Zones CreateVPC: default: Create a new VPC CreateNATGateways: default: Create NAT Gateways (per AZ) ExistingVPCId: default: Existing VPC Id ExistingS3VPCEndpointId: default: Existing S3 VPC endpoint Id VPCCIDR: default: CIDR block for a new or existing VPC CreatePrivateSubnets: default: Create private subnets PrivateSubnet1ACIDR: default: Private subnet 1 CIDR PrivateSubnet2ACIDR: default: Private subnet 2 CIDR PrivateSubnet3ACIDR: default: Private subnet 3 CIDR PrivateSubnet4ACIDR: default: Private subnet 4 CIDR PublicSubnet1CIDR: default: Public subnet 1 CIDR PublicSubnet2CIDR: default: Public subnet 2 CIDR PublicSubnet3CIDR: default: Public subnet 3 CIDR PublicSubnet4CIDR: default: Public subnet 4 CIDR CreateVPCFlowLogsToCloudWatch: default: Create VPC Flow Logs (CloudWatch) CreateVPCFlowLogsRole: default: Create VPC Flow Logs execution role VPCFlowLogsRoleArn: default: VPC Flow Logs Role ARN VPCFlowLogsLogFormat: default: VPC Flow Logs - Log Format VPCFlowLogsLogGroupRetention: default: VPC Flow Logs - Log Group Retention VPCFlowLogsMaxAggregationInterval: default: VPC Flow Logs - Max Aggregation Interval VPCFlowLogsTrafficType: default: VPC Flow Logs - Traffic Type VPCFlowLogsCloudWatchKMSKey: default: CloudWatch Logs KMS Key for VPC flow logs Outputs: AssumeTeamAdminRole: Description: URL for assuming the role of a environment admin Value: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.AssumeTeamAdminRole - !Sub - 'https://signin.aws.amazon.com/switchrole?account=${AWS::AccountId}&roleName=${DSTeamAdministratorRoleName}&displayName=${DSTeamAdministratorRoleName}' - DSTeamAdministratorRoleName: !Select ['1', !Split [ '/', !Select ['5', !Split [':', !Ref DSTeamAdministratorRoleArn ]]]] AssumeDataScientistRole: Description: URL for assuming the role of a environment user Value: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.AssumeDataScientistRole - !Sub - 'https://signin.aws.amazon.com/switchrole?account=${AWS::AccountId}&roleName=${DataScientistRoleName}&displayName=${DataScientistRoleName}' - DataScientistRoleName: !Select ['1', !Split [ '/', !Select ['5', !Split [':', !Ref DataScientistRoleArn ]]]] SageMakerDomainId: Description: SageMaker Domain Id Value: !GetAtt EnvironmentSageMakerStudio.Outputs.SageMakerDomainId GetOUAccountsLambdaArn: Description: Lambda function to retrieve the accounts id from the parent OUs Value: !GetAtt GetOUAccountsLambda.Arn Export: Name: 'ds-ou-accounts-lambda-arn' Parameters: EnvName: Type: String AllowedPattern: '[a-z0-9\-]*' Description: Please specify your data science environment name. Used as a suffix for environment resource names. Default: 'sm-mlops' EnvType: Description: System Environment (e.g. dev, test, prod). Used as a suffix for environment resource names. Type: String Default: 'dev' EnvTypeStagingName: Description: Environment name for staging environment Type: String Default: 'staging' EnvTypeProdName: Description: Environment name for production environment Type: String Default: 'prod' StartKernelGatewayApps: Type: String Description: Start the KernelGateway Apps (Data Science and Data Wrangler) AllowedValues: - 'YES' - 'NO' Default: 'NO' DataBucketName: Description: S3 bucket name to store data (only if created outside of this stack, otherwise leave empty) Type: String Default: '' ModelBucketName: Description: S3 bucket name to store models (only if created outside of this stack, otherwise leave empty) Type: String Default: '' SeedCodeS3BucketName: Description: S3 bucket name to store MLOps seed code (the S3 bucket must exist) Type: String CreateEnvironmentIAMRoles: Description: If YES, all IAM Roles for Data Science environment will be automatically provisioned. If NO, you have to provide the IAM roles Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateEnvironmentS3Buckets: Description: Select YES if you want to create Amazon S3 buckets for models and data Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateS3VPCEndpoint: Description: Set to NO to disable creation of an S3 VPC endpoint (and use an existing one) Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateSSMVPCEndpoint: Description: Set to NO to disable creation of an SSM VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateCWVPCEndpoint: Description: Set to NO to disable creation of a CloudWatch VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateCWLVPCEndpoint: Description: Set to NO to disable creation of a CloudWatch logs VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateSageMakerAPIVPCEndpoint: Description: Set to NO to disable creation of a SageMaker API VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateSageMakerRuntimeVPCEndpoint: Description: Set to NO to disable creation of a SageMaker Runtime VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateSageMakerNotebookVPCEndpoint: Description: Set to NO to disable creation of a SageMaker Notebook VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateSTSVPCEndpoint: Description: Set to NO to disable creation of an STS VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateServiceCatalogVPCEndpoint: Description: Set to NO to disable creation of an Service Catalog VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateCodeCommitVPCEndpoint: Description: Set to NO to disable creation of a CodeCommit VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateCodeCommitAPIVPCEndpoint: Description: Set to NO to disable creation of a CodeCommit API VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateCodePipelineVPCEndpoint: Description: Set to NO to disable creation of a CodePipeline VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateCodeBuildVPCEndpoint: Description: Set to NO to disable creation of a CodeBuild VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateECRAPIVPCEndpoint: Description: Set to NO to disable creation of an ECR API VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateECRVPCEndpoint: Description: Set to NO to disable creation of a ECR VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateKMSVPCEndpoint: Description: Set to NO to disable creation of a KMS VPC endpoint Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' UseSharedServicesPyPiMirror: Description: Select YES if you want to use shared services PyPI mirror (must be created in the core stack) Type: String AllowedValues: - 'YES' - 'NO' Default: 'NO' DSTeamAdministratorRoleArn: Description: The ARN of the DataScienceTeamAdministrator role if created outside the stack. Must be provided if Create IAM Roles = NO Type: String Default: '' DataScientistRoleArn: Description: The ARN of the DataScientist role if created outside the stack. Must be provided if Create IAM Roles = NO Type: String Default: '' SageMakerExecutionRoleArn: Description: The ARN of the SageMaker Execution role if created outside the stack. Must be provided if Create IAM Roles = NO Type: String Default: '' SageMakerPipelineExecutionRoleArn: Description: The ARN of the SageMaker Pipeline Execution role if created outside the stack. Must be provided if Create IAM Roles = NO Type: String Default: '' SageMakerModelExecutionRoleName: Description: The name of the SageMaker model endpoint execution role if created outside the stack. Must be provided if Create IAM Roles = NO Type: String Default: '' StackSetAdministrationRoleArn: Description: The ARN of the CloudFormation StackSet administration role if created outside the stack. Must be provided if Create IAM Roles = NO Type: String Default: '' StackSetExecutionRoleName: Description: The name of the CloudFormation StackSet execution role if created outside the stack. Must be provided if Create IAM Roles = NO Type: String Default: '' SetupLambdaExecutionRoleArn: Description: The ARN of the execution role for the Lambda function for SageMaker Studio setup Type: String Default: '' SCProjectLaunchRoleArn: Description: The ARN of the Service Catalog Project launch role role if created outside the stack. Must be provided if Create IAM Roles = NO Type: String Default: '' OrganizationalUnitStagingId: Type: String Description: OU Id of the organizational unit that holds the data science staging account Default: '' OrganizationalUnitProdId: Type: String Description: OU Id of the organizational unit that holds the data science production account Default: '' StagingAccountList: Type: String Description: Comma-delimited list of staging account ids for multi-account model deployment Default: '' ProductionAccountList: Type: String Description: Comma-delimited list of production account ids for multi-account model deployment Default: '' SetupStackSetExecutionRoleName: Type: String Description: Stack set execution role name for initial infrastructure setup in the target accounts (staging and production OUs). Default: '' CreateTargetAccountNetworkInfra: AllowedValues: - 'YES' - 'NO' Default: 'YES' Description: Set to NO when you do not want to create any network infra hosting in the target account. Model hosting VPC and VPC endpoints are required for multi-account model deployment. Type: String AvailabilityZones: Description: 'List of Availability Zones to use for the subnets in the VPC. Note: The logical order is preserved.' Type: List NumberOfAZs: AllowedValues: - '1' - '2' - '3' - '4' Default: '2' Description: Number of Availability Zones to use in the VPC. This must match your selections in the list of Availability Zones parameter. Type: String CreateVPC: AllowedValues: - 'YES' - 'NO' Default: 'YES' Description: Set to NO when you want to re-use an existing VPC. If NO, the Existing VPC Id must be provided Type: String CreateNATGateways: AllowedValues: - 'YES' - 'NO' Default: 'YES' Description: Set to NO when creating only private subnets. If YES, public subnets for each AZ will be created as well as an Internet Gateway Type: String ExistingVPCId: Description: Enter an existing VPC Id for deployment or leave empty if "Create a new VPC" is set to YES Default: '' Type: String ExistingS3VPCEndpointId: Description: Id of the existing S3 VPC endpoint (must be provided if CreateS3VPCEndpoint = NO) Type: String Default: '' VPCCIDR: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.0.0.0/16 Description: CIDR block for the new VPC Type: String CreatePrivateSubnets: AllowedValues: - 'YES' - 'NO' Default: 'YES' Description: Set to NO when you want to re-use existing subnets in the existing VPC (VPC Id must be provided) Type: String PrivateSubnet1ACIDR: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.0.0.0/19 Description: CIDR block for private subnet 1A located in Availability Zone 1 Type: String PrivateSubnet2ACIDR: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.0.32.0/19 Description: CIDR block for private subnet 2A located in Availability Zone 2 Type: String PrivateSubnet3ACIDR: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.0.64.0/19 Description: CIDR block for private subnet 3A located in Availability Zone 3 Type: String PrivateSubnet4ACIDR: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.0.96.0/19 Description: CIDR block for private subnet 4A located in Availability Zone 4 Type: String PublicSubnet1CIDR: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.0.128.0/20 Description: CIDR block for the public DMZ subnet 1 located in Availability Zone 1 Type: String PublicSubnet2CIDR: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.0.144.0/20 Description: CIDR block for the public DMZ subnet 2 located in Availability Zone 2 Type: String PublicSubnet3CIDR: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.0.160.0/20 Description: CIDR block for the public DMZ subnet 3 located in Availability Zone 3 Type: String PublicSubnet4CIDR: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.0.176.0/20 Description: CIDR block for the public DMZ subnet 4 located in Availability Zone 4 Type: String CreateVPCFlowLogsToCloudWatch: Description: Select YES if you want to create an IAM Role for VPC Flow Logs to write the logs into a CloudWatch log group Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' CreateVPCFlowLogsRole: Description: Select YES if you want to create a VPC Flow Logs execution role Type: String AllowedValues: - 'YES' - 'NO' Default: 'YES' VPCFlowLogsRoleArn: Default: '' Description: VPC Flow Logs role ARN used to deliver the logs into CloudWatch log group. Type: String VPCFlowLogsLogFormat: AllowedPattern: '^(\$\{[a-z-]+\})$|^((\$\{[a-z-]+\} )*\$\{[a-z-]+\})$' Default: '${version} ${account-id} ${interface-id} ${srcaddr} ${dstaddr} ${srcport} ${dstport} ${protocol} ${packets} ${bytes} ${start} ${end} ${action} ${log-status}' Description: The fields to include in the flow log record, in the order in which they should appear. Specify the fields using the ${field-id} format, separated by spaces. Using the Default Format as the default value. Type: String VPCFlowLogsLogGroupRetention: AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653] Default: 14 Description: Number of days to retain the VPC Flow Logs in CloudWatch Type: String VPCFlowLogsMaxAggregationInterval: AllowedValues: - 60 - 600 Default: 600 Description: The maximum interval of time during which a flow of packets is captured and aggregated into a flow log record. You can specify 60 seconds (1 minute) or 600 seconds (10 minutes). Type: String VPCFlowLogsTrafficType: AllowedValues: - ACCEPT - ALL - REJECT Default: REJECT Description: The type of traffic to log. You can log traffic that the resource accepts or rejects, or all traffic. Type: String VPCFlowLogsCloudWatchKMSKey: AllowedPattern: '^$|^arn:(aws[a-zA-Z-]*)?:kms:[a-z0-9-]+:\d{12}:key\/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$' ConstraintDescription: 'Key ARN example: arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab' Default: '' Description: (Optional) KMS Key ARN to use for encrypting the VPC flow logs data. If empty, encryption is enabled with CloudWatch Logs managing the server-side encryption keys. Type: String Conditions: IAMRolesCondition: !Equals [ !Ref CreateEnvironmentIAMRoles, 'YES' ] VPCFlowLogsRoleCondition: !Equals [ !Ref CreateVPCFlowLogsRole, 'YES' ] ExistingVPCCondition: !Equals [!Ref 'CreateVPC', 'NO'] ExistingPrivateSubnetsCondition: !And - !Condition ExistingVPCCondition - !Equals [!Ref CreatePrivateSubnets, 'NO'] IAMRoles&VPCFlowLogsRoleCondition: !And [ !Condition IAMRolesCondition, !Condition VPCFlowLogsRoleCondition] S3BucketsConditions: !Equals [ !Ref CreateEnvironmentS3Buckets, 'YES' ] SharedServicesPyPiMirrorCondition: !Equals [ !Ref UseSharedServicesPyPiMirror, 'YES' ] OrganizationalUnitCondition: !And - !Not [ !Equals [ !Ref OrganizationalUnitStagingId, ''] ] - !Not [ !Equals [ !Ref OrganizationalUnitProdId, ''] ] AccountListCondition: !And - !Not [ !Equals [ !Ref StagingAccountList, ''] ] - !Not [ !Equals [ !Ref ProductionAccountList, ''] ] MultiAccountDeploymentCondition: !Or - !Condition OrganizationalUnitCondition - !Condition AccountListCondition IAMRolesStackSetDeploymentCondition: !And - !Condition MultiAccountDeploymentCondition - !Condition IAMRolesCondition TargetAccountNetworkInfraCondition: !Equals [ !Ref CreateTargetAccountNetworkInfra, 'YES' ] NetworkInfraStackSetDeploymentCondition: !And - !Condition MultiAccountDeploymentCondition - !Condition TargetAccountNetworkInfraCondition Rules: S3BucketsNew: RuleCondition: !Equals [ !Ref CreateEnvironmentS3Buckets, 'YES' ] Assertions: - Assert: !And - !Equals [ !Ref DataBucketName, '' ] - !Equals [ !Ref ModelBucketName, '' ] AssertDescription: You cannot use existing S3 buckets if Create environment S3 buckets = YES S3BucketsExisting: RuleCondition: !Equals [ !Ref CreateEnvironmentS3Buckets, 'NO' ] Assertions: - Assert: !And - !Not [ !Equals [ !Ref DataBucketName, '' ] ] - !Not [ !Equals [ !Ref ModelBucketName, '' ] ] AssertDescription: You must provide existing S3 bucket names if Create environment S3 buckets = NO IAMRoles: RuleCondition: !Equals [ !Ref CreateEnvironmentIAMRoles, 'NO' ] Assertions: - Assert: !And - !Not [ !Equals [ !Ref DSTeamAdministratorRoleArn, '' ] ] - !Not [ !Equals [ !Ref DataScientistRoleArn, '' ] ] - !Not [ !Equals [ !Ref SageMakerExecutionRoleArn, '' ] ] - !Not [ !Equals [ !Ref SageMakerPipelineExecutionRoleArn, '' ] ] - !Not [ !Equals [ !Ref SageMakerModelExecutionRoleName, '' ] ] - !Not [ !Equals [ !Ref StackSetAdministrationRoleArn, '' ] ] - !Not [ !Equals [ !Ref StackSetExecutionRoleName, '' ] ] - !Not [ !Equals [ !Ref SCProjectLaunchRoleArn, '' ] ] - !Not [ !Equals [ !Ref SetupLambdaExecutionRoleArn, '' ] ] AssertDescription: ARNs and role names must be provided for all IAM roles if Create environment IAM Roles = NO VPCFlowLogsRole: RuleCondition: !Equals [ !Ref CreateEnvironmentIAMRoles, 'NO' ] Assertions: - Assert: !Equals [ !Ref CreateVPCFlowLogsRole, 'NO' ] AssertDescription: Create VPC Flow Logs execution role cannot be set to YES if Create environment IAM Roles is set NO OrganizationalUnitIds: RuleCondition: !Or - !Not [ !Equals [ !Ref OrganizationalUnitStagingId, '' ] ] - !Not [ !Equals [ !Ref OrganizationalUnitProdId, '' ] ] Assertions: - Assert: !And - !Not [ !Equals [ !Ref OrganizationalUnitProdId, '' ] ] - !Not [ !Equals [ !Ref OrganizationalUnitStagingId, '' ] ] - !Equals [ !Ref StagingAccountList, ''] - !Equals [ !Ref ProductionAccountList, ''] AssertDescription: Both staging and production OU ids must be provided if one OU id is provided and both StagingAccountList and ProductionAccountList must be empty AccountList: RuleCondition: !Or - !Not [ !Equals [ !Ref StagingAccountList, '' ] ] - !Not [ !Equals [ !Ref ProductionAccountList, '' ] ] Assertions: - Assert: !And - !Equals [ !Ref OrganizationalUnitProdId, '' ] - !Equals [ !Ref OrganizationalUnitStagingId, '' ] - !Not [ !Equals [ !Ref StagingAccountList, ''] ] - !Not [ !Equals [ !Ref ProductionAccountList, ''] ] AssertDescription: Both StagingAccountList and ProductionAccountList must be provided if one list is provided and both OrganizationalUnitProdId and OrganizationalUnitProdId must be empty SetupStackSetExecutionRole: RuleCondition: !And - !Not [ !Equals [ !Ref OrganizationalUnitStagingId, ''] ] - !Not [ !Equals [ !Ref OrganizationalUnitProdId, ''] ] - !Or - !Equals [ !Ref CreateEnvironmentIAMRoles, 'YES' ] - !Equals [ !Ref CreateTargetAccountNetworkInfra, 'YES' ] Assertions: - Assert: !Not [ !Equals [ !Ref SetupStackSetExecutionRoleName, '' ] ] AssertDescription: SetupStackSetExecutionRoleName must be provided for multi-account model deployment option Resources: # Environment IAM roles for personas and services EnvironmentIAMRoles: Type: AWS::CloudFormation::Stack Condition: IAMRolesCondition Properties: Parameters: EnvName: !Ref EnvName EnvType: !Ref EnvType CreateVPCFlowLogsRole: !Ref CreateVPCFlowLogsRole SetupStackSetExecutionRoleName: !If - IAMRolesStackSetDeploymentCondition - !Ref SetupStackSetExecutionRoleName - !Ref AWS::NoValue TemplateURL: env-iam.yaml Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType # This stack deploys the IAM Roles which will be provisioned in the target accounts (staging and production) into the current account # These roles are needed for StackSet operations and model deployment # This stack deploys these roles to _the current account_ to enable a trivial single-account model deployment # Single-account model deployment is still uses StackSet operations and a separate model execution role EnvironmentTargetAccountRoles: Type: AWS::CloudFormation::Stack Condition: IAMRolesCondition Properties: Parameters: EnvName: !Ref EnvName EnvType: !Ref EnvType PipelineExecutionRoleArn: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/service-role/AmazonSageMakerServiceCatalogProductsUseRole' AdministratorAccountId: !Ref AWS::AccountId ModelS3KMSKeyArn: !GetAtt EnvironmentKMSKeys.Outputs.S3BucketKMSKeyArn ModelBucketName: !GetAtt EnvironmentVPC.Outputs.ModelBucketName TemplateURL: env-iam-target-account-roles.yaml Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType # These two stack set deploys the IAM roles in the target accounts - staging and production - defined by the provided OU Ids or account lists # Using 'SELF_MANAGED' permission model for this deployment # Deploy IAM roles into the target accounts (staging and production) EnvironmentTargetAccountRolesStackSet: Type: AWS::CloudFormation::StackSet Condition: IAMRolesStackSetDeploymentCondition Properties: AdministrationRoleARN: !GetAtt EnvironmentIAMRoles.Outputs.StackSetAdministrationRoleArn Capabilities: - CAPABILITY_NAMED_IAM Description: Deploys stack set execution and model execution roles to the target accounts ExecutionRoleName: !Ref SetupStackSetExecutionRoleName OperationPreferences: MaxConcurrentPercentage: 100 Parameters: - ParameterKey: 'EnvName' ParameterValue: !Ref EnvName - ParameterKey: 'SageMakerModelExecutionRoleName' ParameterValue: !GetAtt EnvironmentTargetAccountRoles.Outputs.SageMakerModelExecutionRoleName - ParameterKey: 'StackSetExecutionRoleName' ParameterValue: !GetAtt EnvironmentTargetAccountRoles.Outputs.StackSetExecutionRoleName - ParameterKey: 'PipelineExecutionRoleArn' ParameterValue: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/service-role/AmazonSageMakerServiceCatalogProductsUseRole' - ParameterKey: 'AdministratorAccountId' ParameterValue: !Ref AWS::AccountId - ParameterKey: 'ModelS3KMSKeyArn' ParameterValue: !GetAtt EnvironmentKMSKeys.Outputs.S3BucketKMSKeyArn - ParameterKey: "ModelBucketName" ParameterValue: !GetAtt EnvironmentVPC.Outputs.ModelBucketName PermissionModel: 'SELF_MANAGED' StackInstancesGroup: - Regions: - !Ref AWS::Region DeploymentTargets: Accounts: !If - OrganizationalUnitCondition - !GetAtt GetOUAccountsStaging.Accounts - !Split [',', !Ref StagingAccountList] ParameterOverrides: - ParameterKey: 'EnvType' ParameterValue: !Ref EnvTypeStagingName - Regions: - !Ref AWS::Region DeploymentTargets: Accounts: !If - OrganizationalUnitCondition - !GetAtt GetOUAccountsProd.Accounts - !Split [',', !Ref ProductionAccountList] ParameterOverrides: - ParameterKey: 'EnvType' ParameterValue: !Ref EnvTypeProdName StackSetName: !Sub '${EnvName}-${AWS::Region}-target-account-roles' TemplateURL: 'https://s3.amazonaws.com/< S3_CFN_STAGING_BUCKET_PATH >/env-iam-target-account-roles.yaml' Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType # Environment VPC and network resources EnvironmentVPC: Type: AWS::CloudFormation::Stack Properties: Parameters: EnvName: !Ref EnvName EnvType: !Ref EnvType AvailabilityZones: !Join [ ',', !Ref AvailabilityZones ] NumberOfAZs: !Ref NumberOfAZs CreateVPC: !Ref CreateVPC CreateNATGateways: !Ref CreateNATGateways ExistingVPCId: !Ref ExistingVPCId VPCCIDR: !Ref VPCCIDR CreatePrivateSubnets: !Ref CreatePrivateSubnets ExistingPrivateSubnetIds: !If - ExistingPrivateSubnetsCondition - !Join [',', !GetAtt GetNetworkConfiguration.SubnetIds] - !Ref AWS::NoValue PrivateSubnet1ACIDR: !Ref PrivateSubnet1ACIDR PrivateSubnet2ACIDR: !Ref PrivateSubnet2ACIDR PrivateSubnet3ACIDR: !Ref PrivateSubnet3ACIDR PrivateSubnet4ACIDR: !Ref PrivateSubnet4ACIDR PublicSubnet1CIDR: !Ref PublicSubnet1CIDR PublicSubnet2CIDR: !Ref PublicSubnet2CIDR PublicSubnet3CIDR: !Ref PublicSubnet3CIDR PublicSubnet4CIDR: !Ref PublicSubnet4CIDR CreateVPCFlowLogsToCloudWatch: !Ref CreateVPCFlowLogsToCloudWatch VPCFlowLogsRoleArn: !If - IAMRoles&VPCFlowLogsRoleCondition - !GetAtt EnvironmentIAMRoles.Outputs.VPCFlowLogsRoleArn - !Ref VPCFlowLogsRoleArn VPCFlowLogsCloudWatchKMSKey: !Ref VPCFlowLogsCloudWatchKMSKey VPCFlowLogsLogFormat: !Ref VPCFlowLogsLogFormat VPCFlowLogsLogGroupRetention: !Ref VPCFlowLogsLogGroupRetention VPCFlowLogsMaxAggregationInterval: !Ref VPCFlowLogsMaxAggregationInterval VPCFlowLogsTrafficType: !Ref VPCFlowLogsTrafficType # VPC endpoints parameters CreateS3VPCEndpoint: !Ref CreateS3VPCEndpoint CreateSSMVPCEndpoint: !Ref CreateSSMVPCEndpoint CreateCWVPCEndpoint: !Ref CreateCWVPCEndpoint CreateCWLVPCEndpoint: !Ref CreateCWLVPCEndpoint CreateSageMakerAPIVPCEndpoint: !Ref CreateSageMakerAPIVPCEndpoint CreateSageMakerRuntimeVPCEndpoint: !Ref CreateSageMakerRuntimeVPCEndpoint CreateSageMakerNotebookVPCEndpoint: !Ref CreateSageMakerNotebookVPCEndpoint CreateSTSVPCEndpoint: !Ref CreateSTSVPCEndpoint CreateServiceCatalogVPCEndpoint: !Ref CreateServiceCatalogVPCEndpoint CreateCodeCommitVPCEndpoint: !Ref CreateCodeCommitVPCEndpoint CreateCodeCommitAPIVPCEndpoint: !Ref CreateCodeCommitAPIVPCEndpoint CreateCodePipelineVPCEndpoint: !Ref CreateCodePipelineVPCEndpoint CreateCodeBuildVPCEndpoint: !Ref CreateCodeBuildVPCEndpoint CreateECRAPIVPCEndpoint: !Ref CreateECRAPIVPCEndpoint CreateECRVPCEndpoint: !Ref CreateECRVPCEndpoint CreateKMSVPCEndpoint: !Ref CreateKMSVPCEndpoint ExistingS3VPCEndpointId: !Ref ExistingS3VPCEndpointId ExistingPrivateRouteTableIds: !If - ExistingPrivateSubnetsCondition - !Join [',', !GetAtt GetNetworkConfiguration.RouteTableIds] - !Ref AWS::NoValue PyPIMirrorEndpointServiceName: !If - SharedServicesPyPiMirrorCondition - !ImportValue 'ds-pypimirror-shared-service-endpoint-name' - !Ref AWS::NoValue DataBucketName: !If - S3BucketsConditions - !Sub '${EnvName}-${EnvType}-${AWS::Region}-${AWS::AccountId}-data' - !Ref DataBucketName ModelBucketName: !If - S3BucketsConditions - !Sub '${EnvName}-${EnvType}-${AWS::Region}-${AWS::AccountId}-models' - !Ref ModelBucketName TemplateURL: env-vpc.yaml Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType # Staging environment SSM parameters for single-account model deployment use case SageMakerSecurityGroupIdsSSMStaging: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvTypeStagingName}-sagemaker-sg-ids" Type: String Value: !GetAtt EnvironmentVPC.Outputs.SageMakerSecurityGroupIds Description: !Sub 'SageMaker Security Group id for ${EnvName}-${EnvTypeStagingName} environment' PrivateSubnetsSSMStaging: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvTypeStagingName}-private-subnet-ids" Type: String Value: !GetAtt EnvironmentVPC.Outputs.PrivateSubnetIds Description: !Sub 'Private subnets for SageMaker deployment for ${EnvName}-${EnvTypeStagingName} environment' # Production environment SSM parameters for single-account model deployment use case SageMakerSecurityGroupIdsSSMProd: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvTypeProdName}-sagemaker-sg-ids" Type: String Value: !GetAtt EnvironmentVPC.Outputs.SageMakerSecurityGroupIds Description: !Sub 'SageMaker Security Group id for ${EnvName}-${EnvTypeProdName} environment' PrivateSubnetsSSMProd: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvTypeProdName}-private-subnet-ids" Type: String Value: !GetAtt EnvironmentVPC.Outputs.PrivateSubnetIds Description: !Sub 'Private subnets for SageMaker deployment for ${EnvName}-${EnvTypeProdName} environment' # VPC stack set for model hosting in the target accounts in the staging and production OUs or account lists # Deploy VPC infrastructure into the target accounts EnvironmentVPCStackSet: Type: AWS::CloudFormation::StackSet Condition: NetworkInfraStackSetDeploymentCondition Properties: AdministrationRoleARN: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.StackSetAdministrationRoleArn - !Ref StackSetAdministrationRoleArn Description: Deploys VPC infrastructure for model hosting to the target accounts ExecutionRoleName: !Ref SetupStackSetExecutionRoleName OperationPreferences: MaxConcurrentPercentage: 100 FailureTolerancePercentage: 0 Parameters: - ParameterKey: 'EnvName' ParameterValue: !Ref EnvName - ParameterKey: 'AvailabilityZones' ParameterValue: !Join [ ',', !Ref AvailabilityZones ] - ParameterKey: 'NumberOfAZs' ParameterValue: !Ref NumberOfAZs - ParameterKey: 'CreateNATGateways' ParameterValue: 'NO' - ParameterKey: 'VPCCIDR' ParameterValue: !Ref VPCCIDR - ParameterKey: 'PrivateSubnet1ACIDR' ParameterValue: !Ref PrivateSubnet1ACIDR - ParameterKey: 'PrivateSubnet2ACIDR' ParameterValue: !Ref PrivateSubnet2ACIDR - ParameterKey: 'PrivateSubnet3ACIDR' ParameterValue: !Ref PrivateSubnet3ACIDR - ParameterKey: 'PrivateSubnet4ACIDR' ParameterValue: !Ref PrivateSubnet4ACIDR - ParameterKey: 'CreateS3VPCEndpoint' ParameterValue: 'YES' - ParameterKey: 'CreateSSMVPCEndpoint' ParameterValue: 'YES' - ParameterKey: 'CreateCWVPCEndpoint' ParameterValue: 'YES' - ParameterKey: 'CreateCWLVPCEndpoint' ParameterValue: 'YES' - ParameterKey: 'CreateSageMakerAPIVPCEndpoint' ParameterValue: 'YES' - ParameterKey: 'CreateSageMakerRuntimeVPCEndpoint' ParameterValue: 'YES' - ParameterKey: 'CreateSageMakerNotebookVPCEndpoint' ParameterValue: 'NO' - ParameterKey: 'CreateSTSVPCEndpoint' ParameterValue: 'YES' - ParameterKey: 'CreateServiceCatalogVPCEndpoint' ParameterValue: 'NO' - ParameterKey: 'CreateCodeCommitVPCEndpoint' ParameterValue: 'NO' - ParameterKey: 'CreateCodeCommitAPIVPCEndpoint' ParameterValue: 'NO' - ParameterKey: 'CreateCodePipelineVPCEndpoint' ParameterValue: 'NO' - ParameterKey: 'CreateCodeBuildVPCEndpoint' ParameterValue: 'NO' - ParameterKey: 'CreateECRAPIVPCEndpoint' ParameterValue: 'YES' - ParameterKey: 'CreateECRVPCEndpoint' ParameterValue: 'YES' - ParameterKey: 'CreateKMSVPCEndpoint' ParameterValue: 'YES' - ParameterKey: 'DataBucketName' ParameterValue: !If - S3BucketsConditions - !Sub '${EnvName}-${EnvType}-${AWS::Region}-${AWS::AccountId}-data' - !Ref DataBucketName - ParameterKey: 'ModelBucketName' ParameterValue: !If - S3BucketsConditions - !Sub '${EnvName}-${EnvType}-${AWS::Region}-${AWS::AccountId}-models' - !Ref ModelBucketName PermissionModel: 'SELF_MANAGED' StackInstancesGroup: - Regions: - !Ref AWS::Region DeploymentTargets: Accounts: !If - OrganizationalUnitCondition - !GetAtt GetOUAccountsStaging.Accounts - !Split [',', !Ref StagingAccountList] ParameterOverrides: - ParameterKey: 'EnvType' ParameterValue: !Ref EnvTypeStagingName - Regions: - !Ref AWS::Region DeploymentTargets: Accounts: !If - OrganizationalUnitCondition - !GetAtt GetOUAccountsProd.Accounts - !Split [',', !Ref ProductionAccountList] ParameterOverrides: - ParameterKey: 'EnvType' ParameterValue: !Ref EnvTypeProdName StackSetName: !Sub '${EnvName}-${AWS::Region}-target-account-network' TemplateURL: 'https://s3.amazonaws.com/< S3_CFN_STAGING_BUCKET_PATH >/env-vpc.yaml' Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType # Environment KMS keys EnvironmentKMSKeys: Type: AWS::CloudFormation::Stack Properties: Parameters: EnvName: !Ref EnvName EnvType: !Ref EnvType DSTeamAdministratorRoleArn: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.DSTeamAdministratorRoleArn - !Ref DSTeamAdministratorRoleArn DataScientistRoleArn: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.DataScientistRoleArn - !Ref DataScientistRoleArn SageMakerPipelineExecutionRoleArn: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.SageMakerPipelineExecutionRoleArn - !Ref SageMakerPipelineExecutionRoleArn SageMakerExecutionRoleArn: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.SageMakerExecutionRoleArn - !Ref SageMakerExecutionRoleArn SCLaunchRoleArn: !ImportValue 'ds-service-catalog-launch-role-arn' VPCEndpointKMSId: !GetAtt EnvironmentVPC.Outputs.VPCEndpointKMSId TemplateURL: env-kms.yaml Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType # Environment S3 buckets EnvironmentS3: Type: AWS::CloudFormation::Stack Condition: S3BucketsConditions Properties: Parameters: EnvName: !Ref EnvName EnvType: !Ref EnvType DataBucketName: !GetAtt EnvironmentVPC.Outputs.DataBucketName ModelBucketName: !GetAtt EnvironmentVPC.Outputs.ModelBucketName VPCEndpointS3Id: !GetAtt EnvironmentVPC.Outputs.VPCEndpointS3Id S3BucketKMSKeyId: !GetAtt EnvironmentKMSKeys.Outputs.S3BucketKMSKeyId TemplateURL: env-s3.yaml Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType # Environment Service Catalog portfolio EnvironmentSCPortfolio: Type: AWS::CloudFormation::Stack Properties: Parameters: EnvName: !Ref EnvName EnvType: !Ref EnvType SCMLOpsPortfolioPrincipalRoleArn: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.SageMakerExecutionRoleArn - !Ref SageMakerExecutionRoleArn SCMLOpsProductLaunchRoleArn: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/service-role/AmazonSageMakerServiceCatalogProductsLaunchRole' TemplateURL: env-sc-portfolio.yaml Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType # SageMaker Studio domain, user profiles and SageMaker applications EnvironmentSageMakerStudio: Type: AWS::CloudFormation::Stack Properties: Parameters: EnvName: !Ref EnvName EnvType: !Ref EnvType StartKernelGatewayApps: !Ref StartKernelGatewayApps VPCId: !GetAtt EnvironmentVPC.Outputs.VPCId SageMakerStudioSubnetIds: !GetAtt EnvironmentVPC.Outputs.PrivateSubnetIds SageMakerSecurityGroupIds: !GetAtt EnvironmentVPC.Outputs.SageMakerSecurityGroupIds SageMakerStudioStorageKMSKeyId: !GetAtt EnvironmentKMSKeys.Outputs.SageMakerStudioStorageKMSKeyId SageMakerExecutionRoleArn: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.SageMakerExecutionRoleArn - !Ref SageMakerExecutionRoleArn SetupLambdaExecutionRoleArn: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.SetupLambdaExecutionRoleArn - !Ref SetupLambdaExecutionRoleArn TemplateURL: env-sagemaker-studio.yaml Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType # Resource group EnvironmentResourceGroup: Type: "AWS::ResourceGroups::Group" Properties: Name: !Sub '${EnvName}-${EnvType}-resource-group' Description: !Sub 'AWS Resources belonging to Data Science environment ${EnvName} in its ${EnvType} stage' ResourceQuery: Type: "TAG_FILTERS_1_0" Query: ResourceTypeFilters: - "AWS::AllSupported" TagFilters: - Key: "EnvironmentName" Values: - !Sub '${EnvName}' - Key: "EnvironmentType" Values: - !Sub '${EnvType}' Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType # Call a Lambda function to setup cross-account permissions for S3 model bucket and S3 KMS key SetupCrossAccountPermissionsStaging: Type: Custom::SetupCrossAccountPermissions Condition: MultiAccountDeploymentCondition DependsOn: EnvironmentSageMakerStudio Properties: ServiceToken: !GetAtt SetupCrossAccountPermissionsLambda.Arn Accounts: !If - OrganizationalUnitCondition - !GetAtt GetOUAccountsStaging.Accounts - !Split [',', !Ref StagingAccountList] PrincipalRoleName: !If - IAMRolesCondition - !GetAtt EnvironmentTargetAccountRoles.Outputs.SageMakerModelExecutionRoleName - !Ref SageMakerModelExecutionRoleName SetupRoleName: !If - IAMRolesCondition - !GetAtt EnvironmentTargetAccountRoles.Outputs.StackSetExecutionRoleName - !Ref StackSetExecutionRoleName S3BucketName: !If - S3BucketsConditions - !GetAtt EnvironmentS3.Outputs.ModelBucketName - !Ref ModelBucketName KMSKeyId: !GetAtt EnvironmentKMSKeys.Outputs.S3BucketKMSKeyId S3VPCESSMParamName: !Sub '${EnvName}-${EnvTypeStagingName}-s3-vpce-id' KMSVPCESSMParamName: !Sub '${EnvName}-${EnvTypeStagingName}-kms-vpce-id' # Called _after_ SetupCrossAccountPermissionsStaging because these two calls update the same policies SetupCrossAccountPermissionsProd: Type: Custom::SetupCrossAccountPermissions Condition: MultiAccountDeploymentCondition DependsOn: SetupCrossAccountPermissionsStaging Properties: ServiceToken: !GetAtt SetupCrossAccountPermissionsLambda.Arn Accounts: !If - OrganizationalUnitCondition - !GetAtt GetOUAccountsProd.Accounts - !Split [',', !Ref ProductionAccountList] PrincipalRoleName: !If - IAMRolesCondition - !GetAtt EnvironmentTargetAccountRoles.Outputs.SageMakerModelExecutionRoleName - !Ref SageMakerModelExecutionRoleName SetupRoleName: !If - IAMRolesCondition - !GetAtt EnvironmentTargetAccountRoles.Outputs.StackSetExecutionRoleName - !Ref StackSetExecutionRoleName S3BucketName: !If - S3BucketsConditions - !GetAtt EnvironmentS3.Outputs.ModelBucketName - !Ref ModelBucketName KMSKeyId: !GetAtt EnvironmentKMSKeys.Outputs.S3BucketKMSKeyId S3VPCESSMParamName: !Sub '${EnvName}-${EnvTypeProdName}-s3-vpce-id' KMSVPCESSMParamName: !Sub '${EnvName}-${EnvTypeProdName}-kms-vpce-id' SetupCrossAccountPermissionsLambda: Type: AWS::Lambda::Function Properties: Code: ZipFile: | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 import json import boto3 import cfnresponse from botocore.exceptions import ClientError sts = boto3.client("sts") s3 = boto3.client("s3") kms = boto3.client("kms") def lambda_handler(event, context): try: response_status = cfnresponse.SUCCESS r = {} if 'RequestType' in event and event['RequestType'] == 'Create': r["Result"] = setup_cross_account_permissions( accounts=event['ResourceProperties']['Accounts'], principal_role_name=event['ResourceProperties']['PrincipalRoleName'], setup_role_name=event['ResourceProperties']['SetupRoleName'], bucket_name=event['ResourceProperties']['S3BucketName'], kms_key_id=event['ResourceProperties']['KMSKeyId'], s3_vpce_ssm_param_name=event['ResourceProperties']['S3VPCESSMParamName'], kms_vpce_ssm_pram_name=event['ResourceProperties']['KMSVPCESSMParamName'], ) cfnresponse.send(event, context, response_status, r, '') except Exception as e: print(str(e)) cfnresponse.send(event, context, cfnresponse.FAILED, {}, physicalResourceId=event.get('PhysicalResourceId'), reason=str(e)) def get_ssm_parameter(accounts, role, p_name): values = [] for account_id in accounts: print(f"assume the role {role} in {account_id}") stsresponse = sts.assume_role( RoleArn=f"arn:aws:iam::{account_id}:role/{role}", RoleSessionName=f"newsession" ) values.append( boto3.client("ssm", aws_access_key_id=stsresponse["Credentials"]["AccessKeyId"], aws_secret_access_key=stsresponse["Credentials"]["SecretAccessKey"], aws_session_token=stsresponse["Credentials"]["SessionToken"] ).get_parameter(Name=p_name)["Parameter"]["Value"] ) print(f"values retrieved: {values}") return values def append_list(value, list): if type(value) is str: return list + [value] else: return list + value def setup_policy( policy, principals, vpce_ids ): p = json.loads(policy) s = [s for s in p["Statement"] if s["Sid"] == "AllowCrossAccount"] v = [s for s in p["Statement"] if s["Sid"] == "DenyNoVPC"][0] s[0]["Principal"]["AWS"] = append_list(s[0]["Principal"]["AWS"], principals) v["Condition"]["StringNotEquals"]["aws:sourceVpce"] = append_list(v["Condition"]["StringNotEquals"]["aws:sourceVpce"], vpce_ids) print(f"policy:{p}") return json.dumps(p) def setup_cross_account_permissions( accounts, principal_role_name, setup_role_name, bucket_name, kms_key_id, s3_vpce_ssm_param_name, kms_vpce_ssm_pram_name ): principals = [f"arn:aws:iam::{a}:role/{principal_role_name}" for a in accounts] # update S3 bucket policy s3.put_bucket_policy( Bucket=bucket_name, Policy=setup_policy( policy=s3.get_bucket_policy(Bucket=bucket_name)["Policy"], principals=principals, vpce_ids=get_ssm_parameter(accounts, setup_role_name, s3_vpce_ssm_param_name) )) # update KMS key policy kms.put_key_policy( KeyId=kms_key_id, PolicyName="default", Policy=setup_policy( policy=kms.get_key_policy(KeyId=kms_key_id,PolicyName="default")["Policy"], principals=principals, vpce_ids=get_ssm_parameter(accounts, setup_role_name, kms_vpce_ssm_pram_name) )) return "SUCCESS" Description: Setup cross-account permissions in resource policies Handler: index.lambda_handler MemorySize: 128 Role: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.SetupLambdaExecutionRoleArn - !Ref SetupLambdaExecutionRoleArn Runtime: python3.8 Timeout: 60 Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType GetOUAccountsStaging: Type: Custom::GetOUAccounts Condition: MultiAccountDeploymentCondition Properties: ServiceToken: !GetAtt GetOUAccountsLambda.Arn OUIds: - !Ref OrganizationalUnitStagingId GetOUAccountsProd: Type: Custom::GetOUAccounts Condition: MultiAccountDeploymentCondition Properties: ServiceToken: !GetAtt GetOUAccountsLambda.Arn OUIds: - !Ref OrganizationalUnitProdId GetOUAccountsLambda: Type: AWS::Lambda::Function Properties: Code: ZipFile: | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 import json import boto3 import cfnresponse from botocore.exceptions import ClientError org_client = boto3.client("organizations") def lambda_handler(event, context): try: response_status = cfnresponse.SUCCESS r = {} if 'RequestType' in event and event['RequestType'] == 'Create': r["Accounts"] = get_ou_accounts( event['ResourceProperties']['OUIds'] ) cfnresponse.send(event, context, response_status, r, '') except (Exception, ClientError) as exception: print(exception) cfnresponse.send(event, context, cfnresponse.FAILED, {}, physicalResourceId=event.get('PhysicalResourceId'), reason=str(exception)) # This operation can be called only from the organization's management account or by a member account that is a delegated administrator for an AWS service def get_ou_accounts(ou_ids): print(f"get account ids for OUs: {ou_ids}") return [ i['Id'] for ou_id in ou_ids if ou_id for i in org_client.list_accounts_for_parent(ParentId=ou_id)['Accounts'] ] Description: Get accounts from Organizations OUs Handler: index.lambda_handler MemorySize: 128 Role: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.SetupLambdaExecutionRoleArn - !Ref SetupLambdaExecutionRoleArn Runtime: python3.8 Timeout: 60 Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType GetNetworkConfiguration: Type: Custom::GetNetworkConfiguration Condition: ExistingPrivateSubnetsCondition Properties: ServiceToken: !GetAtt GetNetworkConfigurationLambda.Arn AvailabilityZones: !Ref AvailabilityZones SubnetCIDRBlocks: - !Ref PrivateSubnet1ACIDR - !Ref PrivateSubnet2ACIDR - !Ref PrivateSubnet3ACIDR - !Ref PrivateSubnet4ACIDR GetNetworkConfigurationLambda: Type: AWS::Lambda::Function Condition: ExistingPrivateSubnetsCondition Properties: FunctionName: GetNetworkConfiguration Code: ZipFile: | import json import boto3 import cfnresponse from botocore.exceptions import ClientError ec2 = boto3.client("ec2") def lambda_handler(event, context): try: response_status = cfnresponse.SUCCESS r = {} if 'RequestType' in event and event['RequestType'] == 'Create': r["SubnetIds"] = get_subnet_ids( event['ResourceProperties']['SubnetCIDRBlocks'], event['ResourceProperties']['AvailabilityZones'] ) r["RouteTableIds"] = get_rt_ids(r["SubnetIds"]) cfnresponse.send(event, context, response_status, r, '') except (Exception, ClientError) as exception: print(exception) cfnresponse.send(event, context, cfnresponse.FAILED, {}, physicalResourceId=event.get('PhysicalResourceId'), reason=str(exception)) def get_subnet_ids(cidr_blocks, azs): print(azs) print(cidr_blocks) return [sn["SubnetId"] for sn in ec2.describe_subnets()["Subnets"] if sn["AvailabilityZone"] in azs and sn["CidrBlock"] in cidr_blocks] def get_rt_ids(subnet_ids): print(subnet_ids) return [r["RouteTableId"] for r in ec2.describe_route_tables( Filters=[ { "Name":"association.subnet-id", "Values":subnet_ids } ] )["RouteTables"]] Description: Get network configuration for SageMaker domain Handler: index.lambda_handler MemorySize: 128 Role: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.SetupLambdaExecutionRoleArn - !Ref SetupLambdaExecutionRoleArn Runtime: python3.8 Timeout: 60 Tags: - Key: EnvironmentName Value: !Ref EnvName - Key: EnvironmentType Value: !Ref EnvType StagingAccountListSSM: Type: 'AWS::SSM::Parameter' Condition: MultiAccountDeploymentCondition Properties: Name: !Sub '${EnvName}-${EnvType}-staging-account-list' Type: String Value: !If - AccountListCondition - !Ref StagingAccountList - !Join [',', !GetAtt GetOUAccountsStaging.Accounts] Description: !Sub 'List of staging accounts for model deployment for ${EnvName}-${EnvType} data science environment' ProductionAccountListSSM: Type: 'AWS::SSM::Parameter' Condition: MultiAccountDeploymentCondition Properties: Name: !Sub '${EnvName}-${EnvType}-production-account-list' Type: String Value: !If - AccountListCondition - !Ref ProductionAccountList - !Join [',', !GetAtt GetOUAccountsProd.Accounts] Description: !Sub 'List of production accounts for model deployment for ${EnvName}-${EnvType} data science environment' OrganizationalUnitStagingIdSSM: Type: 'AWS::SSM::Parameter' Condition: OrganizationalUnitCondition Properties: Name: !Sub '${EnvName}-${EnvType}-ou-staging-id' Type: String Value: !Ref OrganizationalUnitStagingId Description: !Sub 'OU Id of the organizational unit that holds the data science staging account for ${EnvName}-${EnvType} data science environment' OrganizationalUnitProdIdSSM: Type: 'AWS::SSM::Parameter' Condition: OrganizationalUnitCondition Properties: Name: !Sub '${EnvName}-${EnvType}-ou-prod-id' Type: String Value: !Ref OrganizationalUnitProdId Description: !Sub 'OU Id of the organizational unit that holds the data science production account for ${EnvName}-${EnvType} data science environment' SageMakerPipelineExecutionRoleSSM: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvType}-sm-pipeline-execution-role-arn" Type: String Value: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.SageMakerPipelineExecutionRoleArn - !Ref SageMakerPipelineExecutionRoleArn Description: !Sub 'SageMaker pipeline execution role ${EnvName}-${EnvType} data science environment' SageMakerModelExecutionRoleSSM: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvType}-sm-model-execution-role-name" Type: String Value: !If - IAMRolesCondition - !GetAtt EnvironmentTargetAccountRoles.Outputs.SageMakerModelExecutionRoleName - !Ref SageMakerModelExecutionRoleName Description: !Sub 'SageMaker model execution role for ${EnvName}-${EnvType} data science environment' StackSetAdminstratorRoleSSM: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvType}-stackset-administration-role-arn" Type: String Value: !If - IAMRolesCondition - !GetAtt EnvironmentIAMRoles.Outputs.StackSetAdministrationRoleArn - !Ref StackSetAdministrationRoleArn Description: !Sub 'CloudFormation StackSet administrator role for ${EnvName}-${EnvType} data science environment' StackSetExecutionRoleSSM: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvType}-stackset-execution-role-name" Type: String Value: !If - IAMRolesCondition - !GetAtt EnvironmentTargetAccountRoles.Outputs.StackSetExecutionRoleName - !Ref StackSetExecutionRoleName Description: !Sub 'CloudFormation StackSet execution role for ${EnvName}-${EnvType} data science environment' ModelBucketSSM: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvType}-model-bucket-name" Type: String Value: !GetAtt EnvironmentVPC.Outputs.ModelBucketName Description: !Sub 'Amazon S3 bucket to store models for ${EnvName}-${EnvType} data science environment' DataBucketSSM: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvType}-data-bucket-name" Type: String Value: !GetAtt EnvironmentVPC.Outputs.DataBucketName Description: !Sub 'Amazon S3 bucket to store data for ${EnvName}-${EnvType} data science environment' EnvTypeStagingNameSSM: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvType}-env-type-staging-name" Type: String Value: !Ref EnvTypeStagingName Description: Environment name for staging environment EnvTypeProdNameSSM: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvType}-env-type-prod-name" Type: String Value: !Ref EnvTypeProdName Description: Environment name for production environment SeedCodeS3BucketNameSSM: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "${EnvName}-${EnvType}-seed-code-s3bucket-name" Type: String Value: !Ref SeedCodeS3BucketName Description: S3 bucket name where MLOps seed code is stored