Skip to main content

SageMaker Notebook Instance Should Be In VPC

More Info:​

Sagemaker Notebook instance should be associated to a VPC

Risk Level​

Medium

Address​

Security

Compliance Standards​

  • APRA CPS 234 (Australia)
  • BSI C5 (Germany)
  • Brazil LGPD
  • CCPA / CPRA (California)
  • CIS Critical Security Controls v8
  • CMMC 2.0
  • CSA Cloud Controls Matrix v4
  • DPDPA
  • Digital Operational Resilience Act (EU)
  • ISO 27001
  • ISO/IEC 27018
  • ISO/IEC 27701
  • MAS Technology Risk Management (Singapore)
  • MITRE ATT&CK (Cloud)
  • NIST SP 800-171
  • NYDFS 23 NYCRR 500
  • SOC2
  • SWIFT Customer Security Controls Framework
  • Sarbanes-Oxley IT General Controls
  • UK NCSC Cyber Assessment Framework

Triage and Remediation​

Remediation​

Using Console

For SageMaker Notebook Instances, you cannot change an existing notebook from “no VPC” to “in VPC.” You must create a new notebook instance in a VPC and then decommission the old one.

Below are step‑by‑step instructions using the AWS Console:


1. Prerequisites: Identify / Prepare VPC, Subnet, and Security Group​

  1. In the AWS Console, go to VPC service.
  2. Choose (or create) a VPC you want to use.
  3. Under Subnets, pick a private subnet (recommended) with:
    • Route to required services (e.g., NAT Gateway for internet access if needed).
  4. Under Security Groups, either:
    • Choose an existing one, or
    • Create a new security group with:
      • Outbound rules to allow access to SageMaker endpoints, S3, etc. (usually “All traffic” outbound is fine if routed via NAT).
      • Inbound rules only if you need specific inbound connections (often none are needed for notebooks).

2. Create a New Notebook Instance in the VPC​

  1. Go to Amazon SageMaker in the console.
  2. In the left navigation pane, select Notebook instances.
  3. Click Create notebook instance.
  4. Fill in:
    • Notebook instance name: e.g., sagemaker-notebook-in-vpc.
    • Notebook instance type: same as your current instance or as needed.
  5. IAM role:
    • Select Existing role if you already have a SageMaker execution role.
    • Otherwise, choose Create a new role and allow required S3 access.
  6. Scroll down to Network (or Network and security) section:
    • VPC: Select your target VPC.
    • Subnet: Select the private subnet you prepared.
    • Security group(s): Select the security group you prepared.
  7. (Optional) Under Additional configuration:
    • Attach the same Lifecycle configuration as the old instance if you used one.
    • Configure any volume size, encryption, etc., to match your requirements.
  8. Click Create notebook instance.
  9. Wait until the status becomes InService.

3. Migrate Your Work (Notebooks, Code, Data)​

  1. Open the old notebook instance:
    • From Notebook instances, click Open Jupyter or Open JupyterLab.
  2. Copy notebooks and files to durable storage:
    • Preferred: Save them in Amazon S3 (if they’re not already).
      • Use the Jupyter file browser “Download” or upload them to S3 using the AWS CLI / SDK / Jupyter extensions.
  3. Open the new VPC‑based notebook instance:
    • From Notebook instances, click Open Jupyter or Open JupyterLab.
  4. Restore content:
    • Download from S3 or upload from local machine into the new notebook instance.
  5. Verify:
    • Run a few notebooks to confirm they can access needed data (S3, databases, external APIs via NAT, etc.).

4. Decommission the Old Non‑VPC Notebook Instance​

  1. In Amazon SageMaker → Notebook instances:
    • Select the old notebook instance.
  2. Choose Stop.
  3. Wait until the status is Stopped.
  4. With the instance selected, choose Delete.
  5. Confirm deletion.

5. (Optional) Tighten Network Controls​

In VPC → Security groups:

  • Restrict inbound rules to only what is necessary.
  • Leave outbound open or restrict to required destinations if you have more advanced network controls (VPC endpoints, proxy, etc.).

This process results in your SageMaker Notebook Instance running inside a VPC, satisfying the “Notebook Instance Should Be In VPC” requirement.

Using CLI

For SageMaker, you cannot “move” an existing Notebook Instance into a VPC; you must create a new notebook in a VPC, move any code/data, then delete the old one.

Below are step‑by‑step AWS CLI–based instructions.


1. Identify notebook instances not in a VPC​

aws sagemaker list-notebook-instances \
--query 'NotebookInstances[*].NotebookInstanceName' \
--output text

For each notebook:

NB_NAME="your-notebook-name"

aws sagemaker describe-notebook-instance \
--notebook-instance-name "$NB_NAME" \
--query '{Name:NotebookInstanceName,SubnetId:SubnetId,SecurityGroups:SecurityGroups}' \
--output json

Any instance with "SubnetId": null is not in a VPC.


2. Choose / prepare VPC resources​

You need:

  • A VPC ID
  • A private subnet ID in that VPC
  • One or more security group IDs allowing needed outbound traffic (and inbound if required, e.g., for SSM or Git)

Get VPCs:

aws ec2 describe-vpcs \
--query 'Vpcs[*].{VpcId:VpcId,CidrBlock:CidrBlock}' \
--output table

Get subnets for a chosen VPC:

VPC_ID="vpc-xxxxxxxx"

aws ec2 describe-subnets \
--filters "Name=vpc-id,Values=$VPC_ID" \
--query 'Subnets[*].{SubnetId:SubnetId,Az:AvailabilityZone,Name:Tags[?Key==`Name`]|[0].Value}' \
--output table

Get or create a security group:

# List existing SGs in VPC
aws ec2 describe-security-groups \
--filters "Name=vpc-id,Values=$VPC_ID" \
--query 'SecurityGroups[*].{GroupId:GroupId,GroupName:GroupName}' \
--output table

Example: create a new SG:

SG_ID=$(aws ec2 create-security-group \
--group-name sagemaker-notebook-sg \
--description "SageMaker Notebook SG" \
--vpc-id "$VPC_ID" \
--query 'GroupId' \
--output text)

# (Optional) allow outbound to anywhere
aws ec2 authorize-security-group-egress \
--group-id "$SG_ID" \
--ip-permissions '[
{
"IpProtocol": "-1",
"IpRanges": [{"CidrIp": "0.0.0.0/0"}]
}
]'

3. Create a new Notebook Instance in the VPC​

Decide:

  • INSTANCE_TYPE (e.g., ml.t3.medium)
  • ROLE_ARN – execution role with sagemaker:* / S3 permissions
  • SUBNET_ID – from step 2
  • SG_ID – from step 2
  • Optional: lifecycle config, volume size, etc.
NB_NEW_NAME="your-new-notebook-name"
INSTANCE_TYPE="ml.t3.medium"
ROLE_ARN="arn:aws:iam::123456789012:role/SageMakerExecutionRole"
SUBNET_ID="subnet-xxxxxxxx"
SG_ID="sg-xxxxxxxx"

aws sagemaker create-notebook-instance \
--notebook-instance-name "$NB_NEW_NAME" \
--instance-type "$INSTANCE_TYPE" \
--role-arn "$ROLE_ARN" \
--subnet-id "$SUBNET_ID" \
--security-group-ids "$SG_ID"

Wait for it to be InService:

aws sagemaker wait notebook-instance-in-service \
--notebook-instance-name "$NB_NEW_NAME"

Confirm VPC settings:

aws sagemaker describe-notebook-instance \
--notebook-instance-name "$NB_NEW_NAME" \
--query '{Name:NotebookInstanceName,SubnetId:SubnetId,SecurityGroups:SecurityGroups}' \
--output json

4. Migrate notebooks / data​

Typical pattern (if old notebook is still accessible via console):

  1. From the old notebook, ensure all notebooks and code are saved to S3 (e.g., ~/SageMaker auto‑syncs to S3 if configured).

  2. Alternatively, sync manually:

    aws s3 sync /home/ec2-user/SageMaker s3://your-bucket/path/
  3. On the new notebook instance, pull from S3:

    aws s3 sync s3://your-bucket/path/ /home/ec2-user/SageMaker

If you used Git in your old instance, simply re‑clone your repos in the new instance.


5. Stop and delete old non‑VPC notebook instance​

OLD_NB_NAME="old-notebook-name"

aws sagemaker stop-notebook-instance \
--notebook-instance-name "$OLD_NB_NAME"

aws sagemaker wait notebook-instance-stopped \
--notebook-instance-name "$OLD_NB_NAME"

aws sagemaker delete-notebook-instance \
--notebook-instance-name "$OLD_NB_NAME"

6. (Optional) Enforce “must be in VPC” via SCP or IaC​

To prevent future non‑VPC notebooks, you can:

  • Use a Service Control Policy denying sagemaker:CreateNotebookInstance if sagemaker:SubnetId is not present.
  • Use Infrastructure‑as‑Code (CloudFormation/Terraform) that always sets SubnetId and SecurityGroupIds.

If you share your current notebook config (from describe-notebook-instance), I can give an exact create-notebook-instance CLI command that reproduces it inside a VPC.

Using Python

For SageMaker, a notebook instance cannot be moved into a VPC after creation. You must:

  1. Create (or choose) a VPC, subnet, and security group.
  2. Create a new SageMaker notebook instance configured to use that VPC.
  3. Migrate code/data from the old notebook (e.g., via Git, S3).
  4. Delete the old non‑VPC notebook.

Below is the Python/boto3 approach.


1. Prerequisites​

  • boto3 installed and configured with credentials/region.
  • You have (or will create) the following:
    • VPC ID: vpc-xxxxxxxx
    • Subnet ID in that VPC: subnet-xxxxxxxx
    • Security Group ID that allows needed traffic: sg-xxxxxxxx
    • IAM role with SageMaker permissions: arn:aws:iam::<account-id>:role/<SageMakerRole>

2. Create a SageMaker Notebook in a VPC (Python/boto3)​

import boto3

sagemaker = boto3.client("sagemaker", region_name="us-east-1")

notebook_instance_name = "my-vpc-notebook"
role_arn = "arn:aws:iam::<account-id>:role/<SageMakerRole>"
subnet_id = "subnet-xxxxxxxx"
security_group_ids = ["sg-xxxxxxxx"]

response = sagemaker.create_notebook_instance(
NotebookInstanceName=notebook_instance_name,
InstanceType="ml.t3.medium", # choose appropriate instance type
RoleArn=role_arn,
SubnetId=subnet_id,
SecurityGroupIds=security_group_ids,
DirectInternetAccess="Disabled", # or "Enabled" if you require it
VolumeSizeInGB=10, # adjust as needed
Tags=[
{"Key": "Name", "Value": "my-vpc-notebook"},
{"Key": "Environment", "Value": "prod"},
],
)

print("CreateNotebookInstance response:", response)

To wait until it’s ready:

import time

def wait_for_notebook_inservice(name):
while True:
desc = sagemaker.describe_notebook_instance(NotebookInstanceName=name)
status = desc["NotebookInstanceStatus"]
print("Status:", status)
if status in ["InService", "Failed", "Stopped"]:
break
time.sleep(30)

wait_for_notebook_inservice(notebook_instance_name)

3. (Optional) Stop and Delete the Old Non‑VPC Notebook​

old_notebook_name = "my-old-nonvpc-notebook"

# Stop (if running)
try:
sagemaker.stop_notebook_instance(NotebookInstanceName=old_notebook_name)
except sagemaker.exceptions.ResourceNotFound:
pass

wait_for_notebook_inservice(old_notebook_name) # will show "Stopped"

# Delete
sagemaker.delete_notebook_instance(NotebookInstanceName=old_notebook_name)

4. Notes for Compliance​

  • Ensure SubnetId and SecurityGroupIds are always specified when calling create_notebook_instance.
  • Enforce via CI/CD or internal tooling so that notebooks are only created with VPC settings.
  • If needed, you can verify compliance by listing and checking:
paginator = sagemaker.get_paginator("list_notebook_instances")
for page in paginator.paginate():
for ni in page["NotebookInstances"]:
desc = sagemaker.describe_notebook_instance(
NotebookInstanceName=ni["NotebookInstanceName"]
)
if "SubnetId" not in desc:
print("Non‑VPC notebook:", ni["NotebookInstanceName"])
Using Terraform
resource "aws_sagemaker_notebook_instance" "THIS_NOTEBOOK" {
# Replace THIS_NOTEBOOK with the actual notebook name or keep as-is if already managed
name = "THIS_NOTEBOOK_NAME" # TODO: replace with your notebook name
instance_type = "ml.t3.medium" # TODO: keep your current instance type
role_arn = aws_iam_role.sagemaker_role.arn # TODO: keep your current IAM role

# VPC configuration – this is what fixes the finding
subnet_id = aws_subnet.NOTEBOOK_SUBNET.id # TODO: replace with your target private subnet

security_groups = [
aws_security_group.NOTEBOOK_SG.id # TODO: replace with one or more SGs that allow required access
]

# ...keep any other existing arguments (tags, lifecycle_config_name, etc.)
}

resource "aws_subnet" "NOTEBOOK_SUBNET" {
vpc_id = aws_vpc.MAIN.id # TODO: reference your VPC
cidr_block = "10.0.1.0/24" # TODO: match your network design
availability_zone = "us-east-1a" # TODO: match your AZ
map_public_ip_on_launch = false
# ...other subnet settings as needed
}

resource "aws_security_group" "NOTEBOOK_SG" {
name = "sagemaker-notebook-sg"
description = "Security group for SageMaker notebook in VPC"
vpc_id = aws_vpc.MAIN.id # TODO: reference your VPC

# TODO: tighten rules as appropriate; example for VPC‑internal access only:
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["10.0.0.0/16"] # TODO: adjust to your internal CIDR
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}

Changing an existing SageMaker Notebook Instance from “no VPC” to having subnet_id/security_groups forces resource replacement; expect downtime for this notebook during terraform apply.

Verification: terraform plan should show aws_sagemaker_notebook_instance.THIS_NOTEBOOK being replaced with a new instance that includes subnet_id and security_groups, and no other unrelated changes.