function addPermissions()

in source/patterns/@aws-solutions-constructs/core/lib/sagemaker-helper.ts [61:215]


function addPermissions(_role: iam.Role, props?: BuildSagemakerEndpointProps) {
  // Grant permissions to NoteBookInstance for creating and training the model
  _role.addToPolicy(
    new iam.PolicyStatement({
      resources: [`arn:${Aws.PARTITION}:sagemaker:${Aws.REGION}:${Aws.ACCOUNT_ID}:*`],
      actions: [
        'sagemaker:CreateTrainingJob',
        'sagemaker:DescribeTrainingJob',
        'sagemaker:CreateModel',
        'sagemaker:DescribeModel',
        'sagemaker:DeleteModel',
        'sagemaker:CreateEndpoint',
        'sagemaker:CreateEndpointConfig',
        'sagemaker:DescribeEndpoint',
        'sagemaker:DescribeEndpointConfig',
        'sagemaker:DeleteEndpoint',
        'sagemaker:DeleteEndpointConfig',
        'sagemaker:InvokeEndpoint',
      ],
    })
  );

  // Grant CloudWatch Logging permissions
  _role.addToPolicy(
    new iam.PolicyStatement({
      resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/sagemaker/*`],
      actions: [
        'logs:CreateLogGroup',
        'logs:CreateLogStream',
        'logs:DescribeLogStreams',
        'logs:GetLogEvents',
        'logs:PutLogEvents',
      ],
    })
  );

  // To place the Sagemaker endpoint in a VPC
  if (props && props.vpc) {
    _role.addToPolicy(
      new iam.PolicyStatement({
        resources: ['*'],
        actions: [
          'ec2:CreateNetworkInterface',
          'ec2:CreateNetworkInterfacePermission',
          'ec2:DeleteNetworkInterface',
          'ec2:DeleteNetworkInterfacePermission',
          'ec2:DescribeNetworkInterfaces',
          'ec2:AssignPrivateIpAddresses',
          'ec2:UnassignPrivateIpAddresses',
          'ec2:DescribeVpcs',
          'ec2:DescribeDhcpOptions',
          'ec2:DescribeSubnets',
          'ec2:DescribeSecurityGroups',
        ],
      })
    );
  }

  // To create a Sagemaker model using Bring-Your-Own-Model (BYOM) algorith image
  // The image URL is specified in the modelProps
  _role.addToPolicy(
    new iam.PolicyStatement({
      resources: [`arn:${cdk.Aws.PARTITION}:ecr:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:repository/*`],
      actions: [
        'ecr:BatchCheckLayerAvailability',
        'ecr:GetDownloadUrlForLayer',
        'ecr:DescribeRepositories',
        'ecr:DescribeImages',
        'ecr:BatchGetImage',
      ],
    })
  );

  // Add GetAuthorizationToken (it can not be bound to resources other than *)
  _role.addToPolicy(
    new iam.PolicyStatement({
      resources: ['*'],
      actions: ['ecr:GetAuthorizationToken'],
    })
  );

  // add permission to use Elastic Inference accelerator
  if (props && props.endpointConfigProps) {
    // Get the acceleratorType, if any
    const acceleratorType = (props.endpointConfigProps
      ?.productionVariants as sagemaker.CfnEndpointConfig.ProductionVariantProperty[])[0].acceleratorType;
    if (acceleratorType !== undefined) {
      _role.addToPolicy(
        new iam.PolicyStatement({
          resources: ['*'],
          actions: ['elastic-inference:Connect'],
        })
      );
    }
  }

  // add kms permissions
  _role.addToPolicy(
    new iam.PolicyStatement({
      // the kmsKeyId in the endpointConfigProps can be any of the following formats:
      // Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab
      // Key ARN: arn:aws:kms:<region>:<accountID>:key/1234abcd-12ab-34cd-56ef-1234567890ab
      // Alias name: alias/ExampleAlias
      // Alias name ARN: arn:aws:kms:<region>:<accountID>:alias/ExampleAlias
      // the key is used to encrypt/decrypt data captured by the Sagemaker endpoint and stored in S3 bucket
      resources: [
        `arn:${cdk.Aws.PARTITION}:kms:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:key/*`,
        `arn:${cdk.Aws.PARTITION}:kms:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:alias/*`,
      ],
      actions: ['kms:Encrypt', 'kms:Decrypt', 'kms:ReEncrypt*', 'kms:GenerateDataKey*', 'kms:DescribeKey'],
    })
  );

  // Add S3 permissions to get Model artifact, put data capture files, etc.
  _role.addToPolicy(
    new iam.PolicyStatement({
      actions: ['s3:GetObject', 's3:PutObject', 's3:DeleteObject', 's3:ListBucket'],
      resources: ['arn:aws:s3:::*'],
    })
  );

  // Grant GetRole permissions to the Sagemaker service
  _role.addToPolicy(
    new iam.PolicyStatement({
      resources: [_role.roleArn],
      actions: ['iam:GetRole'],
    })
  );

  // Grant PassRole permissions to the Sagemaker service
  _role.addToPolicy(
    new iam.PolicyStatement({
      resources: [_role.roleArn],
      actions: ['iam:PassRole'],
      conditions: {
        StringLike: { 'iam:PassedToService': 'sagemaker.amazonaws.com' },
      },
    })
  );

  // Add CFN NAG uppress to allow for "Resource": "*" for ENI access in VPC,
  // ECR authorization token for custom model images, and elastic inference
  // Add CFN NAG for Complex Role because Sagmaker needs permissions to access several services
  const roleDefaultPolicy = _role.node.tryFindChild('DefaultPolicy')?.node.findChild('Resource') as iam.CfnPolicy;
  addCfnSuppressRules(roleDefaultPolicy, [
    {
      id: 'W12',
      reason: `Sagemaker needs the following minimum required permissions to access ENIs in a VPC, ECR for custom model images, and elastic inference.`,
    },
    {
      id: 'W76',
      reason: 'Complex role becuase Sagemaker needs permissions to access several services',
    }
  ]);
}