diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/README.md b/packages/@aws-cdk/aws-sagemaker-alpha/README.md index f4798faf19036..7bc79f61300a7 100644 --- a/packages/@aws-cdk/aws-sagemaker-alpha/README.md +++ b/packages/@aws-cdk/aws-sagemaker-alpha/README.md @@ -280,3 +280,66 @@ productionVariant.metricModelLatency().createAlarm(this, 'ModelLatencyAlarm', { evaluationPeriods: 3, }); ``` + +## Pipeline + +You can import existing SageMaker Pipelines into your CDK application to use with other AWS services like EventBridge Scheduler. + +### Importing an existing Pipeline + +Import a pipeline by ARN: + +```typescript +import * as sagemaker from '@aws-cdk/aws-sagemaker-alpha'; + +const pipeline = sagemaker.Pipeline.fromPipelineArn(this, 'MyPipeline', + 'arn:aws:sagemaker:us-east-1:123456789012:pipeline/my-pipeline'); +``` + +Import a pipeline by name (must be in the same account and region as the stack): + +```typescript +import * as sagemaker from '@aws-cdk/aws-sagemaker-alpha'; + +const pipeline = sagemaker.Pipeline.fromPipelineName(this, 'MyPipeline', 'my-pipeline'); +``` + +### Using with EventBridge Scheduler + +You can use imported pipelines with EventBridge Scheduler to trigger pipeline executions on a schedule: + +```typescript +import * as scheduler from 'aws-cdk-lib/aws-scheduler'; +import * as targets from 'aws-cdk-lib/aws-scheduler-targets'; +import * as sagemaker from '@aws-cdk/aws-sagemaker-alpha'; +import { Duration } from 'aws-cdk-lib'; + +const pipeline = sagemaker.Pipeline.fromPipelineName(this, 'MyPipeline', 'my-pipeline'); + +new scheduler.Schedule(this, 'PipelineSchedule', { + schedule: scheduler.ScheduleExpression.rate(Duration.hours(12)), + target: new targets.SageMakerStartPipelineExecution(pipeline, { + pipelineParameterList: [ + { name: 'InputDataPath', value: 's3://my-bucket/input/' }, + { name: 'ModelName', value: 'my-model-v1' } + ] + }) +}); +``` + +### IAM Permissions + +Grant permissions to start pipeline executions: + +```typescript +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as sagemaker from '@aws-cdk/aws-sagemaker-alpha'; + +const pipeline = sagemaker.Pipeline.fromPipelineName(this, 'MyPipeline', 'my-pipeline'); +const role = new iam.Role(this, 'PipelineRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') +}); + +// Grant permission to start pipeline execution +pipeline.grantStartPipelineExecution(role); +``` diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/lib/index.ts b/packages/@aws-cdk/aws-sagemaker-alpha/lib/index.ts index 3d2b2e1341b3c..1d9280d97f414 100644 --- a/packages/@aws-cdk/aws-sagemaker-alpha/lib/index.ts +++ b/packages/@aws-cdk/aws-sagemaker-alpha/lib/index.ts @@ -5,6 +5,7 @@ export * from './endpoint-config'; export * from './instance-type'; export * from './model'; export * from './model-data'; +export * from './pipeline'; export * from './scalable-instance-count'; // AWS::SageMaker CloudFormation Resources: diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/lib/pipeline.ts b/packages/@aws-cdk/aws-sagemaker-alpha/lib/pipeline.ts new file mode 100644 index 0000000000000..c2975a8340021 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker-alpha/lib/pipeline.ts @@ -0,0 +1,189 @@ +import { Construct } from 'constructs'; +import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam'; +import { IPipeline } from 'aws-cdk-lib/aws-sagemaker'; +import { Arn, Resource, Stack, Token } from 'aws-cdk-lib'; +import { ValidationError } from 'aws-cdk-lib/core/lib/errors'; + +/** + * Validates a SageMaker Pipeline name according to AWS requirements + * @param pipelineName The pipeline name to validate + * @param scope The construct scope for error reporting + * @throws ValidationError if the pipeline name is invalid + */ +function validatePipelineName(pipelineName: string, scope: Construct): void { + // Skip validation for CDK tokens + if (Token.isUnresolved(pipelineName)) { + return; + } + + // Check length constraints (1-256 characters) + if (!pipelineName || pipelineName.length === 0 || pipelineName.length > 256) { + throw new ValidationError(`Invalid pipeline name format: ${pipelineName}. Pipeline name must be between 1-256 characters.`, scope); + } + + // Check character pattern: must start and end with alphanumeric, can contain hyphens + if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(pipelineName)) { + throw new ValidationError(`Invalid pipeline name format: ${pipelineName}. Must start and end with alphanumeric characters, can contain hyphens but not consecutive hyphens, underscores, dots, or spaces.`, scope); + } + + // Check for consecutive hyphens (not allowed) + if (pipelineName.includes('--')) { + throw new ValidationError(`Invalid pipeline name format: ${pipelineName}. Cannot contain consecutive hyphens.`, scope); + } +} + +/** + * Attributes for importing a SageMaker Pipeline + */ +export interface PipelineAttributes { + /** + * The ARN of the pipeline + * @default - Either this or pipelineName must be provided + */ + readonly pipelineArn?: string; + + /** + * The name of the pipeline + * @default - Either this or pipelineArn must be provided + */ + readonly pipelineName?: string; + + /** + * The account the pipeline is in + * @default - same account as the stack + */ + readonly account?: string; + + /** + * The region the pipeline is in + * @default - same region as the stack + */ + readonly region?: string; +} + +/** + * Properties for defining a SageMaker Pipeline + */ +export interface PipelineProps { + /** + * The physical name of the pipeline + * @default - CDK generated name + */ + readonly pipelineName?: string; +} + +/** + * A SageMaker Pipeline + * + * @resource AWS::SageMaker::Pipeline + */ +export class Pipeline extends Resource implements IPipeline { + /** + * Import a pipeline from its ARN + * + * @param scope The parent construct + * @param id The construct id + * @param pipelineArn The ARN of the pipeline + */ + public static fromPipelineArn(scope: Construct, id: string, pipelineArn: string): IPipeline { + return Pipeline.fromPipelineAttributes(scope, id, { pipelineArn }); + } + + /** + * Import a pipeline from its name + * + * For this to work, the pipeline must be in the same account and region as the stack. + * + * @param scope The parent construct + * @param id The construct id + * @param pipelineName The name of the pipeline + */ + public static fromPipelineName(scope: Construct, id: string, pipelineName: string): IPipeline { + return Pipeline.fromPipelineAttributes(scope, id, { pipelineName }); + } + + /** + * Import a pipeline from attributes + * + * @param scope The parent construct + * @param id The construct id + * @param attrs The attributes of the pipeline to import + */ + public static fromPipelineAttributes(scope: Construct, id: string, attrs: PipelineAttributes): IPipeline { + const stack = Stack.of(scope); + + // Determine pipeline name and ARN + let pipelineName: string; + let pipelineArn: string; + + if (attrs.pipelineArn) { + pipelineArn = attrs.pipelineArn; + pipelineName = Arn.extractResourceName(pipelineArn, 'pipeline'); + } else if (attrs.pipelineName !== undefined) { + pipelineName = attrs.pipelineName; + // Validate pipeline name format + validatePipelineName(pipelineName, scope); + + pipelineArn = stack.formatArn({ + service: 'sagemaker', + resource: 'pipeline', + resourceName: pipelineName, + account: attrs.account, + region: attrs.region, + }); + } else { + throw new ValidationError('Either pipelineArn or pipelineName must be provided', scope); + } + + class Import extends Resource implements IPipeline { + public readonly pipelineArn = pipelineArn; + public readonly pipelineName = pipelineName; + + public grantStartPipelineExecution(grantee: IGrantable): Grant { + return Grant.addToPrincipal({ + grantee, + actions: ['sagemaker:StartPipelineExecution'], + resourceArns: [this.pipelineArn], + }); + } + } + + return new Import(scope, id, { + account: attrs.account, + region: attrs.region, + }); + } + + /** + * The ARN of the pipeline. + */ + public readonly pipelineArn!: string; + + /** + * The name of the pipeline. + */ + public readonly pipelineName!: string; + + /** + * Create a new Pipeline (not supported - use import methods instead) + * @internal + */ + constructor(scope: Construct, id: string, props?: PipelineProps) { + super(scope, id); + // Suppress unused parameter warning + void props; + throw new ValidationError('Pipeline construct cannot be instantiated directly. Use Pipeline.fromPipelineArn() or Pipeline.fromPipelineName() to import existing pipelines.', scope); + } + + /** + * Permits an IAM principal to start this pipeline execution + * @param grantee The principal to grant access to + */ + public grantStartPipelineExecution(grantee: IGrantable): Grant { + return Grant.addToPrincipal({ + grantee, + actions: ['sagemaker:StartPipelineExecution'], + resourceArns: [this.pipelineArn], + }); + } +} diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/aws-cdk-sagemaker-alpha-pipeline-import.assets.json b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/aws-cdk-sagemaker-alpha-pipeline-import.assets.json new file mode 100644 index 0000000000000..94fdf561629a9 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/aws-cdk-sagemaker-alpha-pipeline-import.assets.json @@ -0,0 +1,34 @@ +{ + "version": "48.0.0", + "files": { + "44e9c4d7a5d3fd2d677e1a7e416b2b56f6b0104bd5eff9cac5557b4c65a9dc61": { + "displayName": "aws-cdk-sagemaker-alpha-pipeline-import/Custom::S3AutoDeleteObjectsCustomResourceProvider Code", + "source": { + "path": "asset.44e9c4d7a5d3fd2d677e1a7e416b2b56f6b0104bd5eff9cac5557b4c65a9dc61", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region-094cbf39": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "44e9c4d7a5d3fd2d677e1a7e416b2b56f6b0104bd5eff9cac5557b4c65a9dc61.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "38f75d36b7b0ca2fc712704d74edaddbba1eb9caaca0e652a62f7aae9d948684": { + "displayName": "aws-cdk-sagemaker-alpha-pipeline-import Template", + "source": { + "path": "aws-cdk-sagemaker-alpha-pipeline-import.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region-4ad31fcc": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "38f75d36b7b0ca2fc712704d74edaddbba1eb9caaca0e652a62f7aae9d948684.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/aws-cdk-sagemaker-alpha-pipeline-import.template.json b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/aws-cdk-sagemaker-alpha-pipeline-import.template.json new file mode 100644 index 0000000000000..3890c1a8d0be6 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/aws-cdk-sagemaker-alpha-pipeline-import.template.json @@ -0,0 +1,411 @@ +{ + "Resources": { + "SourceBucketDDD2130A": { + "Type": "AWS::S3::Bucket", + "Properties": { + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SourceBucketPolicy703DFBF9": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "SourceBucketDDD2130A" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:List*", + "s3:PutBucketPolicy" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "SourceBucketAutoDeleteObjectsCustomResourceC68FC040": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "SourceBucketDDD2130A" + } + }, + "DependsOn": [ + "SourceBucketPolicy703DFBF9" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "44e9c4d7a5d3fd2d677e1a7e416b2b56f6b0104bd5eff9cac5557b4c65a9dc61.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + }, + "Runtime": "nodejs22.x", + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "SourceBucketDDD2130A" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ] + }, + "OutputBucket7114EB27": { + "Type": "AWS::S3::Bucket", + "Properties": { + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "OutputBucketPolicyAE009DDC": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "OutputBucket7114EB27" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:List*", + "s3:PutBucketPolicy" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "OutputBucket7114EB27", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "OutputBucket7114EB27", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "OutputBucketAutoDeleteObjectsCustomResource6C7A161F": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "OutputBucket7114EB27" + } + }, + "DependsOn": [ + "OutputBucketPolicyAE009DDC" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "PipelineRoleDCFDBB91": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "sagemaker.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonSageMakerFullAccess" + ] + ] + } + ] + } + }, + "TestPipeline": { + "Type": "AWS::SageMaker::Pipeline", + "Properties": { + "PipelineDefinition": { + "PipelineDefinitionBody": { + "Fn::Join": [ + "", + [ + "{\"Version\":\"2020-12-01\",\"Metadata\":{},\"Parameters\":[{\"Name\":\"InputPath\",\"Type\":\"String\",\"DefaultValue\":\"s3://", + { + "Ref": "SourceBucketDDD2130A" + }, + "/input/\"}],\"Steps\":[{\"Name\":\"ProcessingStep\",\"Type\":\"Processing\",\"Arguments\":{\"ProcessingResources\":{\"ClusterConfig\":{\"InstanceCount\":1,\"InstanceType\":\"ml.t3.medium\",\"VolumeSizeInGB\":20}},\"AppSpecification\":{\"ImageUri\":\"382416733822.dkr.ecr.us-east-1.amazonaws.com/sagemaker-scikit-learn:0.23-1-cpu-py3\",\"ContainerEntrypoint\":[\"python3\"],\"ContainerArguments\":[\"-c\",\"print(\\\"Hello from SageMaker Pipeline!\\\")\"]},\"ProcessingOutputConfig\":{\"Outputs\":[{\"OutputName\":\"output\",\"S3Output\":{\"S3Uri\":\"s3://", + { + "Ref": "OutputBucket7114EB27" + }, + "/output/\",\"LocalPath\":\"/opt/ml/processing/output\"}}]}}}]}" + ] + ] + } + }, + "PipelineName": "test-import-pipeline-alpha", + "RoleArn": { + "Fn::GetAtt": [ + "PipelineRoleDCFDBB91", + "Arn" + ] + } + } + }, + "TestRole6C9272DF": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TestRoleDefaultPolicyD1C92014": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sagemaker:StartPipelineExecution", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":sagemaker:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":pipeline/test-import-pipeline-alpha" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TestRoleDefaultPolicyD1C92014", + "Roles": [ + { + "Ref": "TestRole6C9272DF" + } + ] + } + } + }, + "Outputs": { + "PipelineName": { + "Description": "Name of the imported pipeline", + "Value": "test-import-pipeline-alpha" + }, + "PipelineArn": { + "Description": "ARN of the imported pipeline", + "Value": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":sagemaker:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":pipeline/test-import-pipeline-alpha" + ] + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/cdk.out b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/cdk.out new file mode 100644 index 0000000000000..523a9aac37cbf --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"48.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/integ.json b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/integ.json new file mode 100644 index 0000000000000..fccc5892637f5 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/integ.json @@ -0,0 +1,14 @@ +{ + "version": "48.0.0", + "testCases": { + "sagemaker-alpha-pipeline-import-test/DefaultTest": { + "stacks": [ + "aws-cdk-sagemaker-alpha-pipeline-import" + ], + "stackUpdateWorkflow": false, + "assertionStack": "sagemaker-alpha-pipeline-import-test/DefaultTest/DeployAssert", + "assertionStackName": "sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C" + } + }, + "minimumCliVersion": "2.1023.0" +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/manifest.json b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/manifest.json new file mode 100644 index 0000000000000..de308541ea2e2 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/manifest.json @@ -0,0 +1,812 @@ +{ + "version": "48.0.0", + "artifacts": { + "aws-cdk-sagemaker-alpha-pipeline-import.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-sagemaker-alpha-pipeline-import.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-sagemaker-alpha-pipeline-import": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-sagemaker-alpha-pipeline-import.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/38f75d36b7b0ca2fc712704d74edaddbba1eb9caaca0e652a62f7aae9d948684.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-sagemaker-alpha-pipeline-import.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-cdk-sagemaker-alpha-pipeline-import.assets" + ], + "metadata": { + "/aws-cdk-sagemaker-alpha-pipeline-import/SourceBucket": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "removalPolicy": "destroy", + "autoDeleteObjects": true + } + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/SourceBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceBucketDDD2130A" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/SourceBucket/Policy": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "bucket": "*" + } + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/SourceBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceBucketPolicy703DFBF9" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/SourceBucket/AutoDeleteObjectsCustomResource": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/SourceBucket/AutoDeleteObjectsCustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceBucketAutoDeleteObjectsCustomResourceC68FC040" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/Custom::S3AutoDeleteObjectsCustomResourceProvider": [ + { + "type": "aws:cdk:is-custom-resource-handler-customResourceProvider", + "data": true + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/OutputBucket": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "removalPolicy": "destroy", + "autoDeleteObjects": true + } + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/OutputBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OutputBucket7114EB27" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/OutputBucket/Policy": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "bucket": "*" + } + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/OutputBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OutputBucketPolicyAE009DDC" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/OutputBucket/AutoDeleteObjectsCustomResource": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/OutputBucket/AutoDeleteObjectsCustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "OutputBucketAutoDeleteObjectsCustomResource6C7A161F" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/PipelineRole": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "assumedBy": { + "principalAccount": "*", + "assumeRoleAction": "*" + } + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addManagedPolicy": [ + { + "managedPolicyArn": "*" + } + ] + } + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/PipelineRole/ImportPipelineRole": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/PipelineRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineRoleDCFDBB91" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/TestPipeline": [ + { + "type": "aws:cdk:logicalId", + "data": "TestPipeline" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/TestRole": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "assumedBy": { + "principalAccount": "*", + "assumeRoleAction": "*" + } + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPrincipalPolicy": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachInlinePolicy": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachInlinePolicy": [ + "*" + ] + } + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/TestRole/ImportTestRole": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/TestRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TestRole6C9272DF" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/TestRole/DefaultPolicy": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachToRole": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachToRole": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addStatements": [ + {} + ] + } + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/TestRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TestRoleDefaultPolicyD1C92014" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/PipelineName": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineName" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/PipelineArn": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineArn" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-sagemaker-alpha-pipeline-import/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-sagemaker-alpha-pipeline-import" + }, + "sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C.assets" + ], + "metadata": { + "/sagemaker-alpha-pipeline-import-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/sagemaker-alpha-pipeline-import-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "sagemaker-alpha-pipeline-import-test/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "aws-cdk-lib/feature-flag-report": { + "type": "cdk:feature-flag-report", + "properties": { + "module": "aws-cdk-lib", + "flags": { + "@aws-cdk/core:newStyleStackSynthesis": { + "recommendedValue": true, + "explanation": "Switch to new stack synthesis method which enables CI/CD", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:stackRelativeExports": { + "recommendedValue": true, + "explanation": "Name exports based on the construct paths relative to the stack, rather than the global construct path", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-rds:lowercaseDbIdentifier": { + "recommendedValue": true, + "explanation": "Force lowercasing of RDS Cluster names in CDK", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": { + "recommendedValue": true, + "explanation": "Allow adding/removing multiple UsagePlanKeys independently", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-lambda:recognizeVersionProps": { + "recommendedValue": true, + "explanation": "Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-lambda:recognizeLayerVersion": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`." + }, + "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": { + "recommendedValue": true, + "explanation": "Enable this feature flag to have cloudfront distributions use the security policy TLSv1.2_2021 by default.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:checkSecretUsage": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this flag to make it impossible to accidentally use SecretValues in unsafe locations" + }, + "@aws-cdk/core:target-partitions": { + "recommendedValue": [ + "aws", + "aws-cn" + ], + "explanation": "What regions to include in lookup tables of environment agnostic stacks" + }, + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": { + "userValue": true, + "recommendedValue": true, + "explanation": "ECS extensions will automatically add an `awslogs` driver if no logging is specified" + }, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to have Launch Templates generated by the `InstanceRequireImdsv2Aspect` use unique names." + }, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": { + "userValue": true, + "recommendedValue": true, + "explanation": "ARN format used by ECS. In the new ARN format, the cluster name is part of the resource ID." + }, + "@aws-cdk/aws-iam:minimizePolicies": { + "userValue": true, + "recommendedValue": true, + "explanation": "Minimize IAM policies by combining Statements" + }, + "@aws-cdk/core:validateSnapshotRemovalPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Error on snapshot removal policies on resources that do not support it." + }, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate key aliases that include the stack name" + }, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to create an S3 bucket policy by default in cases where an AWS service would automatically create the Policy if one does not exist." + }, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": { + "userValue": true, + "recommendedValue": true, + "explanation": "Restrict KMS key policy for encrypted Queues a bit more" + }, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": { + "userValue": true, + "recommendedValue": true, + "explanation": "Make default CloudWatch Role behavior safe for multiple API Gateways in one environment" + }, + "@aws-cdk/core:enablePartitionLiterals": { + "userValue": true, + "recommendedValue": true, + "explanation": "Make ARNs concrete if AWS partition is known" + }, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": { + "userValue": true, + "recommendedValue": true, + "explanation": "Event Rules may only push to encrypted SQS queues in the same account" + }, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": { + "userValue": true, + "recommendedValue": true, + "explanation": "Avoid setting the \"ECS\" deployment controller when adding a circuit breaker" + }, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature to by default create default policy names for imported roles that depend on the stack the role is in." + }, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use S3 Bucket Policy instead of ACLs for Server Access Logging" + }, + "@aws-cdk/aws-route53-patters:useCertificate": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use the official `Certificate` resource instead of `DnsValidatedCertificate`" + }, + "@aws-cdk/customresources:installLatestAwsSdkDefault": { + "userValue": false, + "recommendedValue": false, + "explanation": "Whether to install the latest SDK by default in AwsCustomResource" + }, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use unique resource name for Database Proxy" + }, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "Remove CloudWatch alarms from deployment group" + }, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Include authorizer configuration in the calculation of the API deployment logical ID." + }, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": { + "userValue": true, + "recommendedValue": true, + "explanation": "Define user data for a launch template by default when a machine image is provided." + }, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": { + "userValue": true, + "recommendedValue": true, + "explanation": "SecretTargetAttachments uses the ResourcePolicy of the attached Secret." + }, + "@aws-cdk/aws-redshift:columnId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Whether to use an ID to track Redshift column changes" + }, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable AmazonEMRServicePolicy_v2 managed policies" + }, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "Restrict access to the VPC default security group" + }, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate a unique id for each RequestValidator added to a method" + }, + "@aws-cdk/aws-kms:aliasNameRef": { + "userValue": true, + "recommendedValue": true, + "explanation": "KMS Alias name and keyArn will have implicit reference to KMS Key" + }, + "@aws-cdk/aws-kms:applyImportedAliasPermissionsToPrincipal": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable grant methods on Aliases imported by name to use kms:ResourceAliases condition" + }, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate a launch template when creating an AutoScalingGroup" + }, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": { + "userValue": true, + "recommendedValue": true, + "explanation": "Include the stack prefix in the stack name generation process" + }, + "@aws-cdk/aws-efs:denyAnonymousAccess": { + "userValue": true, + "recommendedValue": true, + "explanation": "EFS denies anonymous clients accesses" + }, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables support for Multi-AZ with Standby deployment for opensearch domains" + }, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables aws-lambda-nodejs.Function to use the latest available NodeJs runtime as the default" + }, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, mount targets will have a stable logicalId that is linked to the associated subnet." + }, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, a scope of InstanceParameterGroup for AuroraClusterInstance with each parameters will change." + }, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, will always use the arn for identifiers for CfnSourceApiAssociation in the GraphqlApi construct rather than id." + }, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, creating an RDS database cluster from a snapshot will only render credentials for snapshot credentials." + }, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the CodeCommit source action is using the default branch name 'main'." + }, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the logical ID of a Lambda permission for a Lambda action includes an alarm ID." + }, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables Pipeline to set the default value for crossAccountKeys to false." + }, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables Pipeline to set the default pipeline type to V2." + }, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, IAM Policy created from KMS key grant will reduce the resource scope to this key only." + }, + "@aws-cdk/pipelines:reduceAssetRoleTrustScope": { + "recommendedValue": true, + "explanation": "Remove the root account principal from PipelineAssetsFileRole trust policy", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-eks:nodegroupNameAttribute": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, nodegroupName attribute of the provisioned EKS NodeGroup will not have the cluster name prefix." + }, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default volume type of the EBS volume will be GP3" + }, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, remove default deployment alarm settings" + }, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": { + "userValue": false, + "recommendedValue": false, + "explanation": "When enabled, the custom resource used for `AwsCustomResource` will configure the `logApiResponseData` property as true by default" + }, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": { + "userValue": false, + "recommendedValue": false, + "explanation": "When enabled, Adding notifications to a bucket in the current stack will not remove notification from imported stack." + }, + "@aws-cdk/aws-stepfunctions-tasks:useNewS3UriParametersForBedrockInvokeModelTask": { + "recommendedValue": true, + "explanation": "When enabled, use new props for S3 URI field in task definition of state machine for bedrock invoke model.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:explicitStackTags": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, stack tags need to be assigned explicitly on a Stack." + }, + "@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": { + "userValue": false, + "recommendedValue": false, + "explanation": "When set to true along with canContainersAccessInstanceRole=false in ECS cluster, new updated commands will be added to UserData to block container accessing IMDS. **Applicable to Linux only. IMPORTANT: See [details.](#aws-cdkaws-ecsenableImdsBlockingDeprecatedFeature)**" + }, + "@aws-cdk/aws-ecs:disableEcsImdsBlocking": { + "userValue": true, + "recommendedValue": true, + "explanation": "When set to true, CDK synth will throw exception if canContainersAccessInstanceRole is false. **IMPORTANT: See [details.](#aws-cdkaws-ecsdisableEcsImdsBlocking)**" + }, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, we will only grant the necessary permissions when users specify cloudwatch log group through logConfiguration" + }, + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled will allow you to specify a resource policy per replica, and not copy the source table policy to all replicas" + }, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, initOptions.timeout and resourceSignalTimeout values will be summed together." + }, + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, a Lambda authorizer Permission created when using GraphqlApi will be properly scoped with a SourceArn." + }, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the value of property `instanceResourceId` in construct `DatabaseInstanceReadReplica` will be set to the correct value which is `DbiResourceId` instead of currently `DbInstanceArn`" + }, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CFN templates added with `cfn-include` will error if the template contains Resource Update or Create policies with CFN Intrinsics that include non-primitive values." + }, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, both `@aws-sdk` and `@smithy` packages will be excluded from the Lambda Node.js 18.x runtime to prevent version mismatches in bundled applications." + }, + "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the resource of IAM Run Ecs policy generated by SFN EcsRunTask will reference the definition, instead of constructing ARN." + }, + "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the BastionHost construct will use the latest Amazon Linux 2023 AMI, instead of Amazon Linux 2." + }, + "@aws-cdk/core:aspectStabilization": { + "recommendedValue": true, + "explanation": "When enabled, a stabilization loop will be run when invoking Aspects during synthesis.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, use a new method for DNS Name of user pool domain target without creating a custom resource." + }, + "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default security group ingress rules will allow IPv6 ingress from anywhere" + }, + "@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default behaviour of OIDC provider will reject unauthorized connections" + }, + "@aws-cdk/core:enableAdditionalMetadataCollection": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK will expand the scope of usage data collected to better inform CDK development and improve communication for security concerns and emerging issues." + }, + "@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": { + "userValue": false, + "recommendedValue": false, + "explanation": "[Deprecated] When enabled, Lambda will create new inline policies with AddToRolePolicy instead of adding to the Default Policy Statement" + }, + "@aws-cdk/aws-s3:setUniqueReplicationRoleName": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK will automatically generate a unique role name that is used for s3 object replication." + }, + "@aws-cdk/pipelines:reduceStageRoleTrustScope": { + "recommendedValue": true, + "explanation": "Remove the root account principal from Stage addActions trust policy", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-events:requireEventBusPolicySid": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, grantPutEventsTo() will use resource policies with Statement IDs for service principals." + }, + "@aws-cdk/core:aspectPrioritiesMutating": { + "userValue": true, + "recommendedValue": true, + "explanation": "When set to true, Aspects added by the construct library on your behalf will be given a priority of MUTATING." + }, + "@aws-cdk/aws-dynamodb:retainTableReplica": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, table replica will be default to the removal policy of source table unless specified otherwise." + }, + "@aws-cdk/cognito:logUserPoolClientSecretValue": { + "recommendedValue": false, + "explanation": "When disabled, the value of the user pool client secret will not be logged in the custom resource lambda function logs." + }, + "@aws-cdk/pipelines:reduceCrossAccountActionRoleTrustScope": { + "recommendedValue": true, + "explanation": "When enabled, scopes down the trust policy for the cross-account action role", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the resultWriterV2 property of DistributedMap will be used insted of resultWriter" + }, + "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": { + "userValue": true, + "recommendedValue": true, + "explanation": "Add an S3 trust policy to a KMS key resource policy for SNS subscriptions." + }, + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the EgressOnlyGateway resource is only created if private subnets are defined in the dual-stack VPC." + }, + "@aws-cdk/aws-ec2-alpha:useResourceIdForVpcV2Migration": { + "recommendedValue": false, + "explanation": "When enabled, use resource IDs for VPC V2 migration" + }, + "@aws-cdk/aws-s3:publicAccessBlockedByDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, setting any combination of options for BlockPublicAccess will automatically set true for any options not defined." + }, + "@aws-cdk/aws-lambda:useCdkManagedLogGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK creates and manages loggroup for the lambda function" + } + } + } + } + }, + "minimumCliVersion": "2.1023.0" +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C.assets.json b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C.assets.json new file mode 100644 index 0000000000000..9613d0c874693 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C.assets.json @@ -0,0 +1,20 @@ +{ + "version": "48.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "displayName": "sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C Template", + "source": { + "path": "sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region-d8d86b35": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C.template.json b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/sagemakeralphapipelineimporttestDefaultTestDeployAssert6B940F5C.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/tree.json b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/tree.json new file mode 100644 index 0000000000000..404be7195c2c7 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.js.snapshot/tree.json @@ -0,0 +1 @@ +{"version":"tree-0.1","tree":{"id":"App","path":"","constructInfo":{"fqn":"aws-cdk-lib.App","version":"0.0.0"},"children":{"aws-cdk-sagemaker-alpha-pipeline-import":{"id":"aws-cdk-sagemaker-alpha-pipeline-import","path":"aws-cdk-sagemaker-alpha-pipeline-import","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"SourceBucket":{"id":"SourceBucket","path":"aws-cdk-sagemaker-alpha-pipeline-import/SourceBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.Bucket","version":"0.0.0","metadata":[{"removalPolicy":"destroy","autoDeleteObjects":true}]},"children":{"Resource":{"id":"Resource","path":"aws-cdk-sagemaker-alpha-pipeline-import/SourceBucket/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.CfnBucket","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::S3::Bucket","aws:cdk:cloudformation:props":{"tags":[{"key":"aws-cdk:auto-delete-objects","value":"true"}]}}},"Policy":{"id":"Policy","path":"aws-cdk-sagemaker-alpha-pipeline-import/SourceBucket/Policy","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketPolicy","version":"0.0.0","metadata":[{"bucket":"*"}]},"children":{"Resource":{"id":"Resource","path":"aws-cdk-sagemaker-alpha-pipeline-import/SourceBucket/Policy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.CfnBucketPolicy","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::S3::BucketPolicy","aws:cdk:cloudformation:props":{"bucket":{"Ref":"SourceBucketDDD2130A"},"policyDocument":{"Statement":[{"Action":["s3:DeleteObject*","s3:GetBucket*","s3:List*","s3:PutBucketPolicy"],"Effect":"Allow","Principal":{"AWS":{"Fn::GetAtt":["CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092","Arn"]}},"Resource":[{"Fn::GetAtt":["SourceBucketDDD2130A","Arn"]},{"Fn::Join":["",[{"Fn::GetAtt":["SourceBucketDDD2130A","Arn"]},"/*"]]}]}],"Version":"2012-10-17"}}}}}},"AutoDeleteObjectsCustomResource":{"id":"AutoDeleteObjectsCustomResource","path":"aws-cdk-sagemaker-alpha-pipeline-import/SourceBucket/AutoDeleteObjectsCustomResource","constructInfo":{"fqn":"aws-cdk-lib.CustomResource","version":"0.0.0","metadata":["*"]},"children":{"Default":{"id":"Default","path":"aws-cdk-sagemaker-alpha-pipeline-import/SourceBucket/AutoDeleteObjectsCustomResource/Default","constructInfo":{"fqn":"aws-cdk-lib.CfnResource","version":"0.0.0"}}}}}},"Custom::S3AutoDeleteObjectsCustomResourceProvider":{"id":"Custom::S3AutoDeleteObjectsCustomResourceProvider","path":"aws-cdk-sagemaker-alpha-pipeline-import/Custom::S3AutoDeleteObjectsCustomResourceProvider","constructInfo":{"fqn":"aws-cdk-lib.CustomResourceProviderBase","version":"0.0.0"},"children":{"Staging":{"id":"Staging","path":"aws-cdk-sagemaker-alpha-pipeline-import/Custom::S3AutoDeleteObjectsCustomResourceProvider/Staging","constructInfo":{"fqn":"aws-cdk-lib.AssetStaging","version":"0.0.0"}},"Role":{"id":"Role","path":"aws-cdk-sagemaker-alpha-pipeline-import/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role","constructInfo":{"fqn":"aws-cdk-lib.CfnResource","version":"0.0.0"}},"Handler":{"id":"Handler","path":"aws-cdk-sagemaker-alpha-pipeline-import/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler","constructInfo":{"fqn":"aws-cdk-lib.CfnResource","version":"0.0.0"}}}},"OutputBucket":{"id":"OutputBucket","path":"aws-cdk-sagemaker-alpha-pipeline-import/OutputBucket","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.Bucket","version":"0.0.0","metadata":[{"removalPolicy":"destroy","autoDeleteObjects":true}]},"children":{"Resource":{"id":"Resource","path":"aws-cdk-sagemaker-alpha-pipeline-import/OutputBucket/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.CfnBucket","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::S3::Bucket","aws:cdk:cloudformation:props":{"tags":[{"key":"aws-cdk:auto-delete-objects","value":"true"}]}}},"Policy":{"id":"Policy","path":"aws-cdk-sagemaker-alpha-pipeline-import/OutputBucket/Policy","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.BucketPolicy","version":"0.0.0","metadata":[{"bucket":"*"}]},"children":{"Resource":{"id":"Resource","path":"aws-cdk-sagemaker-alpha-pipeline-import/OutputBucket/Policy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_s3.CfnBucketPolicy","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::S3::BucketPolicy","aws:cdk:cloudformation:props":{"bucket":{"Ref":"OutputBucket7114EB27"},"policyDocument":{"Statement":[{"Action":["s3:DeleteObject*","s3:GetBucket*","s3:List*","s3:PutBucketPolicy"],"Effect":"Allow","Principal":{"AWS":{"Fn::GetAtt":["CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092","Arn"]}},"Resource":[{"Fn::GetAtt":["OutputBucket7114EB27","Arn"]},{"Fn::Join":["",[{"Fn::GetAtt":["OutputBucket7114EB27","Arn"]},"/*"]]}]}],"Version":"2012-10-17"}}}}}},"AutoDeleteObjectsCustomResource":{"id":"AutoDeleteObjectsCustomResource","path":"aws-cdk-sagemaker-alpha-pipeline-import/OutputBucket/AutoDeleteObjectsCustomResource","constructInfo":{"fqn":"aws-cdk-lib.CustomResource","version":"0.0.0","metadata":["*"]},"children":{"Default":{"id":"Default","path":"aws-cdk-sagemaker-alpha-pipeline-import/OutputBucket/AutoDeleteObjectsCustomResource/Default","constructInfo":{"fqn":"aws-cdk-lib.CfnResource","version":"0.0.0"}}}}}},"PipelineRole":{"id":"PipelineRole","path":"aws-cdk-sagemaker-alpha-pipeline-import/PipelineRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"0.0.0","metadata":[{"assumedBy":{"principalAccount":"*","assumeRoleAction":"*"}},{"addManagedPolicy":[{"managedPolicyArn":"*"}]}]},"children":{"ImportPipelineRole":{"id":"ImportPipelineRole","path":"aws-cdk-sagemaker-alpha-pipeline-import/PipelineRole/ImportPipelineRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"0.0.0","metadata":["*"]}},"Resource":{"id":"Resource","path":"aws-cdk-sagemaker-alpha-pipeline-import/PipelineRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:props":{"assumeRolePolicyDocument":{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"sagemaker.amazonaws.com"}}],"Version":"2012-10-17"},"managedPolicyArns":[{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/AmazonSageMakerFullAccess"]]}]}}}}},"TestPipeline":{"id":"TestPipeline","path":"aws-cdk-sagemaker-alpha-pipeline-import/TestPipeline","constructInfo":{"fqn":"aws-cdk-lib.aws_sagemaker.CfnPipeline","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::SageMaker::Pipeline","aws:cdk:cloudformation:props":{"pipelineDefinition":{"PipelineDefinitionBody":{"Fn::Join":["",["{\"Version\":\"2020-12-01\",\"Metadata\":{},\"Parameters\":[{\"Name\":\"InputPath\",\"Type\":\"String\",\"DefaultValue\":\"s3://",{"Ref":"SourceBucketDDD2130A"},"/input/\"}],\"Steps\":[{\"Name\":\"ProcessingStep\",\"Type\":\"Processing\",\"Arguments\":{\"ProcessingResources\":{\"ClusterConfig\":{\"InstanceCount\":1,\"InstanceType\":\"ml.t3.medium\",\"VolumeSizeInGB\":20}},\"AppSpecification\":{\"ImageUri\":\"382416733822.dkr.ecr.us-east-1.amazonaws.com/sagemaker-scikit-learn:0.23-1-cpu-py3\",\"ContainerEntrypoint\":[\"python3\"],\"ContainerArguments\":[\"-c\",\"print(\\\"Hello from SageMaker Pipeline!\\\")\"]},\"ProcessingOutputConfig\":{\"Outputs\":[{\"OutputName\":\"output\",\"S3Output\":{\"S3Uri\":\"s3://",{"Ref":"OutputBucket7114EB27"},"/output/\",\"LocalPath\":\"/opt/ml/processing/output\"}}]}}}]}"]]}},"pipelineName":"test-import-pipeline-alpha","roleArn":{"Fn::GetAtt":["PipelineRoleDCFDBB91","Arn"]}}}},"ImportedByName":{"id":"ImportedByName","path":"aws-cdk-sagemaker-alpha-pipeline-import/ImportedByName","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"0.0.0","metadata":[]}},"ImportedByArn":{"id":"ImportedByArn","path":"aws-cdk-sagemaker-alpha-pipeline-import/ImportedByArn","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"0.0.0","metadata":[]}},"TestRole":{"id":"TestRole","path":"aws-cdk-sagemaker-alpha-pipeline-import/TestRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"0.0.0","metadata":[{"assumedBy":{"principalAccount":"*","assumeRoleAction":"*"}},{"addToPrincipalPolicy":[{}]},{"attachInlinePolicy":["*"]},{"attachInlinePolicy":["*"]}]},"children":{"ImportTestRole":{"id":"ImportTestRole","path":"aws-cdk-sagemaker-alpha-pipeline-import/TestRole/ImportTestRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"0.0.0","metadata":["*"]}},"Resource":{"id":"Resource","path":"aws-cdk-sagemaker-alpha-pipeline-import/TestRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:props":{"assumeRolePolicyDocument":{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"}}],"Version":"2012-10-17"}}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"aws-cdk-sagemaker-alpha-pipeline-import/TestRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"0.0.0","metadata":["*",{"attachToRole":["*"]},{"attachToRole":["*"]},{"addStatements":[{}]}]},"children":{"Resource":{"id":"Resource","path":"aws-cdk-sagemaker-alpha-pipeline-import/TestRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":"sagemaker:StartPipelineExecution","Effect":"Allow","Resource":{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":sagemaker:",{"Ref":"AWS::Region"},":",{"Ref":"AWS::AccountId"},":pipeline/test-import-pipeline-alpha"]]}}],"Version":"2012-10-17"},"policyName":"TestRoleDefaultPolicyD1C92014","roles":[{"Ref":"TestRole6C9272DF"}]}}}}}}},"PipelineName":{"id":"PipelineName","path":"aws-cdk-sagemaker-alpha-pipeline-import/PipelineName","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"PipelineArn":{"id":"PipelineArn","path":"aws-cdk-sagemaker-alpha-pipeline-import/PipelineArn","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"BootstrapVersion":{"id":"BootstrapVersion","path":"aws-cdk-sagemaker-alpha-pipeline-import/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"aws-cdk-sagemaker-alpha-pipeline-import/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}}},"sagemaker-alpha-pipeline-import-test":{"id":"sagemaker-alpha-pipeline-import-test","path":"sagemaker-alpha-pipeline-import-test","constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTest","version":"0.0.0"},"children":{"DefaultTest":{"id":"DefaultTest","path":"sagemaker-alpha-pipeline-import-test/DefaultTest","constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTestCase","version":"0.0.0"},"children":{"Default":{"id":"Default","path":"sagemaker-alpha-pipeline-import-test/DefaultTest/Default","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"DeployAssert":{"id":"DeployAssert","path":"sagemaker-alpha-pipeline-import-test/DefaultTest/DeployAssert","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"BootstrapVersion":{"id":"BootstrapVersion","path":"sagemaker-alpha-pipeline-import-test/DefaultTest/DeployAssert/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"sagemaker-alpha-pipeline-import-test/DefaultTest/DeployAssert/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}}}}}}},"Tree":{"id":"Tree","path":"Tree","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}}}}} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.ts b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.ts new file mode 100644 index 0000000000000..c504f6173b1cf --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker-alpha/test/integ.pipeline-import.ts @@ -0,0 +1,118 @@ +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import * as cdk from 'aws-cdk-lib'; +import { Role, ServicePrincipal, ManagedPolicy } from 'aws-cdk-lib/aws-iam'; +import { Bucket } from 'aws-cdk-lib/aws-s3'; +import { CfnPipeline } from 'aws-cdk-lib/aws-sagemaker'; +import * as sagemaker from '../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-sagemaker-alpha-pipeline-import'); + +// Create supporting resources for the pipeline +const sourceBucket = new Bucket(stack, 'SourceBucket', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + autoDeleteObjects: true, +}); + +const outputBucket = new Bucket(stack, 'OutputBucket', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + autoDeleteObjects: true, +}); + +const pipelineRole = new Role(stack, 'PipelineRole', { + assumedBy: new ServicePrincipal('sagemaker.amazonaws.com'), +}); +pipelineRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('AmazonSageMakerFullAccess')); + +// Create a minimal SageMaker Pipeline using L1 construct +const cfnPipeline = new CfnPipeline(stack, 'TestPipeline', { + pipelineName: 'test-import-pipeline-alpha', + pipelineDefinition: { + PipelineDefinitionBody: JSON.stringify({ + Version: '2020-12-01', + Metadata: {}, + Parameters: [ + { + Name: 'InputPath', + Type: 'String', + DefaultValue: sourceBucket.s3UrlForObject('input/'), + }, + ], + Steps: [ + { + Name: 'ProcessingStep', + Type: 'Processing', + Arguments: { + ProcessingResources: { + ClusterConfig: { + InstanceCount: 1, + InstanceType: 'ml.t3.medium', + VolumeSizeInGB: 20, + }, + }, + AppSpecification: { + ImageUri: '382416733822.dkr.ecr.us-east-1.amazonaws.com/sagemaker-scikit-learn:0.23-1-cpu-py3', + ContainerEntrypoint: ['python3'], + ContainerArguments: ['-c', 'print("Hello from SageMaker Pipeline!")'], + }, + ProcessingOutputConfig: { + Outputs: [ + { + OutputName: 'output', + S3Output: { + S3Uri: outputBucket.s3UrlForObject('output/'), + LocalPath: '/opt/ml/processing/output', + }, + }, + ], + }, + }, + }, + ], + }), + }, + roleArn: pipelineRole.roleArn, +}); + +// Test importing by pipeline name +const importedByName = sagemaker.Pipeline.fromPipelineName( + stack, + 'ImportedByName', + cfnPipeline.pipelineName!, +); + +// Test importing by ARN +const pipelineArn = cdk.Stack.of(stack).formatArn({ + service: 'sagemaker', + resource: 'pipeline', + resourceName: cfnPipeline.pipelineName!, +}); + +const importedByArn = sagemaker.Pipeline.fromPipelineArn( + stack, + 'ImportedByArn', + pipelineArn, +); + +// Test IAM permissions +const testRole = new Role(stack, 'TestRole', { + assumedBy: new ServicePrincipal('lambda.amazonaws.com'), +}); + +importedByName.grantStartPipelineExecution(testRole); + +// Output the pipeline details for verification +new cdk.CfnOutput(stack, 'PipelineName', { + value: importedByName.pipelineName, + description: 'Name of the imported pipeline', +}); + +new cdk.CfnOutput(stack, 'PipelineArn', { + value: importedByArn.pipelineArn, + description: 'ARN of the imported pipeline', +}); + +new IntegTest(app, 'sagemaker-alpha-pipeline-import-test', { + testCases: [stack], + stackUpdateWorkflow: false, +}); diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/test/pipeline.test.ts b/packages/@aws-cdk/aws-sagemaker-alpha/test/pipeline.test.ts new file mode 100644 index 0000000000000..b1d4cb781f9b3 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker-alpha/test/pipeline.test.ts @@ -0,0 +1,269 @@ +import * as cdk from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; +import { Schedule, ScheduleExpression } from 'aws-cdk-lib/aws-scheduler'; +import { SageMakerStartPipelineExecution } from 'aws-cdk-lib/aws-scheduler-targets'; +import * as sagemaker from '../lib'; + +describe('Pipeline', () => { + let app: cdk.App; + let stack: cdk.Stack; + + beforeEach(() => { + app = new cdk.App(); + stack = new cdk.Stack(app, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + }); + + describe('fromPipelineArn', () => { + test('imports pipeline from ARN', () => { + const pipelineArn = 'arn:aws:sagemaker:us-east-1:123456789012:pipeline/my-pipeline'; + + const pipeline = sagemaker.Pipeline.fromPipelineArn(stack, 'ImportedPipeline', pipelineArn); + + expect(pipeline.pipelineArn).toBe(pipelineArn); + expect(pipeline.pipelineName).toBe('my-pipeline'); + }); + + test('extracts pipeline name from ARN correctly', () => { + const pipelineArn = 'arn:aws:sagemaker:us-west-2:987654321098:pipeline/test-pipeline-123'; + + const pipeline = sagemaker.Pipeline.fromPipelineArn(stack, 'ImportedPipeline', pipelineArn); + + expect(pipeline.pipelineName).toBe('test-pipeline-123'); + }); + + test('handles cross-account ARNs', () => { + const pipelineArn = 'arn:aws:sagemaker:us-east-1:987654321098:pipeline/cross-account-pipeline'; + + const pipeline = sagemaker.Pipeline.fromPipelineArn(stack, 'ImportedPipeline', pipelineArn); + + expect(pipeline.pipelineArn).toBe(pipelineArn); + expect(pipeline.pipelineName).toBe('cross-account-pipeline'); + }); + + test('handles cross-region ARNs', () => { + const pipelineArn = 'arn:aws:sagemaker:eu-west-1:123456789012:pipeline/eu-pipeline'; + + const pipeline = sagemaker.Pipeline.fromPipelineArn(stack, 'ImportedPipeline', pipelineArn); + + expect(pipeline.pipelineArn).toBe(pipelineArn); + expect(pipeline.pipelineName).toBe('eu-pipeline'); + }); + }); + + describe('fromPipelineName', () => { + test('imports pipeline from name', () => { + const pipelineName = 'my-test-pipeline'; + + const pipeline = sagemaker.Pipeline.fromPipelineName(stack, 'ImportedPipeline', pipelineName); + + expect(pipeline.pipelineName).toBe(pipelineName); + // ARN should contain the pipeline name and use CDK tokens for partition + expect(pipeline.pipelineArn).toContain('sagemaker:us-east-1:123456789012:pipeline/my-test-pipeline'); + }); + + test('constructs ARN correctly', () => { + const pipelineName = 'another-pipeline'; + + const pipeline = sagemaker.Pipeline.fromPipelineName(stack, 'ImportedPipeline', pipelineName); + + // ARN should contain the pipeline name and use CDK tokens for partition + expect(pipeline.pipelineArn).toContain('sagemaker:us-east-1:123456789012:pipeline/another-pipeline'); + }); + + test('uses stack account and region by default', () => { + const customStack = new cdk.Stack(app, 'CustomStack', { + env: { account: '234567890123', region: 'ap-southeast-1' }, + }); + + const pipeline = sagemaker.Pipeline.fromPipelineName(customStack, 'ImportedPipeline', 'custom-pipeline'); + + // ARN should contain the custom account/region and pipeline name + expect(pipeline.pipelineArn).toContain('sagemaker:ap-southeast-1:234567890123:pipeline/custom-pipeline'); + }); + }); + + describe('fromPipelineAttributes', () => { + test('works with pipelineArn attribute', () => { + const pipelineArn = 'arn:aws:sagemaker:us-east-1:123456789012:pipeline/attr-pipeline'; + + const pipeline = sagemaker.Pipeline.fromPipelineAttributes(stack, 'ImportedPipeline', { + pipelineArn, + }); + + expect(pipeline.pipelineArn).toBe(pipelineArn); + expect(pipeline.pipelineName).toBe('attr-pipeline'); + }); + + test('works with pipelineName attribute', () => { + const pipelineName = 'attr-name-pipeline'; + + const pipeline = sagemaker.Pipeline.fromPipelineAttributes(stack, 'ImportedPipeline', { + pipelineName, + }); + + expect(pipeline.pipelineName).toBe(pipelineName); + // ARN should contain the pipeline name and use CDK tokens for partition + expect(pipeline.pipelineArn).toContain('sagemaker:us-east-1:123456789012:pipeline/attr-name-pipeline'); + }); + + test('works with custom account and region', () => { + const pipeline = sagemaker.Pipeline.fromPipelineAttributes(stack, 'ImportedPipeline', { + pipelineName: 'custom-env-pipeline', + account: '234567890123', + region: 'eu-central-1', + }); + + // ARN should contain the custom account/region and pipeline name + expect(pipeline.pipelineArn).toContain('sagemaker:eu-central-1:234567890123:pipeline/custom-env-pipeline'); + }); + + test('throws when neither ARN nor name provided', () => { + expect(() => { + sagemaker.Pipeline.fromPipelineAttributes(stack, 'ImportedPipeline', {}); + }).toThrow('Either pipelineArn or pipelineName must be provided'); + }); + + test('validates pipeline name format - valid names', () => { + const validNames = [ + 'valid-pipeline', + 'ValidPipeline123', + 'a', + 'pipeline-with-123-numbers', + 'PipelineWithMixedCase', + ]; + + validNames.forEach(name => { + expect(() => { + sagemaker.Pipeline.fromPipelineAttributes(stack, `Pipeline-${name}`, { + pipelineName: name, + }); + }).not.toThrow(); + }); + }); + + test('validates pipeline name format - invalid names', () => { + const invalidNames = [ + '-invalid-start', + 'invalid-end-', + 'invalid--double-dash', + 'invalid_underscore', + 'invalid.dot', + 'invalid space', + '', + ]; + + invalidNames.forEach((name, index) => { + expect(() => { + sagemaker.Pipeline.fromPipelineAttributes(stack, `Pipeline-${index}`, { + pipelineName: name, + }); + }).toThrow(/Invalid pipeline name format/); + }); + }); + }); + + describe('grantStartPipelineExecution', () => { + test('grants correct permissions to role', () => { + const pipeline = sagemaker.Pipeline.fromPipelineName(stack, 'TestPipeline', 'test-pipeline'); + const role = new Role(stack, 'TestRole', { + assumedBy: new ServicePrincipal('lambda.amazonaws.com'), + }); + + pipeline.grantStartPipelineExecution(role); + + // Check that IAM policy is created with correct action + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [{ + Effect: 'Allow', + Action: 'sagemaker:StartPipelineExecution', + // Resource will be a CDK token, so we check the structure exists + }], + }, + }); + }); + + test('works with imported pipeline from ARN', () => { + const pipelineArn = 'arn:aws:sagemaker:us-west-2:987654321098:pipeline/imported-pipeline'; + const pipeline = sagemaker.Pipeline.fromPipelineArn(stack, 'ImportedPipeline', pipelineArn); + const role = new Role(stack, 'TestRole', { + assumedBy: new ServicePrincipal('events.amazonaws.com'), + }); + + pipeline.grantStartPipelineExecution(role); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [{ + Effect: 'Allow', + Action: 'sagemaker:StartPipelineExecution', + Resource: pipelineArn, + }], + }, + }); + }); + }); + + describe('integration with scheduler targets', () => { + test('works with SageMakerStartPipelineExecution', () => { + const pipeline = sagemaker.Pipeline.fromPipelineName(stack, 'TestPipeline', 'scheduler-test-pipeline'); + + const target = new SageMakerStartPipelineExecution(pipeline, { + pipelineParameterList: [{ + name: 'TestParam', + value: 'TestValue', + }], + }); + + new Schedule(stack, 'TestSchedule', { + schedule: ScheduleExpression.rate(cdk.Duration.hours(1)), + target, + }); + + // Verify the schedule is created with correct structure + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Scheduler::Schedule', { + Target: { + // Arn will be a CDK token, so we check the structure exists + SageMakerPipelineParameters: { + PipelineParameterList: [{ + Name: 'TestParam', + Value: 'TestValue', + }], + }, + }, + }); + }); + + test('scheduler target gets correct IAM permissions', () => { + const pipeline = sagemaker.Pipeline.fromPipelineArn( + stack, + 'ImportedPipeline', + 'arn:aws:sagemaker:eu-west-1:123456789012:pipeline/imported-scheduler-pipeline', + ); + + const target = new SageMakerStartPipelineExecution(pipeline); + + new Schedule(stack, 'TestSchedule', { + schedule: ScheduleExpression.rate(cdk.Duration.minutes(30)), + target, + }); + + // Verify IAM role has correct permissions + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [{ + Effect: 'Allow', + Action: 'sagemaker:StartPipelineExecution', + Resource: 'arn:aws:sagemaker:eu-west-1:123456789012:pipeline/imported-scheduler-pipeline', + }], + }, + }); + }); + }); +});