# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 AWSTemplateFormatVersion: 2010-09-09 Description: Creates a VPC with one internet gateway, one private subnet, and one network firewall private subnet. Parameters: ProjectName: Type: String VPCCIDR: Type: String AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$' FirewallSubnetCIDR: Type: String AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$' NATGatewaySubnetCIDR: Type: String AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$' SageMakerStudioSubnetCIDR: Type: String AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$' DataBucketName: Type: String ModelBucketName: Type: String Resources: # VPC and subnets # Set DeletionPolicy to 'Retain' # SageMaker Studio creates an EFS file system with mounting points in VPC and does not delete that file system on deletion of SageMaker Studio # This EFS cause the CloudFormation deletion process for VPC template to fail VPC: Type: AWS::EC2::VPC DeletionPolicy: Retain UpdateReplacePolicy: Delete Properties: CidrBlock: !Ref VPCCIDR EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Sub ${ProjectName}-vpc FirewallSubnet: Type: AWS::EC2::Subnet Properties: CidrBlock: !Ref FirewallSubnetCIDR VpcId: !Ref VPC AvailabilityZone: !Sub "${AWS::Region}a" Tags: - Key: Name Value: !Sub sn-${ProjectName}-firewall NATGatewaySubnet: Type: AWS::EC2::Subnet Properties: CidrBlock: !Ref NATGatewaySubnetCIDR VpcId: !Ref VPC AvailabilityZone: !Sub "${AWS::Region}a" Tags: - Key: Name Value: !Sub sn-${ProjectName}-nat-gateway SageMakerStudioSubnet: Type: AWS::EC2::Subnet DeletionPolicy: Retain UpdateReplacePolicy: Delete Properties: CidrBlock: !Ref SageMakerStudioSubnetCIDR VpcId: !Ref VPC AvailabilityZone: !Sub "${AWS::Region}a" Tags: - Key: Name Value: !Sub sn-${ProjectName}-sagemaker-studio SageMakerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: security group for SageMaker notebook instance, training jobs and hosting endpoint VpcId: !Ref VPC Tags: - Key: Name Value: !Sub sg-sagemaker-${ProjectName} # Self-referencing the security group to enable communication between instances within the same SG SageMakerSecurityGroupIngress: Type: AWS::EC2::SecurityGroupIngress Properties: IpProtocol: '-1' GroupId: !Ref SageMakerSecurityGroup SourceSecurityGroupId: !Ref SageMakerSecurityGroup # Internet Gateway, NAT Gateway, and Network Firewall EIP: Type: AWS::EC2::EIP Properties: Domain: vpc DomainAllowStatefulRuleGroup: Type: AWS::NetworkFirewall::RuleGroup Properties: RuleGroupName: !Sub "domain-allow-${ProjectName}" Type: STATEFUL Capacity: 100 RuleGroup: RuleVariables: IPSets: HOME_NET: Definition: - !Ref VPCCIDR RulesSource: RulesSourceList: TargetTypes: - HTTP_HOST - TLS_SNI Targets: - ".kaggle.com" - ".amazonaws.com" GeneratedRulesType: "ALLOWLIST" Tags: - Key: Name Value: !Sub "domain-allow-${ProjectName}" FirewallPolicy: Type: AWS::NetworkFirewall::FirewallPolicy Properties: Description: Network firewall policy to control SageMaker Studio internet egress and ingress FirewallPolicyName: !Sub "network-firewall-policy-${ProjectName}" FirewallPolicy: StatelessDefaultActions: - "aws:forward_to_sfe" StatelessFragmentDefaultActions: - "aws:pass" StatefulRuleGroupReferences: - ResourceArn: !Ref DomainAllowStatefulRuleGroup IGW: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub igw-${ProjectName} NatGateway: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt EIP.AllocationId SubnetId: !Ref NATGatewaySubnet Tags: - Key: Name Value: !Sub nat-gateway-${ProjectName} NetworkFirewall: Type: AWS::NetworkFirewall::Firewall Properties: DeleteProtection: false Description: AWS Network Firewall to control internet egress and ingress FirewallName: !Sub "network-firewall-${ProjectName}" FirewallPolicyArn: !Ref FirewallPolicy FirewallPolicyChangeProtection: false SubnetChangeProtection: false SubnetMappings: - SubnetId: !Ref FirewallSubnet VpcId: !Ref VPC IGWVPCAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref VPC InternetGatewayId: !Ref IGW # Route tables IGWIngressRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub rtb-${ProjectName}-igw FirewallRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub rtb-${ProjectName}-firewall NATGatewayRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub rtb-${ProjectName}-nat-gateway SageMakerStudioRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub rtb-${ProjectName}-sagemaker # Route table associations IGWRouteTableAssociation: Type: AWS::EC2::GatewayRouteTableAssociation Properties: RouteTableId: !Ref IGWIngressRouteTable GatewayId: !Ref IGW FirewallSubnetRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref FirewallRouteTable SubnetId: !Ref FirewallSubnet NATGatewaySubnetRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref NATGatewayRouteTable SubnetId: !Ref NATGatewaySubnet SageMakerStudioRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref SageMakerStudioRouteTable SubnetId: !Ref SageMakerStudioSubnet # Routes # Since we have only one-AZ Firewall setup, we take the first Network Firewall VPC endpoint from the EndpointsIds list # This will not work in multi-AZ setup! IGWIngressRoute: Type: AWS::EC2::Route Properties: RouteTableId: !Ref IGWIngressRouteTable DestinationCidrBlock: !Ref NATGatewaySubnetCIDR VpcEndpointId: !Select ["1", !Split [":", !Select ["0", !GetAtt NetworkFirewall.EndpointIds]]] FirewallEgressRoute: Type: AWS::EC2::Route Properties: RouteTableId: !Ref FirewallRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref IGW # Since we have only one-AZ Firewall setup, we take the first Network Firewall VPC endpoint from the EndpointsIds list # This will not work in multi-AZ setup! NATGatewayEgressRoute: Type: AWS::EC2::Route Properties: RouteTableId: !Ref NATGatewayRouteTable DestinationCidrBlock: 0.0.0.0/0 VpcEndpointId: !Select ["1", !Split [":", !Select ["0", !GetAtt NetworkFirewall.EndpointIds]]] SageMakerEgressRoute: Type: AWS::EC2::Route Properties: RouteTableId: !Ref SageMakerStudioRouteTable DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway # VPC Endpoints VPCEndpointsSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Allow TLS for VPC Endpoint VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 443 ToPort: 443 SourceSecurityGroupId: !GetAtt SageMakerSecurityGroup.GroupId - IpProtocol: '-1' CidrIp: !Ref 'VPCCIDR' Description: Allow all traffic from the VPC Tags: - Key: Name Value: !Sub sg-vpce-${ProjectName} VPCEndpointS3: Type: AWS::EC2::VPCEndpoint Properties: ServiceName: !Sub com.amazonaws.${AWS::Region}.s3 VpcEndpointType: Gateway VpcId: !Ref VPC PolicyDocument: !Sub | { "Version":"2012-10-17", "Statement":[{ "Effect":"Allow", "Principal": "*", "Action":[ "s3:GetObject", "s3:PutObject", "s3:ListBucket", "s3:DeleteObject" ], "Resource": ["arn:aws:s3:::${DataBucketName}", "arn:aws:s3:::${ModelBucketName}", "arn:aws:s3:::${DataBucketName}/*", "arn:aws:s3:::${ModelBucketName}/*" ] }] } RouteTableIds: - !Ref SageMakerStudioRouteTable VPCEndpointS3Id: Type: 'AWS::SSM::Parameter' Properties: Name: !Sub "s3-endpoint-${ProjectName}-id" Type: String Value: !Ref VPCEndpointS3 Description: S3 VPC Endpoint ID VPCEndpointSagemakerAPI: Type: AWS::EC2::VPCEndpoint Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: '*' Action: '*' Resource: '*' VpcEndpointType: Interface PrivateDnsEnabled: true SubnetIds: - !Ref SageMakerStudioSubnet SecurityGroupIds: - !Ref VPCEndpointsSecurityGroup ServiceName: !Sub 'com.amazonaws.${AWS::Region}.sagemaker.api' VpcId: !Ref VPC VPCEndpointSageMakerRuntime: Type: AWS::EC2::VPCEndpoint Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: '*' Action: '*' Resource: '*' VpcEndpointType: Interface PrivateDnsEnabled: true SubnetIds: - !Ref SageMakerStudioSubnet SecurityGroupIds: - !Ref VPCEndpointsSecurityGroup ServiceName: !Sub 'com.amazonaws.${AWS::Region}.sagemaker.runtime' VpcId: !Ref VPC VPCEndpointSageMakerNotebook: Type: AWS::EC2::VPCEndpoint Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: '*' Action: '*' Resource: '*' VpcEndpointType: Interface PrivateDnsEnabled: true SubnetIds: - !Ref SageMakerStudioSubnet SecurityGroupIds: - !Ref VPCEndpointsSecurityGroup ServiceName: !Sub 'aws.sagemaker.${AWS::Region}.notebook' VpcId: !Ref VPC VPCEndpointSTS: Type: 'AWS::EC2::VPCEndpoint' Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: '*' Action: '*' Resource: '*' VpcEndpointType: Interface PrivateDnsEnabled: true SubnetIds: - !Ref SageMakerStudioSubnet SecurityGroupIds: - !Ref VPCEndpointsSecurityGroup ServiceName: !Sub 'com.amazonaws.${AWS::Region}.sts' VpcId: !Ref VPC VPCEndpointSSM: Type: 'AWS::EC2::VPCEndpoint' Properties: VpcEndpointType: Interface PrivateDnsEnabled: true SubnetIds: - !Ref SageMakerStudioSubnet SecurityGroupIds: - !Ref VPCEndpointsSecurityGroup ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ssm' VpcId: !Ref VPC VPCEndpointCW: Type: 'AWS::EC2::VPCEndpoint' Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: '*' Action: '*' Resource: '*' VpcEndpointType: Interface PrivateDnsEnabled: true SubnetIds: - !Ref SageMakerStudioSubnet SecurityGroupIds: - !Ref VPCEndpointsSecurityGroup ServiceName: !Sub 'com.amazonaws.${AWS::Region}.monitoring' VpcId: !Ref VPC VPCEndpointCWL: Type: 'AWS::EC2::VPCEndpoint' Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: '*' Action: '*' Resource: '*' VpcEndpointType: Interface PrivateDnsEnabled: true SubnetIds: - !Ref SageMakerStudioSubnet SecurityGroupIds: - !Ref VPCEndpointsSecurityGroup ServiceName: !Sub 'com.amazonaws.${AWS::Region}.logs' VpcId: !Ref VPC VPCEndpointECR: Type: 'AWS::EC2::VPCEndpoint' Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: '*' Action: '*' Resource: '*' VpcEndpointType: Interface PrivateDnsEnabled: true SubnetIds: - !Ref SageMakerStudioSubnet SecurityGroupIds: - !Ref VPCEndpointsSecurityGroup ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ecr.dkr' VpcId: !Ref VPC VPCEndpointECRAPI: Type: 'AWS::EC2::VPCEndpoint' Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: '*' Action: '*' Resource: '*' VpcEndpointType: Interface PrivateDnsEnabled: true SubnetIds: - !Ref SageMakerStudioSubnet SecurityGroupIds: - !Ref VPCEndpointsSecurityGroup ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ecr.api' VpcId: !Ref VPC VPCEndpointServiceCatalog: Type: 'AWS::EC2::VPCEndpoint' Properties: VpcEndpointType: Interface PrivateDnsEnabled: true SubnetIds: - !Ref SageMakerStudioSubnet SecurityGroupIds: - !Ref VPCEndpointsSecurityGroup ServiceName: !Sub 'com.amazonaws.${AWS::Region}.servicecatalog' VpcId: !Ref VPC Outputs: VPCId: Description: The ID of VPC where SageMaker Studio will reside Value: !Ref VPC SageMakerStudioSubnetId: Value: !Ref SageMakerStudioSubnet SageMakerSecurityGroupId: Value: !Ref SageMakerSecurityGroup NATGWSubnetCIDR: Value: !Ref NATGatewaySubnetCIDR IGWRouteTableId: Value: !Ref IGWIngressRouteTable NATGWRouteTableId: Value: !Ref NATGatewayRouteTable DataBucketName: Value: !Ref DataBucketName ModelBucketName: Value: !Ref ModelBucketName S3VPCEndpointId: Description: The ID of the S3 VPC Endpoint Value: !Ref VPCEndpointS3 Export: Name: !Sub "s3-endpoint-${ProjectName}-id"