diff --git a/deployments/aws/parameters/ai-unlimited-with-alb.json b/deployments/aws/parameters/ai-unlimited-with-alb.json deleted file mode 100644 index c705d68..0000000 --- a/deployments/aws/parameters/ai-unlimited-with-alb.json +++ /dev/null @@ -1,114 +0,0 @@ -[ - { - "ParameterKey": "AiUnlimitedName", - "ParameterValue": "test-unlimited" - }, - { - "ParameterKey": "InstanceType", - "ParameterValue": "t3.small" - }, - { - "ParameterKey": "RootVolumeSize", - "ParameterValue": "20" - }, - { - "ParameterKey": "TerminationProtection", - "ParameterValue": "false" - }, - { - "ParameterKey": "IamRole", - "ParameterValue": "New" - }, - { - "ParameterKey": "IamRoleName", - "ParameterValue": "" - }, - { - "ParameterKey": "IamPermissionsBoundary", - "ParameterValue": "" - }, - { - "ParameterKey": "AvailabilityZone", - "ParameterValue": "" - }, - { - "ParameterKey": "LoadBalancerScheme", - "ParameterValue": "internet-facing" - }, - { - "ParameterKey": "LoadBalancerSubnetOne", - "ParameterValue": "" - }, - { - "ParameterKey": "LoadBalancerSubnetTwo", - "ParameterValue": "" - }, - { - "ParameterKey": "Private", - "ParameterValue": "true" - }, - { - "ParameterKey": "Session", - "ParameterValue": "true" - }, - { - "ParameterKey": "Vpc", - "ParameterValue": "" - }, - { - "ParameterKey": "Subnet", - "ParameterValue": "" - }, - { - "ParameterKey": "KeyName", - "ParameterValue": "" - }, - { - "ParameterKey": "AccessCIDR", - "ParameterValue": "" - }, - { - "ParameterKey": "PrefixList", - "ParameterValue": "" - }, - { - "ParameterKey": "SecurityGroup", - "ParameterValue": "" - }, - { - "ParameterKey": "AiUnlimitedHttpPort", - "ParameterValue": "3000" - }, - { - "ParameterKey": "AiUnlimitedGrpcPort", - "ParameterValue": "3282" - }, - { - "ParameterKey": "AiUnlimitedVersion", - "ParameterValue": "v0.2.23" - }, - { - "ParameterKey": "UsePersistentVolume", - "ParameterValue": "New" - }, - { - "ParameterKey": "PersistentVolumeSize", - "ParameterValue": "20" - }, - { - "ParameterKey": "ExistingPersistentVolumeId", - "ParameterValue": "" - }, - { - "ParameterKey": "PersistentVolumeDeletionPolicy", - "ParameterValue": "Delete" - }, - { - "ParameterKey": "HostedZoneId", - "ParameterValue": "" - }, - { - "ParameterKey": "DnsName", - "ParameterValue": "example.domain.com" - } -] \ No newline at end of file diff --git a/deployments/aws/parameters/all-in-one-with-alb.json b/deployments/aws/parameters/all-in-one-with-alb.json deleted file mode 100644 index 89c0042..0000000 --- a/deployments/aws/parameters/all-in-one-with-alb.json +++ /dev/null @@ -1,118 +0,0 @@ - [ - { - "ParameterKey": "AiUnlimitedName", - "ParameterValue": "test-unlimited" - }, - { - "ParameterKey": "JupyterToken", - "ParameterValue": "usealongSIMPLEtokenwithoutSPECIALchars" - }, - { - "ParameterKey": "InstanceType", - "ParameterValue": "t3.small" - }, - { - "ParameterKey": "RootVolumeSize", - "ParameterValue": "20" - }, - { - "ParameterKey": "TerminationProtection", - "ParameterValue": "false" - }, - { - "ParameterKey": "IamRole", - "ParameterValue": "New" - }, - { - "ParameterKey": "IamRoleName", - "ParameterValue": "" - }, - { - "ParameterKey": "IamPermissionsBoundary", - "ParameterValue": "" - }, - { - "ParameterKey": "AvailabilityZone", - "ParameterValue": "" - }, - { - "ParameterKey": "LoadBalancerScheme", - "ParameterValue": "internet-facing" - }, - { - "ParameterKey": "LoadBalancerSubnetOne", - "ParameterValue": "" - }, - { - "ParameterKey": "LoadBalancerSubnetTwo", - "ParameterValue": "" - }, - { - "ParameterKey": "Private", - "ParameterValue": "true" - }, - { - "ParameterKey": "Session", - "ParameterValue": "true" - }, - { - "ParameterKey": "Vpc", - "ParameterValue": "" - }, - { - "ParameterKey": "Subnet", - "ParameterValue": "" - }, - { - "ParameterKey": "KeyName", - "ParameterValue": "" - }, - { - "ParameterKey": "AccessCIDR", - "ParameterValue": "" - }, - { - "ParameterKey": "PrefixList", - "ParameterValue": "" - }, - { - "ParameterKey": "SecurityGroup", - "ParameterValue": "" - }, - { - "ParameterKey": "AiUnlimitedHttpPort", - "ParameterValue": "3000" - }, - { - "ParameterKey": "AiUnlimitedGrpcPort", - "ParameterValue": "3282" - }, - { - "ParameterKey": "AiUnlimitedVersion", - "ParameterValue": "v0.2.23" - }, - { - "ParameterKey": "UsePersistentVolume", - "ParameterValue": "New" - }, - { - "ParameterKey": "PersistentVolumeSize", - "ParameterValue": "20" - }, - { - "ParameterKey": "ExistingPersistentVolumeId", - "ParameterValue": "" - }, - { - "ParameterKey": "PersistentVolumeDeletionPolicy", - "ParameterValue": "Delete" - }, - { - "ParameterKey": "HostedZoneId", - "ParameterValue": "" - }, - { - "ParameterKey": "DnsName", - "ParameterValue": "example.domain.com" - } - ] \ No newline at end of file diff --git a/deployments/aws/parameters/jupyter-with-alb.json b/deployments/aws/parameters/jupyter-with-alb.json deleted file mode 100644 index 0a5a1a1..0000000 --- a/deployments/aws/parameters/jupyter-with-alb.json +++ /dev/null @@ -1,114 +0,0 @@ -[ - { - "ParameterKey": "JupyterName", - "ParameterValue": "test-unlimited" - }, - { - "ParameterKey": "JupyterToken", - "ParameterValue": "usealongSIMPLEtokenwithoutSPECIALchars" - }, - { - "ParameterKey": "InstanceType", - "ParameterValue": "t3.small" - }, - { - "ParameterKey": "RootVolumeSize", - "ParameterValue": "20" - }, - { - "ParameterKey": "TerminationProtection", - "ParameterValue": "false" - }, - { - "ParameterKey": "IamRole", - "ParameterValue": "New" - }, - { - "ParameterKey": "IamRoleName", - "ParameterValue": "" - }, - { - "ParameterKey": "IamPermissionsBoundary", - "ParameterValue": "" - }, - { - "ParameterKey": "AvailabilityZone", - "ParameterValue": "" - }, - { - "ParameterKey": "LoadBalancerScheme", - "ParameterValue": "internet-facing" - }, - { - "ParameterKey": "LoadBalancerSubnetOne", - "ParameterValue": "" - }, - { - "ParameterKey": "LoadBalancerSubnetTwo", - "ParameterValue": "" - }, - { - "ParameterKey": "Private", - "ParameterValue": "true" - }, - { - "ParameterKey": "Session", - "ParameterValue": "true" - }, - { - "ParameterKey": "Vpc", - "ParameterValue": "" - }, - { - "ParameterKey": "Subnet", - "ParameterValue": "" - }, - { - "ParameterKey": "KeyName", - "ParameterValue": "" - }, - { - "ParameterKey": "AccessCIDR", - "ParameterValue": "" - }, - { - "ParameterKey": "PrefixList", - "ParameterValue": "" - }, - { - "ParameterKey": "SecurityGroup", - "ParameterValue": "" - }, - { - "ParameterKey": "JupyterHttpPort", - "ParameterValue": "3000" - }, - { - "ParameterKey": "JupyterVersion", - "ParameterValue": "latest" - }, - { - "ParameterKey": "UsePersistentVolume", - "ParameterValue": "New" - }, - { - "ParameterKey": "PersistentVolumeSize", - "ParameterValue": "20" - }, - { - "ParameterKey": "ExistingPersistentVolumeId", - "ParameterValue": "" - }, - { - "ParameterKey": "PersistentVolumeDeletionPolicy", - "ParameterValue": "Delete" - }, - { - "ParameterKey": "HostedZoneId", - "ParameterValue": "" - }, - { - "ParameterKey": "DnsName", - "ParameterValue": "example.domain.com" - } -] \ No newline at end of file diff --git a/deployments/aws/policies/ai-unlimited-workspaces-without-iam-role-permissions.json b/deployments/aws/policies/ai-unlimited-workspaces-without-iam-role-permissions.json index 3e0476d..26c9caa 100644 --- a/deployments/aws/policies/ai-unlimited-workspaces-without-iam-role-permissions.json +++ b/deployments/aws/policies/ai-unlimited-workspaces-without-iam-role-permissions.json @@ -92,7 +92,10 @@ "ec2:AssociateRouteTable", "ec2:CreateRoute", "ec2:DisassociateRouteTable", - "ec2:DeleteRoute" + "ec2:DeleteRoute", + "ec2:DescribeAddresses", + "ec2:AssociateAddress", + "ec2:DisassociateAddress" ], "Resource": "*", "Effect": "Allow" diff --git a/deployments/aws/policies/ai-unlimited-workspaces.json b/deployments/aws/policies/ai-unlimited-workspaces.json index 7cdbe22..46aaae3 100644 --- a/deployments/aws/policies/ai-unlimited-workspaces.json +++ b/deployments/aws/policies/ai-unlimited-workspaces.json @@ -95,7 +95,10 @@ "ec2:AssociateRouteTable", "ec2:CreateRoute", "ec2:DisassociateRouteTable", - "ec2:DeleteRoute" + "ec2:DeleteRoute", + "ec2:DescribeAddresses", + "ec2:AssociateAddress", + "ec2:DisassociateAddress" ], "Resource": "*", "Effect": "Allow" diff --git a/deployments/aws/templates/ai-unlimited/ai-unlimited-with-alb.yaml b/deployments/aws/templates/ai-unlimited/ai-unlimited-with-alb.yaml deleted file mode 100644 index 5650191..0000000 --- a/deployments/aws/templates/ai-unlimited/ai-unlimited-with-alb.yaml +++ /dev/null @@ -1,1247 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" - -Description: 'AWS CloudFormation Template AI Unlimited: AI Unlimited is a instance based service for deploying and suspending clusters of AI Unlimited compute engines, and managing project lifecycles. Note: You will be billed for the AWS resources used if you create a stack from this template.' - -Metadata: - AWS::CloudFormation::Interface: - ParameterGroups: - - Label: - default: AI Unlimited - Parameters: - - AiUnlimitedName - - InstanceType - - RootVolumeSize - - TerminationProtection - - IamRole - - IamRoleName - - IamPermissionsBoundary - - Label: - default: AI Unlimited connection - Parameters: - - AvailabilityZone - - LoadBalancerScheme - - LoadBalancerSubnetOne - - LoadBalancerSubnetTwo - - HostedZoneId - - DnsName - - Private - - Session - - Vpc - - Subnet - - KeyName - - AccessCIDR - - PrefixList - - SecurityGroup - - AiUnlimitedHttpPort - - AiUnlimitedGrpcPort - - AiUnlimitedVersion - - AiUnlimitedSchedulerVersion - - AiUnlimitedSchedulerHttpPort - - AiUnlimitedSchedulerGrpcPort - - Label: - default: Persistent volume - Parameters: - - UsePersistentVolume - - PersistentVolumeSize - - ExistingPersistentVolumeId - - PersistentVolumeDeletionPolicy - -Parameters: - LatestAmiId: - Type: AWS::SSM::Parameter::Value - Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 - - AiUnlimitedName: - Description: The AI Unlimited instance name - Type: String - Default: ai-unlimited - AllowedPattern: ^[a-zA-Z][a-zA-Z0-9-]* - ConstraintDescription: must begin with a letter and contain only alphanumeric characters. - MaxLength: "20" - MinLength: "1" - - Private: - Description: Will AI Unlimited be deployed in a private network without public IPs? - Type: String - AllowedValues: - - true - - false - Default: false - - LoadBalancerScheme: - Description: "If using a LoadBalancer, will it be internal or internet-facing? \nThe DNS name of an Internet-facing load balancer is publicly resolvable to the public IP addresses of the nodes.\nTherefore, Internet-facing load balancers can route requests from clients over the internet. The nodes of an \ninternal load balancer have only private IP addresses. The DNS name of an internal load balancer is publicly\nresolvable to the private IP addresses of the nodes. Therefore, internal load balancers can route requests only\nfrom clients with access to the VPC for the load balancer.\n" - Type: String - AllowedValues: - - internal - - internet-facing - Default: internet-facing - - Session: - Description: Should AI Unlimited be accessible via AWS Session Manager? - Type: String - AllowedValues: - - true - - false - Default: false - - Vpc: - Description: Network to deploy the AI Unlimited to. - Type: AWS::EC2::VPC::Id - ConstraintDescription: must be the name of an existing vpc. - - Subnet: - Description: Subnetwork to deploy the AI Unlimited to. - Type: AWS::EC2::Subnet::Id - ConstraintDescription: must be the name of a existing subnet. - - AvailabilityZone: - Description: "Availability zone to deploy the AI Unlimited to.\nThis must match the subnet, the zone of any pre existing volumes if used, \nand the instance type must be available in the selected zone.\n" - Type: AWS::EC2::AvailabilityZone::Name - ConstraintDescription: must be the name of a existing subnet. - - LoadBalancerSubnetOne: - Description: First subnetwork to deploy the Application Load Balancer to. - Type: AWS::EC2::Subnet::Id - - LoadBalancerSubnetTwo: - Description: Second subnetwork to deploy the Application Load Balancer to. - Type: AWS::EC2::Subnet::Id - - HostedZoneId: - Description: Zone ID of an existing Route 53 zone to add an entry for the Application Load Balancer to. - Type: AWS::Route53::HostedZone::Id - - DnsName: - Description: | - Name for Load Balancer DNS Entry, must fit as subdomain in the provides Route 53 Hosted Zone ID. - Example: unlimited.yourhostedzonedomainname.com - Type: String - - AiUnlimitedHttpPort: - Description: port to access the AI Unlimited UI. - Type: Number - Default: 3000 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedSchedulerHttpPort: - Description: port to access the AI Unlimited Scheduler API. - Type: Number - Default: 50061 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedSchedulerGrpcPort: - Description: port to access the AI Unlimited Scheduler API. - Type: Number - Default: 50051 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedGrpcPort: - Description: port to access the AI Unlimited API. - Type: Number - Default: 3282 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedVersion: - Description: Which version of AI Unlimited to deploy, uses container version tags, defaults to "latest" - Type: String - Default: v0.2.23 - - AiUnlimitedSchedulerVersion: - Description: Which version of AI Unlimited Scheduler to deploy, uses container version tags, defaults to "latest" - Type: String - Default: latest - - RootVolumeSize: - Description: size of the root disk to the AI Unlimited server. - Type: Number - Default: 20 - ConstraintDescription: Size in GB, between 10 and 1000. - MinValue: 8 - MaxValue: 1000 - - UsePersistentVolume: - Description: Should we use a new or existing volume for persistent data on the AI Unlimited server. - Type: String - AllowedValues: - - New - - Existing - Default: New - ConstraintDescription: Specify if you are using a a new persistent volume, an existing one, or none. - - PersistentVolumeSize: - Description: size of the optional persistent disk to the AI Unlimited server. - Type: Number - Default: 20 - ConstraintDescription: Size in GB, between 10 and 1000. - MinValue: 8 - MaxValue: 1000 - - ExistingPersistentVolumeId: - Description: Id of the existing persistent volume to attach. Must be int the same availability zone as the AI Unlimited server. - Type: String - Default: None - - PersistentVolumeDeletionPolicy: - Description: Behavior for the Persistent Volume when deleting the cloudformations deployment. - Type: String - AllowedValues: - - Delete - - Retain - - RetainExceptOnCreate - - Snapshot - Default: Retain - - TerminationProtection: - Description: Enable instance termination protection. - Type: String - AllowedValues: - - true - - false - Default: false - - InstanceType: - Description: AI Unlimited EC2 instance type - Type: String - AllowedValues: - - t3.nano - - t3.micro - - t3.small - - t3.medium - - t3.large - - m3.medium - - m3.large - - m3.xlarge - - m3.2xlarge - - m4.large - - m4.xlarge - - m4.2xlarge - - m4.4xlarge - - m4.10xlarge - - c3.large - - c3.xlarge - - c3.2xlarge - - c3.4xlarge - - c3.8xlarge - - c4.large - - c4.xlarge - - c4.2xlarge - - c4.4xlarge - - c4.8xlarge - - r3.large - - r3.xlarge - - r3.2xlarge - - r3.4xlarge - - r3.8xlarge - - i2.xlarge - - i2.2xlarge - - i2.4xlarge - - i2.8xlarge - Default: t3.micro - ConstraintDescription: must be a valid EC2 instance type. - - KeyName: - Description: Name of an existing EC2 KeyPair to enable SSH access to the AI Unlimited instance, leave empty if no ssh keys should be included - Type: String - - IamRole: - Description: | - Create a new IAM role for AI Unlimited or use an exiting one. - Requires CAPABILITY_IAM if creating a new IAM Role - Type: String - AllowedValues: - - New - - Existing - Default: New - - IamRoleName: - Description: | - Name of an existing IAM Role to assign to AI Unlimited, - or the name to give to the newly created role. - Leave blank to use an autogenerated name. - Requires CAPABILITY_NAMED_IAM if naming a new IAM Role. - Type: String - - IamPermissionsBoundary: - Description: | - Optional: Arn of a permissions boundary to pass to the IAM Role assigned to AI Unlimited. - Type: String - - AccessCIDR: - Description: The IP address range that can be used to communicate with the AI Unlimited instance. - Type: String - AllowedPattern: ((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2}))|^$ - ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. - - PrefixList: - Description: The PrefixList that can be used to communicate with the AI Unlimited instance. - Type: String - ConstraintDescription: must be a valid prefixlist - - SecurityGroup: - Description: The SecurityGroup that can be used to communicate with the AI Unlimited instance. - Type: String - ConstraintDescription: must be a valid securityGroup ID - -Rules: - subnetsInVpc: - Assertions: - - Assert: - Fn::EachMemberEquals: - - Fn::ValueOfAll: - - AWS::EC2::Subnet::Id - - VpcId - - !Ref Vpc - AssertDescription: The subnet you selected is not in the VPC - - instanceTypeInZone: - Assertions: - - Assert: - Fn::EachMemberEquals: - - Fn::ValueOfAll: - - AWS::EC2::Subnet::Id - - VpcId - - !Ref Vpc - AssertDescription: The subnet you selected is not in the VPC - -Conditions: - NEEDSROLE: !Equals - - !Ref IamRole - - New - - HASPUBLICIP: !Not - - !Equals - - !Ref Private - - "true" - - HASKEY: !Not - - !Equals - - !Ref KeyName - - "" - - HASCIDR: !Not - - !Equals - - !Ref AccessCIDR - - "" - - HASPREFIXLIST: !Not - - !Equals - - !Ref PrefixList - - "" - - HASSECURITYGROUP: !Not - - !Equals - - !Ref SecurityGroup - - "" - - HASCIDRORPREFIXLIST: !Or - - !Condition HASCIDR - - !Condition HASPREFIXLIST - - HASCIDRORPREFIXLISTORSECGROUP: !Or - - !Condition HASCIDR - - !Condition HASPREFIXLIST - - !Condition HASSECURITYGROUP - - USESESSIONMANAGER: !Equals - - !Ref Session - - "true" - - NEEDSROLEANDSESSIONMANAGER: !And - - !Condition NEEDSROLE - - !Condition USESESSIONMANAGER - - HASKEYANDPUBLIC: !And - - !Condition HASKEY - - !Condition HASPUBLICIP - - HASKEYANDCIDRORPREFIXLISTORSECGROUP: !And - - !Condition HASKEY - - !Condition HASCIDRORPREFIXLISTORSECGROUP - - USENEWPERSISTENTVOLUME: !Equals - - !Ref UsePersistentVolume - - New - - HASIAMPERMISSIONSBOUNDARY: !Not - - !Equals - - !Ref IamPermissionsBoundary - - "" - - HASIAMROLENAME: !Not - - !Equals - - !Ref IamRoleName - - "" - -Resources: - AiUnlimitedVolume: - DeletionPolicy: !Ref PersistentVolumeDeletionPolicy - Type: AWS::EC2::Volume - Properties: - AvailabilityZone: !Ref AvailabilityZone - Size: !Ref PersistentVolumeSize - Encrypted: true - Tags: - - Key: Name - Value: !Join - - '-' - - - !Ref AiUnlimitedName - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - Key: Usage - Value: persistent storage - Condition: USENEWPERSISTENTVOLUME - - AiUnlimitedServer: - CreationPolicy: - ResourceSignal: - Timeout: PT15M - Type: AWS::EC2::Instance - Metadata: - AWS::CloudFormation::Init: - configSets: - ai_unlimited_install: - - prepare_directory - - !If - - USENEWPERSISTENTVOLUME - - prepare_new_storage - - !Ref AWS::NoValue - - bind_storage - - mount_storage - - install_docker - - configure_ai_unlimited_service - - start_ai_unlimited_service - - configure_ai_unlimited_scheduler_service - - start_ai_unlimited_scheduler_service - prepare_directory: - commands: - mkdir: - command: !Sub | - #!/bin/bash -xe - mkdir -p /etc/td - prepare_new_storage: - commands: - mkfs: - command: !Sub | - #!/bin/bash -xe - /usr/sbin/mkfs -t ext4 /dev/nvme1n1 - bind_storage: - commands: - fstab: - command: !Sub | - #!/bin/bash -xe - /usr/bin/echo "/dev/nvme1n1 /etc/td ext4 defaults 0 2" >> /etc/fstab - mount_storage: - commands: - mount: - command: !Sub | - #!/bin/bash -xe - /usr/bin/mount -a - install_docker: - files: - /usr/lib/systemd/system/docker-install.service: - content: !Sub | - [Unit] - Description=Install docker - - [Service] - Type=oneshot - ExecStart=/bin/bash -c "while ! dnf update; do sleep 2; done && while ! dnf install -y docker; do sleep 2; done" - RemainAfterExit=yes - - [Install] - WantedBy=multi-user.target - commands: - verify_docker: - command: !Sub | - #!/bin/bash -xe - systemctl start docker-install - systemctl start docker - systemctl enable docker - services: - systemd: - docker: - enabled: "true" - ensureRunning: "true" - configure_ai_unlimited_service: - files: - /usr/lib/systemd/system/ai-unlimited.service: - content: !Sub | - [Unit] - Description=AI Unlimited - After=docker.service - Requires=docker.service - StartLimitInterval=200 - StartLimitBurst=10 - - [Service] - TimeoutStartSec=0 - Restart=always - RestartSec=2 - ExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited - ExecStartPre=-/usr/bin/docker exec %n stop || true - ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=-/bin/bash -c '/usr/bin/docker network create -d bridge ai_unlimited || true' - ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-workspaces:${ AiUnlimitedVersion } - ExecStart=/usr/bin/docker run \ - -e accept_license=Y \ - -e PLATFORM=aws \ - -v /etc/td/ai-unlimited:/etc/td \ - -p ${ AiUnlimitedHttpPort }:3000 \ - -p ${ AiUnlimitedGrpcPort }:3282 \ - --rm --name %n teradata/ai-unlimited-workspaces:${ AiUnlimitedVersion } workspaces serve -v - - [Install] - WantedBy=multi-user.target - group: root - mode: "000400" - owner: root - start_ai_unlimited_service: - services: - systemd: - ai-unlimited: - enabled: "true" - ensureRunning: "true" - configure_ai_unlimited_scheduler_service: - files: - /usr/lib/systemd/system/ai-unlimited-scheduler.service: - content: !Sub | - [Unit] - Description=AI Unlimited Scheduler - After=docker.service - Requires=docker.service - StartLimitInterval=200 - StartLimitBurst=10 - - [Service] - TimeoutStartSec=0 - Restart=always - RestartSec=2 - ExecStartPre=-/usr/bin/docker exec %n stop || true - ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-scheduler:latest - ExecStart=/usr/bin/docker run \ - --network ai_unlimited \ - -p ${ AiUnlimitedSchedulerGrpcPort }:50051 \ - -p ${ AiUnlimitedSchedulerHttpPort }:50061 \ - -v /etc/td/ai-unlimited:/etc/td \ - -e TD_WSSCHED_LOG_PATH=/etc/td/workspaces/scheduler_logs \ - -e TD_WSSCHED_TASK_LOG_PATH=/etc/td/workspaces/scheduler_logs/projects \ - -e TD_WSSCHED_POL_INTERVAL=60 \ - -e TD_WS_CONTAINER_NAME=ai-unlimited.service \ - --rm --name %n teradata/ai-unlimited-scheduler:latest workspace-event-scheduler serve - [Install] - WantedBy=multi-user.target - group: root - mode: "000400" - owner: root - start_ai_unlimited_scheduler_service: - services: - systemd: - ai-unlimited-scheduler: - enabled: "true" - ensureRunning: "true" - Properties: - PropagateTagsToVolumeOnCreation: true - BlockDeviceMappings: - - DeviceName: /dev/xvda - Ebs: - VolumeSize: !Ref RootVolumeSize - Encrypted: true - NetworkInterfaces: - - DeviceIndex: 0 - SubnetId: !Ref Subnet - GroupSet: - - !GetAtt AiUnlimitedSecurityGroup.GroupId - - !GetAtt AiUnlimitedSchedulerSecurityGroup.GroupId - AssociatePublicIpAddress: !If - - HASPUBLICIP - - true - - !Ref AWS::NoValue - ImageId: !Ref LatestAmiId - InstanceType: !Ref InstanceType - KeyName: !If - - HASKEY - - !Ref KeyName - - !Ref AWS::NoValue - DisableApiTermination: !Ref TerminationProtection - IamInstanceProfile: !Ref AiUnlimitedInstanceProfile - Volumes: - - Device: /dev/xvdb - VolumeId: !If - - USENEWPERSISTENTVOLUME - - !Ref AiUnlimitedVolume - - !Ref ExistingPersistentVolumeId - Tags: - - Key: Name - Value: !Join - - '-' - - - !Ref AiUnlimitedName - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - UserData: !Base64 - Fn::Sub: | - #!/bin/bash -xe - yum update -y - yum update -y aws-cfn-bootstrap - /opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource AiUnlimitedServer --configsets ai_unlimited_install --region ${AWS::Region} - /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource AiUnlimitedServer --region ${AWS::Region} - - LoadBalancerAiUnlimitedSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server from LoadBalancer over http, grpc, and ssh - SecurityGroupIngress: - - FromPort: !Ref AiUnlimitedHttpPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - - FromPort: !Ref AiUnlimitedGrpcPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedGrpcPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASCIDRORPREFIXLISTORSECGROUP - - LoadBalancerSchedulerSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server from LoadBalancer over http, grpc, and ssh - SecurityGroupIngress: - - FromPort: !Ref AiUnlimitedSchedulerHttpPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedSchedulerHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - - FromPort: !Ref AiUnlimitedSchedulerGrpcPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedSchedulerGrpcPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASCIDRORPREFIXLISTORSECGROUP - - LoadBalancer: - Type: AWS::ElasticLoadBalancingV2::LoadBalancer - Properties: - Scheme: !Ref LoadBalancerScheme - Subnets: - - !Ref LoadBalancerSubnetOne - - !Ref LoadBalancerSubnetTwo - SecurityGroups: - - !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - - !GetAtt LoadBalancerSchedulerSecurityGroup.GroupId - Type: application - - AiUnlimitedHTTPListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref AiUnlimitedHTTPTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref AiUnlimitedHttpPort - Protocol: HTTPS - Certificates: - - CertificateArn: !Ref ACMCertificate - - AiUnlimitedGRPCListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref AiUnlimitedGRPCTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref AiUnlimitedGrpcPort - Protocol: HTTPS - Certificates: - - CertificateArn: !Ref ACMCertificate - - AiUnlimitedSchedulerHTTPListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref AiUnlimitedSchedulerHTTPTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref AiUnlimitedSchedulerHttpPort - Protocol: HTTPS - Certificates: - - CertificateArn: !Ref ACMCertificate - - AiUnlimitedSchedulerGRPCListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref AiUnlimitedSchedulerGRPCTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref AiUnlimitedSchedulerGrpcPort - Protocol: HTTPS - Certificates: - - CertificateArn: !Ref ACMCertificate - - AiUnlimitedHTTPTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTP - HealthCheckTimeoutSeconds: 15 - Matcher: - HttpCode: "200" - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - td-aiu - - ui - - http - Port: !Ref AiUnlimitedHttpPort - Protocol: HTTP - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: app_cookie - - Key: stickiness.app_cookie.cookie_name - Value: TDWUNLIMITEDHTTPSSESSION - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedHttpPort - VpcId: !Ref Vpc - - AiUnlimitedGRPCTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTPS - HealthCheckTimeoutSeconds: 15 - HealthyThresholdCount: 5 - Matcher: - GrpcCode: "0" - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - td-aiu - - api - - grpc - Port: !Ref AiUnlimitedGrpcPort - Protocol: HTTP - ProtocolVersion: GRPC - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: app_cookie - - Key: stickiness.app_cookie.cookie_name - Value: TDUNLIMITEDSGRPCSESSION - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedGrpcPort - VpcId: !Ref Vpc - - AiUnlimitedSchedulerHTTPTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTP - HealthCheckTimeoutSeconds: 15 - HealthCheckPath: /healthcheck - Matcher: - HttpCode: "200" - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - aisch - - ui - - http - Port: !Ref AiUnlimitedSchedulerHttpPort - Protocol: HTTPS - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: app_cookie - - Key: stickiness.app_cookie.cookie_name - Value: TDWUNLIMITEDHTTPSSESSION - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedSchedulerHttpPort - VpcId: !Ref Vpc - - AiUnlimitedSchedulerGRPCTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTPS - HealthCheckTimeoutSeconds: 15 - Matcher: - GrpcCode: "0" - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - aisch - - api - - grpc - Port: !Ref AiUnlimitedSchedulerGrpcPort - Protocol: HTTPS - ProtocolVersion: GRPC - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: app_cookie - - Key: stickiness.app_cookie.cookie_name - Value: TDWUNLIMITEDHTTPSSESSION - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedSchedulerGrpcPort - VpcId: !Ref Vpc - - AiUnlimitedSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server over http and grpc - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedHttpPort - ToPort: !Ref AiUnlimitedHttpPort - SourceSecurityGroupId: !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedGrpcPort - ToPort: !Ref AiUnlimitedGrpcPort - SourceSecurityGroupId: !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedHttpPort - ToPort: !Ref AiUnlimitedHttpPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedGrpcPort - ToPort: !Ref AiUnlimitedGrpcPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - AiUnlimitedSchedulerSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server over http and grpc - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedSchedulerGrpcPort - ToPort: !Ref AiUnlimitedSchedulerGrpcPort - SourceSecurityGroupId: !GetAtt LoadBalancerSchedulerSecurityGroup.GroupId - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedSchedulerHttpPort - ToPort: !Ref AiUnlimitedSchedulerHttpPort - SourceSecurityGroupId: !GetAtt LoadBalancerSchedulerSecurityGroup.GroupId - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedSchedulerHttpPort - ToPort: !Ref AiUnlimitedSchedulerHttpPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedSchedulerGrpcPort - ToPort: !Ref AiUnlimitedSchedulerGrpcPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - SecurityGroupIngress: - Type: AWS::EC2::SecurityGroupIngress - Properties: - GroupId: !GetAtt AiUnlimitedSecurityGroup.GroupId - FromPort: 22 - IpProtocol: tcp - ToPort: 22 - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASKEYANDCIDRORPREFIXLISTORSECGROUP - - AiUnlimitedRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Principal: - Service: - - ec2.amazonaws.com - Action: - - sts:AssumeRole - Path: / - RoleName: !If - - HASIAMROLENAME - - !Ref IamRoleName - - !Ref AWS::NoValue - PermissionsBoundary: !If - - HASIAMPERMISSIONSBOUNDARY - - !Ref IamPermissionsBoundary - - !Ref AWS::NoValue - Condition: NEEDSROLE - - SessionManagerPolicies: - Type: AWS::IAM::Policy - Properties: - PolicyName: !Join - - '-' - - - ai-unlimited - - session - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - ssm:DescribeAssociation - - ssm:GetDeployablePatchSnapshotForInstance - - ssm:GetDocument - - ssm:DescribeDocument - - ssm:GetManifest - - ssm:ListAssociations - - ssm:ListInstanceAssociations - - ssm:PutInventory - - ssm:PutComplianceItems - - ssm:PutConfigurePackageResult - - ssm:UpdateAssociationStatus - - ssm:UpdateInstanceAssociationStatus - - ssm:UpdateInstanceInformation - Resource: '*' - - Effect: Allow - Action: - - ssmmessages:CreateControlChannel - - ssmmessages:CreateDataChannel - - ssmmessages:OpenControlChannel - - ssmmessages:OpenDataChannel - Resource: '*' - - Effect: Allow - Action: - - ec2messages:AcknowledgeMessage - - ec2messages:DeleteMessage - - ec2messages:FailMessage - - ec2messages:GetEndpoint - - ec2messages:GetMessages - - ec2messages:SendReply - Resource: '*' - Roles: - - !Ref AiUnlimitedRole - Condition: NEEDSROLEANDSESSIONMANAGER - - AiUnlimitedRolePolicies: - Type: AWS::IAM::Policy - Properties: - PolicyName: !Join - - '-' - - - ai-unlimited - - deploy - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - iam:PassRole - - iam:AddRoleToInstanceProfile - - iam:CreateInstanceProfile - - iam:CreateRole - - iam:DeleteInstanceProfile - - iam:DeleteRole - - iam:DeleteRolePolicy - - iam:GetInstanceProfile - - iam:GetRole - - iam:GetRolePolicy - - iam:ListAttachedRolePolicies - - iam:ListInstanceProfilesForRole - - iam:ListRolePolicies - - iam:PutRolePolicy - - iam:RemoveRoleFromInstanceProfile - - iam:TagRole - - iam:TagInstanceProfile - - ec2:TerminateInstances - - ec2:RunInstances - - ec2:RevokeSecurityGroupEgress - - ec2:ModifyInstanceAttribute - - ec2:ImportKeyPair - - ec2:DescribeVpcs - - ec2:DescribeVolumes - - ec2:DescribeTags - - ec2:DescribeSubnets - - ec2:DescribeSecurityGroups - - ec2:DescribePlacementGroups - - ec2:DescribeNetworkInterfaces - - ec2:DescribeLaunchTemplates - - ec2:DescribeLaunchTemplateVersions - - ec2:DescribeKeyPairs - - ec2:DescribeInstanceTypes - - ec2:DescribeInstanceTypeOfferings - - ec2:DescribeInstances - - ec2:DescribeInstanceAttribute - - ec2:DescribeImages - - ec2:DescribeAccountAttributes - - ec2:DescribeAvailabilityZones - - ec2:DescribeManagedPrefixLists - - ec2:DescribeVpcAttribute - - ec2:DeleteSecurityGroup - - ec2:DeletePlacementGroup - - ec2:DeleteLaunchTemplate - - ec2:DeleteKeyPair - - ec2:CreateTags - - ec2:CreateSecurityGroup - - ec2:CreatePlacementGroup - - ec2:CreateLaunchTemplateVersion - - ec2:CreateLaunchTemplate - - ec2:AuthorizeSecurityGroupIngress - - ec2:AuthorizeSecurityGroupEgress - - secretsmanager:CreateSecret - - secretsmanager:DeleteSecret - - secretsmanager:DescribeSecret - - secretsmanager:GetResourcePolicy - - secretsmanager:GetSecretValue - - secretsmanager:PutSecretValue - - secretsmanager:TagResource - - s3:CreateBucket - - s3:DeleteBucket - - s3:PutObject - - s3:GetObject - - s3:DeleteObject - - cloudformation:CreateStack - - cloudformation:DeleteStack - - cloudformation:DescribeStacks - - cloudformation:ListStacks - - cloudformation:DescribeStackEvents - - ssm:PutParameter - - ssm:GetParameter - - ssm:DeleteParameter - - ec2:RevokeSecurityGroupIngress - - ec2:CreateInternetGateway - - ec2:CreateVpc - - ec2:DescribeInternetGateways - - ec2:DeleteVpc - - ec2:ModifyVpcAttribute - - ec2:DeleteInternetGateway - - ec2:CreateSubnet - - ec2:CreateRouteTable - - ec2:DescribeRouteTables - - ec2:DeleteSubnet - - ec2:DeleteRouteTable - - ec2:AttachInternetGateway - - ec2:ModifySubnetAttribute - - ec2:DetachInternetGateway - - ec2:AssociateRouteTable - - ec2:CreateRoute - - ec2:DisassociateRouteTable - - ec2:DeleteRoute - Resource: '*' - Roles: - - !Ref AiUnlimitedRole - Condition: NEEDSROLE - - AiUnlimitedInstanceProfile: - Type: AWS::IAM::InstanceProfile - Properties: - Path: / - Roles: !If - - NEEDSROLE - - - !Ref AiUnlimitedRole - - - !Ref IamRoleName - - ACMCertificate: - Type: AWS::CertificateManager::Certificate - Properties: - DomainName: !Ref DnsName - DomainValidationOptions: - - DomainName: !Ref DnsName - HostedZoneId: !Ref HostedZoneId - ValidationMethod: DNS - - LoadBalancerDNS: - Type: AWS::Route53::RecordSetGroup - Properties: - HostedZoneId: !Ref HostedZoneId - RecordSets: - - Name: !Ref DnsName - Type: A - AliasTarget: - HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID - DNSName: !GetAtt LoadBalancer.DNSName - -Outputs: - PublicIP: - Description: EC2 public IP - Value: !GetAtt AiUnlimitedServer.PublicIp - Condition: HASPUBLICIP - - PrivateIP: - Description: EC2 private IP - Value: !GetAtt AiUnlimitedServer.PrivateIp - - AiUnlimitedUiAccess: - Description: Loadbalancer access endpoint for AI Unlimited UI Access - Value: !Sub https://${ DnsName }:${ AiUnlimitedHttpPort } - - AiUnlimitedApiAccess: - Description: Loadbalancer access endpoint for AI Unlimited API Access - Value: !Sub ${ DnsName }:${ AiUnlimitedGrpcPort } - - InstanceSecurityGroups: - Description: AI Unlimited Security Group - Value: !Join - - ', ' - - - !GetAtt AiUnlimitedSecurityGroup.GroupId - - !GetAtt AiUnlimitedSchedulerSecurityGroup.GroupId - - LoadBalancerSecurityGroups: - Description: AI Unlimited Load Balancer Security Group - Value: !Join - - ', ' - - - !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - - !GetAtt LoadBalancerSchedulerSecurityGroup.GroupId - - PublicSshConeection: - Description: AI Unlimited ssh connnection string - Value: !Sub ssh ec2-user@${ AiUnlimitedServer.PublicIp } - Condition: HASKEYANDPUBLIC - - PrivateSshConeection: - Description: AI Unlimited ssh connnection string - Value: !Sub ssh ec2-user@${ AiUnlimitedServer.PrivateIp } - Condition: HASKEY - - PersistentVolumeId: - Description: Id of the new persistent volume created for AI Unlimited - Value: !Ref AiUnlimitedVolume - Condition: USENEWPERSISTENTVOLUME diff --git a/deployments/aws/templates/ai-unlimited/ai-unlimited-with-nlb.yaml b/deployments/aws/templates/ai-unlimited/ai-unlimited-with-nlb.yaml index 7248148..1e19a32 100644 --- a/deployments/aws/templates/ai-unlimited/ai-unlimited-with-nlb.yaml +++ b/deployments/aws/templates/ai-unlimited/ai-unlimited-with-nlb.yaml @@ -29,12 +29,15 @@ Metadata: - AccessCIDR - PrefixList - SecurityGroup - - AiUnlimitedHttpPort + - AiUnlimitedAuthPort - AiUnlimitedGrpcPort - AiUnlimitedVersion - AiUnlimitedSchedulerVersion - AiUnlimitedSchedulerHttpPort - AiUnlimitedSchedulerGrpcPort + - AiUnlimitedUiPort + - AiUnlimitedUiHttpsPort + - AiUnlimitedUiVersion - Label: default: Persistent volume Parameters: @@ -101,7 +104,7 @@ Parameters: Type: AWS::EC2::AvailabilityZone::Name ConstraintDescription: must be the name of a existing subnet. - AiUnlimitedHttpPort: + AiUnlimitedAuthPort: Description: port to access the AI Unlimited UI. Type: Number Default: 3000 @@ -133,10 +136,31 @@ Parameters: MinValue: 0 MaxValue: 65535 + AiUnlimitedUiPort: + Description: port to access the AI Unlimited UI. + Type: Number + Default: 80 + ConstraintDescription: must be a valid ununsed port between 0 and 65535. + MinValue: 0 + MaxValue: 65535 + + AiUnlimitedUiHttpsPort: + Description: port to allow https access the AI Unlimited UI. + Type: Number + Default: 443 + ConstraintDescription: must be a valid ununsed port between 0 and 65535. + MinValue: 0 + MaxValue: 65535 + AiUnlimitedVersion: Description: Which version of AI Unlimited to deploy, uses container version tags, defaults to "latest" Type: String - Default: v0.2.23 + Default: v0.3.0 + + AiUnlimitedUiVersion: + Description: Which version of AI Unlimited UI to deploy, uses container version tags, defaults to "latest" + Type: String + Default: v0.1.0 AiUnlimitedSchedulerVersion: Description: Which version of AI Unlimited Scheduler to deploy, uses container version tags, defaults to "latest" @@ -364,6 +388,11 @@ Conditions: - !Ref IamRoleName - "" + PortIsNotEighty: !Not + - !Equals + - !Ref AiUnlimitedUiPort + - 80 + Resources: AiUnlimitedVolume: DeletionPolicy: !Ref PersistentVolumeDeletionPolicy @@ -406,8 +435,12 @@ Resources: - !Ref AWS::NoValue - bind_storage - mount_storage + - create_ai_unlimited_folder + - create_init_api_key - install_docker + - configure_ai_unlimited_ui_service - configure_ai_unlimited_service + - start_ai_unlimited_ui_service - start_ai_unlimited_service - configure_ai_unlimited_scheduler_service - start_ai_unlimited_scheduler_service @@ -435,6 +468,18 @@ Resources: command: !Sub | #!/bin/bash -xe /usr/bin/mount -a + create_ai_unlimited_folder: + commands: + run_command: + command: !Sub | + #!/bin/bash -xe + /usr/bin/mkdir -p /etc/td/ai-unlimited + create_init_api_key: + commands: + run_command: + command: !Sub | + #!/bin/bash -xe + echo "TD_VCD_INIT_API_KEY=$(LC_ALL=C tr -dc A-Za-z0-9 /etc/td/ai-unlimited/init_api_key.txt install_docker: files: /usr/lib/systemd/system/docker-install.service: @@ -461,6 +506,52 @@ Resources: docker: enabled: "true" ensureRunning: "true" + configure_ai_unlimited_ui_service: + files: + /usr/lib/systemd/system/ai-unlimited-ui.service: + content: !Sub | + [Unit] + Description=ai-unlimited-ui + After=docker.service + Requires=docker.service + StartLimitInterval=200 + StartLimitBurst=10 + + [Service] + TimeoutStartSec=0 + Restart=always + RestartSec=2 + + ExecStartPre=-/bin/bash -c '/usr/bin/docker volume create ssl_certs || true' + ExecStartPre=-/bin/bash -c '/usr/bin/docker network create -d bridge ai_unlimited || true' + ExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited-ui + EnvironmentFile=/etc/td/ai-unlimited/init_api_key.txt + ExecStartPre=-/usr/bin/docker stop %n || true + ExecStartPre=-/usr/bin/docker rm %n || true + ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-workspaces-ui:${ AiUnlimitedUiVersion } + ExecStart=/usr/bin/docker run \ + -e accept_license=Y \ + -e PLATFORM=aws \ + -e TD_VCD_USE_TLS=false \ + -e TD_VCD_API_PORT=${ AiUnlimitedGrpcPort } \ + -e TD_VCD_AUTH_PORT=${ AiUnlimitedAuthPort } \ + -e TD_VCD_INIT_API_KEY \ + -p ${ AiUnlimitedUiPort }:80 \ + -p ${ AiUnlimitedUiHttpsPort }:443 \ + -v ssl_certs:/etc/ssl/td \ + --network ai_unlimited \ + --rm --name %n teradata/ai-unlimited-workspaces-ui:${ AiUnlimitedUiVersion } + [Install] + WantedBy=multi-user.target + group: root + mode: "000400" + owner: root + start_ai_unlimited_ui_service: + services: + systemd: + ai-unlimited-ui: + enabled: "true" + ensureRunning: "true" configure_ai_unlimited_service: files: /usr/lib/systemd/system/ai-unlimited.service: @@ -477,16 +568,18 @@ Resources: Restart=always RestartSec=2 ExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited - ExecStartPre=-/usr/bin/docker exec %n stop || true + EnvironmentFile=/etc/td/ai-unlimited/init_api_key.txt + ExecStartPre=-/usr/bin/docker stop %n || true ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=-/bin/bash -c '/usr/bin/docker network create -d bridge ai_unlimited || true' ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-workspaces:${ AiUnlimitedVersion } ExecStart=/usr/bin/docker run \ -e accept_license=Y \ -e PLATFORM=aws \ - -v /etc/td/ai-unlimited:/etc/td \ - -p ${ AiUnlimitedHttpPort }:3000 \ + -e TD_VCD_INIT_API_KEY \ + -p ${ AiUnlimitedAuthPort }:3000 \ -p ${ AiUnlimitedGrpcPort }:3282 \ + -v /etc/td/ai-unlimited:/etc/td \ + -v ssl_certs:/etc/td/ssl \ --network ai_unlimited \ --rm --name %n teradata/ai-unlimited-workspaces:${ AiUnlimitedVersion } workspaces serve -v @@ -516,7 +609,7 @@ Resources: TimeoutStartSec=0 Restart=always RestartSec=2 - ExecStartPre=-/usr/bin/docker exec %n stop || true + ExecStartPre=-/usr/bin/docker stop %n || true ExecStartPre=-/usr/bin/docker rm %n || true ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-scheduler:latest ExecStart=/usr/bin/docker run \ @@ -599,9 +692,9 @@ Resources: VpcId: !Ref Vpc GroupDescription: Enable access to AI Unlimited server from LoadBalancer over http, grpc, and ssh SecurityGroupIngress: - - FromPort: !Ref AiUnlimitedHttpPort + - FromPort: !Ref AiUnlimitedAuthPort IpProtocol: tcp - ToPort: !Ref AiUnlimitedHttpPort + ToPort: !Ref AiUnlimitedAuthPort CidrIp: !If - HASCIDR - !Ref AccessCIDR @@ -625,6 +718,36 @@ Resources: - HASSECURITYGROUP - !Ref SecurityGroup - !Ref AWS::NoValue + - FromPort: !Ref AiUnlimitedUiPort + IpProtocol: tcp + ToPort: !Ref AiUnlimitedUiPort + CidrIp: !If + - HASCIDR + - !Ref AccessCIDR + - !Ref AWS::NoValue + SourcePrefixListId: !If + - HASPREFIXLIST + - !Ref PrefixList + - !Ref AWS::NoValue + SourceSecurityGroupId: !If + - HASSECURITYGROUP + - !Ref SecurityGroup + - !Ref AWS::NoValue + - FromPort: !Ref AiUnlimitedUiHttpsPort + IpProtocol: tcp + ToPort: !Ref AiUnlimitedUiHttpsPort + CidrIp: !If + - HASCIDR + - !Ref AccessCIDR + - !Ref AWS::NoValue + SourcePrefixListId: !If + - HASPREFIXLIST + - !Ref PrefixList + - !Ref AWS::NoValue + SourceSecurityGroupId: !If + - HASSECURITYGROUP + - !Ref SecurityGroup + - !Ref AWS::NoValue Condition: HASCIDRORPREFIXLISTORSECGROUP LoadBalancerSchedulerSecurityGroup: @@ -679,7 +802,7 @@ Resources: - Type: forward TargetGroupArn: !Ref AiUnlimitedHTTPTargetGroup LoadBalancerArn: !Ref LoadBalancer - Port: !Ref AiUnlimitedHttpPort + Port: !Ref AiUnlimitedAuthPort Protocol: TCP AiUnlimitedGRPCListener: @@ -692,6 +815,26 @@ Resources: Port: !Ref AiUnlimitedGrpcPort Protocol: TCP + AiUnlimitedUIListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + DefaultActions: + - Type: forward + TargetGroupArn: !Ref AiUnlimitedUITargetGroup + LoadBalancerArn: !Ref LoadBalancer + Port: !Ref AiUnlimitedUiPort + Protocol: TCP + + AiUnlimitedUISSLListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + DefaultActions: + - Type: forward + TargetGroupArn: !Ref AiUnlimitedUIHTTPSTargetGroup + LoadBalancerArn: !Ref LoadBalancer + Port: !Ref AiUnlimitedUiHttpsPort + Protocol: TCP + AiUnlimitedSchedulerHTTPListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: @@ -732,7 +875,77 @@ Resources: - td-aiu - ui - http - Port: !Ref AiUnlimitedHttpPort + Port: !Ref AiUnlimitedAuthPort + Protocol: TCP + TargetGroupAttributes: + - Key: stickiness.enabled + Value: true + - Key: stickiness.type + Value: source_ip + - Key: deregistration_delay.timeout_seconds + Value: "20" + Targets: + - Id: !Ref AiUnlimitedServer + Port: !Ref AiUnlimitedAuthPort + VpcId: !Ref Vpc + + AiUnlimitedUITargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + Properties: + HealthCheckIntervalSeconds: 30 + HealthCheckProtocol: HTTP + HealthCheckTimeoutSeconds: 15 + Name: !Join + - '-' + - - !Select + - 4 + - !Split + - '-' + - !Select + - 2 + - !Split + - / + - !Ref AWS::StackId + - td-aiu + - ui + - http + - api + Port: !Ref AiUnlimitedUiPort + Protocol: TCP + TargetGroupAttributes: + - Key: stickiness.enabled + Value: true + - Key: stickiness.type + Value: source_ip + - Key: deregistration_delay.timeout_seconds + Value: "20" + Targets: + - Id: !Ref AiUnlimitedServer + Port: !Ref AiUnlimitedUiPort + VpcId: !Ref Vpc + + AiUnlimitedUIHTTPSTargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + Properties: + HealthCheckIntervalSeconds: 30 + HealthCheckProtocol: HTTPS + HealthCheckTimeoutSeconds: 15 + Name: !Join + - '-' + - - !Select + - 4 + - !Split + - '-' + - !Select + - 2 + - !Split + - / + - !Ref AWS::StackId + - td-aiu + - ui + - https + - api + Port: !Ref AiUnlimitedUiHttpsPort Protocol: TCP TargetGroupAttributes: - Key: stickiness.enabled @@ -743,7 +956,7 @@ Resources: Value: "20" Targets: - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedHttpPort + Port: !Ref AiUnlimitedUiHttpsPort VpcId: !Ref Vpc AiUnlimitedGRPCTargetGroup: @@ -859,18 +1072,26 @@ Resources: GroupDescription: Enable access to AI Unlimited server over http and grpc SecurityGroupIngress: - IpProtocol: tcp - FromPort: !Ref AiUnlimitedHttpPort - ToPort: !Ref AiUnlimitedHttpPort + FromPort: !Ref AiUnlimitedAuthPort + ToPort: !Ref AiUnlimitedAuthPort SourceSecurityGroupId: !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - IpProtocol: tcp FromPort: !Ref AiUnlimitedGrpcPort ToPort: !Ref AiUnlimitedGrpcPort SourceSecurityGroupId: !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId + - IpProtocol: tcp + FromPort: !Ref AiUnlimitedUiPort + ToPort: !Ref AiUnlimitedUiPort + SourceSecurityGroupId: !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId + - IpProtocol: tcp + FromPort: !Ref AiUnlimitedUiHttpsPort + ToPort: !Ref AiUnlimitedUiHttpsPort + SourceSecurityGroupId: !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - !If - HASSECURITYGROUP - IpProtocol: tcp - FromPort: !Ref AiUnlimitedHttpPort - ToPort: !Ref AiUnlimitedHttpPort + FromPort: !Ref AiUnlimitedAuthPort + ToPort: !Ref AiUnlimitedAuthPort SourceSecurityGroupId: !Ref SecurityGroup - !Ref AWS::NoValue - !If @@ -880,6 +1101,20 @@ Resources: ToPort: !Ref AiUnlimitedGrpcPort SourceSecurityGroupId: !Ref SecurityGroup - !Ref AWS::NoValue + - !If + - HASSECURITYGROUP + - IpProtocol: tcp + FromPort: !Ref AiUnlimitedUiPort + ToPort: !Ref AiUnlimitedUiPort + SourceSecurityGroupId: !Ref SecurityGroup + - !Ref AWS::NoValue + - !If + - HASSECURITYGROUP + - IpProtocol: tcp + FromPort: !Ref AiUnlimitedUiHttpsPort + ToPort: !Ref AiUnlimitedUiHttpsPort + SourceSecurityGroupId: !Ref SecurityGroup + - !Ref AWS::NoValue AiUnlimitedSchedulerSecurityGroup: Type: AWS::EC2::SecurityGroup @@ -1123,6 +1358,9 @@ Resources: - ec2:CreateRoute - ec2:DisassociateRouteTable - ec2:DeleteRoute + - ec2:DescribeAddresses + - ec2:AssociateAddress + - ec2:DisassociateAddress Resource: '*' Roles: - !Ref AiUnlimitedRole @@ -1149,7 +1387,10 @@ Outputs: AiUnlimitedUiAccess: Description: Loadbalancer access endpoint for AI Unlimited UI Access - Value: !Sub http://${ LoadBalancer.DNSName }:${ AiUnlimitedHttpPort } + Value: !If + - PortIsNotEighty + - !Sub http://${ LoadBalancer.DNSName }:${ AiUnlimitedUiPort }/healthcheck + - !Sub http://${ LoadBalancer.DNSName } AiUnlimitedApiAccess: Description: Loadbalancer access endpoint for AI Unlimited API Access diff --git a/deployments/aws/templates/ai-unlimited/ai-unlimited-without-lb.yaml b/deployments/aws/templates/ai-unlimited/ai-unlimited-without-lb.yaml index ee7796c..4ae712d 100644 --- a/deployments/aws/templates/ai-unlimited/ai-unlimited-without-lb.yaml +++ b/deployments/aws/templates/ai-unlimited/ai-unlimited-without-lb.yaml @@ -27,12 +27,15 @@ Metadata: - AccessCIDR - PrefixList - SecurityGroup - - AiUnlimitedHttpPort + - AiUnlimitedAuthPort - AiUnlimitedGrpcPort - AiUnlimitedVersion - AiUnlimitedSchedulerVersion - AiUnlimitedSchedulerHttpPort - AiUnlimitedSchedulerGrpcPort + - AiUnlimitedUiPort + - AiUnlimitedUiHttpsPort + - AiUnlimitedUiVersion - Label: default: Persistent volume Parameters: @@ -86,7 +89,7 @@ Parameters: Type: AWS::EC2::AvailabilityZone::Name ConstraintDescription: must be the name of a existing subnet. - AiUnlimitedHttpPort: + AiUnlimitedAuthPort: Description: port to access the AI Unlimited UI. Type: Number Default: 3000 @@ -118,16 +121,37 @@ Parameters: MinValue: 0 MaxValue: 65535 + AiUnlimitedUiPort: + Description: port to access the AI Unlimited UI. + Type: Number + Default: 80 + ConstraintDescription: must be a valid ununsed port between 0 and 65535. + MinValue: 0 + MaxValue: 65535 + + AiUnlimitedUiHttpsPort: + Description: port to allow https access the AI Unlimited UI. + Type: Number + Default: 443 + ConstraintDescription: must be a valid ununsed port between 0 and 65535. + MinValue: 0 + MaxValue: 65535 + AiUnlimitedVersion: Description: Which version of AI Unlimited to deploy, uses container version tags, defaults to "latest" Type: String - Default: v0.2.23 + Default: v0.3.0 AiUnlimitedSchedulerVersion: Description: Which version of AI Unlimited Scheduler to deploy, uses container version tags, defaults to "latest" Type: String Default: latest + AiUnlimitedUiVersion: + Description: Which version of AI Unlimited UI to deploy, uses container version tags, defaults to "latest" + Type: String + Default: v0.1.0 + RootVolumeSize: Description: size of the root disk to the AI Unlimited server. Type: Number @@ -349,6 +373,21 @@ Conditions: - !Ref IamRoleName - "" + PortIsNotEightyAndHasPublicIp: !And + - !Not + - !Equals + - !Ref Private + - "true" + - !Not + - !Equals + - !Ref AiUnlimitedUiPort + - 80 + + PortIsNotEighty: !Not + - !Equals + - !Ref AiUnlimitedUiPort + - 80 + Resources: AiUnlimitedVolume: DeletionPolicy: !Ref PersistentVolumeDeletionPolicy @@ -391,8 +430,12 @@ Resources: - !Ref AWS::NoValue - bind_storage - mount_storage + - create_ai_unlimited_folder + - create_init_api_key - install_docker + - configure_ai_unlimited_ui_service - configure_ai_unlimited_service + - start_ai_unlimited_ui_service - start_ai_unlimited_service - configure_ai_unlimited_scheduler_service - start_ai_unlimited_scheduler_service @@ -420,6 +463,18 @@ Resources: command: !Sub | #!/bin/bash -xe /usr/bin/mount -a + create_ai_unlimited_folder: + commands: + mkdir: + command: !Sub | + #!/bin/bash -xe + mkdir -p /etc/td/ai-unlimited + create_init_api_key: + commands: + run_command: + command: !Sub | + #!/bin/bash -xe + echo "TD_VCD_INIT_API_KEY=$(LC_ALL=C tr -dc A-Za-z0-9 /etc/td/ai-unlimited/init_api_key.txt install_docker: files: /usr/lib/systemd/system/docker-install.service: @@ -446,6 +501,52 @@ Resources: docker: enabled: "true" ensureRunning: "true" + configure_ai_unlimited_ui_service: + files: + /usr/lib/systemd/system/ai-unlimited-ui.service: + content: !Sub | + [Unit] + Description=ai-unlimited-ui + After=docker.service + Requires=docker.service + StartLimitInterval=200 + StartLimitBurst=10 + + [Service] + TimeoutStartSec=0 + Restart=always + RestartSec=2 + + ExecStartPre=-/bin/bash -c '/usr/bin/docker volume create ssl_certs || true' + ExecStartPre=-/bin/bash -c '/usr/bin/docker network create -d bridge ai_unlimited || true' + ExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited-ui + EnvironmentFile=/etc/td/ai-unlimited/init_api_key.txt + ExecStartPre=-/usr/bin/docker stop %n || true + ExecStartPre=-/usr/bin/docker rm %n || true + ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-workspaces-ui:${ AiUnlimitedUiVersion } + ExecStart=/usr/bin/docker run \ + -e accept_license=Y \ + -e PLATFORM=aws \ + -e TD_VCD_USE_TLS=false \ + -e TD_VCD_API_PORT=${ AiUnlimitedGrpcPort } \ + -e TD_VCD_AUTH_PORT=${ AiUnlimitedAuthPort } \ + -e TD_VCD_INIT_API_KEY \ + -p ${ AiUnlimitedUiPort }:80 \ + -p ${ AiUnlimitedUiHttpsPort }:443 \ + -v ssl_certs:/etc/ssl/td \ + --network ai_unlimited \ + --rm --name %n teradata/ai-unlimited-workspaces-ui:${ AiUnlimitedUiVersion } + [Install] + WantedBy=multi-user.target + group: root + mode: "000400" + owner: root + start_ai_unlimited_ui_service: + services: + systemd: + ai-unlimited-ui: + enabled: "true" + ensureRunning: "true" configure_ai_unlimited_service: files: /usr/lib/systemd/system/ai-unlimited.service: @@ -462,16 +563,18 @@ Resources: Restart=always RestartSec=2 ExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited - ExecStartPre=-/usr/bin/docker exec %n stop || true + EnvironmentFile=/etc/td/ai-unlimited/init_api_key.txt + ExecStartPre=-/usr/bin/docker stop %n || true ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=-/bin/bash -c '/usr/bin/docker network create -d bridge ai_unlimited || true' ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-workspaces:${ AiUnlimitedVersion } ExecStart=/usr/bin/docker run \ -e accept_license=Y \ -e PLATFORM=aws \ - -v /etc/td/ai-unlimited:/etc/td \ - -p ${ AiUnlimitedHttpPort }:3000 \ + -e TD_VCD_INIT_API_KEY \ + -p ${ AiUnlimitedAuthPort }:3000 \ -p ${ AiUnlimitedGrpcPort }:3282 \ + -v /etc/td/ai-unlimited:/etc/td \ + -v ssl_certs:/etc/td/ssl \ --network ai_unlimited \ --rm --name %n teradata/ai-unlimited-workspaces:${ AiUnlimitedVersion } workspaces serve -v @@ -585,9 +688,9 @@ Resources: VpcId: !Ref Vpc GroupDescription: Enable access to AI Unlimited server over http and grpc SecurityGroupIngress: - - FromPort: !Ref AiUnlimitedHttpPort + - FromPort: !Ref AiUnlimitedAuthPort IpProtocol: tcp - ToPort: !Ref AiUnlimitedHttpPort + ToPort: !Ref AiUnlimitedAuthPort CidrIp: !If - HASCIDR - !Ref AccessCIDR @@ -615,6 +718,36 @@ Resources: - HASSECURITYGROUP - !Ref SecurityGroup - !Ref AWS::NoValue + - FromPort: !Ref AiUnlimitedUiPort + IpProtocol: tcp + ToPort: !Ref AiUnlimitedUiPort + CidrIp: !If + - HASCIDR + - !Ref AccessCIDR + - !Ref AWS::NoValue + SourcePrefixListId: !If + - HASPREFIXLIST + - !Ref PrefixList + - !Ref AWS::NoValue + SourceSecurityGroupId: !If + - HASSECURITYGROUP + - !Ref SecurityGroup + - !Ref AWS::NoValue + - FromPort: !Ref AiUnlimitedUiHttpsPort + IpProtocol: tcp + ToPort: !Ref AiUnlimitedUiHttpsPort + CidrIp: !If + - HASCIDR + - !Ref AccessCIDR + - !Ref AWS::NoValue + SourcePrefixListId: !If + - HASPREFIXLIST + - !Ref PrefixList + - !Ref AWS::NoValue + SourceSecurityGroupId: !If + - HASSECURITYGROUP + - !Ref SecurityGroup + - !Ref AWS::NoValue AiUnlimitedSchedulerSecurityGroup: Type: AWS::EC2::SecurityGroup @@ -863,6 +996,9 @@ Resources: - ec2:CreateRoute - ec2:DisassociateRouteTable - ec2:DeleteRoute + - ec2:DescribeAddresses + - ec2:AssociateAddress + - ec2:DisassociateAddress Resource: '*' Roles: - !Ref AiUnlimitedRole @@ -889,12 +1025,18 @@ Outputs: AiUnlimitedPublicUIAccess: Description: Teradata AI Unlimited public UI Access - Value: !Sub http://${AiUnlimitedServer.PublicDnsName}:${ AiUnlimitedHttpPort } + Value: !If + - PortIsNotEightyAndHasPublicIp + - !Sub http://${AiUnlimitedServer.PublicDnsName}:${AiUnlimitedUiPort}/healthcheck + - !Sub http://${AiUnlimitedServer.PublicDnsName} Condition: HASPUBLICIP AiUnlimitedPrivateUIAccess: Description: Teradata AI Unlimited private UI Access - Value: !Sub http://${AiUnlimitedServer.PrivateDnsName}:${ AiUnlimitedHttpPort } + Value: !If + - PortIsNotEighty + - !Sub http://${AiUnlimitedServer.PrivateDnsName}:${AiUnlimitedUiPort}/healthcheck + - !Sub http://${AiUnlimitedServer.PrivateDnsName} AiUnlimitedPublicAPIAccess: Description: Teradata AI Unlimited public API Access diff --git a/deployments/aws/templates/all-in-one/all-in-one-with-alb.yaml b/deployments/aws/templates/all-in-one/all-in-one-with-alb.yaml deleted file mode 100644 index dc060cf..0000000 --- a/deployments/aws/templates/all-in-one/all-in-one-with-alb.yaml +++ /dev/null @@ -1,1407 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" - -Description: 'AWS CloudFormation Template with AI Unlimited with Jupyter: AI Unlimited is a instance based service for deploying and suspending ai-unlimited clusters, and managing project lifecycles. This template also includes a Jupyter Lab service running on the same host, suitable for demonstration environments. Note: You will be billed for the AWS resources used if you create a stack from this template.' - -Metadata: - AWS::CloudFormation::Interface: - ParameterGroups: - - Label: - default: AI Unlimited - Parameters: - - AiUnlimitedName - - InstanceType - - RootVolumeSize - - TerminationProtection - - IamRole - - IamRoleName - - IamPermissionsBoundary - - Label: - default: AI Unlimited connection - Parameters: - - AvailabilityZone - - LoadBalancerScheme - - LoadBalancerSubnetOne - - LoadBalancerSubnetTwo - - HostedZoneId - - DnsName - - Private - - Session - - Vpc - - Subnet - - KeyName - - AccessCIDR - - PrefixList - - SecurityGroup - - AiUnlimitedHttpPort - - AiUnlimitedGrpcPort - - AiUnlimitedVersion - - AiUnlimitedSchedulerVersion - - AiUnlimitedSchedulerHttpPort - - AiUnlimitedSchedulerGrpcPort - - Label: - default: Persistent volume - Parameters: - - UsePersistentVolume - - PersistentVolumeSize - - ExistingPersistentVolumeId - - PersistentVolumeDeletionPolicy - - Label: - default: Jupyter connection - Parameters: - - JupyterToken - - JupyterHttpPort - - JupyterVersion - -Parameters: - LatestAmiId: - Type: AWS::SSM::Parameter::Value - Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 - - AiUnlimitedName: - Description: The AI Unlimited instance name - Type: String - Default: ai-unlimited - AllowedPattern: ^[a-zA-Z][a-zA-Z0-9-]* - ConstraintDescription: must begin with a letter and contain only alphanumeric characters. - MaxLength: "20" - MinLength: "1" - - JupyterToken: - Description: The token or password equivalent used to access Jupyter. - Type: String - NoEcho: true - AllowedPattern: ^[a-zA-Z][a-zA-Z0-9-]* - ConstraintDescription: must begin with a letter and contain only alphanumeric characters. - MaxLength: "64" - - Private: - Description: Will AI Unlimited be deployed in a private network without public IPs? - Type: String - AllowedValues: - - true - - false - Default: false - - LoadBalancerScheme: - Description: "If using a LoadBalancer, will it be internal or internet-facing? \nThe DNS name of an Internet-facing load balancer is publicly resolvable to the public IP addresses of the nodes.\nTherefore, Internet-facing load balancers can route requests from clients over the internet. The nodes of an \ninternal load balancer have only private IP addresses. The DNS name of an internal load balancer is publicly\nresolvable to the private IP addresses of the nodes. Therefore, internal load balancers can route requests only\nfrom clients with access to the VPC for the load balancer.\n" - Type: String - AllowedValues: - - internal - - internet-facing - Default: internet-facing - - Session: - Description: Should AI Unlimited be accessible via AWS Session Manager? - Type: String - AllowedValues: - - true - - false - Default: false - - Vpc: - Description: Network to deploy the AI Unlimited to. - Type: AWS::EC2::VPC::Id - ConstraintDescription: must be the name of an existing vpc. - - Subnet: - Description: Subnetwork to deploy the AI Unlimited to. - Type: AWS::EC2::Subnet::Id - ConstraintDescription: must be the name of a existing subnet. - - AvailabilityZone: - Description: "Availability zone to deploy the AI Unlimited to.\nThis must match the subnet, the zone of any pre existing volumes if used, \nand the instance type must be available in the selected zone.\n" - Type: AWS::EC2::AvailabilityZone::Name - ConstraintDescription: must be the name of a existing subnet. - - LoadBalancerSubnetOne: - Description: First subnetwork to deploy the Application Load Balancer to. - Type: AWS::EC2::Subnet::Id - - LoadBalancerSubnetTwo: - Description: Second subnetwork to deploy the Application Load Balancer to. - Type: AWS::EC2::Subnet::Id - - HostedZoneId: - Description: Zone ID of an existing Route 53 zone to add an entry for the Application Load Balancer to. - Type: AWS::Route53::HostedZone::Id - - DnsName: - Description: | - Name for Load Balancer DNS Entry, must fit as subdomain in the provides Route 53 Hosted Zone ID. - Example: unlimited.yourhostedzonedomainname.com - Type: String - - AiUnlimitedHttpPort: - Description: port to access the AI Unlimited UI. - Type: Number - Default: 3000 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedGrpcPort: - Description: port to access the AI Unlimited API. - Type: Number - Default: 3282 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedSchedulerHttpPort: - Description: port to access the AI Unlimited Scheduler API. - Type: Number - Default: 50061 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedSchedulerGrpcPort: - Description: port to access the AI Unlimited Scheduler API. - Type: Number - Default: 50051 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedVersion: - Description: Which version of AI Unlimited to deploy, uses container version tags, defaults to "latest" - Type: String - Default: v0.2.23 - - AiUnlimitedSchedulerVersion: - Description: Which version of AI Unlimited Scheduler to deploy, uses container version tags, defaults to "latest" - Type: String - Default: latest - - JupyterHttpPort: - Description: port to access the Jupyter UI. - Type: Number - Default: 8888 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - JupyterVersion: - Description: Which version of Jupyter to deploy, uses container version tags, defaults to "latest" - Type: String - Default: latest - - RootVolumeSize: - Description: size of the root disk to the AI Unlimited server. - Type: Number - Default: 20 - ConstraintDescription: Size in GB, between 10 and 1000. - MinValue: 8 - MaxValue: 1000 - - UsePersistentVolume: - Description: Should we use a new or existing volume for persistent data on the AI Unlimited server. - Type: String - AllowedValues: - - New - - Existing - Default: New - ConstraintDescription: Specify if you are using a a new persistent volume, an existing one, or none. - - PersistentVolumeSize: - Description: size of the optional persistent disk to the AI Unlimited server. - Type: Number - Default: 20 - ConstraintDescription: Size in GB, between 10 and 1000. - MinValue: 8 - MaxValue: 1000 - - ExistingPersistentVolumeId: - Description: Id of the existing persistent volume to attach. Must be in the same availability zone as the AI Unlimited instance. - Type: String - Default: None - - PersistentVolumeDeletionPolicy: - Description: Behavior for the Persistent Volume when deleting the cloudformations deployment. - Type: String - AllowedValues: - - Delete - - Retain - - RetainExceptOnCreate - - Snapshot - Default: Retain - - TerminationProtection: - Description: Enable instance termination protection. - Type: String - AllowedValues: - - true - - false - Default: false - - InstanceType: - Description: AI Unlimited EC2 instance type - Type: String - AllowedValues: - - t3.small - - t3.medium - - t3.large - - m4.large - - m4.xlarge - - m4.2xlarge - - m4.4xlarge - - m4.10xlarge - - c4.large - - c4.xlarge - - c4.2xlarge - - c4.4xlarge - - c4.8xlarge - - r3.large - - r3.xlarge - - r3.2xlarge - - r3.4xlarge - - r3.8xlarge - - i2.xlarge - - i2.2xlarge - - i2.4xlarge - - i2.8xlarge - Default: t3.small - ConstraintDescription: must be a valid EC2 instance type. - - KeyName: - Description: Name of an existing EC2 KeyPair to enable SSH access to the AI Unlimited instance, leave empty if no ssh keys should be included - Type: String - - IamRole: - Description: | - Create a new IAM role for AI Unlimited or use an exiting one. - Requires CAPABILITY_IAM if creating a new IAM Role - Type: String - AllowedValues: - - New - - Existing - Default: New - - IamRoleName: - Description: | - Name of an existing IAM Role to assign to AI Unlimited, - or the name to give to the newly created role. - Leave blank to use an autogenerated name. - Requires CAPABILITY_NAMED_IAM if naming a new IAM Role. - Type: String - - IamPermissionsBoundary: - Description: | - Optional: Arn of a permissions boundary to pass to the IAM Role assigned to AI Unlimited. - Type: String - - AccessCIDR: - Description: The IP address range that can be used to communicate with the AI Unlimited instance. - Type: String - AllowedPattern: ((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2}))|^$ - ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. - - PrefixList: - Description: The PrefixList that can be used to communicate with the AI Unlimited instance. - Type: String - ConstraintDescription: must be a valid prefixlist - - SecurityGroup: - Description: The SecurityGroup that can be used to communicate with the AI Unlimited instance. - Type: String - ConstraintDescription: must be a valid securityGroup ID - -Rules: - subnetsInVpc: - Assertions: - - Assert: - Fn::EachMemberEquals: - - Fn::ValueOfAll: - - AWS::EC2::Subnet::Id - - VpcId - - !Ref Vpc - AssertDescription: The subnet you selected is not in the VPC - - instanceTypeInZone: - Assertions: - - Assert: - Fn::EachMemberEquals: - - Fn::ValueOfAll: - - AWS::EC2::Subnet::Id - - VpcId - - !Ref Vpc - AssertDescription: The subnet you selected is not in the VPC - -Conditions: - NEEDSROLE: !Equals - - !Ref IamRole - - New - - HASPUBLICIP: !Not - - !Equals - - !Ref Private - - "true" - - HASKEY: !Not - - !Equals - - !Ref KeyName - - "" - - HASCIDR: !Not - - !Equals - - !Ref AccessCIDR - - "" - - HASPREFIXLIST: !Not - - !Equals - - !Ref PrefixList - - "" - - HASSECURITYGROUP: !Not - - !Equals - - !Ref SecurityGroup - - "" - - HASCIDRORPREFIXLIST: !Or - - !Condition HASCIDR - - !Condition HASPREFIXLIST - - HASCIDRORPREFIXLISTORSECGROUP: !Or - - !Condition HASCIDR - - !Condition HASPREFIXLIST - - !Condition HASSECURITYGROUP - - USESESSIONMANAGER: !Equals - - !Ref Session - - "true" - - NEEDSROLEANDSESSIONMANAGER: !And - - !Condition NEEDSROLE - - !Condition USESESSIONMANAGER - - HASKEYANDPUBLIC: !And - - !Condition HASKEY - - !Condition HASPUBLICIP - - HASKEYANDCIDRORPREFIXLISTORSECGROUP: !And - - !Condition HASKEY - - !Condition HASCIDRORPREFIXLISTORSECGROUP - - USENEWPERSISTENTVOLUME: !Equals - - !Ref UsePersistentVolume - - New - - HASIAMPERMISSIONSBOUNDARY: !Not - - !Equals - - !Ref IamPermissionsBoundary - - "" - - HASIAMROLENAME: !Not - - !Equals - - !Ref IamRoleName - - "" - -Resources: - AiUnlimitedVolume: - DeletionPolicy: !Ref PersistentVolumeDeletionPolicy - Type: AWS::EC2::Volume - Properties: - AvailabilityZone: !Ref AvailabilityZone - Size: !Ref PersistentVolumeSize - Encrypted: true - Tags: - - Key: Name - Value: !Join - - '-' - - - !Ref AiUnlimitedName - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - Key: Usage - Value: persistent storage - Condition: USENEWPERSISTENTVOLUME - - AiUnlimitedServer: - CreationPolicy: - ResourceSignal: - Timeout: PT15M - Type: AWS::EC2::Instance - Metadata: - AWS::CloudFormation::Init: - configSets: - ai_unlimited_install: - - prepare_directory - - !If - - USENEWPERSISTENTVOLUME - - prepare_new_storage - - !Ref AWS::NoValue - - bind_storage - - mount_storage - - install_docker - - configure_ai_unlimited_service - - configure_jupyter_service - - start_ai_unlimited_service - - configure_ai_unlimited_scheduler_service - - start_ai_unlimited_scheduler_service - - start_jupyter_service - prepare_directory: - commands: - mkdir: - command: !Sub | - #!/bin/bash -xe - mkdir -p /etc/td - prepare_new_storage: - commands: - mkfs: - command: !Sub | - #!/bin/bash -xe - /usr/sbin/mkfs -t ext4 /dev/nvme1n1 - bind_storage: - commands: - fstab: - command: !Sub | - #!/bin/bash -xe - /usr/bin/echo "/dev/nvme1n1 /etc/td ext4 defaults 0 2" >> /etc/fstab - mount_storage: - commands: - mount: - command: !Sub | - #!/bin/bash -xe - /usr/bin/mount -a - install_docker: - files: - /usr/lib/systemd/system/docker-install.service: - content: !Sub | - [Unit] - Description=Install docker - - [Service] - Type=oneshot - ExecStart=/bin/bash -c "while ! dnf update; do sleep 2; done && while ! dnf install -y docker; do sleep 2; done" - RemainAfterExit=yes - - [Install] - WantedBy=multi-user.target - commands: - verify_docker: - command: !Sub | - #!/bin/bash -xe - systemctl start docker-install - systemctl start docker - systemctl enable docker - services: - systemd: - docker: - enabled: "true" - ensureRunning: "true" - configure_ai_unlimited_service: - files: - /usr/lib/systemd/system/ai-unlimited.service: - content: !Sub | - [Unit] - Description=AI Unlimited - After=docker.service - Requires=docker.service - StartLimitInterval=200 - StartLimitBurst=10 - - [Service] - TimeoutStartSec=0 - Restart=always - RestartSec=2 - ExecStartPre=-/bin/bash -c '/usr/bin/docker network create -d bridge ai_unlimited || true' - ExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited - ExecStartPre=-/usr/bin/docker exec %n stop || true - ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-workspaces:${ AiUnlimitedVersion } - ExecStart=/usr/bin/docker run \ - -e accept_license=Y \ - -e PLATFORM=aws \ - -v /etc/td/ai-unlimited:/etc/td \ - -p ${ AiUnlimitedHttpPort }:3000 \ - -p ${ AiUnlimitedGrpcPort }:3282 \ - --network ai_unlimited \ - --net-alias ${ DnsName } \ - --rm --name %n teradata/ai-unlimited-workspaces:${ AiUnlimitedVersion } workspaces serve -v - [Install] - WantedBy=multi-user.target - group: root - mode: "000400" - owner: root - start_ai_unlimited_service: - services: - systemd: - ai-unlimited: - enabled: "true" - ensureRunning: "true" - configure_ai_unlimited_scheduler_service: - files: - /usr/lib/systemd/system/ai-unlimited-scheduler.service: - content: !Sub | - [Unit] - Description=AI Unlimited Scheduler - After=ai-unlimited.service - Requires=ai-unlimited.service - StartLimitInterval=200 - StartLimitBurst=10 - - [Service] - TimeoutStartSec=0 - Restart=always - RestartSec=2 - ExecStartPre=-/usr/bin/docker exec %n stop || true - ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-scheduler:latest - ExecStart=/usr/bin/docker run \ - --network ai_unlimited \ - -p ${ AiUnlimitedSchedulerGrpcPort }:50051 \ - -p ${ AiUnlimitedSchedulerHttpPort }:50061 \ - -v /etc/td/ai-unlimited:/etc/td \ - -e TD_WSSCHED_LOG_PATH=/etc/td/workspaces/scheduler_logs \ - -e TD_WSSCHED_TASK_LOG_PATH=/etc/td/workspaces/scheduler_logs/projects \ - -e TD_WSSCHED_POL_INTERVAL=2 \ - -e TD_WS_CONTAINER_NAME=ai-unlimited.service \ - --rm --name %n teradata/ai-unlimited-scheduler:latest workspace-event-scheduler serve - [Install] - WantedBy=multi-user.target - group: root - mode: "000400" - owner: root - start_ai_unlimited_scheduler_service: - services: - systemd: - ai-unlimited-scheduler: - enabled: "true" - ensureRunning: "true" - configure_jupyter_service: - files: - /usr/lib/systemd/system/jupyter.service: - content: !Sub | - [Unit] - Description=jupyter - After=ai-unlimited.service - Requires=ai-unlimited.service - StartLimitInterval=200 - StartLimitBurst=10 - - [Service] - TimeoutStartSec=0 - Restart=always - RestartSec=2 - ExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited - ExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{userdata,ipython} - ExecStartPre=-/usr/bin/docker exec %n stop || true - ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-jupyter:${ JupyterVersion } - ExecStart=/usr/bin/docker run \ - -e accept_license=Y \ - -e JUPYTER_TOKEN=${ JupyterToken } \ - -v /etc/td/jupyter/userdata:/home/jovyan/JupyterLabRoot/userdata \ - -v /etc/td/jupyter/ipython:/home/jovyan/.ipython \ - -p ${ JupyterHttpPort }:8888 \ - --network ai_unlimited \ - --rm --name %n teradata/ai-unlimited-jupyter:${ JupyterVersion } - - [Install] - WantedBy=multi-user.target - group: root - mode: "000400" - owner: root - start_jupyter_service: - services: - systemd: - jupyter: - enabled: "true" - ensureRunning: "true" - Properties: - PropagateTagsToVolumeOnCreation: true - BlockDeviceMappings: - - DeviceName: /dev/xvda - Ebs: - VolumeSize: !Ref RootVolumeSize - Encrypted: true - NetworkInterfaces: - - DeviceIndex: 0 - SubnetId: !Ref Subnet - GroupSet: - - !GetAtt AiUnlimitedSecurityGroup.GroupId - - !GetAtt AiUnlimitedSchedulerSecurityGroup.GroupId - - !GetAtt JupyterSecurityGroup.GroupId - AssociatePublicIpAddress: !If - - HASPUBLICIP - - true - - !Ref AWS::NoValue - ImageId: !Ref LatestAmiId - InstanceType: !Ref InstanceType - KeyName: !If - - HASKEY - - !Ref KeyName - - !Ref AWS::NoValue - DisableApiTermination: !Ref TerminationProtection - IamInstanceProfile: !Ref AiUnlimitedInstanceProfile - Volumes: - - Device: /dev/xvdb - VolumeId: !If - - USENEWPERSISTENTVOLUME - - !Ref AiUnlimitedVolume - - !Ref ExistingPersistentVolumeId - Tags: - - Key: Name - Value: !Join - - '-' - - - !Ref AiUnlimitedName - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - UserData: !Base64 - Fn::Sub: | - #!/bin/bash -xe - yum update -y - yum update -y aws-cfn-bootstrap - /opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource AiUnlimitedServer --configsets ai_unlimited_install --region ${AWS::Region} - /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource AiUnlimitedServer --region ${AWS::Region} - - LoadBalancerAiUnlimitedSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server from LoadBalancer over http, grpc, and ssh - SecurityGroupIngress: - - FromPort: !Ref AiUnlimitedHttpPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - - FromPort: !Ref AiUnlimitedGrpcPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedGrpcPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASCIDRORPREFIXLISTORSECGROUP - - LoadBalancerSchedulerSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server from LoadBalancer over http, grpc, and ssh - SecurityGroupIngress: - - FromPort: !Ref AiUnlimitedSchedulerHttpPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedSchedulerHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - - FromPort: !Ref AiUnlimitedSchedulerGrpcPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedSchedulerGrpcPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASCIDRORPREFIXLISTORSECGROUP - - LoadBalancerJupyterSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server from LoadBalancer over http, grpc, and ssh - SecurityGroupIngress: - - FromPort: !Ref JupyterHttpPort - IpProtocol: tcp - ToPort: !Ref JupyterHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASCIDRORPREFIXLISTORSECGROUP - - LoadBalancer: - Type: AWS::ElasticLoadBalancingV2::LoadBalancer - Properties: - Scheme: !Ref LoadBalancerScheme - Subnets: - - !Ref LoadBalancerSubnetOne - - !Ref LoadBalancerSubnetTwo - SecurityGroups: - - !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - - !GetAtt LoadBalancerSchedulerSecurityGroup.GroupId - - !GetAtt LoadBalancerJupyterSecurityGroup.GroupId - Type: application - - AiUnlimitedHTTPListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref AiUnlimitedHTTPTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref AiUnlimitedHttpPort - Protocol: HTTPS - Certificates: - - CertificateArn: !Ref ACMCertificate - - JupyterHTTPListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref JupyterHTTPTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref JupyterHttpPort - Protocol: HTTPS - Certificates: - - CertificateArn: !Ref ACMCertificate - - AiUnlimitedSchedulerHTTPListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref AiUnlimitedSchedulerHTTPTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref AiUnlimitedSchedulerHttpPort - Protocol: HTTPS - Certificates: - - CertificateArn: !Ref ACMCertificate - - AiUnlimitedSchedulerGRPCListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref AiUnlimitedSchedulerGRPCTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref AiUnlimitedSchedulerGrpcPort - Protocol: HTTPS - Certificates: - - CertificateArn: !Ref ACMCertificate - - AiUnlimitedSchedulerHTTPTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTPS - HealthCheckTimeoutSeconds: 15 - HealthCheckPath: /healthcheck - Matcher: - HttpCode: "200" - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - aisch - - ui - - http - Port: !Ref AiUnlimitedSchedulerHttpPort - Protocol: HTTP - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: app_cookie - - Key: stickiness.app_cookie.cookie_name - Value: TDWUNLIMITEDHTTPSSESSION - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedSchedulerHttpPort - VpcId: !Ref Vpc - - AiUnlimitedSchedulerGRPCTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTPS - HealthCheckTimeoutSeconds: 15 - Matcher: - GrpcCode: "0" - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - aisch - - api - - grpc - Port: !Ref AiUnlimitedSchedulerGrpcPort - Protocol: HTTPS - ProtocolVersion: GRPC - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: app_cookie - - Key: stickiness.app_cookie.cookie_name - Value: TDWUNLIMITEDHTTPSSESSION - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedSchedulerGrpcPort - VpcId: !Ref Vpc - - AiUnlimitedHTTPTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTP - HealthCheckTimeoutSeconds: 15 - Matcher: - HttpCode: "200" - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - td-aiu - - ui - - http - Port: !Ref AiUnlimitedHttpPort - Protocol: HTTP - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: app_cookie - - Key: stickiness.app_cookie.cookie_name - Value: TDWUNLIMITEDHTTPSSESSION - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedHttpPort - VpcId: !Ref Vpc - - JupyterHTTPTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTP - HealthCheckTimeoutSeconds: 15 - Matcher: - HttpCode: "200" - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - jupyter - - ui - - http - Port: !Ref JupyterHttpPort - Protocol: HTTP - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: app_cookie - - Key: stickiness.app_cookie.cookie_name - Value: TDJUPYTERHTTPSSESSION - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref JupyterHttpPort - VpcId: !Ref Vpc - - AiUnlimitedGRPCTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTPS - HealthCheckTimeoutSeconds: 15 - HealthyThresholdCount: 5 - Matcher: - GrpcCode: "0" - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - td-aiu - - api - - grpc - Port: !Ref AiUnlimitedGrpcPort - Protocol: HTTP - ProtocolVersion: GRPC - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: app_cookie - - Key: stickiness.app_cookie.cookie_name - Value: TDUNLIMITEDSGRPCSESSION - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedGrpcPort - VpcId: !Ref Vpc - - AiUnlimitedSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server over http and grpc - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedHttpPort - ToPort: !Ref AiUnlimitedHttpPort - SourceSecurityGroupId: !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedGrpcPort - ToPort: !Ref AiUnlimitedGrpcPort - SourceSecurityGroupId: !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedHttpPort - ToPort: !Ref AiUnlimitedHttpPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedGrpcPort - ToPort: !Ref AiUnlimitedGrpcPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - AiUnlimitedSchedulerSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server over http and grpc - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedSchedulerGrpcPort - ToPort: !Ref AiUnlimitedSchedulerGrpcPort - SourceSecurityGroupId: !GetAtt LoadBalancerSchedulerSecurityGroup.GroupId - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedSchedulerHttpPort - ToPort: !Ref AiUnlimitedSchedulerHttpPort - SourceSecurityGroupId: !GetAtt LoadBalancerSchedulerSecurityGroup.GroupId - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedSchedulerHttpPort - ToPort: !Ref AiUnlimitedSchedulerHttpPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedSchedulerGrpcPort - ToPort: !Ref AiUnlimitedSchedulerGrpcPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - JupyterSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to jupyter server over http - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: !Ref JupyterHttpPort - ToPort: !Ref JupyterHttpPort - SourceSecurityGroupId: !GetAtt LoadBalancerJupyterSecurityGroup.GroupId - - FromPort: !Ref JupyterHttpPort - IpProtocol: tcp - ToPort: !Ref JupyterHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - - SecurityGroupIngress: - Type: AWS::EC2::SecurityGroupIngress - Properties: - GroupId: !GetAtt AiUnlimitedSecurityGroup.GroupId - FromPort: 22 - IpProtocol: tcp - ToPort: 22 - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASKEYANDCIDRORPREFIXLISTORSECGROUP - - AiUnlimitedRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Principal: - Service: - - ec2.amazonaws.com - Action: - - sts:AssumeRole - Path: / - RoleName: !If - - HASIAMROLENAME - - !Ref IamRoleName - - !Ref AWS::NoValue - PermissionsBoundary: !If - - HASIAMPERMISSIONSBOUNDARY - - !Ref IamPermissionsBoundary - - !Ref AWS::NoValue - Condition: NEEDSROLE - - SessionManagerPolicies: - Type: AWS::IAM::Policy - Properties: - PolicyName: !Join - - '-' - - - ai-unlimited - - session - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - ssm:DescribeAssociation - - ssm:GetDeployablePatchSnapshotForInstance - - ssm:GetDocument - - ssm:DescribeDocument - - ssm:GetManifest - - ssm:ListAssociations - - ssm:ListInstanceAssociations - - ssm:PutInventory - - ssm:PutComplianceItems - - ssm:PutConfigurePackageResult - - ssm:UpdateAssociationStatus - - ssm:UpdateInstanceAssociationStatus - - ssm:UpdateInstanceInformation - Resource: '*' - - Effect: Allow - Action: - - ssmmessages:CreateControlChannel - - ssmmessages:CreateDataChannel - - ssmmessages:OpenControlChannel - - ssmmessages:OpenDataChannel - Resource: '*' - - Effect: Allow - Action: - - ec2messages:AcknowledgeMessage - - ec2messages:DeleteMessage - - ec2messages:FailMessage - - ec2messages:GetEndpoint - - ec2messages:GetMessages - - ec2messages:SendReply - Resource: '*' - Roles: - - !Ref AiUnlimitedRole - Condition: NEEDSROLEANDSESSIONMANAGER - - AiUnlimitedRolePolicies: - Type: AWS::IAM::Policy - Properties: - PolicyName: !Join - - '-' - - - ai-unlimited - - deploy - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - iam:PassRole - - iam:AddRoleToInstanceProfile - - iam:CreateInstanceProfile - - iam:CreateRole - - iam:DeleteInstanceProfile - - iam:DeleteRole - - iam:DeleteRolePolicy - - iam:GetInstanceProfile - - iam:GetRole - - iam:GetRolePolicy - - iam:ListAttachedRolePolicies - - iam:ListInstanceProfilesForRole - - iam:ListRolePolicies - - iam:PutRolePolicy - - iam:RemoveRoleFromInstanceProfile - - iam:TagRole - - iam:TagInstanceProfile - - ec2:TerminateInstances - - ec2:RunInstances - - ec2:RevokeSecurityGroupEgress - - ec2:ModifyInstanceAttribute - - ec2:ImportKeyPair - - ec2:DescribeVpcs - - ec2:DescribeVolumes - - ec2:DescribeTags - - ec2:DescribeSubnets - - ec2:DescribeSecurityGroups - - ec2:DescribePlacementGroups - - ec2:DescribeNetworkInterfaces - - ec2:DescribeLaunchTemplates - - ec2:DescribeLaunchTemplateVersions - - ec2:DescribeKeyPairs - - ec2:DescribeInstanceTypes - - ec2:DescribeInstanceTypeOfferings - - ec2:DescribeInstances - - ec2:DescribeInstanceAttribute - - ec2:DescribeImages - - ec2:DescribeAccountAttributes - - ec2:DescribeAvailabilityZones - - ec2:DescribeManagedPrefixLists - - ec2:DescribeVpcAttribute - - ec2:DeleteTags - - ec2:DeleteSecurityGroup - - ec2:DeletePlacementGroup - - ec2:DeleteLaunchTemplate - - ec2:DeleteLaunchTemplateVersions - - ec2:DeleteKeyPair - - ec2:CreateTags - - ec2:CreateSecurityGroup - - ec2:CreatePlacementGroup - - ec2:CreateLaunchTemplateVersion - - ec2:CreateLaunchTemplate - - ec2:AuthorizeSecurityGroupIngress - - ec2:AuthorizeSecurityGroupEgress - - secretsmanager:CreateSecret - - secretsmanager:DeleteSecret - - secretsmanager:DescribeSecret - - secretsmanager:GetResourcePolicy - - secretsmanager:GetSecretValue - - secretsmanager:PutSecretValue - - secretsmanager:TagResource - - s3:CreateBucket - - s3:DeleteBucket - - s3:PutObject - - s3:GetObject - - s3:DeleteObject - - cloudformation:CreateStack - - cloudformation:DeleteStack - - cloudformation:DescribeStacks - - cloudformation:ListStacks - - cloudformation:DescribeStackEvents - - ssm:PutParameter - - ssm:GetParameter - - ssm:DeleteParameter - - ec2:RevokeSecurityGroupIngress - - ec2:CreateInternetGateway - - ec2:CreateVpc - - ec2:DescribeInternetGateways - - ec2:DeleteVpc - - ec2:ModifyVpcAttribute - - ec2:DeleteInternetGateway - - ec2:CreateSubnet - - ec2:CreateRouteTable - - ec2:DescribeRouteTables - - ec2:DeleteSubnet - - ec2:DeleteRouteTable - - ec2:AttachInternetGateway - - ec2:ModifySubnetAttribute - - ec2:DetachInternetGateway - - ec2:AssociateRouteTable - - ec2:CreateRoute - - ec2:DisassociateRouteTable - - ec2:DeleteRoute - Resource: '*' - Roles: - - !Ref AiUnlimitedRole - Condition: NEEDSROLE - - AiUnlimitedInstanceProfile: - Type: AWS::IAM::InstanceProfile - Properties: - Path: / - Roles: !If - - NEEDSROLE - - - !Ref AiUnlimitedRole - - - !Ref IamRoleName - - ACMCertificate: - Type: AWS::CertificateManager::Certificate - Properties: - DomainName: !Ref DnsName - DomainValidationOptions: - - DomainName: !Ref DnsName - HostedZoneId: !Ref HostedZoneId - ValidationMethod: DNS - - LoadBalancerDNS: - Type: AWS::Route53::RecordSetGroup - Properties: - HostedZoneId: !Ref HostedZoneId - RecordSets: - - Name: !Ref DnsName - Type: A - AliasTarget: - HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID - DNSName: !GetAtt LoadBalancer.DNSName - -Outputs: - PublicIP: - Description: EC2 public IP - Value: !GetAtt AiUnlimitedServer.PublicIp - Condition: HASPUBLICIP - - PrivateIP: - Description: EC2 private IP - Value: !GetAtt AiUnlimitedServer.PrivateIp - - AiUnlimitedUiAccess: - Description: Loadbalancer access endpoint for AI Unlimited UI Access - Value: !Sub https://${ DnsName }:${ AiUnlimitedHttpPort } - - AiUnlimitedApiAccess: - Description: Loadbalancer access endpoint for AI Unlimited API Access - Value: !Sub ${ DnsName }:${ AiUnlimitedGrpcPort } - - InstanceSecurityGroups: - Description: AI Unlimited Security Group - Value: !Join - - ', ' - - - !GetAtt AiUnlimitedSecurityGroup.GroupId - - !GetAtt AiUnlimitedSchedulerSecurityGroup.GroupId - - !GetAtt JupyterSecurityGroup.GroupId - - LoadBalancerSecurityGroups: - Description: AI Unlimited Load Balancer Security Group - Value: !Join - - ', ' - - - !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - - !GetAtt LoadBalancerSchedulerSecurityGroup.GroupId - - !GetAtt LoadBalancerJupyterSecurityGroup.GroupId - - PublicSshConeection: - Description: AI Unlimited ssh connnection string - Value: !Sub ssh ec2-user@${ AiUnlimitedServer.PublicIp } - Condition: HASKEYANDPUBLIC - - PrivateSshConeection: - Description: AI Unlimited ssh connnection string - Value: !Sub ssh ec2-user@${ AiUnlimitedServer.PrivateIp } - Condition: HASKEY - - PersistentVolumeId: - Description: Id of the new persistent volume created for AI Unlimited - Value: !Ref AiUnlimitedVolume - Condition: USENEWPERSISTENTVOLUME - - JupyterUIAccess: - Description: Loadbalancer access endpoint for API Access - Value: !Sub https://${ DnsName }:${ JupyterHttpPort }?token=${ JupyterToken } - - JupyterInternalAccessToAiUnlimited: - Description: AI Unlimited endpoint for local Jupyter access - Value: !Sub ai-unlimited.service:${ AiUnlimitedGrpcPort } diff --git a/deployments/aws/templates/all-in-one/all-in-one-with-nlb.yaml b/deployments/aws/templates/all-in-one/all-in-one-with-nlb.yaml deleted file mode 100644 index 911e914..0000000 --- a/deployments/aws/templates/all-in-one/all-in-one-with-nlb.yaml +++ /dev/null @@ -1,1348 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" - -Description: 'AWS CloudFormation Template with AI Unlimited with Jupyter: AI Unlimited is a instance based service for deploying and suspending ai-unlimited clusters, and managing project lifecycles. This template also includes a Jupyter Lab service running on the same host, suitable for demonstration environments. Note: You will be billed for the AWS resources used if you create a stack from this template.' - -Metadata: - AWS::CloudFormation::Interface: - ParameterGroups: - - Label: - default: AI Unlimited - Parameters: - - AiUnlimitedName - - InstanceType - - RootVolumeSize - - TerminationProtection - - IamRole - - IamRoleName - - IamPermissionsBoundary - - Label: - default: AI Unlimited connection - Parameters: - - AvailabilityZone - - LoadBalancerScheme - - LoadBalancerSubnet - - Private - - Session - - Vpc - - Subnet - - KeyName - - AccessCIDR - - PrefixList - - SecurityGroup - - AiUnlimitedHttpPort - - AiUnlimitedGrpcPort - - AiUnlimitedVersion - - AiUnlimitedSchedulerVersion - - AiUnlimitedSchedulerHttpPort - - AiUnlimitedSchedulerGrpcPort - - Label: - default: Persistent volume - Parameters: - - UsePersistentVolume - - PersistentVolumeSize - - ExistingPersistentVolumeId - - PersistentVolumeDeletionPolicy - - Label: - default: Jupyter connection - Parameters: - - JupyterToken - - JupyterHttpPort - - JupyterVersion - -Parameters: - LatestAmiId: - Type: AWS::SSM::Parameter::Value - Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 - - AiUnlimitedName: - Description: The AI Unlimited instance name - Type: String - Default: ai-unlimited - AllowedPattern: ^[a-zA-Z][a-zA-Z0-9-]* - ConstraintDescription: must begin with a letter and contain only alphanumeric characters. - MaxLength: "20" - MinLength: "1" - - JupyterToken: - Description: The token or password equivalent used to access Jupyter. - Type: String - NoEcho: true - AllowedPattern: ^[a-zA-Z][a-zA-Z0-9-]* - ConstraintDescription: must begin with a letter and contain only alphanumeric characters. - MaxLength: "64" - - Private: - Description: Will AI Unlimited be deployed in a private network without public IPs? - Type: String - AllowedValues: - - true - - false - Default: false - - LoadBalancerScheme: - Description: "Will the load balancer be internal or internet-facing? \nThe DNS name of an Internet-facing load balancer is publicly resolvable to the public IP addresses of the nodes.\nTherefore, Internet-facing load balancers can route requests from clients over the internet. The nodes of an \ninternal load balancer have only private IP addresses. The DNS name of an internal load balancer is publicly\nresolvable to the private IP addresses of the nodes. Therefore, internal load balancers can route requests only\nfrom clients with access to the VPC for the load balancer.\n" - Type: String - AllowedValues: - - internal - - internet-facing - Default: internet-facing - - LoadBalancerSubnet: - Description: Subnetwork to deploy the AI Unlimited service to. - Type: AWS::EC2::Subnet::Id - ConstraintDescription: must be the name of a existing subnet. - - Session: - Description: Should AI Unlimited be accessible via AWS Session Manager? - Type: String - AllowedValues: - - true - - false - Default: false - - Vpc: - Description: Network to deploy the AI Unlimited to. - Type: AWS::EC2::VPC::Id - ConstraintDescription: must be the name of an existing vpc. - - Subnet: - Description: Subnetwork to deploy the AI Unlimited to. - Type: AWS::EC2::Subnet::Id - ConstraintDescription: must be the name of a existing subnet. - - AvailabilityZone: - Description: "Availability zone to deploy the AI Unlimited to.\nThis must match the subnet, the zone of any pre existing volumes if used, \nand the instance type must be available in the selected zone.\n" - Type: AWS::EC2::AvailabilityZone::Name - ConstraintDescription: must be the name of a existing subnet. - - AiUnlimitedHttpPort: - Description: port to access the AI Unlimited UI. - Type: Number - Default: 3000 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedGrpcPort: - Description: port to access the AI Unlimited API. - Type: Number - Default: 3282 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedSchedulerHttpPort: - Description: port to access the AI Unlimited Scheduler API. - Type: Number - Default: 50061 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedSchedulerGrpcPort: - Description: port to access the AI Unlimited Scheduler API. - Type: Number - Default: 50051 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedVersion: - Description: Which version of AI Unlimited to deploy, uses container version tags, defaults to "latest" - Type: String - Default: v0.2.23 - - AiUnlimitedSchedulerVersion: - Description: Which version of AI Unlimited Scheduler to deploy, uses container version tags, defaults to "latest" - Type: String - Default: latest - - JupyterHttpPort: - Description: port to access the Jupyter UI. - Type: Number - Default: 8888 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - JupyterVersion: - Description: Which version of Jupyter to deploy, uses container version tags, defaults to "latest" - Type: String - Default: latest - - RootVolumeSize: - Description: size of the root disk to the AI Unlimited server. - Type: Number - Default: 20 - ConstraintDescription: Size in GB, between 10 and 1000. - MinValue: 8 - MaxValue: 1000 - - UsePersistentVolume: - Description: Should we use a new or existing volume for persistent data on the AI Unlimited server. - Type: String - AllowedValues: - - New - - Existing - Default: New - ConstraintDescription: Specify if you are using a a new persistent volume, an existing one, or none. - - PersistentVolumeSize: - Description: size of the optional persistent disk to the AI Unlimited server. - Type: Number - Default: 20 - ConstraintDescription: Size in GB, between 10 and 1000. - MinValue: 8 - MaxValue: 1000 - - ExistingPersistentVolumeId: - Description: Id of the existing persistent volume to attach. Must be in the same availability zone as the AI Unlimited instance. - Type: String - Default: None - - PersistentVolumeDeletionPolicy: - Description: Behavior for the Persistent Volume when deleting the cloudformations deployment. - Type: String - AllowedValues: - - Delete - - Retain - - RetainExceptOnCreate - - Snapshot - Default: Retain - - TerminationProtection: - Description: Enable instance termination protection. - Type: String - AllowedValues: - - true - - false - Default: false - - InstanceType: - Description: AI Unlimited EC2 instance type - Type: String - AllowedValues: - - t3.small - - t3.medium - - t3.large - - m4.large - - m4.xlarge - - m4.2xlarge - - m4.4xlarge - - m4.10xlarge - - c4.large - - c4.xlarge - - c4.2xlarge - - c4.4xlarge - - c4.8xlarge - - r3.large - - r3.xlarge - - r3.2xlarge - - r3.4xlarge - - r3.8xlarge - - i2.xlarge - - i2.2xlarge - - i2.4xlarge - - i2.8xlarge - Default: t3.small - ConstraintDescription: must be a valid EC2 instance type. - - KeyName: - Description: Name of an existing EC2 KeyPair to enable SSH access to the AI Unlimited instance, leave empty if no ssh keys should be included - Type: String - - IamRole: - Description: | - Create a new IAM role for AI Unlimited or use an exiting one. - Requires CAPABILITY_IAM if creating a new IAM Role - Type: String - AllowedValues: - - New - - Existing - Default: New - - IamRoleName: - Description: | - Name of an existing IAM Role to assign to AI Unlimited, - or the name to give to the newly created role. - Leave blank to use an autogenerated name. - Requires CAPABILITY_NAMED_IAM if naming a new IAM Role. - Type: String - - IamPermissionsBoundary: - Description: | - Optional: Arn of a permissions boundary to pass to the IAM Role assigned to AI Unlimited. - Type: String - - AccessCIDR: - Description: The IP address range that can be used to communicate with the AI Unlimited instance. - Type: String - AllowedPattern: ((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2}))|^$ - ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. - - PrefixList: - Description: The PrefixList that can be used to communicate with the AI Unlimited instance. - Type: String - ConstraintDescription: must be a valid prefixlist - - SecurityGroup: - Description: The SecurityGroup that can be used to communicate with the AI Unlimited instance. - Type: String - ConstraintDescription: must be a valid securityGroup ID - -Rules: - subnetsInVpc: - Assertions: - - Assert: - Fn::EachMemberEquals: - - Fn::ValueOfAll: - - AWS::EC2::Subnet::Id - - VpcId - - !Ref Vpc - AssertDescription: The subnet you selected is not in the VPC - - instanceTypeInZone: - Assertions: - - Assert: - Fn::EachMemberEquals: - - Fn::ValueOfAll: - - AWS::EC2::Subnet::Id - - VpcId - - !Ref Vpc - AssertDescription: The subnet you selected is not in the VPC - -Conditions: - NEEDSROLE: !Equals - - !Ref IamRole - - New - - HASPUBLICIP: !Not - - !Equals - - !Ref Private - - "true" - - HASKEY: !Not - - !Equals - - !Ref KeyName - - "" - - HASCIDR: !Not - - !Equals - - !Ref AccessCIDR - - "" - - HASPREFIXLIST: !Not - - !Equals - - !Ref PrefixList - - "" - - HASSECURITYGROUP: !Not - - !Equals - - !Ref SecurityGroup - - "" - - HASCIDRORPREFIXLIST: !Or - - !Condition HASCIDR - - !Condition HASPREFIXLIST - - HASCIDRORPREFIXLISTORSECGROUP: !Or - - !Condition HASCIDR - - !Condition HASPREFIXLIST - - !Condition HASSECURITYGROUP - - USESESSIONMANAGER: !Equals - - !Ref Session - - "true" - - NEEDSROLEANDSESSIONMANAGER: !And - - !Condition NEEDSROLE - - !Condition USESESSIONMANAGER - - HASKEYANDPUBLIC: !And - - !Condition HASKEY - - !Condition HASPUBLICIP - - HASKEYANDCIDRORPREFIXLISTORSECGROUP: !And - - !Condition HASKEY - - !Condition HASCIDRORPREFIXLISTORSECGROUP - - USENEWPERSISTENTVOLUME: !Equals - - !Ref UsePersistentVolume - - New - - HASIAMPERMISSIONSBOUNDARY: !Not - - !Equals - - !Ref IamPermissionsBoundary - - "" - - HASIAMROLENAME: !Not - - !Equals - - !Ref IamRoleName - - "" - -Resources: - AiUnlimitedVolume: - DeletionPolicy: !Ref PersistentVolumeDeletionPolicy - Type: AWS::EC2::Volume - Properties: - AvailabilityZone: !Ref AvailabilityZone - Size: !Ref PersistentVolumeSize - Encrypted: true - Tags: - - Key: Name - Value: !Join - - '-' - - - !Ref AiUnlimitedName - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - Key: Usage - Value: persistent storage - Condition: USENEWPERSISTENTVOLUME - - AiUnlimitedServer: - CreationPolicy: - ResourceSignal: - Timeout: PT15M - Type: AWS::EC2::Instance - Metadata: - AWS::CloudFormation::Init: - configSets: - ai_unlimited_install: - - prepare_directory - - !If - - USENEWPERSISTENTVOLUME - - prepare_new_storage - - !Ref AWS::NoValue - - bind_storage - - mount_storage - - install_docker - - configure_ai_unlimited_service - - configure_jupyter_service - - start_ai_unlimited_service - - configure_ai_unlimited_scheduler_service - - start_ai_unlimited_scheduler_service - - start_jupyter_service - prepare_directory: - commands: - mkdir: - command: !Sub | - #!/bin/bash -xe - mkdir -p /etc/td - prepare_new_storage: - commands: - mkfs: - command: !Sub | - #!/bin/bash -xe - /usr/sbin/mkfs -t ext4 /dev/nvme1n1 - bind_storage: - commands: - fstab: - command: !Sub | - #!/bin/bash -xe - /usr/bin/echo "/dev/nvme1n1 /etc/td ext4 defaults 0 2" >> /etc/fstab - mount_storage: - commands: - mount: - command: !Sub | - #!/bin/bash -xe - /usr/bin/mount -a - install_docker: - files: - /usr/lib/systemd/system/docker-install.service: - content: !Sub | - [Unit] - Description=Install docker - - [Service] - Type=oneshot - ExecStart=/bin/bash -c "while ! dnf update; do sleep 2; done && while ! dnf install -y docker; do sleep 2; done" - RemainAfterExit=yes - - [Install] - WantedBy=multi-user.target - commands: - verify_docker: - command: !Sub | - #!/bin/bash -xe - systemctl start docker-install - systemctl start docker - systemctl enable docker - services: - systemd: - docker: - enabled: "true" - ensureRunning: "true" - configure_ai_unlimited_service: - files: - /usr/lib/systemd/system/ai-unlimited.service: - content: !Sub | - [Unit] - Description=AI Unlimited - After=docker.service - Requires=docker.service - StartLimitInterval=200 - StartLimitBurst=10 - - [Service] - TimeoutStartSec=0 - Restart=always - RestartSec=2 - ExecStartPre=-/bin/bash -c '/usr/bin/docker network create -d bridge ai_unlimited || true' - ExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited - ExecStartPre=-/usr/bin/docker exec %n stop || true - ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-workspaces:${ AiUnlimitedVersion } - ExecStart=/usr/bin/docker run \ - -e accept_license=Y \ - -e PLATFORM=aws \ - -v /etc/td/ai-unlimited:/etc/td \ - -p ${ AiUnlimitedHttpPort }:3000 \ - -p ${ AiUnlimitedGrpcPort }:3282 \ - --network ai_unlimited \ - --net-alias ${ LoadBalancer.DNSName } \ - --rm --name %n teradata/ai-unlimited-workspaces:${ AiUnlimitedVersion } workspaces serve -v - [Install] - WantedBy=multi-user.target - group: root - mode: "000400" - owner: root - start_ai_unlimited_service: - services: - systemd: - ai-unlimited: - enabled: "true" - ensureRunning: "true" - configure_ai_unlimited_scheduler_service: - files: - /usr/lib/systemd/system/ai-unlimited-scheduler.service: - content: !Sub | - [Unit] - Description=AI Unlimited Scheduler - After=ai-unlimited.service - Requires=ai-unlimited.service - StartLimitInterval=200 - StartLimitBurst=10 - - [Service] - TimeoutStartSec=0 - Restart=always - RestartSec=2 - ExecStartPre=-/usr/bin/docker exec %n stop || true - ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-scheduler:${ AiUnlimitedSchedulerVersion } - ExecStart=/usr/bin/docker run \ - --network ai_unlimited \ - -p ${ AiUnlimitedSchedulerGrpcPort }:50051 \ - -p ${ AiUnlimitedSchedulerHttpPort }:50061 \ - -v /etc/td/ai-unlimited:/etc/td \ - -e TD_WSSCHED_LOG_PATH=/etc/td/workspaces/scheduler_logs \ - -e TD_WSSCHED_TASK_LOG_PATH=/etc/td/workspaces/scheduler_logs/projects \ - -e TD_WSSCHED_POL_INTERVAL=2 \ - -e TD_WS_CONTAINER_NAME=ai-unlimited.service \ - --rm --name %n teradata/ai-unlimited-scheduler:${ AiUnlimitedSchedulerVersion } workspace-event-scheduler serve - [Install] - WantedBy=multi-user.target - group: root - mode: "000400" - owner: root - start_ai_unlimited_scheduler_service: - services: - systemd: - ai-unlimited-scheduler: - enabled: "true" - ensureRunning: "true" - configure_jupyter_service: - files: - /usr/lib/systemd/system/jupyter.service: - content: !Sub | - [Unit] - Description=jupyter - After=ai-unlimited.service - Requires=ai-unlimited.service - StartLimitInterval=200 - StartLimitBurst=10 - - [Service] - TimeoutStartSec=0 - Restart=always - RestartSec=2 - ExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited - ExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{userdata,ipython} - ExecStartPre=-/usr/bin/docker exec %n stop || true - ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-jupyter:${ JupyterVersion } - ExecStart=/usr/bin/docker run \ - -e accept_license=Y \ - -e JUPYTER_TOKEN=${ JupyterToken } \ - -v /etc/td/jupyter/userdata:/home/jovyan/JupyterLabRoot/userdata \ - -v /etc/td/jupyter/ipython:/home/jovyan/.ipython \ - -p ${ JupyterHttpPort }:8888 \ - --network ai_unlimited \ - --rm --name %n teradata/ai-unlimited-jupyter:${ JupyterVersion } - - [Install] - WantedBy=multi-user.target - group: root - mode: "000400" - owner: root - start_jupyter_service: - services: - systemd: - jupyter: - enabled: "true" - ensureRunning: "true" - Properties: - PropagateTagsToVolumeOnCreation: true - BlockDeviceMappings: - - DeviceName: /dev/xvda - Ebs: - VolumeSize: !Ref RootVolumeSize - Encrypted: true - NetworkInterfaces: - - DeviceIndex: 0 - SubnetId: !Ref Subnet - GroupSet: - - !GetAtt AiUnlimitedSecurityGroup.GroupId - - !GetAtt AiUnlimitedSchedulerSecurityGroup.GroupId - - !GetAtt JupyterSecurityGroup.GroupId - AssociatePublicIpAddress: !If - - HASPUBLICIP - - true - - !Ref AWS::NoValue - ImageId: !Ref LatestAmiId - InstanceType: !Ref InstanceType - KeyName: !If - - HASKEY - - !Ref KeyName - - !Ref AWS::NoValue - DisableApiTermination: !Ref TerminationProtection - IamInstanceProfile: !Ref AiUnlimitedInstanceProfile - Volumes: - - Device: /dev/xvdb - VolumeId: !If - - USENEWPERSISTENTVOLUME - - !Ref AiUnlimitedVolume - - !Ref ExistingPersistentVolumeId - Tags: - - Key: Name - Value: !Join - - '-' - - - !Ref AiUnlimitedName - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - UserData: !Base64 - Fn::Sub: | - #!/bin/bash -xe - yum update -y - yum update -y aws-cfn-bootstrap - /opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource AiUnlimitedServer --configsets ai_unlimited_install --region ${AWS::Region} - /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource AiUnlimitedServer --region ${AWS::Region} - - LoadBalancerAiUnlimitedSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server from LoadBalancer over http, grpc, and ssh - SecurityGroupIngress: - - FromPort: !Ref AiUnlimitedHttpPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - - FromPort: !Ref AiUnlimitedGrpcPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedGrpcPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASCIDRORPREFIXLISTORSECGROUP - - LoadBalancerSchedulerSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server from LoadBalancer over http, grpc, and ssh - SecurityGroupIngress: - - FromPort: !Ref AiUnlimitedSchedulerHttpPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedSchedulerHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - - FromPort: !Ref AiUnlimitedSchedulerGrpcPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedSchedulerGrpcPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASCIDRORPREFIXLISTORSECGROUP - - LoadBalancerJupyterSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server from LoadBalancer over http, grpc, and ssh - SecurityGroupIngress: - - FromPort: !Ref JupyterHttpPort - IpProtocol: tcp - ToPort: !Ref JupyterHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASCIDRORPREFIXLISTORSECGROUP - - LoadBalancer: - Type: AWS::ElasticLoadBalancingV2::LoadBalancer - Properties: - Scheme: !Ref LoadBalancerScheme - Subnets: - - !Ref LoadBalancerSubnet - SecurityGroups: - - !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - - !GetAtt LoadBalancerSchedulerSecurityGroup.GroupId - - !GetAtt LoadBalancerJupyterSecurityGroup.GroupId - Type: network - - AiUnlimitedHTTPListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref AiUnlimitedHTTPTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref AiUnlimitedHttpPort - Protocol: TCP - - JupyterHTTPListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref JupyterHTTPTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref JupyterHttpPort - Protocol: TCP - - AiUnlimitedSchedulerHTTPListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref AiUnlimitedSchedulerHTTPTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref AiUnlimitedSchedulerHttpPort - Protocol: TCP - - AiUnlimitedSchedulerGRPCListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref AiUnlimitedSchedulerGRPCTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref AiUnlimitedSchedulerGrpcPort - Protocol: TCP - - AiUnlimitedGRPCListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref AiUnlimitedGRPCTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref AiUnlimitedGrpcPort - Protocol: TCP - - AiUnlimitedHTTPTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTP - HealthCheckTimeoutSeconds: 15 - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - td-aiu - - ui - - http - Port: !Ref AiUnlimitedHttpPort - Protocol: TCP - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: source_ip - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedHttpPort - VpcId: !Ref Vpc - - JupyterHTTPTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTP - HealthCheckTimeoutSeconds: 15 - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - jupyter - - ui - - http - Port: !Ref JupyterHttpPort - Protocol: TCP - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: source_ip - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref JupyterHttpPort - VpcId: !Ref Vpc - - AiUnlimitedSchedulerHTTPTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTP - HealthCheckTimeoutSeconds: 15 - HealthCheckPath: /healthcheck - Matcher: - HttpCode: "200" - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - aisch - - ui - - http - Port: !Ref AiUnlimitedSchedulerHttpPort - Protocol: TCP - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: source_ip - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedSchedulerHttpPort - VpcId: !Ref Vpc - - AiUnlimitedSchedulerGRPCTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: TCP - HealthCheckTimeoutSeconds: 15 - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - aisch - - api - - grpc - Port: !Ref AiUnlimitedSchedulerGrpcPort - Protocol: TCP - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: source_ip - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedSchedulerGrpcPort - VpcId: !Ref Vpc - - AiUnlimitedGRPCTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: TCP - HealthCheckTimeoutSeconds: 15 - HealthyThresholdCount: 5 - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - td-aiu - - api - - grpc - Port: !Ref AiUnlimitedGrpcPort - Protocol: TCP - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: source_ip - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref AiUnlimitedServer - Port: !Ref AiUnlimitedGrpcPort - VpcId: !Ref Vpc - - AiUnlimitedSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server over http and grpc - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedHttpPort - ToPort: !Ref AiUnlimitedHttpPort - SourceSecurityGroupId: !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedGrpcPort - ToPort: !Ref AiUnlimitedGrpcPort - SourceSecurityGroupId: !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedHttpPort - ToPort: !Ref AiUnlimitedHttpPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedGrpcPort - ToPort: !Ref AiUnlimitedGrpcPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - AiUnlimitedSchedulerSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server over http and grpc - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedSchedulerGrpcPort - ToPort: !Ref AiUnlimitedSchedulerGrpcPort - SourceSecurityGroupId: !GetAtt LoadBalancerSchedulerSecurityGroup.GroupId - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedSchedulerHttpPort - ToPort: !Ref AiUnlimitedSchedulerHttpPort - SourceSecurityGroupId: !GetAtt LoadBalancerSchedulerSecurityGroup.GroupId - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedSchedulerHttpPort - ToPort: !Ref AiUnlimitedSchedulerHttpPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref AiUnlimitedSchedulerGrpcPort - ToPort: !Ref AiUnlimitedSchedulerHttpPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - JupyterSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to jupyter server over http - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: !Ref JupyterHttpPort - ToPort: !Ref JupyterHttpPort - SourceSecurityGroupId: !GetAtt LoadBalancerJupyterSecurityGroup.GroupId - - FromPort: !Ref JupyterHttpPort - IpProtocol: tcp - ToPort: !Ref JupyterHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - - SecurityGroupIngress: - Type: AWS::EC2::SecurityGroupIngress - Properties: - GroupId: !GetAtt AiUnlimitedSecurityGroup.GroupId - FromPort: 22 - IpProtocol: tcp - ToPort: 22 - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASKEYANDCIDRORPREFIXLISTORSECGROUP - - AiUnlimitedRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Principal: - Service: - - ec2.amazonaws.com - Action: - - sts:AssumeRole - Path: / - RoleName: !If - - HASIAMROLENAME - - !Ref IamRoleName - - !Ref AWS::NoValue - PermissionsBoundary: !If - - HASIAMPERMISSIONSBOUNDARY - - !Ref IamPermissionsBoundary - - !Ref AWS::NoValue - Condition: NEEDSROLE - - SessionManagerPolicies: - Type: AWS::IAM::Policy - Properties: - PolicyName: !Join - - '-' - - - ai-unlimited - - session - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - ssm:DescribeAssociation - - ssm:GetDeployablePatchSnapshotForInstance - - ssm:GetDocument - - ssm:DescribeDocument - - ssm:GetManifest - - ssm:ListAssociations - - ssm:ListInstanceAssociations - - ssm:PutInventory - - ssm:PutComplianceItems - - ssm:PutConfigurePackageResult - - ssm:UpdateAssociationStatus - - ssm:UpdateInstanceAssociationStatus - - ssm:UpdateInstanceInformation - Resource: '*' - - Effect: Allow - Action: - - ssmmessages:CreateControlChannel - - ssmmessages:CreateDataChannel - - ssmmessages:OpenControlChannel - - ssmmessages:OpenDataChannel - Resource: '*' - - Effect: Allow - Action: - - ec2messages:AcknowledgeMessage - - ec2messages:DeleteMessage - - ec2messages:FailMessage - - ec2messages:GetEndpoint - - ec2messages:GetMessages - - ec2messages:SendReply - Resource: '*' - Roles: - - !Ref AiUnlimitedRole - Condition: NEEDSROLEANDSESSIONMANAGER - - AiUnlimitedRolePolicies: - Type: AWS::IAM::Policy - Properties: - PolicyName: !Join - - '-' - - - ai-unlimited - - deploy - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - iam:PassRole - - iam:AddRoleToInstanceProfile - - iam:CreateInstanceProfile - - iam:CreateRole - - iam:DeleteInstanceProfile - - iam:DeleteRole - - iam:DeleteRolePolicy - - iam:GetInstanceProfile - - iam:GetRole - - iam:GetRolePolicy - - iam:ListAttachedRolePolicies - - iam:ListInstanceProfilesForRole - - iam:ListRolePolicies - - iam:PutRolePolicy - - iam:RemoveRoleFromInstanceProfile - - iam:TagRole - - iam:TagInstanceProfile - - ec2:TerminateInstances - - ec2:RunInstances - - ec2:RevokeSecurityGroupEgress - - ec2:ModifyInstanceAttribute - - ec2:ImportKeyPair - - ec2:DescribeVpcs - - ec2:DescribeVolumes - - ec2:DescribeTags - - ec2:DescribeSubnets - - ec2:DescribeSecurityGroups - - ec2:DescribePlacementGroups - - ec2:DescribeNetworkInterfaces - - ec2:DescribeLaunchTemplates - - ec2:DescribeLaunchTemplateVersions - - ec2:DescribeKeyPairs - - ec2:DescribeInstanceTypes - - ec2:DescribeInstanceTypeOfferings - - ec2:DescribeInstances - - ec2:DescribeInstanceAttribute - - ec2:DescribeImages - - ec2:DescribeAccountAttributes - - ec2:DescribeAvailabilityZones - - ec2:DescribeManagedPrefixLists - - ec2:DescribeVpcAttribute - - ec2:DeleteTags - - ec2:DeleteSecurityGroup - - ec2:DeletePlacementGroup - - ec2:DeleteLaunchTemplate - - ec2:DeleteLaunchTemplateVersions - - ec2:DeleteKeyPair - - ec2:CreateTags - - ec2:CreateSecurityGroup - - ec2:CreatePlacementGroup - - ec2:CreateLaunchTemplateVersion - - ec2:CreateLaunchTemplate - - ec2:AuthorizeSecurityGroupIngress - - ec2:AuthorizeSecurityGroupEgress - - secretsmanager:CreateSecret - - secretsmanager:DeleteSecret - - secretsmanager:DescribeSecret - - secretsmanager:GetResourcePolicy - - secretsmanager:GetSecretValue - - secretsmanager:PutSecretValue - - secretsmanager:TagResource - - s3:CreateBucket - - s3:DeleteBucket - - s3:PutObject - - s3:GetObject - - s3:DeleteObject - - cloudformation:CreateStack - - cloudformation:DeleteStack - - cloudformation:DescribeStacks - - cloudformation:ListStacks - - cloudformation:DescribeStackEvents - - ssm:PutParameter - - ssm:GetParameter - - ssm:DeleteParameter - - ec2:RevokeSecurityGroupIngress - - ec2:CreateInternetGateway - - ec2:CreateVpc - - ec2:DescribeInternetGateways - - ec2:DeleteVpc - - ec2:ModifyVpcAttribute - - ec2:DeleteInternetGateway - - ec2:CreateSubnet - - ec2:CreateRouteTable - - ec2:DescribeRouteTables - - ec2:DeleteSubnet - - ec2:DeleteRouteTable - - ec2:AttachInternetGateway - - ec2:ModifySubnetAttribute - - ec2:DetachInternetGateway - - ec2:AssociateRouteTable - - ec2:CreateRoute - - ec2:DisassociateRouteTable - - ec2:DeleteRoute - Resource: '*' - Roles: - - !Ref AiUnlimitedRole - Condition: NEEDSROLE - - AiUnlimitedInstanceProfile: - Type: AWS::IAM::InstanceProfile - Properties: - Path: / - Roles: !If - - NEEDSROLE - - - !Ref AiUnlimitedRole - - - !Ref IamRoleName - -Outputs: - PublicIP: - Description: EC2 public IP - Value: !GetAtt AiUnlimitedServer.PublicIp - Condition: HASPUBLICIP - - PrivateIP: - Description: EC2 private IP - Value: !GetAtt AiUnlimitedServer.PrivateIp - - AiUnlimitedUiAccess: - Description: Loadbalancer access endpoint for AI Unlimited UI Access - Value: !Sub http://${ LoadBalancer.DNSName }:${ AiUnlimitedHttpPort } - - AiUnlimitedApiAccess: - Description: Loadbalancer access endpoint for AI Unlimited API Access - Value: !Sub ${ LoadBalancer.DNSName }:${ AiUnlimitedGrpcPort } - - InstanceSecurityGroups: - Description: AI Unlimited Security Groups - Value: !Join - - ', ' - - - !GetAtt AiUnlimitedSecurityGroup.GroupId - - !GetAtt AiUnlimitedSchedulerSecurityGroup.GroupId - - !GetAtt JupyterSecurityGroup.GroupId - - LoadBalancerSecurityGroups: - Description: AI Unlimited Load Balancer Security Group - Value: !Join - - ', ' - - - !GetAtt LoadBalancerAiUnlimitedSecurityGroup.GroupId - - !GetAtt LoadBalancerSchedulerSecurityGroup.GroupId - - !GetAtt LoadBalancerJupyterSecurityGroup.GroupId - - PublicSSHConeection: - Description: AI Unlimited ssh connnection string - Value: !Sub ssh ec2-user@${ AiUnlimitedServer.PublicIp } - Condition: HASKEYANDPUBLIC - - PrivateSSHConeection: - Description: AI Unlimited ssh connnection string - Value: !Sub ssh ec2-user@${ AiUnlimitedServer.PrivateIp } - Condition: HASKEY - - PersistentVolumeId: - Description: Id of the new persistent volume created for AI Unlimited - Value: !Ref AiUnlimitedVolume - Condition: USENEWPERSISTENTVOLUME - - JupyterUIAccess: - Description: Loadbalancer access endpoint for API Access - Value: !Sub http://${ LoadBalancer.DNSName }:${ JupyterHttpPort }?token=${ JupyterToken } - - JupyterInternalAccessToAiUnlimited: - Description: AI Unlimited endpoint for local Jupyter access - Value: !Sub ai-unlimited.service:${ AiUnlimitedGrpcPort } diff --git a/deployments/aws/templates/all-in-one/all-in-one-without-lb.yaml b/deployments/aws/templates/all-in-one/all-in-one-without-lb.yaml deleted file mode 100644 index bfd0ea9..0000000 --- a/deployments/aws/templates/all-in-one/all-in-one-without-lb.yaml +++ /dev/null @@ -1,1024 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" - -Description: 'AWS CloudFormation Template with AI Unlimited with Jupyter: AI Unlimited is a instance based service for deploying and suspending ai-unlimited clusters, and managing project lifecycles. This template also includes a Jupyter Lab service running on the same host, suitable for demonstration environments. Note: You will be billed for the AWS resources used if you create a stack from this template.' - -Metadata: - AWS::CloudFormation::Interface: - ParameterGroups: - - Label: - default: AI Unlimited - Parameters: - - AiUnlimitedName - - InstanceType - - RootVolumeSize - - TerminationProtection - - IamRole - - IamRoleName - - IamPermissionsBoundary - - Label: - default: AI Unlimited connection - Parameters: - - AvailabilityZone - - Private - - Session - - Vpc - - Subnet - - KeyName - - AccessCIDR - - PrefixList - - SecurityGroup - - AiUnlimitedHttpPort - - AiUnlimitedGrpcPort - - AiUnlimitedVersion - - AiUnlimitedSchedulerVersion - - AiUnlimitedSchedulerHttpPort - - AiUnlimitedSchedulerGrpcPort - - Label: - default: Persistent volume - Parameters: - - UsePersistentVolume - - PersistentVolumeSize - - ExistingPersistentVolumeId - - PersistentVolumeDeletionPolicy - - Label: - default: Jupyter connection - Parameters: - - JupyterToken - - JupyterHttpPort - - JupyterVersion - -Parameters: - LatestAmiId: - Type: AWS::SSM::Parameter::Value - Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 - - AiUnlimitedName: - Description: The AI Unlimited instance name - Type: String - Default: ai-unlimited - AllowedPattern: ^[a-zA-Z][a-zA-Z0-9-]* - ConstraintDescription: must begin with a letter and contain only alphanumeric characters. - MaxLength: "20" - MinLength: "1" - - JupyterToken: - Description: The token or password equivalent used to access Jupyter. - Type: String - NoEcho: true - AllowedPattern: ^[a-zA-Z][a-zA-Z0-9-]* - ConstraintDescription: must begin with a letter and contain only alphanumeric characters. - MaxLength: "64" - - Private: - Description: Will AI Unlimited be deployed in a private network without public IPs? - Type: String - AllowedValues: - - true - - false - Default: false - - Session: - Description: Should AI Unlimited be accessible via AWS Session Manager? - Type: String - AllowedValues: - - true - - false - Default: false - - Vpc: - Description: Network to deploy the AI Unlimited to. - Type: AWS::EC2::VPC::Id - ConstraintDescription: must be the name of an existing vpc. - - Subnet: - Description: Subnetwork to deploy the AI Unlimited to. - Type: AWS::EC2::Subnet::Id - ConstraintDescription: must be the name of a existing subnet. - - AvailabilityZone: - Description: "Availability zone to deploy the AI Unlimited to.\nThis must match the subnet, the zone of any pre existing volumes if used, \nand the instance type must be available in the selected zone.\n" - Type: AWS::EC2::AvailabilityZone::Name - ConstraintDescription: must be the name of a existing subnet. - - AiUnlimitedHttpPort: - Description: port to access the AI Unlimited UI. - Type: Number - Default: 3000 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedGrpcPort: - Description: port to access the AI Unlimited API. - Type: Number - Default: 3282 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedSchedulerHttpPort: - Description: port to access the AI Unlimited Scheduler API. - Type: Number - Default: 50061 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedSchedulerGrpcPort: - Description: port to access the AI Unlimited Scheduler API. - Type: Number - Default: 50051 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - AiUnlimitedVersion: - Description: Which version of AI Unlimited to deploy, uses container version tags, defaults to "latest" - Type: String - Default: v0.2.23 - - AiUnlimitedSchedulerVersion: - Description: Which version of AI Unlimited Scheduler to deploy, uses container version tags, defaults to "latest" - Type: String - Default: latest - - JupyterHttpPort: - Description: port to access the Jupyter UI. - Type: Number - Default: 8888 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - JupyterVersion: - Description: Which version of Jupyter to deploy, uses container version tags, defaults to "latest" - Type: String - Default: latest - - RootVolumeSize: - Description: size of the root disk to the AI Unlimited server. - Type: Number - Default: 20 - ConstraintDescription: Size in GB, between 10 and 1000. - MinValue: 8 - MaxValue: 1000 - - UsePersistentVolume: - Description: Should we use a new or existing volume for persistent data on the AI Unlimited server. - Type: String - AllowedValues: - - New - - Existing - Default: New - ConstraintDescription: Specify if you are using a a new persistent volume, an existing one, or none. - - PersistentVolumeSize: - Description: size of the optional persistent disk to the AI Unlimited server. - Type: Number - Default: 20 - ConstraintDescription: Size in GB, between 10 and 1000. - MinValue: 8 - MaxValue: 1000 - - ExistingPersistentVolumeId: - Description: Id of the existing persistent volume to attach. Must be in the same availability zone as the AI Unlimited instance. - Type: String - Default: None - - PersistentVolumeDeletionPolicy: - Description: Behavior for the Persistent Volume when deleting the cloudformations deployment. - Type: String - AllowedValues: - - Delete - - Retain - - RetainExceptOnCreate - - Snapshot - Default: Retain - - TerminationProtection: - Description: Enable instance termination protection. - Type: String - AllowedValues: - - true - - false - Default: false - - InstanceType: - Description: AI Unlimited EC2 instance type - Type: String - AllowedValues: - - t3.small - - t3.medium - - t3.large - - m4.large - - m4.xlarge - - m4.2xlarge - - m4.4xlarge - - m4.10xlarge - - c4.large - - c4.xlarge - - c4.2xlarge - - c4.4xlarge - - c4.8xlarge - - r3.large - - r3.xlarge - - r3.2xlarge - - r3.4xlarge - - r3.8xlarge - - i2.xlarge - - i2.2xlarge - - i2.4xlarge - - i2.8xlarge - Default: t3.small - ConstraintDescription: must be a valid EC2 instance type. - - KeyName: - Description: Name of an existing EC2 KeyPair to enable SSH access to the AI Unlimited instance, leave empty if no ssh keys should be included - Type: String - - IamRole: - Description: | - Create a new IAM role for AI Unlimited or use an exiting one. - Requires CAPABILITY_IAM if creating a new IAM Role - Type: String - AllowedValues: - - New - - Existing - Default: New - - IamRoleName: - Description: | - Name of an existing IAM Role to assign to AI Unlimited, - or the name to give to the newly created role. - Leave blank to use an autogenerated name. - Requires CAPABILITY_NAMED_IAM if naming a new IAM Role. - Type: String - - IamPermissionsBoundary: - Description: | - Optional: Arn of a permissions boundary to pass to the IAM Role assigned to AI Unlimited. - Type: String - - AccessCIDR: - Description: The IP address range that can be used to communicate with the AI Unlimited instance. - Type: String - AllowedPattern: ((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2}))|^$ - ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. - - PrefixList: - Description: The PrefixList that can be used to communicate with the AI Unlimited instance. - Type: String - ConstraintDescription: must be a valid prefixlist - - SecurityGroup: - Description: The SecurityGroup that can be used to communicate with the AI Unlimited instance. - Type: String - ConstraintDescription: must be a valid securityGroup ID - -Rules: - subnetsInVpc: - Assertions: - - Assert: - Fn::EachMemberEquals: - - Fn::ValueOfAll: - - AWS::EC2::Subnet::Id - - VpcId - - !Ref Vpc - AssertDescription: The subnet you selected is not in the VPC - - instanceTypeInZone: - Assertions: - - Assert: - Fn::EachMemberEquals: - - Fn::ValueOfAll: - - AWS::EC2::Subnet::Id - - VpcId - - !Ref Vpc - AssertDescription: The subnet you selected is not in the VPC - -Conditions: - NEEDSROLE: !Equals - - !Ref IamRole - - New - - HASPUBLICIP: !Not - - !Equals - - !Ref Private - - "true" - - HASKEY: !Not - - !Equals - - !Ref KeyName - - "" - - HASCIDR: !Not - - !Equals - - !Ref AccessCIDR - - "" - - HASPREFIXLIST: !Not - - !Equals - - !Ref PrefixList - - "" - - HASSECURITYGROUP: !Not - - !Equals - - !Ref SecurityGroup - - "" - - HASCIDRORPREFIXLIST: !Or - - !Condition HASCIDR - - !Condition HASPREFIXLIST - - HASCIDRORPREFIXLISTORSECGROUP: !Or - - !Condition HASCIDR - - !Condition HASPREFIXLIST - - !Condition HASSECURITYGROUP - - USESESSIONMANAGER: !Equals - - !Ref Session - - "true" - - NEEDSROLEANDSESSIONMANAGER: !And - - !Condition NEEDSROLE - - !Condition USESESSIONMANAGER - - HASKEYANDPUBLIC: !And - - !Condition HASKEY - - !Condition HASPUBLICIP - - HASKEYANDCIDRORPREFIXLISTORSECGROUP: !And - - !Condition HASKEY - - !Condition HASCIDRORPREFIXLISTORSECGROUP - - USENEWPERSISTENTVOLUME: !Equals - - !Ref UsePersistentVolume - - New - - HASIAMPERMISSIONSBOUNDARY: !Not - - !Equals - - !Ref IamPermissionsBoundary - - "" - - HASIAMROLENAME: !Not - - !Equals - - !Ref IamRoleName - - "" - -Resources: - AiUnlimitedVolume: - DeletionPolicy: !Ref PersistentVolumeDeletionPolicy - Type: AWS::EC2::Volume - Properties: - AvailabilityZone: !Ref AvailabilityZone - Size: !Ref PersistentVolumeSize - Encrypted: true - Tags: - - Key: Name - Value: !Join - - '-' - - - !Ref AiUnlimitedName - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - Key: Usage - Value: persistent storage - Condition: USENEWPERSISTENTVOLUME - - AiUnlimitedServer: - CreationPolicy: - ResourceSignal: - Timeout: PT15M - Type: AWS::EC2::Instance - Metadata: - AWS::CloudFormation::Init: - configSets: - ai_unlimited_install: - - prepare_directory - - !If - - USENEWPERSISTENTVOLUME - - prepare_new_storage - - !Ref AWS::NoValue - - bind_storage - - mount_storage - - install_docker - - configure_ai_unlimited_service - - configure_jupyter_service - - start_ai_unlimited_service - - configure_ai_unlimited_scheduler_service - - start_ai_unlimited_scheduler_service - - start_jupyter_service - prepare_directory: - commands: - mkdir: - command: !Sub | - #!/bin/bash -xe - mkdir -p /etc/td - prepare_new_storage: - commands: - mkfs: - command: !Sub | - #!/bin/bash -xe - /usr/sbin/mkfs -t ext4 /dev/nvme1n1 - bind_storage: - commands: - fstab: - command: !Sub | - #!/bin/bash -xe - /usr/bin/echo "/dev/nvme1n1 /etc/td ext4 defaults 0 2" >> /etc/fstab - mount_storage: - commands: - mount: - command: !Sub | - #!/bin/bash -xe - /usr/bin/mount -a - install_docker: - files: - /usr/lib/systemd/system/docker-install.service: - content: !Sub | - [Unit] - Description=Install docker - - [Service] - Type=oneshot - ExecStart=/bin/bash -c "while ! dnf update; do sleep 2; done && while ! dnf install -y docker; do sleep 2; done" - RemainAfterExit=yes - - [Install] - WantedBy=multi-user.target - commands: - verify_docker: - command: !Sub | - #!/bin/bash -xe - systemctl start docker-install - systemctl start docker - systemctl enable docker - services: - systemd: - docker: - enabled: "true" - ensureRunning: "true" - configure_ai_unlimited_service: - files: - /usr/lib/systemd/system/ai-unlimited.service: - content: !Sub | - [Unit] - Description=AI Unlimited - After=docker.service - Requires=docker.service - StartLimitInterval=200 - StartLimitBurst=10 - - [Service] - TimeoutStartSec=0 - Restart=always - RestartSec=2 - ExecStartPre=-/bin/bash -c '/usr/bin/docker network create -d bridge ai_unlimited || true' - ExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited - ExecStartPre=-/usr/bin/docker exec %n stop || true - ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-workspaces:${ AiUnlimitedVersion } - ExecStart=/usr/bin/docker run \ - -e accept_license=Y \ - -e PLATFORM=aws \ - -v /etc/td/ai-unlimited:/etc/td \ - -p ${ AiUnlimitedHttpPort }:3000 \ - -p ${ AiUnlimitedGrpcPort }:3282 \ - --network ai_unlimited \ - --rm --name %n teradata/ai-unlimited-workspaces:${ AiUnlimitedVersion } workspaces serve -v - [Install] - WantedBy=multi-user.target - group: root - mode: "000400" - owner: root - start_ai_unlimited_service: - services: - systemd: - ai-unlimited: - enabled: "true" - ensureRunning: "true" - configure_ai_unlimited_scheduler_service: - files: - /usr/lib/systemd/system/ai-unlimited-scheduler.service: - content: !Sub | - [Unit] - Description=AI Unlimited Scheduler - After=ai-unlimited.service - Requires=ai-unlimited.service - StartLimitInterval=200 - StartLimitBurst=10 - - [Service] - TimeoutStartSec=0 - Restart=always - RestartSec=2 - ExecStartPre=-/usr/bin/docker exec %n stop || true - ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-scheduler:latest - ExecStart=/usr/bin/docker run \ - --network ai_unlimited \ - -p ${ AiUnlimitedSchedulerGrpcPort }:50051 \ - -p ${ AiUnlimitedSchedulerHttpPort }:50061 \ - -v /etc/td/ai-unlimited:/etc/td \ - -e TD_WSSCHED_LOG_PATH=/etc/td/workspaces/scheduler_logs \ - -e TD_WSSCHED_TASK_LOG_PATH=/etc/td/workspaces/scheduler_logs/projects \ - -e TD_WSSCHED_POL_INTERVAL=2 \ - -e TD_WS_CONTAINER_NAME=ai-unlimited.service \ - --rm --name %n teradata/ai-unlimited-scheduler:latest workspace-event-scheduler serve - [Install] - WantedBy=multi-user.target - group: root - mode: "000400" - owner: root - start_ai_unlimited_scheduler_service: - services: - systemd: - ai-unlimited-scheduler: - enabled: "true" - ensureRunning: "true" - configure_jupyter_service: - files: - /usr/lib/systemd/system/jupyter.service: - content: !Sub | - [Unit] - Description=jupyter - After=ai-unlimited.service - Requires=ai-unlimited.service - StartLimitInterval=200 - StartLimitBurst=10 - - [Service] - TimeoutStartSec=0 - Restart=always - RestartSec=2 - ExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{userdata,ipython} - ExecStartPre=-/usr/bin/docker exec %n stop || true - ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-jupyter:${ JupyterVersion } - ExecStart=/usr/bin/docker run \ - -e accept_license=Y \ - -e JUPYTER_TOKEN=${ JupyterToken } \ - -v /etc/td/jupyter/userdata:/home/jovyan/JupyterLabRoot/userdata \ - -v /etc/td/jupyter/ipython:/home/jovyan/.ipython \ - -p ${ JupyterHttpPort }:8888 \ - --network ai_unlimited \ - --rm --name %n teradata/ai-unlimited-jupyter:${ JupyterVersion } - - [Install] - WantedBy=multi-user.target - group: root - mode: "000400" - owner: root - start_jupyter_service: - services: - systemd: - jupyter: - enabled: "true" - ensureRunning: "true" - Properties: - PropagateTagsToVolumeOnCreation: true - BlockDeviceMappings: - - DeviceName: /dev/xvda - Ebs: - VolumeSize: !Ref RootVolumeSize - Encrypted: true - NetworkInterfaces: - - DeviceIndex: 0 - SubnetId: !Ref Subnet - GroupSet: - - !GetAtt AiUnlimitedSecurityGroup.GroupId - - !GetAtt AiUnlimitedSchedulerSecurityGroup.GroupId - - !GetAtt JupyterSecurityGroup.GroupId - AssociatePublicIpAddress: !If - - HASPUBLICIP - - true - - !Ref AWS::NoValue - ImageId: !Ref LatestAmiId - InstanceType: !Ref InstanceType - KeyName: !If - - HASKEY - - !Ref KeyName - - !Ref AWS::NoValue - DisableApiTermination: !Ref TerminationProtection - IamInstanceProfile: !Ref AiUnlimitedInstanceProfile - Volumes: - - Device: /dev/xvdb - VolumeId: !If - - USENEWPERSISTENTVOLUME - - !Ref AiUnlimitedVolume - - !Ref ExistingPersistentVolumeId - Tags: - - Key: Name - Value: !Join - - '-' - - - !Ref AiUnlimitedName - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - UserData: !Base64 - Fn::Sub: | - #!/bin/bash -xe - yum update -y - yum update -y aws-cfn-bootstrap - /opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource AiUnlimitedServer --configsets ai_unlimited_install --region ${AWS::Region} - /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource AiUnlimitedServer --region ${AWS::Region} - - AiUnlimitedSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server over http and grpc - SecurityGroupIngress: - - FromPort: !Ref AiUnlimitedHttpPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - - FromPort: !Ref AiUnlimitedGrpcPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedGrpcPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - - AiUnlimitedSchedulerSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to AI Unlimited server over http and grpc - SecurityGroupIngress: - - FromPort: !Ref AiUnlimitedSchedulerHttpPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedSchedulerHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - - FromPort: !Ref AiUnlimitedSchedulerGrpcPort - IpProtocol: tcp - ToPort: !Ref AiUnlimitedSchedulerGrpcPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - - JupyterSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to jupyter server over http - SecurityGroupIngress: - - FromPort: !Ref JupyterHttpPort - IpProtocol: tcp - ToPort: !Ref JupyterHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASCIDRORPREFIXLISTORSECGROUP - - SecurityGroupIngress: - Type: AWS::EC2::SecurityGroupIngress - Properties: - GroupId: !GetAtt AiUnlimitedSecurityGroup.GroupId - FromPort: 22 - IpProtocol: tcp - ToPort: 22 - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASKEYANDCIDRORPREFIXLISTORSECGROUP - - AiUnlimitedRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Principal: - Service: - - ec2.amazonaws.com - Action: - - sts:AssumeRole - Path: / - RoleName: !If - - HASIAMROLENAME - - !Ref IamRoleName - - !Ref AWS::NoValue - PermissionsBoundary: !If - - HASIAMPERMISSIONSBOUNDARY - - !Ref IamPermissionsBoundary - - !Ref AWS::NoValue - Condition: NEEDSROLE - - SessionManagerPolicies: - Type: AWS::IAM::Policy - Properties: - PolicyName: !Join - - '-' - - - ai-unlimited - - session - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - ssm:DescribeAssociation - - ssm:GetDeployablePatchSnapshotForInstance - - ssm:GetDocument - - ssm:DescribeDocument - - ssm:GetManifest - - ssm:ListAssociations - - ssm:ListInstanceAssociations - - ssm:PutInventory - - ssm:PutComplianceItems - - ssm:PutConfigurePackageResult - - ssm:UpdateAssociationStatus - - ssm:UpdateInstanceAssociationStatus - - ssm:UpdateInstanceInformation - Resource: '*' - - Effect: Allow - Action: - - ssmmessages:CreateControlChannel - - ssmmessages:CreateDataChannel - - ssmmessages:OpenControlChannel - - ssmmessages:OpenDataChannel - Resource: '*' - - Effect: Allow - Action: - - ec2messages:AcknowledgeMessage - - ec2messages:DeleteMessage - - ec2messages:FailMessage - - ec2messages:GetEndpoint - - ec2messages:GetMessages - - ec2messages:SendReply - Resource: '*' - Roles: - - !Ref AiUnlimitedRole - Condition: NEEDSROLEANDSESSIONMANAGER - - AiUnlimitedRolePolicies: - Type: AWS::IAM::Policy - Properties: - PolicyName: !Join - - '-' - - - ai-unlimited - - deploy - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - iam:PassRole - - iam:AddRoleToInstanceProfile - - iam:CreateInstanceProfile - - iam:CreateRole - - iam:DeleteInstanceProfile - - iam:DeleteRole - - iam:DeleteRolePolicy - - iam:GetInstanceProfile - - iam:GetRole - - iam:GetRolePolicy - - iam:ListAttachedRolePolicies - - iam:ListInstanceProfilesForRole - - iam:ListRolePolicies - - iam:PutRolePolicy - - iam:RemoveRoleFromInstanceProfile - - iam:TagRole - - iam:TagInstanceProfile - - ec2:TerminateInstances - - ec2:RunInstances - - ec2:RevokeSecurityGroupEgress - - ec2:ModifyInstanceAttribute - - ec2:ImportKeyPair - - ec2:DescribeVpcs - - ec2:DescribeVolumes - - ec2:DescribeTags - - ec2:DescribeSubnets - - ec2:DescribeSecurityGroups - - ec2:DescribePlacementGroups - - ec2:DescribeNetworkInterfaces - - ec2:DescribeLaunchTemplates - - ec2:DescribeLaunchTemplateVersions - - ec2:DescribeKeyPairs - - ec2:DescribeInstanceTypes - - ec2:DescribeInstanceTypeOfferings - - ec2:DescribeInstances - - ec2:DescribeInstanceAttribute - - ec2:DescribeImages - - ec2:DescribeAccountAttributes - - ec2:DescribeAvailabilityZones - - ec2:DescribeManagedPrefixLists - - ec2:DescribeVpcAttribute - - ec2:DeleteTags - - ec2:DeleteSecurityGroup - - ec2:DeletePlacementGroup - - ec2:DeleteLaunchTemplate - - ec2:DeleteLaunchTemplateVersions - - ec2:DeleteKeyPair - - ec2:CreateTags - - ec2:CreateSecurityGroup - - ec2:CreatePlacementGroup - - ec2:CreateLaunchTemplateVersion - - ec2:CreateLaunchTemplate - - ec2:AuthorizeSecurityGroupIngress - - ec2:AuthorizeSecurityGroupEgress - - secretsmanager:CreateSecret - - secretsmanager:DeleteSecret - - secretsmanager:DescribeSecret - - secretsmanager:GetResourcePolicy - - secretsmanager:GetSecretValue - - secretsmanager:PutSecretValue - - secretsmanager:TagResource - - s3:CreateBucket - - s3:DeleteBucket - - s3:PutObject - - s3:GetObject - - s3:DeleteObject - - cloudformation:CreateStack - - cloudformation:DeleteStack - - cloudformation:DescribeStacks - - cloudformation:ListStacks - - cloudformation:DescribeStackEvents - - ssm:PutParameter - - ssm:GetParameter - - ssm:DeleteParameter - - ec2:RevokeSecurityGroupIngress - - ec2:CreateInternetGateway - - ec2:CreateVpc - - ec2:DescribeInternetGateways - - ec2:DeleteVpc - - ec2:ModifyVpcAttribute - - ec2:DeleteInternetGateway - - ec2:CreateSubnet - - ec2:CreateRouteTable - - ec2:DescribeRouteTables - - ec2:DeleteSubnet - - ec2:DeleteRouteTable - - ec2:AttachInternetGateway - - ec2:ModifySubnetAttribute - - ec2:DetachInternetGateway - - ec2:AssociateRouteTable - - ec2:CreateRoute - - ec2:DisassociateRouteTable - - ec2:DeleteRoute - Resource: '*' - Roles: - - !Ref AiUnlimitedRole - Condition: NEEDSROLE - - AiUnlimitedInstanceProfile: - Type: AWS::IAM::InstanceProfile - Properties: - Path: / - Roles: !If - - NEEDSROLE - - - !Ref AiUnlimitedRole - - - !Ref IamRoleName - -Outputs: - PublicIP: - Description: EC2 public IP - Value: !GetAtt AiUnlimitedServer.PublicIp - Condition: HASPUBLICIP - - PrivateIP: - Description: EC2 private IP - Value: !GetAtt AiUnlimitedServer.PrivateIp - - AiUnlimitedPublicUIAccess: - Description: Teradata AI Unlimited public UI Access - Value: !Sub http://${AiUnlimitedServer.PublicDnsName}:${ AiUnlimitedHttpPort } - Condition: HASPUBLICIP - - AiUnlimitedPrivateUIAccess: - Description: Teradata AI Unlimited private UI Access - Value: !Sub http://${AiUnlimitedServer.PrivateDnsName}:${ AiUnlimitedHttpPort } - - AiUnlimitedPublicAPIAccess: - Description: Teradata AI Unlimited public API Access - Value: !Sub http://${AiUnlimitedServer.PublicDnsName}:${ AiUnlimitedGrpcPort } - Condition: HASPUBLICIP - - AiUnlimitedPrivateAPIAccess: - Description: Teradata AI Unlimited private API Access - Value: !Sub http://${AiUnlimitedServer.PrivateDnsName}:${ AiUnlimitedGrpcPort } - - InstanceSecurityGroups: - Description: AI Unlimited Security Group - Value: !Join - - ', ' - - - !GetAtt AiUnlimitedSecurityGroup.GroupId - - !GetAtt AiUnlimitedSchedulerSecurityGroup.GroupId - - !GetAtt JupyterSecurityGroup.GroupId - - PublicSSHConeection: - Description: AI Unlimited ssh connnection string - Value: !Sub ssh ec2-user@${ AiUnlimitedServer.PublicIp } - Condition: HASKEYANDPUBLIC - - PrivateSSHConeection: - Description: AI Unlimited ssh connnection string - Value: !Sub ssh ec2-user@${ AiUnlimitedServer.PrivateIp } - Condition: HASKEY - - PersistentVolumeId: - Description: Id of the new persistent volume created for AI Unlimited - Value: !Ref AiUnlimitedVolume - Condition: USENEWPERSISTENTVOLUME - - JupyterPublicHttpAccess: - Description: Teradata AI Unlimited Server - Value: !Sub http://${AiUnlimitedServer.PublicDnsName}:${ JupyterHttpPort }?token=${ JupyterToken } - Condition: HASPUBLICIP - - JupyterPrivateHttpAccess: - Description: Teradata jupyter Server - Value: !Sub http://${AiUnlimitedServer.PrivateDnsName}:${ JupyterHttpPort }?token=${ JupyterToken } - - JupyterInternalAccessToAiUnlimited: - Description: AI Unlimited endpoint for local Jupyter access - Value: !Sub ai-unlimited.service:${ AiUnlimitedGrpcPort } diff --git a/deployments/aws/templates/all-in-one/readme.txt b/deployments/aws/templates/all-in-one/readme.txt deleted file mode 100644 index 5d795a4..0000000 --- a/deployments/aws/templates/all-in-one/readme.txt +++ /dev/null @@ -1,7 +0,0 @@ -# Disclaimer - -**IMPORTANT NOTICE** - -The all-in-one CloudFormation templates provided in this directory are for informational purposes only. These templates are currently not supported by Teradata Corporation. - -**Use of these templates is at your own risk. Teradata Corporation assumes no responsibility or liability for any damages or issues that may arise from using these templates.** \ No newline at end of file diff --git a/deployments/aws/templates/jupyter/jupyter-with-alb.yaml b/deployments/aws/templates/jupyter/jupyter-with-alb.yaml deleted file mode 100644 index 200cf11..0000000 --- a/deployments/aws/templates/jupyter/jupyter-with-alb.yaml +++ /dev/null @@ -1,815 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" - -Description: 'AWS CloudFormation Template jupyter: a jupyter instance configured with the ai-unlimited kernel. Note: You will be billed for the AWS resources used if you create a stack from this template.' - -Metadata: - AWS::CloudFormation::Interface: - ParameterGroups: - - Label: - default: Jupyter - Parameters: - - JupyterName - - InstanceType - - RootVolumeSize - - TerminationProtection - - JupyterToken - - IamRole - - IamRoleName - - IamPermissionsBoundary - - Label: - default: Jupyter connection - Parameters: - - AvailabilityZone - - LoadBalancerScheme - - LoadBalancerSubnetOne - - LoadBalancerSubnetTwo - - Private - - Session - - Vpc - - Subnet - - KeyName - - AccessCIDR - - PrefixList - - SecurityGroup - - JupyterToken - - JupyterHttpPort - - JupyterVersion - - Label: - default: Persistent volume - Parameters: - - UsePersistentVolume - - PersistentVolumeSize - - ExistingPersistentVolumeId - - PersistentVolumeDeletionPolicy - -Parameters: - LatestAmiId: - Type: AWS::SSM::Parameter::Value - Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 - - JupyterName: - Description: The jupyter service instance name - Type: String - Default: jupyter - AllowedPattern: ^[a-zA-Z][a-zA-Z0-9-]* - ConstraintDescription: must begin with a letter and contain only alphanumeric characters. - MaxLength: "20" - MinLength: "1" - - JupyterToken: - Description: The token or password equivalent used to access Jupyter. - Type: String - NoEcho: true - AllowedPattern: ^[a-zA-Z][a-zA-Z0-9-]* - ConstraintDescription: must begin with a letter and contain only alphanumeric characters. - MaxLength: "64" - - Private: - Description: Will Jupyter be deployed in a private network without public IPs? - Type: String - AllowedValues: - - true - - false - Default: false - - LoadBalancerScheme: - Description: "If using a LoadBalancer, will it be internal or internet-facing? \nThe DNS name of an Internet-facing load balancer is publicly resolvable to the public IP addresses of the nodes.\nTherefore, Internet-facing load balancers can route requests from clients over the internet. The nodes of an \ninternal load balancer have only private IP addresses. The DNS name of an internal load balancer is publicly\nresolvable to the private IP addresses of the nodes. Therefore, internal load balancers can route requests only\nfrom clients with access to the VPC for the load balancer.\n" - Type: String - AllowedValues: - - internal - - internet-facing - Default: internet-facing - - Session: - Description: Should Jupyter be accessible via AWS Session Manager? - Type: String - AllowedValues: - - true - - false - Default: false - - Vpc: - Description: Network to deploy the jupyter service to. - Type: AWS::EC2::VPC::Id - ConstraintDescription: must be the name of an existing vpc. - - Subnet: - Description: Subnetwork to deploy the Jupyter service to. - Type: AWS::EC2::Subnet::Id - ConstraintDescription: must be the name of a existing subnet. - - AvailabilityZone: - Description: "Availability zone to deploy the Jupyter service to.\nThis must match the subnet, the zone of any pre existing volumes if used, \nand the instance type must be available in the selected zone.\n" - Type: AWS::EC2::AvailabilityZone::Name - ConstraintDescription: must be the name of a existing subnet. - - LoadBalancerSubnetOne: - Description: First subnetwork to deploy the Application Load Balancer to. - Type: AWS::EC2::Subnet::Id - - LoadBalancerSubnetTwo: - Description: Second subnetwork to deploy the Application Load Balancer to. - Type: AWS::EC2::Subnet::Id - - HostedZoneId: - Description: Zone ID of an existing Route 53 zone to add an entry for the Application Load Balancer to. - Type: AWS::Route53::HostedZone::Id - - DnsName: - Description: | - Name for Load Balancer DNS Entry, must fit as subdomain in the provides Route 53 Hosted Zone ID. - Example: jupyter.yourhostedzonedomainname.com - Type: String - - JupyterHttpPort: - Description: port to access the jupyter service ui. - Type: Number - Default: 8888 - ConstraintDescription: must be a valid ununsed port between 0 and 65535. - MinValue: 0 - MaxValue: 65535 - - JupyterVersion: - Description: Which version of jupyter to deploy, uses container version tags, defaults to "latest" - Type: String - Default: latest - - RootVolumeSize: - Description: size of the root disk to the jupyter server. - Type: Number - Default: 20 - ConstraintDescription: Size in GB, between 10 and 1000. - MinValue: 8 - MaxValue: 1000 - - UsePersistentVolume: - Description: Should we use a new or existing volume for persistent data on the jupyter server. - Type: String - AllowedValues: - - New - - Existing - Default: New - ConstraintDescription: Specify if you are using a a new persistent volume, an existing one, or none. - - PersistentVolumeSize: - Description: size of the optional persistent disk to the jupyter server. - Type: Number - Default: 20 - ConstraintDescription: Size in GB, between 10 and 1000. - MinValue: 8 - MaxValue: 1000 - - ExistingPersistentVolumeId: - Description: Id of the existing persistent volume to attach. Must be int the same availability zone as the Jupyter instance. - Type: String - Default: None - - PersistentVolumeDeletionPolicy: - Description: Behavior for the Persistent Volume when deleting the cloudformations deployment. - Type: String - AllowedValues: - - Delete - - Retain - - RetainExceptOnCreate - - Snapshot - Default: Retain - - TerminationProtection: - Description: Enable instance termination protection. - Type: String - AllowedValues: - - true - - false - Default: false - - InstanceType: - Description: jupyter EC2 instance type - Type: String - AllowedValues: - - t3.nano - - t3.micro - - t3.small - - t3.medium - - t3.large - - m3.medium - - m3.large - - m3.xlarge - - m3.2xlarge - - m4.large - - m4.xlarge - - m4.2xlarge - - m4.4xlarge - - m4.10xlarge - - c3.large - - c3.xlarge - - c3.2xlarge - - c3.4xlarge - - c3.8xlarge - - c4.large - - c4.xlarge - - c4.2xlarge - - c4.4xlarge - - c4.8xlarge - - r3.large - - r3.xlarge - - r3.2xlarge - - r3.4xlarge - - r3.8xlarge - - i2.xlarge - - i2.2xlarge - - i2.4xlarge - - i2.8xlarge - Default: t3.micro - ConstraintDescription: must be a valid EC2 instance type. - - KeyName: - Description: Name of an existing EC2 KeyPair to enable SSH access to the instances, leave empty if no ssh keys should be included - Type: String - - IamRole: - Description: | - Create a new IAM role for jupyter or use an exiting one. - Requires CAPABILITY_IAM if creating a new IAM Role - Type: String - AllowedValues: - - None - - New - - Existing - Default: New - - IamRoleName: - Description: | - Name of an existing IAM Role to assign to Jupyter, - or the name to give to the newly created role. - Leave blank to use an autogenerated name. - Requires CAPABILITY_NAMED_IAM if naming a new IAM Role. - Type: String - - IamPermissionsBoundary: - Description: | - Optional: Arn of a permissions boundary to pass to the IAM Role assigned to Jupyter. - Type: String - - AccessCIDR: - Description: The IP address range that can be used to communicate with the juptyer instance. - Type: String - AllowedPattern: ((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2}))|^$ - ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. - - PrefixList: - Description: The PrefixList that can be used to communicate with the jupyter instance. - Type: String - ConstraintDescription: must be a valid prefixlist - - SecurityGroup: - Description: The SecurityGroup that can be used to communicate with the Jupyter instance. - Type: String - ConstraintDescription: must be a valid securityGroup ID - -Rules: - subnetsInVpc: - Assertions: - - Assert: - Fn::EachMemberEquals: - - Fn::ValueOfAll: - - AWS::EC2::Subnet::Id - - VpcId - - !Ref Vpc - AssertDescription: The subnet you selected is not in the VPC - - instanceTypeInZone: - Assertions: - - Assert: - Fn::EachMemberEquals: - - Fn::ValueOfAll: - - AWS::EC2::Subnet::Id - - VpcId - - !Ref Vpc - AssertDescription: The subnet you selected is not in the VPC - -Conditions: - NEEDSROLE: !Equals - - !Ref IamRole - - New - - HASPUBLICIP: !Not - - !Equals - - !Ref Private - - "true" - - HASKEY: !Not - - !Equals - - !Ref KeyName - - "" - - HASCIDR: !Not - - !Equals - - !Ref AccessCIDR - - "" - - HASPREFIXLIST: !Not - - !Equals - - !Ref PrefixList - - "" - - HASSECURITYGROUP: !Not - - !Equals - - !Ref SecurityGroup - - "" - - HASCIDRORPREFIXLIST: !Or - - !Condition HASCIDR - - !Condition HASPREFIXLIST - - HASCIDRORPREFIXLISTORSECGROUP: !Or - - !Condition HASCIDR - - !Condition HASPREFIXLIST - - !Condition HASSECURITYGROUP - - USESESSIONMANAGER: !Equals - - !Ref Session - - "true" - - NEEDSROLEANDSESSIONMANAGER: !And - - !Condition NEEDSROLE - - !Condition USESESSIONMANAGER - - NEEDINSTANCEPROFILE: !Or - - !Not - - !Condition NEEDSROLE - - !Condition NEEDSROLEANDSESSIONMANAGER - - HASKEYANDPUBLIC: !And - - !Condition HASKEY - - !Condition HASPUBLICIP - - HASKEYANDCIDRORPREFIXLISTORSECGROUP: !And - - !Condition HASKEY - - !Condition HASCIDRORPREFIXLISTORSECGROUP - - USENEWPERSISTENTVOLUME: !Equals - - !Ref UsePersistentVolume - - New - - HASIAMPERMISSIONSBOUNDARY: !Not - - !Equals - - !Ref IamPermissionsBoundary - - "" - - HASIAMROLENAME: !Not - - !Equals - - !Ref IamRoleName - - "" - -Resources: - JupyterVolume: - DeletionPolicy: !Ref PersistentVolumeDeletionPolicy - Type: AWS::EC2::Volume - Properties: - AvailabilityZone: !Ref AvailabilityZone - Size: !Ref PersistentVolumeSize - Encrypted: true - Tags: - - Key: Name - Value: !Join - - '-' - - - !Ref JupyterName - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - Key: Usage - Value: persistent storage - Condition: USENEWPERSISTENTVOLUME - - JupyterServer: - CreationPolicy: - ResourceSignal: - Timeout: PT15M - Type: AWS::EC2::Instance - Metadata: - AWS::CloudFormation::Init: - configSets: - jupyter_install: - - prepare_directory - - !If - - USENEWPERSISTENTVOLUME - - prepare_new_storage - - !Ref AWS::NoValue - - bind_storage - - mount_storage - - install_docker - - configure_jupyter_service - - start_jupyter_service - prepare_directory: - commands: - mkdir: - command: !Sub | - #!/bin/bash -xe - /usr/bin/mkdir -p /etc/td - prepare_new_storage: - commands: - mkfs: - command: !Sub | - #!/bin/bash -xe - /usr/sbin/mkfs -t ext4 /dev/nvme1n1 - bind_storage: - commands: - fstab: - command: !Sub | - #!/bin/bash -xe - /usr/bin/echo "/dev/nvme1n1 /etc/td ext4 defaults 0 2" >> /etc/fstab - mount_storage: - commands: - mount: - command: !Sub | - #!/bin/bash -xe - /usr/bin/mount -a - install_docker: - files: - /usr/lib/systemd/system/docker-install.service: - content: !Sub | - [Unit] - Description=Install docker - - [Service] - Type=oneshot - ExecStart=/bin/bash -c "while ! dnf update; do sleep 2; done && while ! dnf install -y docker; do sleep 2; done" - RemainAfterExit=yes - - [Install] - WantedBy=multi-user.target - commands: - verify_docker: - command: !Sub | - #!/bin/bash -xe - systemctl start docker-install - systemctl start docker - systemctl enable docker - services: - systemd: - docker: - enabled: "true" - ensureRunning: "true" - configure_jupyter_service: - files: - /usr/lib/systemd/system/jupyter.service: - content: !Sub | - [Unit] - Description=jupyter - After=docker.service - Requires=docker.service - StartLimitInterval=200 - StartLimitBurst=10 - - [Service] - TimeoutStartSec=0 - Restart=always - RestartSec=2 - ExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited - ExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{userdata,ipython} - ExecStartPre=-/usr/bin/docker exec %n stop || true - ExecStartPre=-/usr/bin/docker rm %n || true - ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-jupyter:${ JupyterVersion } - ExecStart=/usr/bin/docker run \ - -e accept_license=Y \ - -e JUPYTER_TOKEN=${ JupyterToken } \ - -v /etc/td/jupyter/userdata:/home/jovyan/JupyterLabRoot/userdata \ - -v /etc/td/jupyter/ipython:/home/jovyan/.ipython \ - -p ${ JupyterHttpPort }:8888 \ - --network ai_unlimited \ - --rm --name %n teradata/ai-unlimited-jupyter:${ JupyterVersion } - - [Install] - WantedBy=multi-user.target - group: root - mode: "000400" - owner: root - start_jupyter_service: - services: - systemd: - jupyter: - enabled: "true" - ensureRunning: "true" - Properties: - PropagateTagsToVolumeOnCreation: true - BlockDeviceMappings: - - DeviceName: /dev/xvda - Ebs: - VolumeSize: !Ref RootVolumeSize - Encrypted: true - NetworkInterfaces: - - DeviceIndex: 0 - SubnetId: !Ref Subnet - GroupSet: - - !GetAtt JupyterSecurityGroup.GroupId - AssociatePublicIpAddress: !If - - HASPUBLICIP - - true - - !Ref AWS::NoValue - ImageId: !Ref LatestAmiId - InstanceType: !Ref InstanceType - KeyName: !If - - HASKEY - - !Ref KeyName - - !Ref AWS::NoValue - DisableApiTermination: !Ref TerminationProtection - IamInstanceProfile: !If - - NEEDINSTANCEPROFILE - - !Ref JupyterInstanceProfile - - !Ref AWS::NoValue - Volumes: - - Device: /dev/xvdb - VolumeId: !If - - USENEWPERSISTENTVOLUME - - !Ref JupyterVolume - - !Ref ExistingPersistentVolumeId - Tags: - - Key: Name - Value: !Join - - '-' - - - !Ref JupyterName - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - UserData: !Base64 - Fn::Sub: | - #!/bin/bash -xe - yum update -y - yum update -y aws-cfn-bootstrap - /opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource JupyterServer --configsets jupyter_install --region ${AWS::Region} - /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource JupyterServer --region ${AWS::Region} - - LoadBalancerJupyterSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to Jupyter server from LoadBalancer over http, grpc, and ssh - SecurityGroupIngress: - - FromPort: !Ref JupyterHttpPort - IpProtocol: tcp - ToPort: !Ref JupyterHttpPort - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASCIDRORPREFIXLISTORSECGROUP - - LoadBalancer: - Type: AWS::ElasticLoadBalancingV2::LoadBalancer - Properties: - Scheme: !Ref LoadBalancerScheme - Subnets: - - !Ref LoadBalancerSubnetOne - - !Ref LoadBalancerSubnetTwo - SecurityGroups: - - !GetAtt LoadBalancerJupyterSecurityGroup.GroupId - Type: application - - JupyterHTTPListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref JupyterHTTPTargetGroup - LoadBalancerArn: !Ref LoadBalancer - Port: !Ref JupyterHttpPort - Protocol: HTTPS - Certificates: - - CertificateArn: !Ref ACMCertificate - - JupyterHTTPTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - HealthCheckIntervalSeconds: 30 - HealthCheckProtocol: HTTP - HealthCheckTimeoutSeconds: 15 - Matcher: - HttpCode: "200" - Name: !Join - - '-' - - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - - jupyter - - ui - - http - Port: !Ref JupyterHttpPort - Protocol: HTTP - TargetGroupAttributes: - - Key: stickiness.enabled - Value: true - - Key: stickiness.type - Value: app_cookie - - Key: stickiness.app_cookie.cookie_name - Value: TDJUPYTERHTTPSSESSION - - Key: deregistration_delay.timeout_seconds - Value: "20" - Targets: - - Id: !Ref JupyterServer - Port: !Ref JupyterHttpPort - VpcId: !Ref Vpc - - JupyterSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - VpcId: !Ref Vpc - GroupDescription: Enable access to jupyter server over http - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: !Ref JupyterHttpPort - ToPort: !Ref JupyterHttpPort - SourceSecurityGroupId: !GetAtt LoadBalancerJupyterSecurityGroup.GroupId - - !If - - HASSECURITYGROUP - - IpProtocol: tcp - FromPort: !Ref JupyterHttpPort - ToPort: !Ref JupyterHttpPort - SourceSecurityGroupId: !Ref SecurityGroup - - !Ref AWS::NoValue - - SecurityGroupIngress: - Type: AWS::EC2::SecurityGroupIngress - Properties: - GroupId: !GetAtt JupyterSecurityGroup.GroupId - FromPort: 22 - IpProtocol: tcp - ToPort: 22 - CidrIp: !If - - HASCIDR - - !Ref AccessCIDR - - !Ref AWS::NoValue - SourcePrefixListId: !If - - HASPREFIXLIST - - !Ref PrefixList - - !Ref AWS::NoValue - SourceSecurityGroupId: !If - - HASSECURITYGROUP - - !Ref SecurityGroup - - !Ref AWS::NoValue - Condition: HASKEYANDCIDRORPREFIXLISTORSECGROUP - - JupyterRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Principal: - Service: - - ec2.amazonaws.com - Action: - - sts:AssumeRole - Path: / - Condition: NEEDSROLEANDSESSIONMANAGER - - SessionManagerPolicies: - Type: AWS::IAM::Policy - Properties: - PolicyName: !Join - - '-' - - - jupyter - - session - - !Select - - 4 - - !Split - - '-' - - !Select - - 2 - - !Split - - / - - !Ref AWS::StackId - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - ssm:DescribeAssociation - - ssm:GetDeployablePatchSnapshotForInstance - - ssm:GetDocument - - ssm:DescribeDocument - - ssm:GetManifest - - ssm:ListAssociations - - ssm:ListInstanceAssociations - - ssm:PutInventory - - ssm:PutComplianceItems - - ssm:PutConfigurePackageResult - - ssm:UpdateAssociationStatus - - ssm:UpdateInstanceAssociationStatus - - ssm:UpdateInstanceInformation - Resource: '*' - - Effect: Allow - Action: - - ssmmessages:CreateControlChannel - - ssmmessages:CreateDataChannel - - ssmmessages:OpenControlChannel - - ssmmessages:OpenDataChannel - Resource: '*' - - Effect: Allow - Action: - - ec2messages:AcknowledgeMessage - - ec2messages:DeleteMessage - - ec2messages:FailMessage - - ec2messages:GetEndpoint - - ec2messages:GetMessages - - ec2messages:SendReply - Resource: '*' - Roles: - - !Ref JupyterRole - Condition: NEEDSROLEANDSESSIONMANAGER - - JupyterInstanceProfile: - Type: AWS::IAM::InstanceProfile - Properties: - Path: / - Roles: !If - - NEEDSROLEANDSESSIONMANAGER - - - !Ref JupyterRole - - - !Ref IamRoleName - Condition: NEEDINSTANCEPROFILE - - ACMCertificate: - Type: AWS::CertificateManager::Certificate - Properties: - DomainName: !Ref DnsName - DomainValidationOptions: - - DomainName: !Ref DnsName - HostedZoneId: !Ref HostedZoneId - ValidationMethod: DNS - - LoadBalancerDNS: - Type: AWS::Route53::RecordSetGroup - Properties: - HostedZoneId: !Ref HostedZoneId - RecordSets: - - Name: !Ref DnsName - Type: A - AliasTarget: - HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID - DNSName: !GetAtt LoadBalancer.DNSName - -Outputs: - PublicIP: - Description: EC2 public IP - Value: !GetAtt JupyterServer.PublicIp - Condition: HASPUBLICIP - - PrivateIP: - Description: EC2 private IP - Value: !GetAtt JupyterServer.PrivateIp - - LoadBalancerJupyterUIAccess: - Description: Loadbalancer access endpoint for API Access - Value: !Sub https://${ DnsName }:${ JupyterHttpPort }?token=${ JupyterToken } - - InstanceSecurityGroups: - Description: AI Unlimited Security Group - Value: !GetAtt JupyterSecurityGroup.GroupId - - - LoadBalancerSecurityGroups: - Description: AI Unlimited Load Balancer Security Group - Value: !Join - - ', ' - - - !GetAtt LoadBalancerJupyterSecurityGroup.GroupId - - PublicSSHConeection: - Description: Jupyter ssh connnection string - Value: !Sub ssh ec2-user@${ JupyterServer.PublicIp } - Condition: HASKEYANDPUBLIC - - PrivateSSHConeection: - Description: jupyter ssh connnection string - Value: !Sub ssh ec2-user@${ JupyterServer.PrivateIp } - Condition: HASKEY - - PersistentVolumeId: - Description: Id of the new persistent volume created for Jupyter - Value: !Ref JupyterVolume - Condition: USENEWPERSISTENTVOLUME diff --git a/deployments/aws/templates/jupyter/jupyter-with-nlb.yaml b/deployments/aws/templates/jupyter/jupyter-with-nlb.yaml index dd169b4..0db29cd 100644 --- a/deployments/aws/templates/jupyter/jupyter-with-nlb.yaml +++ b/deployments/aws/templates/jupyter/jupyter-with-nlb.yaml @@ -12,7 +12,6 @@ Metadata: - InstanceType - RootVolumeSize - TerminationProtection - - JupyterToken - IamRole - IamRoleName - IamPermissionsBoundary @@ -120,7 +119,7 @@ Parameters: JupyterVersion: Description: Which version of jupyter to deploy, uses container version tags, defaults to "latest" Type: String - Default: latest + Default: v0.1.0 RootVolumeSize: Description: size of the root disk to the jupyter server. @@ -753,7 +752,6 @@ Outputs: Description: AI Unlimited Security Group Value: !GetAtt JupyterSecurityGroup.GroupId - LoadBalancerSecurityGroups: Description: AI Unlimited Load Balancer Security Group Value: !Join diff --git a/deployments/aws/templates/jupyter/jupyter-without-lb.yaml b/deployments/aws/templates/jupyter/jupyter-without-lb.yaml index cfd9fef..f4fb410 100644 --- a/deployments/aws/templates/jupyter/jupyter-without-lb.yaml +++ b/deployments/aws/templates/jupyter/jupyter-without-lb.yaml @@ -12,7 +12,6 @@ Metadata: - InstanceType - RootVolumeSize - TerminationProtection - - JupyterToken - IamRole - IamRoleName - IamPermissionsBoundary @@ -105,7 +104,7 @@ Parameters: JupyterVersion: Description: Which version of jupyter to deploy, uses container version tags, defaults to "latest" Type: String - Default: latest + Default: v0.1.0 RootVolumeSize: Description: size of the root disk to the jupyter server. @@ -671,7 +670,6 @@ Outputs: Description: AI Unlimited Security Group Value: !GetAtt JupyterSecurityGroup.GroupId - PublicSSHConeection: Description: Jupyter ssh connnection string Value: !Sub ssh ec2-user@${ JupyterServer.PublicIp } diff --git a/deployments/azure/scripts/ai-unlimited-scheduler.service b/deployments/azure/scripts/ai-unlimited-scheduler.service index 9df39b5..9a315b9 100644 --- a/deployments/azure/scripts/ai-unlimited-scheduler.service +++ b/deployments/azure/scripts/ai-unlimited-scheduler.service @@ -9,8 +9,8 @@ StartLimitBurst=10 TimeoutStartSec=0 Restart=always RestartSec=2 -ExecStartPre=-/usr/bin/docker exec %n stop || true -ExecStartPre=-/usr/bin/docker rm %n || true +ExecStartPre=-/usr/bin/docker stop %n +ExecStartPre=-/usr/bin/docker rm %n ExecStartPre=/usr/bin/docker pull {0}/{1}:{2} ExecStart=/usr/bin/docker run \ --network ai_unlimited \ diff --git a/deployments/azure/scripts/ai-unlimited-ui.service b/deployments/azure/scripts/ai-unlimited-ui.service new file mode 100644 index 0000000..a2cf43c --- /dev/null +++ b/deployments/azure/scripts/ai-unlimited-ui.service @@ -0,0 +1,29 @@ +[Unit] +Description=AI Unlimited UI +After=ai-unlimited.service +Requires=ai-unlimited.service +StartLimitInterval=200 +StartLimitBurst=10 + +[Service] +TimeoutStartSec=0 +Restart=always +RestartSec=2 +EnvironmentFile=/etc/td/ai-unlimited/init_api_key.txt +ExecStartPre=-/usr/bin/docker stop %n +ExecStartPre=-/usr/bin/docker rm %n +ExecStartPre=/usr/bin/docker pull {0}/{1}:{2} + +ExecStart=/usr/bin/docker run \ + -e TD_VCD_USE_TLS=false \ + -e TD_VCD_AUTH_PORT={4}\ + -e TD_VCD_API_PORT={5}\ + -e TD_VCD_INIT_API_KEY \ + -p 80:80 \ + -p 443:443 \ + -v ssl_certs:/etc/ssl/td \ + --network ai_unlimited {6} \ + --rm --name %n {0}/{1}:{2} + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/deployments/azure/scripts/ai-unlimited.cloudinit.yaml b/deployments/azure/scripts/ai-unlimited.cloudinit.yaml index 8146cb5..98facc2 100644 --- a/deployments/azure/scripts/ai-unlimited.cloudinit.yaml +++ b/deployments/azure/scripts/ai-unlimited.cloudinit.yaml @@ -10,17 +10,26 @@ write_files: owner: root:root path: /usr/lib/systemd/system/ai-unlimited-scheduler.service permissions: '0640' +- encoding: b64 + content: "{2}" + owner: root:root + path: /usr/lib/systemd/system/ai-unlimited-ui.service + permissions: '0640' runcmd: -- mkdir -p /etc/td +- mkdir -p /etc/td - | export PERMDISK=$(lsscsi 1:0:0:0 -b | awk '{{print $2}}'); if [ -n "${{PERMDISK}}" ]; then blkid --match-token TYPE=ext4 ${{PERMDISK}} || (mkfs.ext4 -m0 ${{PERMDISK}} && e2label ${{PERMDISK}} WORKSPACES); fi /usr/bin/echo "LABEL=WORKSPACES /etc/td ext4 defaults 0 2" >> /etc/fstab /usr/bin/mount -a - while [ $(systemctl status docker | grep "active (running)" | wc -l) -lt 1 ]; do sleep 5; done +- mkdir -p /etc/td/ai-unlimited +- echo "TD_VCD_INIT_API_KEY=$(LC_ALL=C tr -dc A-Za-z0-9 /etc/td/ai-unlimited/init_api_key.txt - sleep 60 - systemctl enable ai-unlimited.service - systemctl start ai-unlimited.service - systemctl enable ai-unlimited-scheduler.service - systemctl start ai-unlimited-scheduler.service +- systemctl enable ai-unlimited-ui.service +- systemctl start ai-unlimited-ui.service diff --git a/deployments/azure/scripts/ai-unlimited.service b/deployments/azure/scripts/ai-unlimited.service index f7d100a..da29dd9 100644 --- a/deployments/azure/scripts/ai-unlimited.service +++ b/deployments/azure/scripts/ai-unlimited.service @@ -9,10 +9,12 @@ StartLimitBurst=10 TimeoutStartSec=0 Restart=always RestartSec=2 +EnvironmentFile=/etc/td/ai-unlimited/init_api_key.txt +ExecStartPre=-/usr/bin/docker volume create ssl_certs ExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited ExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited -ExecStartPre=-/usr/bin/docker exec %n stop || true -ExecStartPre=-/usr/bin/docker rm %n || true +ExecStartPre=-/usr/bin/docker stop %n +ExecStartPre=-/usr/bin/docker rm %n ExecStartPre=/usr/bin/docker pull {0}/{1}:{2} ExecStart=/usr/bin/docker run \ @@ -21,9 +23,11 @@ ExecStart=/usr/bin/docker run \ -e ARM_USE_MSI=true \ -e ARM_SUBSCRIPTION_ID={5} \ -e ARM_TENANT_ID={6} \ - -v /etc/td/ai-unlimited:/etc/td \ + -e TD_VCD_INIT_API_KEY \ -p {3}:3000 \ -p {4}:3282 \ + -v /etc/td/ai-unlimited:/etc/td \ + -v ssl_certs:/etc/td/ssl \ --network ai_unlimited {7} \ --rm --name %n {0}/{1}:{2} workspaces serve -v diff --git a/deployments/azure/scripts/all-in-one.cloudinit.yaml b/deployments/azure/scripts/all-in-one.cloudinit.yaml index 0343087..237e870 100644 --- a/deployments/azure/scripts/all-in-one.cloudinit.yaml +++ b/deployments/azure/scripts/all-in-one.cloudinit.yaml @@ -15,6 +15,11 @@ write_files: owner: root:root path: /usr/lib/systemd/system/ai-unlimited-scheduler.service permissions: '0640' +- encoding: b64 + content: "{3}" + owner: root:root + path: /usr/lib/systemd/system/ai-unlimited-ui.service + permissions: '0640' runcmd: - mkdir -p /etc/td @@ -24,6 +29,8 @@ runcmd: /usr/bin/echo "LABEL=WORKSPACES /etc/td ext4 defaults 0 2" >> /etc/fstab /usr/bin/mount -a - while [ $(systemctl status docker | grep "active (running)" | wc -l) -lt 1 ]; do sleep 5; done +- mkdir -p /etc/td/ai-unlimited +- echo "TD_VCD_INIT_API_KEY=$(LC_ALL=C tr -dc A-Za-z0-9 /etc/td/ai-unlimited/init_api_key.txt - sleep 60 - systemctl enable ai-unlimited.service - systemctl start ai-unlimited.service @@ -31,3 +38,5 @@ runcmd: - systemctl start jupyter.service - systemctl enable ai-unlimited-scheduler.service - systemctl start ai-unlimited-scheduler.service +- systemctl enable ai-unlimited-ui.service +- systemctl start ai-unlimited-ui.service diff --git a/deployments/azure/scripts/jupyter.service b/deployments/azure/scripts/jupyter.service index 2fe6cd7..f9b4da7 100644 --- a/deployments/azure/scripts/jupyter.service +++ b/deployments/azure/scripts/jupyter.service @@ -11,8 +11,8 @@ Restart=always RestartSec=2 ExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited ExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{{userdata,ipython}} -ExecStartPre=-/usr/bin/docker exec %n stop || true -ExecStartPre=-/usr/bin/docker rm %n || true +ExecStartPre=-/usr/bin/docker stop %n +ExecStartPre=-/usr/bin/docker rm %n ExecStartPre=/usr/bin/docker pull {0}/{1}:{2} ExecStart=/usr/bin/docker run \ diff --git a/deployments/azure/templates/arm/ai-unlimited/ai-unlimited-with-nlb.json b/deployments/azure/templates/arm/ai-unlimited/ai-unlimited-with-nlb.json index 53cb6b3..c28afa3 100644 --- a/deployments/azure/templates/arm/ai-unlimited/ai-unlimited-with-nlb.json +++ b/deployments/azure/templates/arm/ai-unlimited/ai-unlimited-with-nlb.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "10962030278510784556" + "version": "0.31.92.45157", + "templateHash": "7009335605344942432" } }, "parameters": { @@ -22,12 +22,6 @@ "description": "Name for the AI Unlimited service's virtual machine." } }, - "PublicKey": { - "type": "securestring", - "metadata": { - "description": "SSH public key value" - } - }, "OSVersion": { "type": "string", "defaultValue": "Ubuntu-2004", @@ -75,11 +69,11 @@ "description": "The CIDR ranges that can be used to communicate with the AI Unlimited service instance." } }, - "AiUnlimitedHttpPort": { + "AiUnlimitedAuthPort": { "type": "int", "defaultValue": 3000, "metadata": { - "description": "port to access the AI Unlimited service UI." + "description": "port to access the AI Unlimited auth service." } }, "AiUnlimitedGrpcPort": { @@ -109,13 +103,6 @@ "description": "GUID of the AI Unlimited Role" } }, - "AllowPublicSSH": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "allow access the AI Unlimited ssh port from the access cidr." - } - }, "UseKeyVault": { "type": "string", "defaultValue": "New", @@ -154,11 +141,18 @@ }, "AiUnlimitedVersion": { "type": "string", - "defaultValue": "v0.2.23", + "defaultValue": "v0.3.0", "metadata": { "description": "Container Version of the AI Unlimited service" } }, + "AiUnlimitedUIVersion": { + "type": "string", + "defaultValue": "v0.1.0", + "metadata": { + "description": "Container Version of the AI Unlimited UI service" + } + }, "Tags": { "type": "object", "defaultValue": {}, @@ -168,10 +162,13 @@ } }, "variables": { - "$fxv#0": "#cloud-config\nwrite_files:\n- encoding: b64\n content: \"{0}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited.service\n permissions: '0640'\n- encoding: b64\n content: \"{1}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited-scheduler.service\n permissions: '0640'\n\nruncmd:\n- mkdir -p /etc/td \n- |\n export PERMDISK=$(lsscsi 1:0:0:0 -b | awk '{{print $2}}');\n if [ -n \"${{PERMDISK}}\" ]; then blkid --match-token TYPE=ext4 ${{PERMDISK}} || (mkfs.ext4 -m0 ${{PERMDISK}} && e2label ${{PERMDISK}} WORKSPACES); fi\n /usr/bin/echo \"LABEL=WORKSPACES /etc/td ext4 defaults 0 2\" >> /etc/fstab\n /usr/bin/mount -a\n- while [ $(systemctl status docker | grep \"active (running)\" | wc -l) -lt 1 ]; do sleep 5; done\n- sleep 60\n- systemctl enable ai-unlimited.service\n- systemctl start ai-unlimited.service\n- systemctl enable ai-unlimited-scheduler.service\n- systemctl start ai-unlimited-scheduler.service\n", - "$fxv#1": "[Unit]\nDescription=AI Unlimited\nAfter=docker.service\nRequires=docker.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited\nExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited\nExecStartPre=-/usr/bin/docker exec %n stop || true\nExecStartPre=-/usr/bin/docker rm %n || true\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e accept_license=Y \\\n -e PLATFORM=azure \\\n -e ARM_USE_MSI=true \\\n -e ARM_SUBSCRIPTION_ID={5} \\\n -e ARM_TENANT_ID={6} \\\n -v /etc/td/ai-unlimited:/etc/td \\\n -p {3}:3000 \\\n -p {4}:3282 \\\n --network ai_unlimited {7} \\\n --rm --name %n {0}/{1}:{2} workspaces serve -v\n\n[Install]\nWantedBy=multi-user.target", - "$fxv#2": "[Unit]\nDescription=AI Unlimited Scheduler\nAfter=ai-unlimited.service\nRequires=ai-unlimited.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker exec %n stop || true\nExecStartPre=-/usr/bin/docker rm %n || true\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\nExecStart=/usr/bin/docker run \\\n --network ai_unlimited \\\n -p {3}:50061 \\\n -v /etc/td/ai-unlimited:/etc/td \\\n -e TD_WSSCHED_LOG_PATH=/etc/td/workspaces/scheduler_logs \\\n -e TD_WSSCHED_TASK_LOG_PATH=/etc/td/workspaces/scheduler_logs/projects \\\n -e TD_WSSCHED_POL_INTERVAL=2 \\\n -e TD_WS_CONTAINER_NAME=ai-unlimited.service \\\n --rm --name %n {0}/{1}:{2} workspace-event-scheduler serve\n[Install]\nWantedBy=multi-user.target", + "$fxv#0": "#cloud-config\nwrite_files:\n- encoding: b64\n content: \"{0}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited.service\n permissions: '0640'\n- encoding: b64\n content: \"{1}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited-scheduler.service\n permissions: '0640'\n- encoding: b64\n content: \"{2}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited-ui.service\n permissions: '0640'\n\nruncmd:\n- mkdir -p /etc/td\n- |\n export PERMDISK=$(lsscsi 1:0:0:0 -b | awk '{{print $2}}');\n if [ -n \"${{PERMDISK}}\" ]; then blkid --match-token TYPE=ext4 ${{PERMDISK}} || (mkfs.ext4 -m0 ${{PERMDISK}} && e2label ${{PERMDISK}} WORKSPACES); fi\n /usr/bin/echo \"LABEL=WORKSPACES /etc/td ext4 defaults 0 2\" >> /etc/fstab\n /usr/bin/mount -a\n- while [ $(systemctl status docker | grep \"active (running)\" | wc -l) -lt 1 ]; do sleep 5; done\n- mkdir -p /etc/td/ai-unlimited\n- echo \"TD_VCD_INIT_API_KEY=$(LC_ALL=C tr -dc A-Za-z0-9 /etc/td/ai-unlimited/init_api_key.txt\n- sleep 60\n- systemctl enable ai-unlimited.service\n- systemctl start ai-unlimited.service\n- systemctl enable ai-unlimited-scheduler.service\n- systemctl start ai-unlimited-scheduler.service\n- systemctl enable ai-unlimited-ui.service\n- systemctl start ai-unlimited-ui.service\n", + "$fxv#1": "[Unit]\nDescription=AI Unlimited\nAfter=docker.service\nRequires=docker.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nEnvironmentFile=/etc/td/ai-unlimited/init_api_key.txt\nExecStartPre=-/usr/bin/docker volume create ssl_certs\nExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited\nExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited\nExecStartPre=-/usr/bin/docker stop %n\nExecStartPre=-/usr/bin/docker rm %n\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e accept_license=Y \\\n -e PLATFORM=azure \\\n -e ARM_USE_MSI=true \\\n -e ARM_SUBSCRIPTION_ID={5} \\\n -e ARM_TENANT_ID={6} \\\n -e TD_VCD_INIT_API_KEY \\\n -p {3}:3000 \\\n -p {4}:3282 \\\n -v /etc/td/ai-unlimited:/etc/td \\\n -v ssl_certs:/etc/td/ssl \\\n --network ai_unlimited {7} \\\n --rm --name %n {0}/{1}:{2} workspaces serve -v\n\n[Install]\nWantedBy=multi-user.target", + "$fxv#2": "[Unit]\nDescription=AI Unlimited Scheduler\nAfter=ai-unlimited.service\nRequires=ai-unlimited.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker stop %n\nExecStartPre=-/usr/bin/docker rm %n\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\nExecStart=/usr/bin/docker run \\\n --network ai_unlimited \\\n -p {3}:50061 \\\n -v /etc/td/ai-unlimited:/etc/td \\\n -e TD_WSSCHED_LOG_PATH=/etc/td/workspaces/scheduler_logs \\\n -e TD_WSSCHED_TASK_LOG_PATH=/etc/td/workspaces/scheduler_logs/projects \\\n -e TD_WSSCHED_POL_INTERVAL=2 \\\n -e TD_WS_CONTAINER_NAME=ai-unlimited.service \\\n --rm --name %n {0}/{1}:{2} workspace-event-scheduler serve\n[Install]\nWantedBy=multi-user.target", + "$fxv#3": "[Unit]\nDescription=AI Unlimited UI\nAfter=ai-unlimited.service\nRequires=ai-unlimited.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nEnvironmentFile=/etc/td/ai-unlimited/init_api_key.txt\nExecStartPre=-/usr/bin/docker stop %n\nExecStartPre=-/usr/bin/docker rm %n\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e TD_VCD_USE_TLS=false \\\n -e TD_VCD_AUTH_PORT={4}\\\n -e TD_VCD_API_PORT={5}\\\n -e TD_VCD_INIT_API_KEY \\\n -p 80:80 \\\n -p 443:443 \\\n -v ssl_certs:/etc/ssl/td \\\n --network ai_unlimited {6} \\\n --rm --name %n {0}/{1}:{2} \n\n[Install]\nWantedBy=multi-user.target", "AiUnlimitedSchedulerHttpPort": 50061, + "AiUnlimitedUIHttpPort": 80, + "AiUnlimitedUIHttpsPort": 443, "AiUnlimitedSchedulerVersion": "latest", "roleAssignmentName": "[guid(subscription().id, parameters('AiUnlimitedName'), subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), parameters('RoleDefinitionId'))]", "dnsId": "[uniqueString(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), deployment().name, parameters('AiUnlimitedName'))]", @@ -179,7 +176,8 @@ "nlbDnsLabelPrefix": "[format('td{0}-nlb', variables('dnsId'))]", "registry": "teradata", "workspaceRepository": "ai-unlimited-workspaces", - "workspaceSchedulerRepository": "ai-unlimited-scheduler" + "workspaceSchedulerRepository": "ai-unlimited-scheduler", + "workspaceUIRepository": "ai-unlimited-workspaces-ui" }, "resources": [ { @@ -225,8 +223,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "6929508783023913887" + "version": "0.31.92.45157", + "templateHash": "29287785012335710" } }, "parameters": { @@ -344,8 +342,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "17702031998905620424" + "version": "0.31.92.45157", + "templateHash": "16095084913002426133" } }, "parameters": { @@ -395,8 +393,8 @@ "accessCidrs": { "value": "[parameters('AccessCIDRs')]" }, - "aiUnlimitedHttpPort": { - "value": "[parameters('AiUnlimitedHttpPort')]" + "aiUnlimitedAuthPort": { + "value": "[parameters('AiUnlimitedAuthPort')]" }, "aiUnlimitedGrpcPort": { "value": "[parameters('AiUnlimitedGrpcPort')]" @@ -404,6 +402,12 @@ "aiUnlimitedSchedulerHttpPort": { "value": "[variables('AiUnlimitedSchedulerHttpPort')]" }, + "aiUnlimitedUIHttpPort": { + "value": "[variables('AiUnlimitedUIHttpPort')]" + }, + "aiUnlimitedUIHttpsPort": { + "value": "[variables('AiUnlimitedUIHttpsPort')]" + }, "sourceAppSecGroups": { "value": "[parameters('SourceAppSecGroups')]" }, @@ -411,7 +415,7 @@ "value": "[parameters('detinationAppSecGroups')]" }, "sshAccess": { - "value": "[parameters('AllowPublicSSH')]" + "value": false }, "tags": { "value": "[parameters('Tags')]" @@ -423,8 +427,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3577307217619354540" + "version": "0.31.92.45157", + "templateHash": "392354314460473239" } }, "parameters": { @@ -450,7 +454,7 @@ "type": "bool", "defaultValue": false }, - "aiUnlimitedHttpPort": { + "aiUnlimitedAuthPort": { "type": "int", "defaultValue": 0 }, @@ -462,6 +466,14 @@ "type": "int", "defaultValue": 0 }, + "aiUnlimitedUIHttpPort": { + "type": "int", + "defaultValue": 0 + }, + "aiUnlimitedUIHttpsPort": { + "type": "int", + "defaultValue": 0 + }, "jupyterHttpPort": { "type": "int", "defaultValue": 0 @@ -527,10 +539,10 @@ ] }, { - "condition": "[not(equals(parameters('aiUnlimitedHttpPort'), 0))]", + "condition": "[not(equals(parameters('aiUnlimitedAuthPort'), 0))]", "type": "Microsoft.Network/networkSecurityGroups/securityRules", "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-http-allow', variables('uniqueSecurityGroupName')))]", + "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-auth-allow', variables('uniqueSecurityGroupName')))]", "properties": { "copy": [ { @@ -553,7 +565,7 @@ "access": "Allow", "description": "allow http to the workspace instance", "destinationAddressPrefix": "*", - "destinationPortRange": "[string(parameters('aiUnlimitedHttpPort'))]", + "destinationPortRange": "[string(parameters('aiUnlimitedAuthPort'))]", "direction": "Inbound", "priority": 701, "protocol": "Tcp", @@ -677,6 +689,82 @@ "dependsOn": [ "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" ] + }, + { + "condition": "[not(equals(parameters('aiUnlimitedUIHttpPort'), 0))]", + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-ui-http-allow', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "destinationApplicationSecurityGroups", + "count": "[length(parameters('detinationAppSecGroups'))]", + "input": { + "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + }, + { + "name": "sourceApplicationSecurityGroups", + "count": "[length(parameters('sourceAppSecGroups'))]", + "input": { + "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + } + ], + "access": "Allow", + "description": "allow http to the workspace ui instance", + "destinationAddressPrefix": "*", + "destinationPortRange": "[string(parameters('aiUnlimitedUIHttpPort'))]", + "direction": "Inbound", + "priority": 705, + "protocol": "Tcp", + "sourceAddressPrefixes": "[parameters('accessCidrs')]", + "sourcePortRange": "*" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" + ] + }, + { + "condition": "[not(equals(parameters('aiUnlimitedUIHttpsPort'), 0))]", + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-ui-https-allow', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "destinationApplicationSecurityGroups", + "count": "[length(parameters('detinationAppSecGroups'))]", + "input": { + "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + }, + { + "name": "sourceApplicationSecurityGroups", + "count": "[length(parameters('sourceAppSecGroups'))]", + "input": { + "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + } + ], + "access": "Allow", + "description": "allow https to the workspace ui instance", + "destinationAddressPrefix": "*", + "destinationPortRange": "[string(parameters('aiUnlimitedUIHttpsPort'))]", + "direction": "Inbound", + "priority": 706, + "protocol": "Tcp", + "sourceAddressPrefixes": "[parameters('accessCidrs')]", + "sourcePortRange": "*" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" + ] } ], "outputs": { @@ -708,8 +796,8 @@ "location": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), '2022-09-01', 'full').location]" }, - "aiUnlimitedHttpPort": { - "value": "[parameters('AiUnlimitedHttpPort')]" + "aiUnlimitedAuthPort": { + "value": "[parameters('AiUnlimitedAuthPort')]" }, "aiUnlimitedGrpcPort": { "value": "[parameters('AiUnlimitedGrpcPort')]" @@ -717,6 +805,12 @@ "aiUnlimitedSchedulerHttpPort": { "value": "[variables('AiUnlimitedSchedulerHttpPort')]" }, + "aiUnlimitedUIHttpPort": { + "value": "[variables('AiUnlimitedUIHttpPort')]" + }, + "aiUnlimitedUIHttpsPort": { + "value": "[variables('AiUnlimitedUIHttpsPort')]" + }, "tags": { "value": "[parameters('Tags')]" } @@ -727,8 +821,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "14601682444932366115" + "version": "0.31.92.45157", + "templateHash": "370499549708602593" } }, "parameters": { @@ -741,7 +835,7 @@ "dnsPrefix": { "type": "string" }, - "aiUnlimitedHttpPort": { + "aiUnlimitedAuthPort": { "type": "int", "defaultValue": 0 }, @@ -757,6 +851,14 @@ "type": "int", "defaultValue": 0 }, + "aiUnlimitedUIHttpPort": { + "type": "int", + "defaultValue": 0 + }, + "aiUnlimitedUIHttpsPort": { + "type": "int", + "defaultValue": 0 + }, "tags": { "type": "object", "defaultValue": {} @@ -798,8 +900,8 @@ "name": "[format('{0}OutboundBackendPool', parameters('name'))]" } ], - "loadBalancingRules": "[flatten(createArray(if(not(equals(parameters('aiUnlimitedHttpPort'), 0)), createArray(createObject('name', 'AiUnlimitedUI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedHttpPort'), 'backendPort', parameters('aiUnlimitedHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}UILbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedGrpcPort'), 0)), createArray(createObject('name', 'AiUnlimitedAPI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedGrpcPort'), 'backendPort', parameters('aiUnlimitedGrpcPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}APILbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('jupyterHttpPort'), 0)), createArray(createObject('name', 'JupyterUI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('jupyterHttpPort'), 'backendPort', parameters('jupyterHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}JupyterLbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedSchedulerHttpPort'), 0)), createArray(createObject('name', 'AiUnlimitedSchedulerHttp', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedSchedulerHttpPort'), 'backendPort', parameters('aiUnlimitedSchedulerHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}SchedulerHttpLbProbe', parameters('name'))))))), createArray())))]", - "probes": "[flatten(createArray(if(not(equals(parameters('aiUnlimitedHttpPort'), 0)), createArray(createObject('name', format('{0}UILbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('aiUnlimitedHttpPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedGrpcPort'), 0)), createArray(createObject('name', format('{0}APILbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('aiUnlimitedGrpcPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('jupyterHttpPort'), 0)), createArray(createObject('name', format('{0}JupyterLbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('jupyterHttpPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedSchedulerHttpPort'), 0)), createArray(createObject('name', format('{0}SchedulerHttpLbProbe', parameters('name')), 'properties', createObject('protocol', 'Http', 'port', parameters('aiUnlimitedSchedulerHttpPort'), 'requestPath', '/healthcheck', 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray())))]", + "loadBalancingRules": "[flatten(createArray(if(not(equals(parameters('aiUnlimitedAuthPort'), 0)), createArray(createObject('name', 'AiUnlimitedAuth', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedAuthPort'), 'backendPort', parameters('aiUnlimitedAuthPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}AuthLbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedGrpcPort'), 0)), createArray(createObject('name', 'AiUnlimitedAPI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedGrpcPort'), 'backendPort', parameters('aiUnlimitedGrpcPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}APILbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('jupyterHttpPort'), 0)), createArray(createObject('name', 'JupyterUI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('jupyterHttpPort'), 'backendPort', parameters('jupyterHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}JupyterLbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedSchedulerHttpPort'), 0)), createArray(createObject('name', 'AiUnlimitedSchedulerHttp', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedSchedulerHttpPort'), 'backendPort', parameters('aiUnlimitedSchedulerHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}SchedulerHttpLbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedUIHttpPort'), 0)), createArray(createObject('name', 'AiUnlimitedUIHttp', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedUIHttpPort'), 'backendPort', parameters('aiUnlimitedUIHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}UIHttpLbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedUIHttpsPort'), 0)), createArray(createObject('name', 'AiUnlimitedUIHttps', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedUIHttpsPort'), 'backendPort', parameters('aiUnlimitedUIHttpsPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}UIHttpsLbProbe', parameters('name'))))))), createArray())))]", + "probes": "[flatten(createArray(if(not(equals(parameters('aiUnlimitedAuthPort'), 0)), createArray(createObject('name', format('{0}AuthLbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('aiUnlimitedAuthPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedGrpcPort'), 0)), createArray(createObject('name', format('{0}APILbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('aiUnlimitedGrpcPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('jupyterHttpPort'), 0)), createArray(createObject('name', format('{0}JupyterLbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('jupyterHttpPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedSchedulerHttpPort'), 0)), createArray(createObject('name', format('{0}SchedulerHttpLbProbe', parameters('name')), 'properties', createObject('protocol', 'Http', 'port', parameters('aiUnlimitedSchedulerHttpPort'), 'requestPath', '/healthcheck', 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedUIHttpPort'), 0)), createArray(createObject('name', format('{0}UIHttpLbProbe', parameters('name')), 'properties', createObject('protocol', 'Http', 'port', parameters('aiUnlimitedUIHttpPort'), 'requestPath', '/', 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedUIHttpsPort'), 0)), createArray(createObject('name', format('{0}UIHttpsLbProbe', parameters('name')), 'properties', createObject('protocol', 'Https', 'port', parameters('aiUnlimitedUIHttpsPort'), 'requestPath', '/', 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray())))]", "outboundRules": [ { "name": "myOutboundRule", @@ -855,8 +957,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11278674213062453956" + "version": "0.31.92.45157", + "templateHash": "4325662974998231491" } }, "parameters": { @@ -937,8 +1039,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11278674213062453956" + "version": "0.31.92.45157", + "templateHash": "4325662974998231491" } }, "parameters": { @@ -1032,7 +1134,7 @@ "value": "azureuser" }, "sshPublicKey": { - "value": "[parameters('PublicKey')]" + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'Public-Key'), '2022-09-01').outputs.PublicKey.value]" }, "dnsLabelPrefix": { "value": "[variables('dnsLabelPrefix')]" @@ -1050,7 +1152,7 @@ "value": "[parameters('OSVersion')]" }, "cloudInitData": { - "value": "[base64(format(variables('$fxv#0'), base64(format(variables('$fxv#1'), variables('registry'), variables('workspaceRepository'), parameters('AiUnlimitedVersion'), parameters('AiUnlimitedHttpPort'), parameters('AiUnlimitedGrpcPort'), subscription().subscriptionId, subscription().tenantId, format('--network-alias {0}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer'), '2022-09-01').outputs.PublicDns.value))), base64(format(variables('$fxv#2'), variables('registry'), variables('workspaceSchedulerRepository'), variables('AiUnlimitedSchedulerVersion'), variables('AiUnlimitedSchedulerHttpPort')))))]" + "value": "[base64(format(variables('$fxv#0'), base64(format(variables('$fxv#1'), variables('registry'), variables('workspaceRepository'), parameters('AiUnlimitedVersion'), parameters('AiUnlimitedAuthPort'), parameters('AiUnlimitedGrpcPort'), subscription().subscriptionId, subscription().tenantId, format('--network-alias {0}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer'), '2022-09-01').outputs.PublicDns.value))), base64(format(variables('$fxv#2'), variables('registry'), variables('workspaceSchedulerRepository'), variables('AiUnlimitedSchedulerVersion'), variables('AiUnlimitedSchedulerHttpPort'))), base64(format(variables('$fxv#3'), variables('registry'), variables('workspaceUIRepository'), parameters('AiUnlimitedUIVersion'), variables('AiUnlimitedUIHttpPort'), parameters('AiUnlimitedAuthPort'), parameters('AiUnlimitedGrpcPort'), format('--network-alias {0}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer'), '2022-09-01').outputs.PublicDns.value)))))]" }, "usePersistentVolume": { "value": "[parameters('UsePersistentVolume')]" @@ -1080,8 +1182,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "13207593080095673481" + "version": "0.31.92.45157", + "templateHash": "11687747621230861550" } }, "parameters": { @@ -1361,8 +1463,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11278674213062453956" + "version": "0.31.92.45157", + "templateHash": "4325662974998231491" } }, "parameters": { @@ -1433,7 +1535,140 @@ }, "dependsOn": [ "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'firewall')]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer')]" + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'Public-Key')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "Public-Key", + "resourceGroup": "[parameters('ResourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "Name": { + "value": "[parameters('AiUnlimitedName')]" + }, + "Location": { + "value": "[deployment().location]" + }, + "VaultName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'vault'), '2022-09-01').outputs.name.value]" + }, + "RoleID": { + "value": "[parameters('RoleDefinitionId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "9690061458974637253" + } + }, + "parameters": { + "Location": { + "type": "string" + }, + "Name": { + "type": "string" + }, + "VaultName": { + "type": "string" + }, + "RoleID": { + "type": "string" + }, + "Uuid": { + "type": "string", + "defaultValue": "[newGuid()]" + } + }, + "variables": { + "SecretName": "[format('{0}-PrivateKey', parameters('Name'))]", + "ScriptName": "[format('{0}-createKeys', parameters('Name'))]", + "IdentityName": "[format('{0}-scratch', parameters('Name'))]", + "RoleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('RoleID'))]", + "RoleDefinitionName": "[guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('IdentityName')), variables('RoleDefinitionId'), resourceGroup().id)]" + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[variables('IdentityName')]", + "location": "[parameters('Location')]" + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[variables('RoleDefinitionName')]", + "properties": { + "roleDefinitionId": "[variables('RoleDefinitionId')]", + "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('IdentityName')), '2023-01-31').principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('IdentityName'))]" + ] + }, + { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2023-08-01", + "name": "[variables('ScriptName')]", + "location": "[parameters('Location')]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('IdentityName')))]": {} + } + }, + "kind": "AzureCLI", + "properties": { + "forceUpdateTag": "[parameters('Uuid')]", + "azCliVersion": "2.0.80", + "timeout": "PT30M", + "retentionInterval": "P1D", + "cleanupPreference": "OnSuccess", + "scriptContent": " #/bin/bash -e\n\n echo -e 'y' | ssh-keygen -f scratch\n\n privateKey=$(cat scratch)\n publicKey=$(cat 'scratch.pub')\n\n json=\"{\\\"keyinfo\\\":{\\\"privateKey\\\":\\\"$privateKey\\\",\\\"publicKey\\\":\\\"$publicKey\\\"}}\"\n\n echo \"$json\" > $AZ_SCRIPTS_OUTPUT_PATH\n " + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('IdentityName'))]", + "[resourceId('Microsoft.Authorization/roleAssignments', variables('RoleDefinitionName'))]" + ] + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('VaultName'), variables('SecretName'))]", + "properties": { + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', variables('ScriptName')), '2023-08-01').outputs.keyinfo.privateKey]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deploymentScripts', variables('ScriptName'))]" + ] + } + ], + "outputs": { + "PublicKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', variables('ScriptName')), '2023-08-01').outputs.keyinfo.publicKey]" + }, + "Status": { + "type": "object", + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', variables('ScriptName')), '2023-08-01').status]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'vault')]" ] } ], @@ -1448,11 +1683,11 @@ }, "AiUnlimitedPublicHttpAccess": { "type": "string", - "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer'), '2022-09-01').outputs.PublicDns.value, parameters('AiUnlimitedHttpPort'))]" + "value": "[concat(format('http://{0}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer'), '2022-09-01').outputs.PublicDns.value), if(not(equals(variables('AiUnlimitedUIHttpPort'), 80)), concat(':', string(variables('AiUnlimitedUIHttpPort'))), ''))]" }, "AiUnlimitedPrivateHttpAccess": { "type": "string", - "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value, parameters('AiUnlimitedHttpPort'))]" + "value": "[concat(format('http://{0}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value), if(not(equals(variables('AiUnlimitedUIHttpPort'), 80)), concat(':', string(variables('AiUnlimitedUIHttpPort'))), ''))]" }, "AiUnlimitedPublicGrpcAccess": { "type": "string", @@ -1462,10 +1697,6 @@ "type": "string", "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value, parameters('AiUnlimitedGrpcPort'))]" }, - "sshCommand": { - "type": "string", - "value": "[format('ssh azureuser@{0}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value)]" - }, "KeyVaultName": { "type": "string", "value": "[if(equals(parameters('UseKeyVault'), 'New'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'vault'), '2022-09-01').outputs.name.value, '')]" diff --git a/deployments/azure/templates/arm/ai-unlimited/ai-unlimited-without-lb.json b/deployments/azure/templates/arm/ai-unlimited/ai-unlimited-without-lb.json index 566a740..84936a5 100644 --- a/deployments/azure/templates/arm/ai-unlimited/ai-unlimited-without-lb.json +++ b/deployments/azure/templates/arm/ai-unlimited/ai-unlimited-without-lb.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "7331097313669370218" + "version": "0.31.92.45157", + "templateHash": "7904991390945018605" } }, "parameters": { @@ -22,12 +22,6 @@ "description": "Name for the AI Unlimited service's virtual machine." } }, - "PublicKey": { - "type": "securestring", - "metadata": { - "description": "SSH public key value" - } - }, "OSVersion": { "type": "string", "defaultValue": "Ubuntu-2004", @@ -75,11 +69,11 @@ "description": "The CIDR ranges that can be used to communicate with the AI Unlimited service instance." } }, - "AiUnlimitedHttpPort": { + "AiUnlimitedAuthPort": { "type": "int", "defaultValue": 3000, "metadata": { - "description": "port to access the AI Unlimited service UI." + "description": "port to access the AI Unlimited auth service." } }, "AiUnlimitedGrpcPort": { @@ -109,13 +103,6 @@ "description": "GUID of the AI Unlimited Role" } }, - "AllowPublicSSH": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "allow access the AI Unlimited ssh port from the access cidr." - } - }, "UseKeyVault": { "type": "string", "defaultValue": "New", @@ -154,11 +141,18 @@ }, "AiUnlimitedVersion": { "type": "string", - "defaultValue": "v0.2.23", + "defaultValue": "v0.3.0", "metadata": { "description": "Container Version of the AI Unlimited service" } }, + "AiUnlimitedUIVersion": { + "type": "string", + "defaultValue": "v0.1.0", + "metadata": { + "description": "Container Version of the AI Unlimited UI service" + } + }, "Tags": { "type": "object", "defaultValue": {}, @@ -168,17 +162,21 @@ } }, "variables": { - "$fxv#0": "#cloud-config\nwrite_files:\n- encoding: b64\n content: \"{0}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited.service\n permissions: '0640'\n- encoding: b64\n content: \"{1}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited-scheduler.service\n permissions: '0640'\n\nruncmd:\n- mkdir -p /etc/td \n- |\n export PERMDISK=$(lsscsi 1:0:0:0 -b | awk '{{print $2}}');\n if [ -n \"${{PERMDISK}}\" ]; then blkid --match-token TYPE=ext4 ${{PERMDISK}} || (mkfs.ext4 -m0 ${{PERMDISK}} && e2label ${{PERMDISK}} WORKSPACES); fi\n /usr/bin/echo \"LABEL=WORKSPACES /etc/td ext4 defaults 0 2\" >> /etc/fstab\n /usr/bin/mount -a\n- while [ $(systemctl status docker | grep \"active (running)\" | wc -l) -lt 1 ]; do sleep 5; done\n- sleep 60\n- systemctl enable ai-unlimited.service\n- systemctl start ai-unlimited.service\n- systemctl enable ai-unlimited-scheduler.service\n- systemctl start ai-unlimited-scheduler.service\n", - "$fxv#1": "[Unit]\nDescription=AI Unlimited\nAfter=docker.service\nRequires=docker.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited\nExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited\nExecStartPre=-/usr/bin/docker exec %n stop || true\nExecStartPre=-/usr/bin/docker rm %n || true\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e accept_license=Y \\\n -e PLATFORM=azure \\\n -e ARM_USE_MSI=true \\\n -e ARM_SUBSCRIPTION_ID={5} \\\n -e ARM_TENANT_ID={6} \\\n -v /etc/td/ai-unlimited:/etc/td \\\n -p {3}:3000 \\\n -p {4}:3282 \\\n --network ai_unlimited {7} \\\n --rm --name %n {0}/{1}:{2} workspaces serve -v\n\n[Install]\nWantedBy=multi-user.target", - "$fxv#2": "[Unit]\nDescription=AI Unlimited Scheduler\nAfter=ai-unlimited.service\nRequires=ai-unlimited.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker exec %n stop || true\nExecStartPre=-/usr/bin/docker rm %n || true\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\nExecStart=/usr/bin/docker run \\\n --network ai_unlimited \\\n -p {3}:50061 \\\n -v /etc/td/ai-unlimited:/etc/td \\\n -e TD_WSSCHED_LOG_PATH=/etc/td/workspaces/scheduler_logs \\\n -e TD_WSSCHED_TASK_LOG_PATH=/etc/td/workspaces/scheduler_logs/projects \\\n -e TD_WSSCHED_POL_INTERVAL=2 \\\n -e TD_WS_CONTAINER_NAME=ai-unlimited.service \\\n --rm --name %n {0}/{1}:{2} workspace-event-scheduler serve\n[Install]\nWantedBy=multi-user.target", + "$fxv#0": "#cloud-config\nwrite_files:\n- encoding: b64\n content: \"{0}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited.service\n permissions: '0640'\n- encoding: b64\n content: \"{1}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited-scheduler.service\n permissions: '0640'\n- encoding: b64\n content: \"{2}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited-ui.service\n permissions: '0640'\n\nruncmd:\n- mkdir -p /etc/td\n- |\n export PERMDISK=$(lsscsi 1:0:0:0 -b | awk '{{print $2}}');\n if [ -n \"${{PERMDISK}}\" ]; then blkid --match-token TYPE=ext4 ${{PERMDISK}} || (mkfs.ext4 -m0 ${{PERMDISK}} && e2label ${{PERMDISK}} WORKSPACES); fi\n /usr/bin/echo \"LABEL=WORKSPACES /etc/td ext4 defaults 0 2\" >> /etc/fstab\n /usr/bin/mount -a\n- while [ $(systemctl status docker | grep \"active (running)\" | wc -l) -lt 1 ]; do sleep 5; done\n- mkdir -p /etc/td/ai-unlimited\n- echo \"TD_VCD_INIT_API_KEY=$(LC_ALL=C tr -dc A-Za-z0-9 /etc/td/ai-unlimited/init_api_key.txt\n- sleep 60\n- systemctl enable ai-unlimited.service\n- systemctl start ai-unlimited.service\n- systemctl enable ai-unlimited-scheduler.service\n- systemctl start ai-unlimited-scheduler.service\n- systemctl enable ai-unlimited-ui.service\n- systemctl start ai-unlimited-ui.service\n", + "$fxv#1": "[Unit]\nDescription=AI Unlimited\nAfter=docker.service\nRequires=docker.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nEnvironmentFile=/etc/td/ai-unlimited/init_api_key.txt\nExecStartPre=-/usr/bin/docker volume create ssl_certs\nExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited\nExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited\nExecStartPre=-/usr/bin/docker stop %n\nExecStartPre=-/usr/bin/docker rm %n\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e accept_license=Y \\\n -e PLATFORM=azure \\\n -e ARM_USE_MSI=true \\\n -e ARM_SUBSCRIPTION_ID={5} \\\n -e ARM_TENANT_ID={6} \\\n -e TD_VCD_INIT_API_KEY \\\n -p {3}:3000 \\\n -p {4}:3282 \\\n -v /etc/td/ai-unlimited:/etc/td \\\n -v ssl_certs:/etc/td/ssl \\\n --network ai_unlimited {7} \\\n --rm --name %n {0}/{1}:{2} workspaces serve -v\n\n[Install]\nWantedBy=multi-user.target", + "$fxv#2": "[Unit]\nDescription=AI Unlimited Scheduler\nAfter=ai-unlimited.service\nRequires=ai-unlimited.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker stop %n\nExecStartPre=-/usr/bin/docker rm %n\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\nExecStart=/usr/bin/docker run \\\n --network ai_unlimited \\\n -p {3}:50061 \\\n -v /etc/td/ai-unlimited:/etc/td \\\n -e TD_WSSCHED_LOG_PATH=/etc/td/workspaces/scheduler_logs \\\n -e TD_WSSCHED_TASK_LOG_PATH=/etc/td/workspaces/scheduler_logs/projects \\\n -e TD_WSSCHED_POL_INTERVAL=2 \\\n -e TD_WS_CONTAINER_NAME=ai-unlimited.service \\\n --rm --name %n {0}/{1}:{2} workspace-event-scheduler serve\n[Install]\nWantedBy=multi-user.target", + "$fxv#3": "[Unit]\nDescription=AI Unlimited UI\nAfter=ai-unlimited.service\nRequires=ai-unlimited.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nEnvironmentFile=/etc/td/ai-unlimited/init_api_key.txt\nExecStartPre=-/usr/bin/docker stop %n\nExecStartPre=-/usr/bin/docker rm %n\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e TD_VCD_USE_TLS=false \\\n -e TD_VCD_AUTH_PORT={4}\\\n -e TD_VCD_API_PORT={5}\\\n -e TD_VCD_INIT_API_KEY \\\n -p 80:80 \\\n -p 443:443 \\\n -v ssl_certs:/etc/ssl/td \\\n --network ai_unlimited {6} \\\n --rm --name %n {0}/{1}:{2} \n\n[Install]\nWantedBy=multi-user.target", "AiUnlimitedSchedulerHttpPort": 50061, + "AiUnlimitedUIHttpPort": 80, + "AiUnlimitedUIHttpsPort": 443, "AiUnlimitedSchedulerVersion": "latest", "roleAssignmentName": "[guid(subscription().id, parameters('AiUnlimitedName'), subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), parameters('RoleDefinitionId'))]", "dnsLabelPrefix": "[format('td{0}', uniqueString(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), deployment().name, parameters('AiUnlimitedName')))]", "registry": "teradata", "workspaceRepository": "ai-unlimited-workspaces", "workspaceSchedulerRepository": "ai-unlimited-scheduler", - "cloudInitData": "[base64(format(variables('$fxv#0'), base64(format(variables('$fxv#1'), variables('registry'), variables('workspaceRepository'), parameters('AiUnlimitedVersion'), parameters('AiUnlimitedHttpPort'), parameters('AiUnlimitedGrpcPort'), subscription().subscriptionId, subscription().tenantId, '--network-alias ai-unlimited')), base64(format(variables('$fxv#2'), variables('registry'), variables('workspaceSchedulerRepository'), variables('AiUnlimitedSchedulerVersion'), variables('AiUnlimitedSchedulerHttpPort')))))]" + "workspaceUIRepository": "ai-unlimited-workspaces-ui", + "cloudInitData": "[base64(format(variables('$fxv#0'), base64(format(variables('$fxv#1'), variables('registry'), variables('workspaceRepository'), parameters('AiUnlimitedVersion'), parameters('AiUnlimitedAuthPort'), parameters('AiUnlimitedGrpcPort'), subscription().subscriptionId, subscription().tenantId, '--network-alias ai-unlimited')), base64(format(variables('$fxv#2'), variables('registry'), variables('workspaceSchedulerRepository'), variables('AiUnlimitedSchedulerVersion'), variables('AiUnlimitedSchedulerHttpPort'))), base64(format(variables('$fxv#3'), variables('registry'), variables('workspaceUIRepository'), parameters('AiUnlimitedUIVersion'), variables('AiUnlimitedUIHttpPort'), parameters('AiUnlimitedAuthPort'), parameters('AiUnlimitedGrpcPort'), '--network-alias ai-unlimited'))))]" }, "resources": [ { @@ -224,8 +222,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "6929508783023913887" + "version": "0.31.92.45157", + "templateHash": "29287785012335710" } }, "parameters": { @@ -343,8 +341,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "17702031998905620424" + "version": "0.31.92.45157", + "templateHash": "16095084913002426133" } }, "parameters": { @@ -395,10 +393,10 @@ "value": "[parameters('AccessCIDRs')]" }, "sshAccess": { - "value": "[parameters('AllowPublicSSH')]" + "value": false }, - "aiUnlimitedHttpPort": { - "value": "[parameters('AiUnlimitedHttpPort')]" + "aiUnlimitedAuthPort": { + "value": "[parameters('AiUnlimitedAuthPort')]" }, "aiUnlimitedGrpcPort": { "value": "[parameters('AiUnlimitedGrpcPort')]" @@ -406,6 +404,12 @@ "aiUnlimitedSchedulerHttpPort": { "value": "[variables('AiUnlimitedSchedulerHttpPort')]" }, + "aiUnlimitedUIHttpPort": { + "value": "[variables('AiUnlimitedUIHttpPort')]" + }, + "aiUnlimitedUIHttpsPort": { + "value": "[variables('AiUnlimitedUIHttpsPort')]" + }, "sourceAppSecGroups": { "value": "[parameters('SourceAppSecGroups')]" }, @@ -422,8 +426,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3577307217619354540" + "version": "0.31.92.45157", + "templateHash": "392354314460473239" } }, "parameters": { @@ -449,7 +453,7 @@ "type": "bool", "defaultValue": false }, - "aiUnlimitedHttpPort": { + "aiUnlimitedAuthPort": { "type": "int", "defaultValue": 0 }, @@ -461,6 +465,14 @@ "type": "int", "defaultValue": 0 }, + "aiUnlimitedUIHttpPort": { + "type": "int", + "defaultValue": 0 + }, + "aiUnlimitedUIHttpsPort": { + "type": "int", + "defaultValue": 0 + }, "jupyterHttpPort": { "type": "int", "defaultValue": 0 @@ -526,10 +538,10 @@ ] }, { - "condition": "[not(equals(parameters('aiUnlimitedHttpPort'), 0))]", + "condition": "[not(equals(parameters('aiUnlimitedAuthPort'), 0))]", "type": "Microsoft.Network/networkSecurityGroups/securityRules", "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-http-allow', variables('uniqueSecurityGroupName')))]", + "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-auth-allow', variables('uniqueSecurityGroupName')))]", "properties": { "copy": [ { @@ -552,7 +564,7 @@ "access": "Allow", "description": "allow http to the workspace instance", "destinationAddressPrefix": "*", - "destinationPortRange": "[string(parameters('aiUnlimitedHttpPort'))]", + "destinationPortRange": "[string(parameters('aiUnlimitedAuthPort'))]", "direction": "Inbound", "priority": 701, "protocol": "Tcp", @@ -676,6 +688,82 @@ "dependsOn": [ "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" ] + }, + { + "condition": "[not(equals(parameters('aiUnlimitedUIHttpPort'), 0))]", + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-ui-http-allow', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "destinationApplicationSecurityGroups", + "count": "[length(parameters('detinationAppSecGroups'))]", + "input": { + "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + }, + { + "name": "sourceApplicationSecurityGroups", + "count": "[length(parameters('sourceAppSecGroups'))]", + "input": { + "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + } + ], + "access": "Allow", + "description": "allow http to the workspace ui instance", + "destinationAddressPrefix": "*", + "destinationPortRange": "[string(parameters('aiUnlimitedUIHttpPort'))]", + "direction": "Inbound", + "priority": 705, + "protocol": "Tcp", + "sourceAddressPrefixes": "[parameters('accessCidrs')]", + "sourcePortRange": "*" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" + ] + }, + { + "condition": "[not(equals(parameters('aiUnlimitedUIHttpsPort'), 0))]", + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-ui-https-allow', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "destinationApplicationSecurityGroups", + "count": "[length(parameters('detinationAppSecGroups'))]", + "input": { + "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + }, + { + "name": "sourceApplicationSecurityGroups", + "count": "[length(parameters('sourceAppSecGroups'))]", + "input": { + "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + } + ], + "access": "Allow", + "description": "allow https to the workspace ui instance", + "destinationAddressPrefix": "*", + "destinationPortRange": "[string(parameters('aiUnlimitedUIHttpsPort'))]", + "direction": "Inbound", + "priority": 706, + "protocol": "Tcp", + "sourceAddressPrefixes": "[parameters('accessCidrs')]", + "sourcePortRange": "*" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" + ] } ], "outputs": { @@ -708,7 +796,7 @@ "value": "azureuser" }, "sshPublicKey": { - "value": "[parameters('PublicKey')]" + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'Public-Key'), '2022-09-01').outputs.PublicKey.value]" }, "dnsLabelPrefix": { "value": "[variables('dnsLabelPrefix')]" @@ -750,8 +838,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "13207593080095673481" + "version": "0.31.92.45157", + "templateHash": "11687747621230861550" } }, "parameters": { @@ -1031,8 +1119,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11278674213062453956" + "version": "0.31.92.45157", + "templateHash": "4325662974998231491" } }, "parameters": { @@ -1102,7 +1190,140 @@ } }, "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'firewall')]" + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'firewall')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'Public-Key')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "Public-Key", + "resourceGroup": "[parameters('ResourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "Name": { + "value": "[parameters('AiUnlimitedName')]" + }, + "Location": { + "value": "[deployment().location]" + }, + "VaultName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'vault'), '2022-09-01').outputs.name.value]" + }, + "RoleID": { + "value": "[parameters('RoleDefinitionId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "9690061458974637253" + } + }, + "parameters": { + "Location": { + "type": "string" + }, + "Name": { + "type": "string" + }, + "VaultName": { + "type": "string" + }, + "RoleID": { + "type": "string" + }, + "Uuid": { + "type": "string", + "defaultValue": "[newGuid()]" + } + }, + "variables": { + "SecretName": "[format('{0}-PrivateKey', parameters('Name'))]", + "ScriptName": "[format('{0}-createKeys', parameters('Name'))]", + "IdentityName": "[format('{0}-scratch', parameters('Name'))]", + "RoleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('RoleID'))]", + "RoleDefinitionName": "[guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('IdentityName')), variables('RoleDefinitionId'), resourceGroup().id)]" + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[variables('IdentityName')]", + "location": "[parameters('Location')]" + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[variables('RoleDefinitionName')]", + "properties": { + "roleDefinitionId": "[variables('RoleDefinitionId')]", + "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('IdentityName')), '2023-01-31').principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('IdentityName'))]" + ] + }, + { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2023-08-01", + "name": "[variables('ScriptName')]", + "location": "[parameters('Location')]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('IdentityName')))]": {} + } + }, + "kind": "AzureCLI", + "properties": { + "forceUpdateTag": "[parameters('Uuid')]", + "azCliVersion": "2.0.80", + "timeout": "PT30M", + "retentionInterval": "P1D", + "cleanupPreference": "OnSuccess", + "scriptContent": " #/bin/bash -e\n\n echo -e 'y' | ssh-keygen -f scratch\n\n privateKey=$(cat scratch)\n publicKey=$(cat 'scratch.pub')\n\n json=\"{\\\"keyinfo\\\":{\\\"privateKey\\\":\\\"$privateKey\\\",\\\"publicKey\\\":\\\"$publicKey\\\"}}\"\n\n echo \"$json\" > $AZ_SCRIPTS_OUTPUT_PATH\n " + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('IdentityName'))]", + "[resourceId('Microsoft.Authorization/roleAssignments', variables('RoleDefinitionName'))]" + ] + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('VaultName'), variables('SecretName'))]", + "properties": { + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', variables('ScriptName')), '2023-08-01').outputs.keyinfo.privateKey]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deploymentScripts', variables('ScriptName'))]" + ] + } + ], + "outputs": { + "PublicKey": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', variables('ScriptName')), '2023-08-01').outputs.keyinfo.publicKey]" + }, + "Status": { + "type": "object", + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', variables('ScriptName')), '2023-08-01').status]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'vault')]" ] } ], @@ -1117,11 +1338,11 @@ }, "AiUnlimitedPublicHttpAccess": { "type": "string", - "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PublicIP.value, parameters('AiUnlimitedHttpPort'))]" + "value": "[concat(format('http://{0}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PublicIP.value), if(not(equals(variables('AiUnlimitedUIHttpPort'), 80)), concat(':', string(variables('AiUnlimitedUIHttpPort'))), ''))]" }, "AiUnlimitedPrivateHttpAccess": { "type": "string", - "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value, parameters('AiUnlimitedHttpPort'))]" + "value": "[concat(format('http://{0}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value), if(not(equals(variables('AiUnlimitedUIHttpPort'), 80)), concat(':', string(variables('AiUnlimitedUIHttpPort'))), ''))]" }, "AiUnlimitedPublicGrpcAccess": { "type": "string", @@ -1131,10 +1352,6 @@ "type": "string", "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value, parameters('AiUnlimitedGrpcPort'))]" }, - "sshCommand": { - "type": "string", - "value": "[format('ssh azureuser@{0}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PublicIP.value)]" - }, "KeyVaultName": { "type": "string", "value": "[if(equals(parameters('UseKeyVault'), 'New'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'vault'), '2022-09-01').outputs.name.value, '')]" diff --git a/deployments/azure/templates/arm/all-in-one/all-in-one-with-nlb.json b/deployments/azure/templates/arm/all-in-one/all-in-one-with-nlb.json deleted file mode 100644 index 1007dc0..0000000 --- a/deployments/azure/templates/arm/all-in-one/all-in-one-with-nlb.json +++ /dev/null @@ -1,1511 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "12094445056991366561" - } - }, - "parameters": { - "ResourceGroupName": { - "type": "string", - "defaultValue": "ai-unlimited-workspace", - "metadata": { - "description": "name for the resource group." - } - }, - "AiUnlimitedName": { - "type": "string", - "metadata": { - "description": "Name for the Workspace service's virtual machine." - } - }, - "PublicKey": { - "type": "securestring", - "metadata": { - "description": "SSH public key value" - } - }, - "OSVersion": { - "type": "string", - "defaultValue": "Ubuntu-2004", - "allowedValues": [ - "Ubuntu-1804", - "Ubuntu-2004", - "Ubuntu-2204" - ], - "metadata": { - "description": "The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version." - } - }, - "InstanceType": { - "type": "string", - "defaultValue": "Standard_D2s_v3", - "metadata": { - "description": "The AI Unlimited VM type" - } - }, - "Network": { - "type": "string", - "metadata": { - "description": "Name of the network to run the AI Unlimited service in" - } - }, - "Subnet": { - "type": "string", - "metadata": { - "description": "Name of the subnet to run the AI Unlimited service in" - } - }, - "SecurityGroup": { - "type": "string", - "defaultValue": "AiUnlimitedSecurityGroup", - "metadata": { - "description": "Name of the network security group" - } - }, - "AccessCIDRs": { - "type": "array", - "defaultValue": [ - "0.0.0.0/0" - ], - "metadata": { - "description": "The CIDR ranges that can be used to communicate with the AI Unlimited service instance." - } - }, - "JupyterHttpPort": { - "type": "int", - "defaultValue": 8888, - "metadata": { - "description": "port to access the Jupyter Labs UI." - } - }, - "AiUnlimitedHttpPort": { - "type": "int", - "defaultValue": 3000, - "metadata": { - "description": "port to access the AI Unlimited service UI." - } - }, - "AiUnlimitedGrpcPort": { - "type": "int", - "defaultValue": 3282, - "metadata": { - "description": "port to access the AI Unlimited service api." - } - }, - "SourceAppSecGroups": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Source Application Security Groups to access the AI Unlimited service api." - } - }, - "detinationAppSecGroups": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Destination Application Security Groups to give access to AI Unlimited service instance." - } - }, - "RoleDefinitionId": { - "type": "string", - "metadata": { - "description": "GUID of the AI Unlimited Role" - } - }, - "AllowPublicSSH": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "allow access the AI Unlimited ssh port from the access cidr." - } - }, - "UseKeyVault": { - "type": "string", - "defaultValue": "New", - "allowedValues": [ - "New", - "None" - ], - "metadata": { - "description": "should we create a new Azure Key Vault for bootstrapping the AI Unlimited Engine nodes." - } - }, - "UsePersistentVolume": { - "type": "string", - "defaultValue": "New", - "allowedValues": [ - "New", - "Existing" - ], - "metadata": { - "description": "should we use a new or existing volume for persistent data on the AI Unlimited server." - } - }, - "PersistentVolumeSize": { - "type": "int", - "defaultValue": 100, - "metadata": { - "description": "size of the optional persistent disk to the AI Unlimited server." - } - }, - "ExistingPersistentVolume": { - "type": "string", - "defaultValue": "NONE", - "metadata": { - "description": "Name of the existing persistent volume to attach. Must be in the same region and resourcegroup zone as the AI Unlimited server." - } - }, - "AiUnlimitedVersion": { - "type": "string", - "defaultValue": "v0.2.23", - "metadata": { - "description": "Container Version of the AI Unlimited service" - } - }, - "JupyterVersion": { - "type": "string", - "defaultValue": "latest", - "metadata": { - "description": "Container Version of the Jupyter Labs service" - } - }, - "JupyterToken": { - "type": "securestring", - "metadata": { - "description": "Join token for the Jupyter Labs service" - } - }, - "Tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Tags to apply to all newly created resources, in the form of {\"key_one\":\"value_one\",\"key_two\":\"value_two\"}" - } - } - }, - "variables": { - "$fxv#0": "#cloud-config\nwrite_files:\n- encoding: b64\n content: \"{0}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited.service\n permissions: '0640'\n- encoding: b64\n content: \"{1}\"\n owner: root:root\n path: /usr/lib/systemd/system/jupyter.service\n permissions: '0640'\n- encoding: b64\n content: \"{2}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited-scheduler.service\n permissions: '0640'\n\nruncmd:\n- mkdir -p /etc/td \n- |\n export PERMDISK=$(lsscsi 1:0:0:0 -b | awk '{{print $2}}');\n if [ -n \"${{PERMDISK}}\" ]; then blkid --match-token TYPE=ext4 ${{PERMDISK}} || (mkfs.ext4 -m0 ${{PERMDISK}} && e2label ${{PERMDISK}} WORKSPACES); fi\n /usr/bin/echo \"LABEL=WORKSPACES /etc/td ext4 defaults 0 2\" >> /etc/fstab\n /usr/bin/mount -a\n- while [ $(systemctl status docker | grep \"active (running)\" | wc -l) -lt 1 ]; do sleep 5; done\n- sleep 60\n- systemctl enable ai-unlimited.service\n- systemctl start ai-unlimited.service\n- systemctl enable jupyter.service\n- systemctl start jupyter.service\n- systemctl enable ai-unlimited-scheduler.service\n- systemctl start ai-unlimited-scheduler.service\n", - "$fxv#1": "[Unit]\nDescription=AI Unlimited\nAfter=docker.service\nRequires=docker.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited\nExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited\nExecStartPre=-/usr/bin/docker exec %n stop || true\nExecStartPre=-/usr/bin/docker rm %n || true\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e accept_license=Y \\\n -e PLATFORM=azure \\\n -e ARM_USE_MSI=true \\\n -e ARM_SUBSCRIPTION_ID={5} \\\n -e ARM_TENANT_ID={6} \\\n -v /etc/td/ai-unlimited:/etc/td \\\n -p {3}:3000 \\\n -p {4}:3282 \\\n --network ai_unlimited {7} \\\n --rm --name %n {0}/{1}:{2} workspaces serve -v\n\n[Install]\nWantedBy=multi-user.target", - "$fxv#2": "[Unit]\nDescription=jupyter\nAfter=docker.service\nRequires=docker.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited\nExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{{userdata,ipython}}\nExecStartPre=-/usr/bin/docker exec %n stop || true\nExecStartPre=-/usr/bin/docker rm %n || true\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e accept_license=Y \\\n -e JUPYTER_TOKEN={4} \\\n -v /etc/td/jupyter/userdata:/home/jovyan/JupyterLabRoot/userdata \\\n -v /etc/td/jupyter/ipython:/home/jovyan/.ipython \\\n -p {3}:8888 \\\n --network ai_unlimited \\\n --rm --name %n {0}/{1}:{2}\n\n[Install]\nWantedBy=multi-user.target\n", - "$fxv#3": "[Unit]\nDescription=AI Unlimited Scheduler\nAfter=ai-unlimited.service\nRequires=ai-unlimited.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker exec %n stop || true\nExecStartPre=-/usr/bin/docker rm %n || true\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\nExecStart=/usr/bin/docker run \\\n --network ai_unlimited \\\n -p {3}:50061 \\\n -v /etc/td/ai-unlimited:/etc/td \\\n -e TD_WSSCHED_LOG_PATH=/etc/td/workspaces/scheduler_logs \\\n -e TD_WSSCHED_TASK_LOG_PATH=/etc/td/workspaces/scheduler_logs/projects \\\n -e TD_WSSCHED_POL_INTERVAL=2 \\\n -e TD_WS_CONTAINER_NAME=ai-unlimited.service \\\n --rm --name %n {0}/{1}:{2} workspace-event-scheduler serve\n[Install]\nWantedBy=multi-user.target", - "AiUnlimitedSchedulerGrpcPort": 50051, - "AiUnlimitedSchedulerHttpPort": 50061, - "AiUnlimitedSchedulerVersion": "latest", - "roleAssignmentName": "[guid(subscription().id, parameters('AiUnlimitedName'), subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), parameters('RoleDefinitionId'))]", - "dnsId": "[uniqueString(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), deployment().name, parameters('AiUnlimitedName'))]", - "dnsLabelPrefix": "[format('td{0}', variables('dnsId'))]", - "nlbDnsLabelPrefix": "[format('td{0}-nlb', variables('dnsId'))]", - "registry": "teradata", - "workspaceRepository": "ai-unlimited-workspaces", - "jupyterRepository": "ai-unlimited-jupyter", - "workspaceSchedulerRepository": "ai-unlimited-scheduler" - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "name": "[variables('roleAssignmentName')]", - "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', parameters('RoleDefinitionId'))]", - "principalId": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrincipleId.value]" - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited')]" - ] - }, - { - "condition": "[equals(parameters('UseKeyVault'), 'New')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "vault", - "resourceGroup": "[parameters('ResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "encryptVolumes": { - "value": true - }, - "keyVaultName": { - "value": "[parameters('AiUnlimitedName')]" - }, - "location": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), '2022-09-01', 'full').location]" - }, - "tags": { - "value": "[parameters('Tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "6929508783023913887" - } - }, - "parameters": { - "encryptVolumes": { - "type": "bool" - }, - "keyVaultName": { - "type": "string" - }, - "location": { - "type": "string" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "uuid": { - "type": "string", - "defaultValue": "[newGuid()]" - } - }, - "variables": { - "nameCharLimit": 24, - "uniqueName": "[format('{0}-{1}', parameters('keyVaultName'), uniqueString(parameters('uuid')))]", - "uniqueKeyVaultName": "[substring(format('{0}', variables('uniqueName')), 0, if(less(length(variables('uniqueName')), variables('nameCharLimit')), length(variables('uniqueName')), variables('nameCharLimit')))]" - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", - "name": "[variables('uniqueKeyVaultName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "sku": { - "family": "A", - "name": "standard" - }, - "tenantId": "[subscription().tenantId]", - "softDeleteRetentionInDays": 7, - "enableSoftDelete": true, - "enablePurgeProtection": "[if(parameters('encryptVolumes'), true(), null())]", - "enabledForDiskEncryption": "[parameters('encryptVolumes')]", - "accessPolicies": [] - } - } - ], - "outputs": { - "id": { - "type": "string", - "value": "[resourceId('Microsoft.KeyVault/vaults', variables('uniqueKeyVaultName'))]" - }, - "name": { - "type": "string", - "value": "[variables('uniqueKeyVaultName')]" - } - } - } - } - }, - { - "condition": "[equals(parameters('UseKeyVault'), 'New')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "vault-access-policy", - "resourceGroup": "[parameters('ResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "vaultName": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'vault'), '2022-09-01').outputs.name.value]" - }, - "accessPolicy": { - "value": { - "tenantId": "[subscription().tenantId]", - "objectId": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrincipleId.value]", - "permissions": { - "keys": [ - "Create", - "Delete", - "Get", - "List", - "Update", - "Purge", - "Recover", - "Decrypt", - "Encrypt", - "Sign", - "UnwrapKey", - "Verify", - "WrapKey", - "GetRotationPolicy", - "SetRotationPolicy" - ], - "secrets": [ - "Get", - "Set", - "Delete", - "List", - "Purge" - ], - "storage": [ - "Get" - ] - } - } - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "17702031998905620424" - } - }, - "parameters": { - "vaultName": { - "type": "string" - }, - "accessPolicy": { - "type": "object" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults/accessPolicies", - "apiVersion": "2022-07-01", - "name": "[format('{0}/add', parameters('vaultName'))]", - "properties": { - "accessPolicies": [ - "[parameters('accessPolicy')]" - ] - } - } - ] - } - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited')]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'vault')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "firewall", - "resourceGroup": "[parameters('ResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), '2022-09-01', 'full').location]" - }, - "name": { - "value": "[parameters('SecurityGroup')]" - }, - "accessCidrs": { - "value": "[parameters('AccessCIDRs')]" - }, - "sshAccess": { - "value": "[parameters('AllowPublicSSH')]" - }, - "aiUnlimitedHttpPort": { - "value": "[parameters('AiUnlimitedHttpPort')]" - }, - "aiUnlimitedGrpcPort": { - "value": "[parameters('AiUnlimitedGrpcPort')]" - }, - "aiUnlimitedSchedulerHttpPort": { - "value": "[variables('AiUnlimitedSchedulerHttpPort')]" - }, - "jupyterHttpPort": { - "value": "[parameters('JupyterHttpPort')]" - }, - "sourceAppSecGroups": { - "value": "[parameters('SourceAppSecGroups')]" - }, - "detinationAppSecGroups": { - "value": "[parameters('detinationAppSecGroups')]" - }, - "tags": { - "value": "[parameters('Tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3577307217619354540" - } - }, - "parameters": { - "location": { - "type": "string" - }, - "name": { - "type": "string" - }, - "accessCidrs": { - "type": "array", - "defaultValue": [] - }, - "sourceAppSecGroups": { - "type": "array", - "defaultValue": [] - }, - "detinationAppSecGroups": { - "type": "array", - "defaultValue": [] - }, - "sshAccess": { - "type": "bool", - "defaultValue": false - }, - "aiUnlimitedHttpPort": { - "type": "int", - "defaultValue": 0 - }, - "aiUnlimitedGrpcPort": { - "type": "int", - "defaultValue": 0 - }, - "aiUnlimitedSchedulerHttpPort": { - "type": "int", - "defaultValue": 0 - }, - "jupyterHttpPort": { - "type": "int", - "defaultValue": 0 - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "uuid": { - "type": "string", - "defaultValue": "[newGuid()]" - } - }, - "variables": { - "nameCharLimit": 60, - "uniqueName": "[format('{0}-{1}', parameters('name'), uniqueString(parameters('uuid')))]", - "uniqueSecurityGroupName": "[substring(format('{0}', variables('uniqueName')), 0, if(less(length(variables('uniqueName')), variables('nameCharLimit')), length(variables('uniqueName')), variables('nameCharLimit')))]" - }, - "resources": [ - { - "type": "Microsoft.Network/networkSecurityGroups", - "apiVersion": "2022-11-01", - "name": "[variables('uniqueSecurityGroupName')]", - "tags": "[parameters('tags')]", - "location": "[parameters('location')]" - }, - { - "condition": "[parameters('sshAccess')]", - "type": "Microsoft.Network/networkSecurityGroups/securityRules", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-ssh-allow', variables('uniqueSecurityGroupName')))]", - "properties": { - "copy": [ - { - "name": "destinationApplicationSecurityGroups", - "count": "[length(parameters('detinationAppSecGroups'))]", - "input": { - "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - }, - { - "name": "sourceApplicationSecurityGroups", - "count": "[length(parameters('sourceAppSecGroups'))]", - "input": { - "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - } - ], - "access": "Allow", - "description": "allow ssh to the workspace instance", - "destinationAddressPrefix": "*", - "destinationPortRange": "22", - "direction": "Inbound", - "priority": 700, - "protocol": "Tcp", - "sourceAddressPrefixes": "[parameters('accessCidrs')]", - "sourcePortRange": "*" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" - ] - }, - { - "condition": "[not(equals(parameters('aiUnlimitedHttpPort'), 0))]", - "type": "Microsoft.Network/networkSecurityGroups/securityRules", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-http-allow', variables('uniqueSecurityGroupName')))]", - "properties": { - "copy": [ - { - "name": "destinationApplicationSecurityGroups", - "count": "[length(parameters('detinationAppSecGroups'))]", - "input": { - "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - }, - { - "name": "sourceApplicationSecurityGroups", - "count": "[length(parameters('sourceAppSecGroups'))]", - "input": { - "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - } - ], - "access": "Allow", - "description": "allow http to the workspace instance", - "destinationAddressPrefix": "*", - "destinationPortRange": "[string(parameters('aiUnlimitedHttpPort'))]", - "direction": "Inbound", - "priority": 701, - "protocol": "Tcp", - "sourceAddressPrefixes": "[parameters('accessCidrs')]", - "sourcePortRange": "*" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" - ] - }, - { - "condition": "[not(equals(parameters('aiUnlimitedGrpcPort'), 0))]", - "type": "Microsoft.Network/networkSecurityGroups/securityRules", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-grpc-allow', variables('uniqueSecurityGroupName')))]", - "properties": { - "copy": [ - { - "name": "destinationApplicationSecurityGroups", - "count": "[length(parameters('detinationAppSecGroups'))]", - "input": { - "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - }, - { - "name": "sourceApplicationSecurityGroups", - "count": "[length(parameters('sourceAppSecGroups'))]", - "input": { - "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - } - ], - "access": "Allow", - "description": "allow grpc to the workspace instance", - "destinationAddressPrefix": "*", - "destinationPortRange": "[string(parameters('aiUnlimitedGrpcPort'))]", - "direction": "Inbound", - "priority": 702, - "protocol": "Tcp", - "sourceAddressPrefixes": "[parameters('accessCidrs')]", - "sourcePortRange": "*" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" - ] - }, - { - "condition": "[not(equals(parameters('jupyterHttpPort'), 0))]", - "type": "Microsoft.Network/networkSecurityGroups/securityRules", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-juptyer-http-allow', variables('uniqueSecurityGroupName')))]", - "properties": { - "copy": [ - { - "name": "destinationApplicationSecurityGroups", - "count": "[length(parameters('detinationAppSecGroups'))]", - "input": { - "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - }, - { - "name": "sourceApplicationSecurityGroups", - "count": "[length(parameters('sourceAppSecGroups'))]", - "input": { - "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - } - ], - "access": "Allow", - "description": "allow http to the jupyter instance", - "destinationAddressPrefix": "*", - "destinationPortRange": "[string(parameters('jupyterHttpPort'))]", - "direction": "Inbound", - "priority": 703, - "protocol": "Tcp", - "sourceAddressPrefixes": "[parameters('accessCidrs')]", - "sourcePortRange": "*" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" - ] - }, - { - "condition": "[not(equals(parameters('aiUnlimitedSchedulerHttpPort'), 0))]", - "type": "Microsoft.Network/networkSecurityGroups/securityRules", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-scheduler-http-allow', variables('uniqueSecurityGroupName')))]", - "properties": { - "copy": [ - { - "name": "destinationApplicationSecurityGroups", - "count": "[length(parameters('detinationAppSecGroups'))]", - "input": { - "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - }, - { - "name": "sourceApplicationSecurityGroups", - "count": "[length(parameters('sourceAppSecGroups'))]", - "input": { - "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - } - ], - "access": "Allow", - "description": "allow http to the scheduler instance", - "destinationAddressPrefix": "*", - "destinationPortRange": "[string(parameters('aiUnlimitedSchedulerHttpPort'))]", - "direction": "Inbound", - "priority": 704, - "protocol": "Tcp", - "sourceAddressPrefixes": "[parameters('accessCidrs')]", - "sourcePortRange": "*" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" - ] - } - ], - "outputs": { - "Id": { - "type": "string", - "value": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "loadbalancer", - "resourceGroup": "[parameters('ResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[parameters('AiUnlimitedName')]" - }, - "location": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), '2022-09-01', 'full').location]" - }, - "dnsPrefix": { - "value": "[variables('dnsLabelPrefix')]" - }, - "aiUnlimitedHttpPort": { - "value": "[parameters('AiUnlimitedHttpPort')]" - }, - "aiUnlimitedGrpcPort": { - "value": "[parameters('AiUnlimitedGrpcPort')]" - }, - "aiUnlimitedSchedulerHttpPort": { - "value": "[variables('AiUnlimitedSchedulerHttpPort')]" - }, - "jupyterHttpPort": { - "value": "[parameters('JupyterHttpPort')]" - }, - "tags": { - "value": "[parameters('Tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "14601682444932366115" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string" - }, - "dnsPrefix": { - "type": "string" - }, - "aiUnlimitedHttpPort": { - "type": "int", - "defaultValue": 0 - }, - "aiUnlimitedGrpcPort": { - "type": "int", - "defaultValue": 0 - }, - "aiUnlimitedSchedulerHttpPort": { - "type": "int", - "defaultValue": 0 - }, - "jupyterHttpPort": { - "type": "int", - "defaultValue": 0 - }, - "tags": { - "type": "object", - "defaultValue": {} - } - }, - "resources": [ - { - "type": "Microsoft.Network/loadBalancers", - "apiVersion": "2021-08-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "sku": { - "name": "Standard" - }, - "properties": { - "frontendIPConfigurations": [ - { - "name": "[format('{0}Inbound', parameters('name'))]", - "properties": { - "publicIPAddress": { - "id": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-inbound', parameters('name'))), '2022-09-01').outputs.Id.value]" - } - } - }, - { - "name": "[format('{0}Outbound', parameters('name'))]", - "properties": { - "publicIPAddress": { - "id": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-outbound', parameters('name'))), '2022-09-01').outputs.Id.value]" - } - } - } - ], - "backendAddressPools": [ - { - "name": "[format('{0}InboundBackendPool', parameters('name'))]" - }, - { - "name": "[format('{0}OutboundBackendPool', parameters('name'))]" - } - ], - "loadBalancingRules": "[flatten(createArray(if(not(equals(parameters('aiUnlimitedHttpPort'), 0)), createArray(createObject('name', 'AiUnlimitedUI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedHttpPort'), 'backendPort', parameters('aiUnlimitedHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}UILbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedGrpcPort'), 0)), createArray(createObject('name', 'AiUnlimitedAPI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedGrpcPort'), 'backendPort', parameters('aiUnlimitedGrpcPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}APILbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('jupyterHttpPort'), 0)), createArray(createObject('name', 'JupyterUI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('jupyterHttpPort'), 'backendPort', parameters('jupyterHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}JupyterLbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedSchedulerHttpPort'), 0)), createArray(createObject('name', 'AiUnlimitedSchedulerHttp', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedSchedulerHttpPort'), 'backendPort', parameters('aiUnlimitedSchedulerHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}SchedulerHttpLbProbe', parameters('name'))))))), createArray())))]", - "probes": "[flatten(createArray(if(not(equals(parameters('aiUnlimitedHttpPort'), 0)), createArray(createObject('name', format('{0}UILbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('aiUnlimitedHttpPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedGrpcPort'), 0)), createArray(createObject('name', format('{0}APILbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('aiUnlimitedGrpcPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('jupyterHttpPort'), 0)), createArray(createObject('name', format('{0}JupyterLbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('jupyterHttpPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedSchedulerHttpPort'), 0)), createArray(createObject('name', format('{0}SchedulerHttpLbProbe', parameters('name')), 'properties', createObject('protocol', 'Http', 'port', parameters('aiUnlimitedSchedulerHttpPort'), 'requestPath', '/healthcheck', 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray())))]", - "outboundRules": [ - { - "name": "myOutboundRule", - "properties": { - "allocatedOutboundPorts": 10000, - "protocol": "All", - "enableTcpReset": false, - "idleTimeoutInMinutes": 15, - "backendAddressPool": { - "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))]" - }, - "frontendIPConfigurations": [ - { - "id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Outbound', parameters('name')))]" - } - ] - } - } - ] - }, - "tags": "[parameters('tags')]", - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('{0}-inbound', parameters('name')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-outbound', parameters('name')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-inbound', parameters('name'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('{0}-inbound', parameters('name'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "dnsPrefix": { - "value": "[parameters('dnsPrefix')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11278674213062453956" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string" - }, - "dnsPrefix": { - "type": "string" - }, - "tags": { - "type": "object" - } - }, - "resources": [ - { - "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "2021-05-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "sku": { - "name": "Standard" - }, - "properties": { - "dnsSettings": "[if(not(equals(parameters('dnsPrefix'), '')), createObject('domainNameLabel', parameters('dnsPrefix')), null())]", - "publicIPAddressVersion": "IPv4", - "publicIPAllocationMethod": "Static", - "idleTimeoutInMinutes": 5 - }, - "tags": "[parameters('tags')]" - } - ], - "outputs": { - "Id": { - "type": "string", - "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" - }, - "Ip": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), '2021-05-01').ipAddress]" - }, - "Dns": { - "type": "string", - "value": "[if(not(equals(parameters('dnsPrefix'), '')), reference(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), '2021-05-01').dnsSettings.fqdn, '')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-outbound', parameters('name'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[format('{0}-outbound', parameters('name'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "dnsPrefix": { - "value": "" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11278674213062453956" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string" - }, - "dnsPrefix": { - "type": "string" - }, - "tags": { - "type": "object" - } - }, - "resources": [ - { - "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "2021-05-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "sku": { - "name": "Standard" - }, - "properties": { - "dnsSettings": "[if(not(equals(parameters('dnsPrefix'), '')), createObject('domainNameLabel', parameters('dnsPrefix')), null())]", - "publicIPAddressVersion": "IPv4", - "publicIPAllocationMethod": "Static", - "idleTimeoutInMinutes": 5 - }, - "tags": "[parameters('tags')]" - } - ], - "outputs": { - "Id": { - "type": "string", - "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" - }, - "Ip": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), '2021-05-01').ipAddress]" - }, - "Dns": { - "type": "string", - "value": "[if(not(equals(parameters('dnsPrefix'), '')), reference(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), '2021-05-01').dnsSettings.fqdn, '')]" - } - } - } - } - } - ], - "outputs": { - "nlbPools": { - "type": "array", - "value": [ - "[format('{0}InboundBackendPool', parameters('name'))]", - "[format('{0}OutboundBackendPool', parameters('name'))]" - ] - }, - "PublicIp": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-inbound', parameters('name'))), '2022-09-01').outputs.Ip.value]" - }, - "PublicDns": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-inbound', parameters('name'))), '2022-09-01').outputs.Dns.value]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "ai-unlimited", - "resourceGroup": "[parameters('ResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), '2022-09-01', 'full').location]" - }, - "name": { - "value": "[parameters('AiUnlimitedName')]" - }, - "adminUsername": { - "value": "azureuser" - }, - "sshPublicKey": { - "value": "[parameters('PublicKey')]" - }, - "dnsLabelPrefix": { - "value": "[variables('dnsLabelPrefix')]" - }, - "vmSize": { - "value": "[parameters('InstanceType')]" - }, - "subnetId": { - "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', parameters('Network'), parameters('Subnet'))]" - }, - "networkSecurityGroupID": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'firewall'), '2022-09-01').outputs.Id.value]" - }, - "osVersion": { - "value": "[parameters('OSVersion')]" - }, - "cloudInitData": { - "value": "[base64(format(variables('$fxv#0'), base64(format(variables('$fxv#1'), variables('registry'), variables('workspaceRepository'), parameters('AiUnlimitedVersion'), parameters('AiUnlimitedHttpPort'), parameters('AiUnlimitedGrpcPort'), subscription().subscriptionId, subscription().tenantId, format('--network-alias {0}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer'), '2022-09-01').outputs.PublicDns.value))), base64(format(variables('$fxv#2'), variables('registry'), variables('jupyterRepository'), parameters('JupyterVersion'), parameters('JupyterHttpPort'), parameters('JupyterToken'))), base64(format(variables('$fxv#3'), variables('registry'), variables('workspaceSchedulerRepository'), variables('AiUnlimitedSchedulerVersion'), variables('AiUnlimitedSchedulerGrpcPort'), variables('AiUnlimitedSchedulerHttpPort')))))]" - }, - "usePersistentVolume": { - "value": "[parameters('UsePersistentVolume')]" - }, - "persistentVolumeSize": { - "value": "[parameters('PersistentVolumeSize')]" - }, - "existingPersistentVolume": { - "value": "[parameters('ExistingPersistentVolume')]" - }, - "nlbName": { - "value": "[parameters('AiUnlimitedName')]" - }, - "nlbPoolNames": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer'), '2022-09-01').outputs.nlbPools.value]" - }, - "usePublicIp": { - "value": false - }, - "tags": { - "value": "[parameters('Tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "13207593080095673481" - } - }, - "parameters": { - "location": { - "type": "string" - }, - "name": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "sshPublicKey": { - "type": "string" - }, - "vmSize": { - "type": "string" - }, - "subnetId": { - "type": "string" - }, - "networkSecurityGroupID": { - "type": "string" - }, - "osVersion": { - "type": "string" - }, - "usePersistentVolume": { - "type": "string" - }, - "persistentVolumeSize": { - "type": "int" - }, - "existingPersistentVolume": { - "type": "string" - }, - "cloudInitData": { - "type": "string" - }, - "usePublicIp": { - "type": "bool", - "defaultValue": false - }, - "nlbName": { - "type": "string", - "defaultValue": "" - }, - "albName": { - "type": "string", - "defaultValue": "" - }, - "nlbPoolNames": { - "type": "array", - "defaultValue": [] - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "dnsLabelPrefix": { - "type": "string", - "defaultValue": "" - } - }, - "variables": { - "copy": [ - { - "name": "resourcePools", - "count": "[length(parameters('nlbPoolNames'))]", - "input": { - "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('nlbName'), parameters('nlbPoolNames')[copyIndex('resourcePools')])]" - } - } - ], - "imageReference": { - "Ubuntu-1804": { - "publisher": "Canonical", - "offer": "UbuntuServer", - "sku": "18_04-lts-gen2", - "version": "latest" - }, - "Ubuntu-2004": { - "publisher": "Canonical", - "offer": "0001-com-ubuntu-server-focal", - "sku": "20_04-lts-gen2", - "version": "latest" - }, - "Ubuntu-2204": { - "publisher": "Canonical", - "offer": "0001-com-ubuntu-server-jammy", - "sku": "22_04-lts-gen2", - "version": "latest" - } - }, - "publicIPAddressName": "[format('{0}PublicIP', parameters('name'))]", - "networkInterfaceName": "[format('{0}-nic', parameters('name'))]", - "osDiskType": "Standard_LRS", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "path": "[format('/home/{0}/.ssh/authorized_keys', parameters('adminUsername'))]", - "keyData": "[parameters('sshPublicKey')]" - } - ] - } - }, - "trustedExtensionName": "GuestAttestation", - "trustedExtensionPublisher": "Microsoft.Azure.Security.LinuxAttestation", - "trustedExtensionVersion": "1.0", - "trustedMaaTenantName": "GuestAttestation", - "trustedMaaEndpoint": "[substring('emptystring', 0, 0)]", - "dockerExtensionName": "DockerExtension", - "dockerExtensionPublisher": "Microsoft.Azure.Extensions", - "dockerExtensionVersion": "1.1" - }, - "resources": [ - { - "condition": "[equals(parameters('usePersistentVolume'), 'New')]", - "type": "Microsoft.Compute/disks", - "apiVersion": "2023-04-02", - "name": "[format('{0}-disk', parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "creationData": { - "createOption": "Empty" - }, - "diskSizeGB": "[parameters('persistentVolumeSize')]", - "maxShares": 1, - "osType": "Linux" - } - }, - { - "type": "Microsoft.Network/networkInterfaces", - "apiVersion": "2022-11-01", - "name": "[variables('networkInterfaceName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "ipConfigurations": [ - "[if(parameters('usePublicIp'), createObject('name', 'ipconfigpublic', 'properties', createObject('privateIPAllocationMethod', 'Dynamic', 'subnet', createObject('id', parameters('subnetId')), 'publicIPAddress', createObject('id', reference(resourceId('Microsoft.Resources/deployments', variables('publicIPAddressName')), '2022-09-01').outputs.Id.value), 'loadBalancerBackendAddressPools', variables('resourcePools'))), if(equals(parameters('albName'), ''), createObject('name', 'ipconfigprivate', 'properties', createObject('privateIPAllocationMethod', 'Dynamic', 'subnet', createObject('id', parameters('subnetId')), 'loadBalancerBackendAddressPools', variables('resourcePools'))), createObject('name', 'ipconfigprivate', 'properties', createObject('privateIPAllocationMethod', 'Dynamic', 'subnet', createObject('id', parameters('subnetId'))))))]" - ], - "networkSecurityGroup": { - "id": "[parameters('networkSecurityGroupID')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', variables('publicIPAddressName'))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines", - "apiVersion": "2023-03-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "storageProfile": { - "osDisk": { - "createOption": "FromImage", - "managedDisk": { - "storageAccountType": "[variables('osDiskType')]" - } - }, - "dataDisks": [ - { - "lun": 0, - "createOption": "Attach", - "managedDisk": { - "id": "[if(equals(parameters('usePersistentVolume'), 'New'), resourceId('Microsoft.Compute/disks', format('{0}-disk', parameters('name'))), resourceId('Microsoft.Compute/disks', parameters('existingPersistentVolume')))]" - } - } - ], - "imageReference": "[variables('imageReference')[parameters('osVersion')]]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]" - } - ] - }, - "osProfile": { - "computerName": "[parameters('name')]", - "adminUsername": "[parameters('adminUsername')]", - "linuxConfiguration": "[variables('linuxConfiguration')]" - }, - "securityProfile": { - "securityType": "TrustedLaunch", - "uefiSettings": { - "secureBootEnabled": true, - "vTpmEnabled": true - } - }, - "userData": "[parameters('cloudInitData')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]", - "[resourceId('Microsoft.Compute/disks', format('{0}-disk', parameters('name')))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2023-03-01", - "name": "[format('{0}/{1}', parameters('name'), variables('trustedExtensionName'))]", - "tags": "[parameters('tags')]", - "location": "[parameters('location')]", - "properties": { - "publisher": "[variables('trustedExtensionPublisher')]", - "type": "[variables('trustedExtensionName')]", - "typeHandlerVersion": "[variables('trustedExtensionVersion')]", - "autoUpgradeMinorVersion": true, - "settings": { - "AttestationConfig": { - "MaaSettings": { - "maaEndpoint": "[variables('trustedMaaEndpoint')]", - "maaTenantName": "[variables('trustedMaaTenantName')]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2023-03-01", - "name": "[format('{0}/{1}', parameters('name'), variables('dockerExtensionName'))]", - "tags": "[parameters('tags')]", - "location": "[parameters('location')]", - "properties": { - "publisher": "[variables('dockerExtensionPublisher')]", - "type": "[variables('dockerExtensionName')]", - "typeHandlerVersion": "[variables('dockerExtensionVersion')]", - "autoUpgradeMinorVersion": true - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]" - ] - }, - { - "condition": "[parameters('usePublicIp')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[variables('publicIPAddressName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[variables('publicIPAddressName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "dnsPrefix": { - "value": "[parameters('dnsLabelPrefix')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11278674213062453956" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string" - }, - "dnsPrefix": { - "type": "string" - }, - "tags": { - "type": "object" - } - }, - "resources": [ - { - "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "2021-05-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "sku": { - "name": "Standard" - }, - "properties": { - "dnsSettings": "[if(not(equals(parameters('dnsPrefix'), '')), createObject('domainNameLabel', parameters('dnsPrefix')), null())]", - "publicIPAddressVersion": "IPv4", - "publicIPAllocationMethod": "Static", - "idleTimeoutInMinutes": 5 - }, - "tags": "[parameters('tags')]" - } - ], - "outputs": { - "Id": { - "type": "string", - "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" - }, - "Ip": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), '2021-05-01').ipAddress]" - }, - "Dns": { - "type": "string", - "value": "[if(not(equals(parameters('dnsPrefix'), '')), reference(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), '2021-05-01').dnsSettings.fqdn, '')]" - } - } - } - } - } - ], - "outputs": { - "PublicIP": { - "type": "string", - "value": "[if(parameters('usePublicIp'), reference(resourceId('Microsoft.Resources/deployments', variables('publicIPAddressName')), '2022-09-01').outputs.Ip.value, '')]" - }, - "PrivateIP": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName')), '2022-11-01').ipConfigurations[0].properties.privateIPAddress]" - }, - "PrincipleId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Compute/virtualMachines', parameters('name')), '2023-03-01', 'full').identity.principalId]" - } - } - } - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'firewall')]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer')]" - ] - } - ], - "outputs": { - "PublicIP": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer'), '2022-09-01').outputs.PublicIp.value]" - }, - "PrivateIP": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value]" - }, - "AiUnlimitedPublicHttpAccess": { - "type": "string", - "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer'), '2022-09-01').outputs.PublicDns.value, parameters('AiUnlimitedHttpPort'))]" - }, - "AiUnlimitedPrivateHttpAccess": { - "type": "string", - "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value, parameters('AiUnlimitedHttpPort'))]" - }, - "AiUnlimitedPublicGrpcAccess": { - "type": "string", - "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer'), '2022-09-01').outputs.PublicDns.value, parameters('AiUnlimitedGrpcPort'))]" - }, - "AiUnlimitedPrivateGrpcAccess": { - "type": "string", - "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value, parameters('AiUnlimitedGrpcPort'))]" - }, - "JupyterLabPublicHttpAccess": { - "type": "string", - "value": "[format('http://{0}:{1}?token={2}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'loadbalancer'), '2022-09-01').outputs.PublicDns.value, parameters('JupyterHttpPort'), parameters('JupyterToken'))]" - }, - "JupyterLabPrivateHttpAccess": { - "type": "string", - "value": "[format('http://{0}:{1}?token={2}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value, parameters('JupyterHttpPort'), parameters('JupyterToken'))]" - }, - "sshCommand": { - "type": "string", - "value": "[format('ssh azureuser@{0}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value)]" - }, - "SecurityGroup": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'firewall'), '2022-09-01').outputs.Id.value]" - } - } -} \ No newline at end of file diff --git a/deployments/azure/templates/arm/all-in-one/all-in-one-without-lb.json b/deployments/azure/templates/arm/all-in-one/all-in-one-without-lb.json deleted file mode 100644 index d32b169..0000000 --- a/deployments/azure/templates/arm/all-in-one/all-in-one-without-lb.json +++ /dev/null @@ -1,1177 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "5181328523083770552" - } - }, - "parameters": { - "ResourceGroupName": { - "type": "string", - "defaultValue": "ai-unlimited-workspace", - "metadata": { - "description": "name for the resource group." - } - }, - "AiUnlimitedName": { - "type": "string", - "metadata": { - "description": "Name for the Workspace service's virtual machine." - } - }, - "PublicKey": { - "type": "securestring", - "metadata": { - "description": "SSH public key value" - } - }, - "OSVersion": { - "type": "string", - "defaultValue": "Ubuntu-2004", - "allowedValues": [ - "Ubuntu-1804", - "Ubuntu-2004", - "Ubuntu-2204" - ], - "metadata": { - "description": "The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version." - } - }, - "InstanceType": { - "type": "string", - "defaultValue": "Standard_D2s_v3", - "metadata": { - "description": "The AI Unlimited VM type" - } - }, - "Network": { - "type": "string", - "metadata": { - "description": "Name of the network to run the AI Unlimited service in" - } - }, - "Subnet": { - "type": "string", - "metadata": { - "description": "Name of the subnet to run the AI Unlimited service in" - } - }, - "SecurityGroup": { - "type": "string", - "defaultValue": "AiUnlimitedSecurityGroup", - "metadata": { - "description": "Name of the network security group" - } - }, - "AccessCIDRs": { - "type": "array", - "defaultValue": [ - "0.0.0.0/0" - ], - "metadata": { - "description": "The CIDR ranges that can be used to communicate with the AI Unlimited service instance." - } - }, - "JupyterHttpPort": { - "type": "int", - "defaultValue": 8888, - "metadata": { - "description": "port to access the Jupyter Labs UI." - } - }, - "AiUnlimitedHttpPort": { - "type": "int", - "defaultValue": 3000, - "metadata": { - "description": "port to access the AI Unlimited service UI." - } - }, - "AiUnlimitedGrpcPort": { - "type": "int", - "defaultValue": 3282, - "metadata": { - "description": "port to access the AI Unlimited service api." - } - }, - "SourceAppSecGroups": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Source Application Security Groups to access the AI Unlimited service api." - } - }, - "detinationAppSecGroups": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Destination Application Security Groups to give access to AI Unlimited service instance." - } - }, - "RoleDefinitionId": { - "type": "string", - "metadata": { - "description": "GUID of the AI Unlimited Role" - } - }, - "AllowPublicSSH": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "allow access the AI Unlimited ssh port from the access cidr." - } - }, - "UseKeyVault": { - "type": "string", - "defaultValue": "New", - "allowedValues": [ - "New", - "None" - ], - "metadata": { - "description": "should we create a new Azure Key Vault for bootstrapping the AI Unlimited Engine nodes." - } - }, - "UsePersistentVolume": { - "type": "string", - "defaultValue": "New", - "allowedValues": [ - "New", - "Existing" - ], - "metadata": { - "description": "should we use a new or existing volume for persistent data on the AI Unlimited server." - } - }, - "PersistentVolumeSize": { - "type": "int", - "defaultValue": 100, - "metadata": { - "description": "size of the optional persistent disk to the AI Unlimited server." - } - }, - "ExistingPersistentVolume": { - "type": "string", - "defaultValue": "NONE", - "metadata": { - "description": "Name of the existing persistent volume to attach. Must be in the same region and resourcegroup zone as the AI Unlimited server." - } - }, - "AiUnlimitedVersion": { - "type": "string", - "defaultValue": "v0.2.23", - "metadata": { - "description": "Container Version of the AI Unlimited service" - } - }, - "JupyterVersion": { - "type": "string", - "defaultValue": "latest", - "metadata": { - "description": "Container Version of the Jupyter Labs service" - } - }, - "JupyterToken": { - "type": "securestring", - "metadata": { - "description": "Join token for the Jupyter Labs service" - } - }, - "Tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Tags to apply to all newly created resources, in the form of {\"key_one\":\"value_one\",\"key_two\":\"value_two\"}" - } - } - }, - "variables": { - "$fxv#0": "#cloud-config\nwrite_files:\n- encoding: b64\n content: \"{0}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited.service\n permissions: '0640'\n- encoding: b64\n content: \"{1}\"\n owner: root:root\n path: /usr/lib/systemd/system/jupyter.service\n permissions: '0640'\n- encoding: b64\n content: \"{2}\"\n owner: root:root\n path: /usr/lib/systemd/system/ai-unlimited-scheduler.service\n permissions: '0640'\n\nruncmd:\n- mkdir -p /etc/td \n- |\n export PERMDISK=$(lsscsi 1:0:0:0 -b | awk '{{print $2}}');\n if [ -n \"${{PERMDISK}}\" ]; then blkid --match-token TYPE=ext4 ${{PERMDISK}} || (mkfs.ext4 -m0 ${{PERMDISK}} && e2label ${{PERMDISK}} WORKSPACES); fi\n /usr/bin/echo \"LABEL=WORKSPACES /etc/td ext4 defaults 0 2\" >> /etc/fstab\n /usr/bin/mount -a\n- while [ $(systemctl status docker | grep \"active (running)\" | wc -l) -lt 1 ]; do sleep 5; done\n- sleep 60\n- systemctl enable ai-unlimited.service\n- systemctl start ai-unlimited.service\n- systemctl enable jupyter.service\n- systemctl start jupyter.service\n- systemctl enable ai-unlimited-scheduler.service\n- systemctl start ai-unlimited-scheduler.service\n", - "$fxv#1": "[Unit]\nDescription=AI Unlimited\nAfter=docker.service\nRequires=docker.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited\nExecStartPre=-/usr/bin/mkdir -p /etc/td/ai-unlimited\nExecStartPre=-/usr/bin/docker exec %n stop || true\nExecStartPre=-/usr/bin/docker rm %n || true\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e accept_license=Y \\\n -e PLATFORM=azure \\\n -e ARM_USE_MSI=true \\\n -e ARM_SUBSCRIPTION_ID={5} \\\n -e ARM_TENANT_ID={6} \\\n -v /etc/td/ai-unlimited:/etc/td \\\n -p {3}:3000 \\\n -p {4}:3282 \\\n --network ai_unlimited {7} \\\n --rm --name %n {0}/{1}:{2} workspaces serve -v\n\n[Install]\nWantedBy=multi-user.target", - "$fxv#2": "[Unit]\nDescription=jupyter\nAfter=docker.service\nRequires=docker.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited\nExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{{userdata,ipython}}\nExecStartPre=-/usr/bin/docker exec %n stop || true\nExecStartPre=-/usr/bin/docker rm %n || true\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e accept_license=Y \\\n -e JUPYTER_TOKEN={4} \\\n -v /etc/td/jupyter/userdata:/home/jovyan/JupyterLabRoot/userdata \\\n -v /etc/td/jupyter/ipython:/home/jovyan/.ipython \\\n -p {3}:8888 \\\n --network ai_unlimited \\\n --rm --name %n {0}/{1}:{2}\n\n[Install]\nWantedBy=multi-user.target\n", - "$fxv#3": "[Unit]\nDescription=AI Unlimited Scheduler\nAfter=ai-unlimited.service\nRequires=ai-unlimited.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker exec %n stop || true\nExecStartPre=-/usr/bin/docker rm %n || true\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\nExecStart=/usr/bin/docker run \\\n --network ai_unlimited \\\n -p {3}:50061 \\\n -v /etc/td/ai-unlimited:/etc/td \\\n -e TD_WSSCHED_LOG_PATH=/etc/td/workspaces/scheduler_logs \\\n -e TD_WSSCHED_TASK_LOG_PATH=/etc/td/workspaces/scheduler_logs/projects \\\n -e TD_WSSCHED_POL_INTERVAL=2 \\\n -e TD_WS_CONTAINER_NAME=ai-unlimited.service \\\n --rm --name %n {0}/{1}:{2} workspace-event-scheduler serve\n[Install]\nWantedBy=multi-user.target", - "AiUnlimitedSchedulerGrpcPort": 50051, - "AiUnlimitedSchedulerHttpPort": 50061, - "AiUnlimitedSchedulerVersion": "latest", - "roleAssignmentName": "[guid(subscription().id, parameters('AiUnlimitedName'), subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), parameters('RoleDefinitionId'))]", - "dnsLabelPrefix": "[format('td{0}', uniqueString(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), deployment().name, parameters('AiUnlimitedName')))]", - "registry": "teradata", - "workspaceRepository": "ai-unlimited-workspaces", - "jupyterRepository": "ai-unlimited-jupyter", - "workspaceSchedulerRepository": "ai-unlimited-scheduler", - "cloudInitData": "[base64(format(variables('$fxv#0'), base64(format(variables('$fxv#1'), variables('registry'), variables('workspaceRepository'), parameters('AiUnlimitedVersion'), parameters('AiUnlimitedHttpPort'), parameters('AiUnlimitedGrpcPort'), subscription().subscriptionId, subscription().tenantId, '--network-alias ai-unlimited')), base64(format(variables('$fxv#2'), variables('registry'), variables('jupyterRepository'), parameters('JupyterVersion'), parameters('JupyterHttpPort'), parameters('JupyterToken'))), base64(format(variables('$fxv#3'), variables('registry'), variables('workspaceSchedulerRepository'), variables('AiUnlimitedSchedulerVersion'), variables('AiUnlimitedSchedulerGrpcPort'), variables('AiUnlimitedSchedulerHttpPort')))))]" - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "name": "[variables('roleAssignmentName')]", - "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', parameters('RoleDefinitionId'))]", - "principalId": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrincipleId.value]" - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited')]" - ] - }, - { - "condition": "[equals(parameters('UseKeyVault'), 'New')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "vault", - "resourceGroup": "[parameters('ResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "encryptVolumes": { - "value": true - }, - "keyVaultName": { - "value": "[parameters('AiUnlimitedName')]" - }, - "location": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), '2022-09-01', 'full').location]" - }, - "tags": { - "value": "[parameters('Tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "6929508783023913887" - } - }, - "parameters": { - "encryptVolumes": { - "type": "bool" - }, - "keyVaultName": { - "type": "string" - }, - "location": { - "type": "string" - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "uuid": { - "type": "string", - "defaultValue": "[newGuid()]" - } - }, - "variables": { - "nameCharLimit": 24, - "uniqueName": "[format('{0}-{1}', parameters('keyVaultName'), uniqueString(parameters('uuid')))]", - "uniqueKeyVaultName": "[substring(format('{0}', variables('uniqueName')), 0, if(less(length(variables('uniqueName')), variables('nameCharLimit')), length(variables('uniqueName')), variables('nameCharLimit')))]" - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", - "name": "[variables('uniqueKeyVaultName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "sku": { - "family": "A", - "name": "standard" - }, - "tenantId": "[subscription().tenantId]", - "softDeleteRetentionInDays": 7, - "enableSoftDelete": true, - "enablePurgeProtection": "[if(parameters('encryptVolumes'), true(), null())]", - "enabledForDiskEncryption": "[parameters('encryptVolumes')]", - "accessPolicies": [] - } - } - ], - "outputs": { - "id": { - "type": "string", - "value": "[resourceId('Microsoft.KeyVault/vaults', variables('uniqueKeyVaultName'))]" - }, - "name": { - "type": "string", - "value": "[variables('uniqueKeyVaultName')]" - } - } - } - } - }, - { - "condition": "[equals(parameters('UseKeyVault'), 'New')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "vault-access-policy", - "resourceGroup": "[parameters('ResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "vaultName": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'vault'), '2022-09-01').outputs.name.value]" - }, - "accessPolicy": { - "value": { - "tenantId": "[subscription().tenantId]", - "objectId": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrincipleId.value]", - "permissions": { - "keys": [ - "Create", - "Delete", - "Get", - "List", - "Update", - "Purge", - "Recover", - "Decrypt", - "Encrypt", - "Sign", - "UnwrapKey", - "Verify", - "WrapKey", - "GetRotationPolicy", - "SetRotationPolicy" - ], - "secrets": [ - "Get", - "Set", - "Delete", - "List", - "Purge" - ], - "storage": [ - "Get" - ] - } - } - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "17702031998905620424" - } - }, - "parameters": { - "vaultName": { - "type": "string" - }, - "accessPolicy": { - "type": "object" - } - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults/accessPolicies", - "apiVersion": "2022-07-01", - "name": "[format('{0}/add', parameters('vaultName'))]", - "properties": { - "accessPolicies": [ - "[parameters('accessPolicy')]" - ] - } - } - ] - } - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited')]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'vault')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "firewall", - "resourceGroup": "[parameters('ResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), '2022-09-01', 'full').location]" - }, - "name": { - "value": "[parameters('SecurityGroup')]" - }, - "accessCidrs": { - "value": "[parameters('AccessCIDRs')]" - }, - "sshAccess": { - "value": "[parameters('AllowPublicSSH')]" - }, - "aiUnlimitedHttpPort": { - "value": "[parameters('AiUnlimitedHttpPort')]" - }, - "aiUnlimitedGrpcPort": { - "value": "[parameters('AiUnlimitedGrpcPort')]" - }, - "aiUnlimitedSchedulerHttpPort": { - "value": "[variables('AiUnlimitedSchedulerHttpPort')]" - }, - "jupyterHttpPort": { - "value": "[parameters('JupyterHttpPort')]" - }, - "sourceAppSecGroups": { - "value": "[parameters('SourceAppSecGroups')]" - }, - "detinationAppSecGroups": { - "value": "[parameters('detinationAppSecGroups')]" - }, - "tags": { - "value": "[parameters('Tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3577307217619354540" - } - }, - "parameters": { - "location": { - "type": "string" - }, - "name": { - "type": "string" - }, - "accessCidrs": { - "type": "array", - "defaultValue": [] - }, - "sourceAppSecGroups": { - "type": "array", - "defaultValue": [] - }, - "detinationAppSecGroups": { - "type": "array", - "defaultValue": [] - }, - "sshAccess": { - "type": "bool", - "defaultValue": false - }, - "aiUnlimitedHttpPort": { - "type": "int", - "defaultValue": 0 - }, - "aiUnlimitedGrpcPort": { - "type": "int", - "defaultValue": 0 - }, - "aiUnlimitedSchedulerHttpPort": { - "type": "int", - "defaultValue": 0 - }, - "jupyterHttpPort": { - "type": "int", - "defaultValue": 0 - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "uuid": { - "type": "string", - "defaultValue": "[newGuid()]" - } - }, - "variables": { - "nameCharLimit": 60, - "uniqueName": "[format('{0}-{1}', parameters('name'), uniqueString(parameters('uuid')))]", - "uniqueSecurityGroupName": "[substring(format('{0}', variables('uniqueName')), 0, if(less(length(variables('uniqueName')), variables('nameCharLimit')), length(variables('uniqueName')), variables('nameCharLimit')))]" - }, - "resources": [ - { - "type": "Microsoft.Network/networkSecurityGroups", - "apiVersion": "2022-11-01", - "name": "[variables('uniqueSecurityGroupName')]", - "tags": "[parameters('tags')]", - "location": "[parameters('location')]" - }, - { - "condition": "[parameters('sshAccess')]", - "type": "Microsoft.Network/networkSecurityGroups/securityRules", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-ssh-allow', variables('uniqueSecurityGroupName')))]", - "properties": { - "copy": [ - { - "name": "destinationApplicationSecurityGroups", - "count": "[length(parameters('detinationAppSecGroups'))]", - "input": { - "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - }, - { - "name": "sourceApplicationSecurityGroups", - "count": "[length(parameters('sourceAppSecGroups'))]", - "input": { - "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - } - ], - "access": "Allow", - "description": "allow ssh to the workspace instance", - "destinationAddressPrefix": "*", - "destinationPortRange": "22", - "direction": "Inbound", - "priority": 700, - "protocol": "Tcp", - "sourceAddressPrefixes": "[parameters('accessCidrs')]", - "sourcePortRange": "*" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" - ] - }, - { - "condition": "[not(equals(parameters('aiUnlimitedHttpPort'), 0))]", - "type": "Microsoft.Network/networkSecurityGroups/securityRules", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-http-allow', variables('uniqueSecurityGroupName')))]", - "properties": { - "copy": [ - { - "name": "destinationApplicationSecurityGroups", - "count": "[length(parameters('detinationAppSecGroups'))]", - "input": { - "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - }, - { - "name": "sourceApplicationSecurityGroups", - "count": "[length(parameters('sourceAppSecGroups'))]", - "input": { - "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - } - ], - "access": "Allow", - "description": "allow http to the workspace instance", - "destinationAddressPrefix": "*", - "destinationPortRange": "[string(parameters('aiUnlimitedHttpPort'))]", - "direction": "Inbound", - "priority": 701, - "protocol": "Tcp", - "sourceAddressPrefixes": "[parameters('accessCidrs')]", - "sourcePortRange": "*" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" - ] - }, - { - "condition": "[not(equals(parameters('aiUnlimitedGrpcPort'), 0))]", - "type": "Microsoft.Network/networkSecurityGroups/securityRules", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-grpc-allow', variables('uniqueSecurityGroupName')))]", - "properties": { - "copy": [ - { - "name": "destinationApplicationSecurityGroups", - "count": "[length(parameters('detinationAppSecGroups'))]", - "input": { - "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - }, - { - "name": "sourceApplicationSecurityGroups", - "count": "[length(parameters('sourceAppSecGroups'))]", - "input": { - "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - } - ], - "access": "Allow", - "description": "allow grpc to the workspace instance", - "destinationAddressPrefix": "*", - "destinationPortRange": "[string(parameters('aiUnlimitedGrpcPort'))]", - "direction": "Inbound", - "priority": 702, - "protocol": "Tcp", - "sourceAddressPrefixes": "[parameters('accessCidrs')]", - "sourcePortRange": "*" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" - ] - }, - { - "condition": "[not(equals(parameters('jupyterHttpPort'), 0))]", - "type": "Microsoft.Network/networkSecurityGroups/securityRules", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-juptyer-http-allow', variables('uniqueSecurityGroupName')))]", - "properties": { - "copy": [ - { - "name": "destinationApplicationSecurityGroups", - "count": "[length(parameters('detinationAppSecGroups'))]", - "input": { - "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - }, - { - "name": "sourceApplicationSecurityGroups", - "count": "[length(parameters('sourceAppSecGroups'))]", - "input": { - "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - } - ], - "access": "Allow", - "description": "allow http to the jupyter instance", - "destinationAddressPrefix": "*", - "destinationPortRange": "[string(parameters('jupyterHttpPort'))]", - "direction": "Inbound", - "priority": 703, - "protocol": "Tcp", - "sourceAddressPrefixes": "[parameters('accessCidrs')]", - "sourcePortRange": "*" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" - ] - }, - { - "condition": "[not(equals(parameters('aiUnlimitedSchedulerHttpPort'), 0))]", - "type": "Microsoft.Network/networkSecurityGroups/securityRules", - "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-scheduler-http-allow', variables('uniqueSecurityGroupName')))]", - "properties": { - "copy": [ - { - "name": "destinationApplicationSecurityGroups", - "count": "[length(parameters('detinationAppSecGroups'))]", - "input": { - "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - }, - { - "name": "sourceApplicationSecurityGroups", - "count": "[length(parameters('sourceAppSecGroups'))]", - "input": { - "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", - "location": "[parameters('location')]" - } - } - ], - "access": "Allow", - "description": "allow http to the scheduler instance", - "destinationAddressPrefix": "*", - "destinationPortRange": "[string(parameters('aiUnlimitedSchedulerHttpPort'))]", - "direction": "Inbound", - "priority": 704, - "protocol": "Tcp", - "sourceAddressPrefixes": "[parameters('accessCidrs')]", - "sourcePortRange": "*" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" - ] - } - ], - "outputs": { - "Id": { - "type": "string", - "value": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "ai-unlimited", - "resourceGroup": "[parameters('ResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), '2022-09-01', 'full').location]" - }, - "name": { - "value": "[parameters('AiUnlimitedName')]" - }, - "adminUsername": { - "value": "azureuser" - }, - "sshPublicKey": { - "value": "[parameters('PublicKey')]" - }, - "dnsLabelPrefix": { - "value": "[variables('dnsLabelPrefix')]" - }, - "vmSize": { - "value": "[parameters('InstanceType')]" - }, - "subnetId": { - "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', parameters('Network'), parameters('Subnet'))]" - }, - "networkSecurityGroupID": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'firewall'), '2022-09-01').outputs.Id.value]" - }, - "osVersion": { - "value": "[parameters('OSVersion')]" - }, - "cloudInitData": { - "value": "[variables('cloudInitData')]" - }, - "usePersistentVolume": { - "value": "[parameters('UsePersistentVolume')]" - }, - "persistentVolumeSize": { - "value": "[parameters('PersistentVolumeSize')]" - }, - "existingPersistentVolume": { - "value": "[parameters('ExistingPersistentVolume')]" - }, - "usePublicIp": { - "value": true - }, - "tags": { - "value": "[parameters('Tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "13207593080095673481" - } - }, - "parameters": { - "location": { - "type": "string" - }, - "name": { - "type": "string" - }, - "adminUsername": { - "type": "string" - }, - "sshPublicKey": { - "type": "string" - }, - "vmSize": { - "type": "string" - }, - "subnetId": { - "type": "string" - }, - "networkSecurityGroupID": { - "type": "string" - }, - "osVersion": { - "type": "string" - }, - "usePersistentVolume": { - "type": "string" - }, - "persistentVolumeSize": { - "type": "int" - }, - "existingPersistentVolume": { - "type": "string" - }, - "cloudInitData": { - "type": "string" - }, - "usePublicIp": { - "type": "bool", - "defaultValue": false - }, - "nlbName": { - "type": "string", - "defaultValue": "" - }, - "albName": { - "type": "string", - "defaultValue": "" - }, - "nlbPoolNames": { - "type": "array", - "defaultValue": [] - }, - "tags": { - "type": "object", - "defaultValue": {} - }, - "dnsLabelPrefix": { - "type": "string", - "defaultValue": "" - } - }, - "variables": { - "copy": [ - { - "name": "resourcePools", - "count": "[length(parameters('nlbPoolNames'))]", - "input": { - "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('nlbName'), parameters('nlbPoolNames')[copyIndex('resourcePools')])]" - } - } - ], - "imageReference": { - "Ubuntu-1804": { - "publisher": "Canonical", - "offer": "UbuntuServer", - "sku": "18_04-lts-gen2", - "version": "latest" - }, - "Ubuntu-2004": { - "publisher": "Canonical", - "offer": "0001-com-ubuntu-server-focal", - "sku": "20_04-lts-gen2", - "version": "latest" - }, - "Ubuntu-2204": { - "publisher": "Canonical", - "offer": "0001-com-ubuntu-server-jammy", - "sku": "22_04-lts-gen2", - "version": "latest" - } - }, - "publicIPAddressName": "[format('{0}PublicIP', parameters('name'))]", - "networkInterfaceName": "[format('{0}-nic', parameters('name'))]", - "osDiskType": "Standard_LRS", - "linuxConfiguration": { - "disablePasswordAuthentication": true, - "ssh": { - "publicKeys": [ - { - "path": "[format('/home/{0}/.ssh/authorized_keys', parameters('adminUsername'))]", - "keyData": "[parameters('sshPublicKey')]" - } - ] - } - }, - "trustedExtensionName": "GuestAttestation", - "trustedExtensionPublisher": "Microsoft.Azure.Security.LinuxAttestation", - "trustedExtensionVersion": "1.0", - "trustedMaaTenantName": "GuestAttestation", - "trustedMaaEndpoint": "[substring('emptystring', 0, 0)]", - "dockerExtensionName": "DockerExtension", - "dockerExtensionPublisher": "Microsoft.Azure.Extensions", - "dockerExtensionVersion": "1.1" - }, - "resources": [ - { - "condition": "[equals(parameters('usePersistentVolume'), 'New')]", - "type": "Microsoft.Compute/disks", - "apiVersion": "2023-04-02", - "name": "[format('{0}-disk', parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "creationData": { - "createOption": "Empty" - }, - "diskSizeGB": "[parameters('persistentVolumeSize')]", - "maxShares": 1, - "osType": "Linux" - } - }, - { - "type": "Microsoft.Network/networkInterfaces", - "apiVersion": "2022-11-01", - "name": "[variables('networkInterfaceName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "ipConfigurations": [ - "[if(parameters('usePublicIp'), createObject('name', 'ipconfigpublic', 'properties', createObject('privateIPAllocationMethod', 'Dynamic', 'subnet', createObject('id', parameters('subnetId')), 'publicIPAddress', createObject('id', reference(resourceId('Microsoft.Resources/deployments', variables('publicIPAddressName')), '2022-09-01').outputs.Id.value), 'loadBalancerBackendAddressPools', variables('resourcePools'))), if(equals(parameters('albName'), ''), createObject('name', 'ipconfigprivate', 'properties', createObject('privateIPAllocationMethod', 'Dynamic', 'subnet', createObject('id', parameters('subnetId')), 'loadBalancerBackendAddressPools', variables('resourcePools'))), createObject('name', 'ipconfigprivate', 'properties', createObject('privateIPAllocationMethod', 'Dynamic', 'subnet', createObject('id', parameters('subnetId'))))))]" - ], - "networkSecurityGroup": { - "id": "[parameters('networkSecurityGroupID')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', variables('publicIPAddressName'))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines", - "apiVersion": "2023-03-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "hardwareProfile": { - "vmSize": "[parameters('vmSize')]" - }, - "storageProfile": { - "osDisk": { - "createOption": "FromImage", - "managedDisk": { - "storageAccountType": "[variables('osDiskType')]" - } - }, - "dataDisks": [ - { - "lun": 0, - "createOption": "Attach", - "managedDisk": { - "id": "[if(equals(parameters('usePersistentVolume'), 'New'), resourceId('Microsoft.Compute/disks', format('{0}-disk', parameters('name'))), resourceId('Microsoft.Compute/disks', parameters('existingPersistentVolume')))]" - } - } - ], - "imageReference": "[variables('imageReference')[parameters('osVersion')]]" - }, - "networkProfile": { - "networkInterfaces": [ - { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]" - } - ] - }, - "osProfile": { - "computerName": "[parameters('name')]", - "adminUsername": "[parameters('adminUsername')]", - "linuxConfiguration": "[variables('linuxConfiguration')]" - }, - "securityProfile": { - "securityType": "TrustedLaunch", - "uefiSettings": { - "secureBootEnabled": true, - "vTpmEnabled": true - } - }, - "userData": "[parameters('cloudInitData')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]", - "[resourceId('Microsoft.Compute/disks', format('{0}-disk', parameters('name')))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2023-03-01", - "name": "[format('{0}/{1}', parameters('name'), variables('trustedExtensionName'))]", - "tags": "[parameters('tags')]", - "location": "[parameters('location')]", - "properties": { - "publisher": "[variables('trustedExtensionPublisher')]", - "type": "[variables('trustedExtensionName')]", - "typeHandlerVersion": "[variables('trustedExtensionVersion')]", - "autoUpgradeMinorVersion": true, - "settings": { - "AttestationConfig": { - "MaaSettings": { - "maaEndpoint": "[variables('trustedMaaEndpoint')]", - "maaTenantName": "[variables('trustedMaaTenantName')]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2023-03-01", - "name": "[format('{0}/{1}', parameters('name'), variables('dockerExtensionName'))]", - "tags": "[parameters('tags')]", - "location": "[parameters('location')]", - "properties": { - "publisher": "[variables('dockerExtensionPublisher')]", - "type": "[variables('dockerExtensionName')]", - "typeHandlerVersion": "[variables('dockerExtensionVersion')]", - "autoUpgradeMinorVersion": true - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]" - ] - }, - { - "condition": "[parameters('usePublicIp')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[variables('publicIPAddressName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "name": { - "value": "[variables('publicIPAddressName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "dnsPrefix": { - "value": "[parameters('dnsLabelPrefix')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11278674213062453956" - } - }, - "parameters": { - "name": { - "type": "string" - }, - "location": { - "type": "string" - }, - "dnsPrefix": { - "type": "string" - }, - "tags": { - "type": "object" - } - }, - "resources": [ - { - "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "2021-05-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "sku": { - "name": "Standard" - }, - "properties": { - "dnsSettings": "[if(not(equals(parameters('dnsPrefix'), '')), createObject('domainNameLabel', parameters('dnsPrefix')), null())]", - "publicIPAddressVersion": "IPv4", - "publicIPAllocationMethod": "Static", - "idleTimeoutInMinutes": 5 - }, - "tags": "[parameters('tags')]" - } - ], - "outputs": { - "Id": { - "type": "string", - "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" - }, - "Ip": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), '2021-05-01').ipAddress]" - }, - "Dns": { - "type": "string", - "value": "[if(not(equals(parameters('dnsPrefix'), '')), reference(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), '2021-05-01').dnsSettings.fqdn, '')]" - } - } - } - } - } - ], - "outputs": { - "PublicIP": { - "type": "string", - "value": "[if(parameters('usePublicIp'), reference(resourceId('Microsoft.Resources/deployments', variables('publicIPAddressName')), '2022-09-01').outputs.Ip.value, '')]" - }, - "PrivateIP": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName')), '2022-11-01').ipConfigurations[0].properties.privateIPAddress]" - }, - "PrincipleId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Compute/virtualMachines', parameters('name')), '2023-03-01', 'full').identity.principalId]" - } - } - } - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'firewall')]" - ] - } - ], - "outputs": { - "PublicIP": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PublicIP.value]" - }, - "PrivateIP": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value]" - }, - "AiUnlimitedPublicHttpAccess": { - "type": "string", - "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PublicIP.value, parameters('AiUnlimitedHttpPort'))]" - }, - "AiUnlimitedPrivateHttpAccess": { - "type": "string", - "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value, parameters('AiUnlimitedHttpPort'))]" - }, - "AiUnlimitedPublicGrpcAccess": { - "type": "string", - "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PublicIP.value, parameters('AiUnlimitedGrpcPort'))]" - }, - "AiUnlimitedPrivateGrpcAccess": { - "type": "string", - "value": "[format('http://{0}:{1}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value, parameters('AiUnlimitedGrpcPort'))]" - }, - "JupyterLabPublicHttpAccess": { - "type": "string", - "value": "[format('http://{0}:{1}?token={2}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PublicIP.value, parameters('JupyterHttpPort'), parameters('JupyterToken'))]" - }, - "JupyterLabPrivateHttpAccess": { - "type": "string", - "value": "[format('http://{0}:{1}?token={2}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PrivateIP.value, parameters('JupyterHttpPort'), parameters('JupyterToken'))]" - }, - "sshCommand": { - "type": "string", - "value": "[format('ssh azureuser@{0}', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'ai-unlimited'), '2022-09-01').outputs.PublicIP.value)]" - }, - "SecurityGroup": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'firewall'), '2022-09-01').outputs.Id.value]" - } - } -} \ No newline at end of file diff --git a/deployments/azure/templates/arm/all-in-one/readme.txt b/deployments/azure/templates/arm/all-in-one/readme.txt deleted file mode 100644 index b07593c..0000000 --- a/deployments/azure/templates/arm/all-in-one/readme.txt +++ /dev/null @@ -1,7 +0,0 @@ -# Disclaimer - -**IMPORTANT NOTICE** - -The all-in-one arm templates provided in this directory are for informational purposes only. These templates are currently not supported by Teradata Corporation. - -**Use of these templates is at your own risk. Teradata Corporation assumes no responsibility or liability for any damages or issues that may arise from using these templates.** \ No newline at end of file diff --git a/deployments/azure/templates/arm/init/resources.json b/deployments/azure/templates/arm/init/resources.json index 47cf3a6..a2c19a3 100644 --- a/deployments/azure/templates/arm/init/resources.json +++ b/deployments/azure/templates/arm/init/resources.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3755223610480160492" + "version": "0.29.47.4906", + "templateHash": "11746473283873690149" } }, "parameters": { @@ -74,6 +74,7 @@ "Microsoft.KeyVault/vaults/read", "Microsoft.KeyVault/vaults/write", "Microsoft.KeyVault/vaults/delete", + "Microsoft.KeyVault/vaults/secrets/write", "Microsoft.KeyVault/vaults/accessPolicies/write", "Microsoft.KeyVault/locations/operationResults/read", "Microsoft.KeyVault/locations/deletedVaults/purge/action", @@ -163,8 +164,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3893635077912970094" + "version": "0.29.47.4906", + "templateHash": "10026202347308594610" } }, "parameters": { @@ -240,4 +241,4 @@ "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('name')), 'Microsoft.Resources/deployments', 'networkDeployment'), '2022-09-01').outputs.subnetName.value]" } } -} +} \ No newline at end of file diff --git a/deployments/azure/templates/arm/jupyter/jupyter-with-nlb.json b/deployments/azure/templates/arm/jupyter/jupyter-with-nlb.json index 0743441..8b6c555 100644 --- a/deployments/azure/templates/arm/jupyter/jupyter-with-nlb.json +++ b/deployments/azure/templates/arm/jupyter/jupyter-with-nlb.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13407002185802552800" + "version": "0.31.92.45157", + "templateHash": "1520819731474419291" } }, "parameters": { @@ -130,7 +130,7 @@ }, "JupyterVersion": { "type": "string", - "defaultValue": "latest", + "defaultValue": "v0.1.0", "metadata": { "description": "Container Version of the Jupyter Labs service" } @@ -151,7 +151,7 @@ }, "variables": { "$fxv#0": "#cloud-config\nwrite_files:\n- encoding: b64\n content: \"{0}\"\n owner: root:root\n path: /usr/lib/systemd/system/jupyter.service\n permissions: '0640'\n\nruncmd:\n- mkdir -p /etc/td \n- |\n export PERMDISK=$(lsscsi 1:0:0:0 -b | awk '{{print $2}}');\n if [ -n \"${{PERMDISK}}\" ]; then blkid --match-token TYPE=ext4 ${{PERMDISK}} || (mkfs.ext4 -m0 ${{PERMDISK}} && e2label ${{PERMDISK}} WORKSPACES); fi\n /usr/bin/echo \"LABEL=WORKSPACES /etc/td ext4 defaults 0 2\" >> /etc/fstab\n /usr/bin/mount -a\n- while [ $(systemctl status docker | grep \"active (running)\" | wc -l) -lt 1 ]; do sleep 5; done\n- sleep 60\n- systemctl enable jupyter.service\n- systemctl start jupyter.service\n", - "$fxv#1": "[Unit]\nDescription=jupyter\nAfter=docker.service\nRequires=docker.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited\nExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{{userdata,ipython}}\nExecStartPre=-/usr/bin/docker exec %n stop || true\nExecStartPre=-/usr/bin/docker rm %n || true\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e accept_license=Y \\\n -e JUPYTER_TOKEN={4} \\\n -v /etc/td/jupyter/userdata:/home/jovyan/JupyterLabRoot/userdata \\\n -v /etc/td/jupyter/ipython:/home/jovyan/.ipython \\\n -p {3}:8888 \\\n --network ai_unlimited \\\n --rm --name %n {0}/{1}:{2}\n\n[Install]\nWantedBy=multi-user.target\n", + "$fxv#1": "[Unit]\nDescription=jupyter\nAfter=docker.service\nRequires=docker.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited\nExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{{userdata,ipython}}\nExecStartPre=-/usr/bin/docker stop %n\nExecStartPre=-/usr/bin/docker rm %n\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e accept_license=Y \\\n -e JUPYTER_TOKEN={4} \\\n -v /etc/td/jupyter/userdata:/home/jovyan/JupyterLabRoot/userdata \\\n -v /etc/td/jupyter/ipython:/home/jovyan/.ipython \\\n -p {3}:8888 \\\n --network ai_unlimited \\\n --rm --name %n {0}/{1}:{2}\n\n[Install]\nWantedBy=multi-user.target\n", "dnsId": "[uniqueString(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), deployment().name, parameters('JupyterName'))]", "dnsLabelPrefix": "[format('td{0}', variables('dnsId'))]", "nlbDnsLabelPrefix": "[format('td{0}-nlb', variables('dnsId'))]", @@ -202,8 +202,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4929926268271391774" + "version": "0.31.92.45157", + "templateHash": "392354314460473239" } }, "parameters": { @@ -229,7 +229,7 @@ "type": "bool", "defaultValue": false }, - "aiUnlimitedHttpPort": { + "aiUnlimitedAuthPort": { "type": "int", "defaultValue": 0 }, @@ -241,6 +241,14 @@ "type": "int", "defaultValue": 0 }, + "aiUnlimitedUIHttpPort": { + "type": "int", + "defaultValue": 0 + }, + "aiUnlimitedUIHttpsPort": { + "type": "int", + "defaultValue": 0 + }, "jupyterHttpPort": { "type": "int", "defaultValue": 0 @@ -306,10 +314,10 @@ ] }, { - "condition": "[not(equals(parameters('aiUnlimitedHttpPort'), 0))]", + "condition": "[not(equals(parameters('aiUnlimitedAuthPort'), 0))]", "type": "Microsoft.Network/networkSecurityGroups/securityRules", "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-http-allow', variables('uniqueSecurityGroupName')))]", + "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-auth-allow', variables('uniqueSecurityGroupName')))]", "properties": { "copy": [ { @@ -332,7 +340,7 @@ "access": "Allow", "description": "allow http to the workspace instance", "destinationAddressPrefix": "*", - "destinationPortRange": "[string(parameters('aiUnlimitedHttpPort'))]", + "destinationPortRange": "[string(parameters('aiUnlimitedAuthPort'))]", "direction": "Inbound", "priority": 701, "protocol": "Tcp", @@ -456,6 +464,82 @@ "dependsOn": [ "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" ] + }, + { + "condition": "[not(equals(parameters('aiUnlimitedUIHttpPort'), 0))]", + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-ui-http-allow', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "destinationApplicationSecurityGroups", + "count": "[length(parameters('detinationAppSecGroups'))]", + "input": { + "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + }, + { + "name": "sourceApplicationSecurityGroups", + "count": "[length(parameters('sourceAppSecGroups'))]", + "input": { + "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + } + ], + "access": "Allow", + "description": "allow http to the workspace ui instance", + "destinationAddressPrefix": "*", + "destinationPortRange": "[string(parameters('aiUnlimitedUIHttpPort'))]", + "direction": "Inbound", + "priority": 705, + "protocol": "Tcp", + "sourceAddressPrefixes": "[parameters('accessCidrs')]", + "sourcePortRange": "*" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" + ] + }, + { + "condition": "[not(equals(parameters('aiUnlimitedUIHttpsPort'), 0))]", + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-ui-https-allow', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "destinationApplicationSecurityGroups", + "count": "[length(parameters('detinationAppSecGroups'))]", + "input": { + "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + }, + { + "name": "sourceApplicationSecurityGroups", + "count": "[length(parameters('sourceAppSecGroups'))]", + "input": { + "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + } + ], + "access": "Allow", + "description": "allow https to the workspace ui instance", + "destinationAddressPrefix": "*", + "destinationPortRange": "[string(parameters('aiUnlimitedUIHttpsPort'))]", + "direction": "Inbound", + "priority": 706, + "protocol": "Tcp", + "sourceAddressPrefixes": "[parameters('accessCidrs')]", + "sourcePortRange": "*" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" + ] } ], "outputs": { @@ -500,8 +584,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9026566997103330654" + "version": "0.31.92.45157", + "templateHash": "370499549708602593" } }, "parameters": { @@ -514,7 +598,7 @@ "dnsPrefix": { "type": "string" }, - "aiUnlimitedHttpPort": { + "aiUnlimitedAuthPort": { "type": "int", "defaultValue": 0 }, @@ -530,6 +614,14 @@ "type": "int", "defaultValue": 0 }, + "aiUnlimitedUIHttpPort": { + "type": "int", + "defaultValue": 0 + }, + "aiUnlimitedUIHttpsPort": { + "type": "int", + "defaultValue": 0 + }, "tags": { "type": "object", "defaultValue": {} @@ -571,8 +663,8 @@ "name": "[format('{0}OutboundBackendPool', parameters('name'))]" } ], - "loadBalancingRules": "[flatten(createArray(if(not(equals(parameters('aiUnlimitedHttpPort'), 0)), createArray(createObject('name', 'AiUnlimitedUI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedHttpPort'), 'backendPort', parameters('aiUnlimitedHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}UILbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedGrpcPort'), 0)), createArray(createObject('name', 'AiUnlimitedAPI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedGrpcPort'), 'backendPort', parameters('aiUnlimitedGrpcPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}APILbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('jupyterHttpPort'), 0)), createArray(createObject('name', 'JupyterUI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('jupyterHttpPort'), 'backendPort', parameters('jupyterHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}JupyterLbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedSchedulerHttpPort'), 0)), createArray(createObject('name', 'AiUnlimitedSchedulerHttp', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedSchedulerHttpPort'), 'backendPort', parameters('aiUnlimitedSchedulerHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}SchedulerHttpLbProbe', parameters('name'))))))), createArray())))]", - "probes": "[flatten(createArray(if(not(equals(parameters('aiUnlimitedHttpPort'), 0)), createArray(createObject('name', format('{0}UILbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('aiUnlimitedHttpPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedGrpcPort'), 0)), createArray(createObject('name', format('{0}APILbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('aiUnlimitedGrpcPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('jupyterHttpPort'), 0)), createArray(createObject('name', format('{0}JupyterLbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('jupyterHttpPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedSchedulerHttpPort'), 0)), createArray(createObject('name', format('{0}SchedulerHttpLbProbe', parameters('name')), 'properties', createObject('protocol', 'Http', 'port', parameters('aiUnlimitedSchedulerHttpPort'), 'requestPath', '/healthcheck', 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray())))]", + "loadBalancingRules": "[flatten(createArray(if(not(equals(parameters('aiUnlimitedAuthPort'), 0)), createArray(createObject('name', 'AiUnlimitedAuth', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedAuthPort'), 'backendPort', parameters('aiUnlimitedAuthPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}AuthLbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedGrpcPort'), 0)), createArray(createObject('name', 'AiUnlimitedAPI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedGrpcPort'), 'backendPort', parameters('aiUnlimitedGrpcPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}APILbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('jupyterHttpPort'), 0)), createArray(createObject('name', 'JupyterUI', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('jupyterHttpPort'), 'backendPort', parameters('jupyterHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}JupyterLbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedSchedulerHttpPort'), 0)), createArray(createObject('name', 'AiUnlimitedSchedulerHttp', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedSchedulerHttpPort'), 'backendPort', parameters('aiUnlimitedSchedulerHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}SchedulerHttpLbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedUIHttpPort'), 0)), createArray(createObject('name', 'AiUnlimitedUIHttp', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedUIHttpPort'), 'backendPort', parameters('aiUnlimitedUIHttpPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}UIHttpLbProbe', parameters('name'))))))), createArray()), if(not(equals(parameters('aiUnlimitedUIHttpsPort'), 0)), createArray(createObject('name', 'AiUnlimitedUIHttps', 'properties', createObject('frontendIPConfiguration', createObject('id', resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('name'), format('{0}Inbound', parameters('name')))), 'backendAddressPool', createObject('id', resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('name'), format('{0}OutboundBackendPool', parameters('name')))), 'frontendPort', parameters('aiUnlimitedUIHttpsPort'), 'backendPort', parameters('aiUnlimitedUIHttpsPort'), 'enableFloatingIP', false(), 'idleTimeoutInMinutes', 15, 'protocol', 'Tcp', 'enableTcpReset', true(), 'loadDistribution', 'Default', 'disableOutboundSnat', true(), 'probe', createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('name'), format('{0}UIHttpsLbProbe', parameters('name'))))))), createArray())))]", + "probes": "[flatten(createArray(if(not(equals(parameters('aiUnlimitedAuthPort'), 0)), createArray(createObject('name', format('{0}AuthLbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('aiUnlimitedAuthPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedGrpcPort'), 0)), createArray(createObject('name', format('{0}APILbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('aiUnlimitedGrpcPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('jupyterHttpPort'), 0)), createArray(createObject('name', format('{0}JupyterLbProbe', parameters('name')), 'properties', createObject('protocol', 'Tcp', 'port', parameters('jupyterHttpPort'), 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedSchedulerHttpPort'), 0)), createArray(createObject('name', format('{0}SchedulerHttpLbProbe', parameters('name')), 'properties', createObject('protocol', 'Http', 'port', parameters('aiUnlimitedSchedulerHttpPort'), 'requestPath', '/healthcheck', 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedUIHttpPort'), 0)), createArray(createObject('name', format('{0}UIHttpLbProbe', parameters('name')), 'properties', createObject('protocol', 'Http', 'port', parameters('aiUnlimitedUIHttpPort'), 'requestPath', '/', 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray()), if(not(equals(parameters('aiUnlimitedUIHttpsPort'), 0)), createArray(createObject('name', format('{0}UIHttpsLbProbe', parameters('name')), 'properties', createObject('protocol', 'Https', 'port', parameters('aiUnlimitedUIHttpsPort'), 'requestPath', '/', 'intervalInSeconds', 5, 'numberOfProbes', 2))), createArray())))]", "outboundRules": [ { "name": "myOutboundRule", @@ -628,8 +720,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6759154498137298299" + "version": "0.31.92.45157", + "templateHash": "4325662974998231491" } }, "parameters": { @@ -710,8 +802,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6759154498137298299" + "version": "0.31.92.45157", + "templateHash": "4325662974998231491" } }, "parameters": { @@ -853,8 +945,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14119123377490335719" + "version": "0.31.92.45157", + "templateHash": "11687747621230861550" } }, "parameters": { @@ -1134,8 +1226,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6759154498137298299" + "version": "0.31.92.45157", + "templateHash": "4325662974998231491" } }, "parameters": { @@ -1236,4 +1328,4 @@ "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'firewall'), '2022-09-01').outputs.Id.value]" } } -} +} \ No newline at end of file diff --git a/deployments/azure/templates/arm/jupyter/jupyter-without-lb.json b/deployments/azure/templates/arm/jupyter/jupyter-without-lb.json index 0120d25..ed1c946 100644 --- a/deployments/azure/templates/arm/jupyter/jupyter-without-lb.json +++ b/deployments/azure/templates/arm/jupyter/jupyter-without-lb.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8452004607038960062" + "version": "0.31.92.45157", + "templateHash": "2742666112813260424" } }, "parameters": { @@ -130,7 +130,7 @@ }, "JupyterVersion": { "type": "string", - "defaultValue": "latest", + "defaultValue": "v0.1.0", "metadata": { "description": "Container Version of the Jupyter Labs service" } @@ -151,7 +151,7 @@ }, "variables": { "$fxv#0": "#cloud-config\nwrite_files:\n- encoding: b64\n content: \"{0}\"\n owner: root:root\n path: /usr/lib/systemd/system/jupyter.service\n permissions: '0640'\n\nruncmd:\n- mkdir -p /etc/td \n- |\n export PERMDISK=$(lsscsi 1:0:0:0 -b | awk '{{print $2}}');\n if [ -n \"${{PERMDISK}}\" ]; then blkid --match-token TYPE=ext4 ${{PERMDISK}} || (mkfs.ext4 -m0 ${{PERMDISK}} && e2label ${{PERMDISK}} WORKSPACES); fi\n /usr/bin/echo \"LABEL=WORKSPACES /etc/td ext4 defaults 0 2\" >> /etc/fstab\n /usr/bin/mount -a\n- while [ $(systemctl status docker | grep \"active (running)\" | wc -l) -lt 1 ]; do sleep 5; done\n- sleep 60\n- systemctl enable jupyter.service\n- systemctl start jupyter.service\n", - "$fxv#1": "[Unit]\nDescription=jupyter\nAfter=docker.service\nRequires=docker.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited\nExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{{userdata,ipython}}\nExecStartPre=-/usr/bin/docker exec %n stop || true\nExecStartPre=-/usr/bin/docker rm %n || true\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e accept_license=Y \\\n -e JUPYTER_TOKEN={4} \\\n -v /etc/td/jupyter/userdata:/home/jovyan/JupyterLabRoot/userdata \\\n -v /etc/td/jupyter/ipython:/home/jovyan/.ipython \\\n -p {3}:8888 \\\n --network ai_unlimited \\\n --rm --name %n {0}/{1}:{2}\n\n[Install]\nWantedBy=multi-user.target\n", + "$fxv#1": "[Unit]\nDescription=jupyter\nAfter=docker.service\nRequires=docker.service\nStartLimitInterval=200\nStartLimitBurst=10\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nRestartSec=2\nExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited\nExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{{userdata,ipython}}\nExecStartPre=-/usr/bin/docker stop %n\nExecStartPre=-/usr/bin/docker rm %n\nExecStartPre=/usr/bin/docker pull {0}/{1}:{2}\n\nExecStart=/usr/bin/docker run \\\n -e accept_license=Y \\\n -e JUPYTER_TOKEN={4} \\\n -v /etc/td/jupyter/userdata:/home/jovyan/JupyterLabRoot/userdata \\\n -v /etc/td/jupyter/ipython:/home/jovyan/.ipython \\\n -p {3}:8888 \\\n --network ai_unlimited \\\n --rm --name %n {0}/{1}:{2}\n\n[Install]\nWantedBy=multi-user.target\n", "dnsLabelPrefix": "[format('td{0}', uniqueString(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('ResourceGroupName')), deployment().name, parameters('JupyterName')))]", "registry": "teradata", "jupyterRepository": "ai-unlimited-jupyter", @@ -200,8 +200,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4929926268271391774" + "version": "0.31.92.45157", + "templateHash": "392354314460473239" } }, "parameters": { @@ -227,7 +227,7 @@ "type": "bool", "defaultValue": false }, - "aiUnlimitedHttpPort": { + "aiUnlimitedAuthPort": { "type": "int", "defaultValue": 0 }, @@ -239,6 +239,14 @@ "type": "int", "defaultValue": 0 }, + "aiUnlimitedUIHttpPort": { + "type": "int", + "defaultValue": 0 + }, + "aiUnlimitedUIHttpsPort": { + "type": "int", + "defaultValue": 0 + }, "jupyterHttpPort": { "type": "int", "defaultValue": 0 @@ -304,10 +312,10 @@ ] }, { - "condition": "[not(equals(parameters('aiUnlimitedHttpPort'), 0))]", + "condition": "[not(equals(parameters('aiUnlimitedAuthPort'), 0))]", "type": "Microsoft.Network/networkSecurityGroups/securityRules", "apiVersion": "2023-04-01", - "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-http-allow', variables('uniqueSecurityGroupName')))]", + "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-auth-allow', variables('uniqueSecurityGroupName')))]", "properties": { "copy": [ { @@ -330,7 +338,7 @@ "access": "Allow", "description": "allow http to the workspace instance", "destinationAddressPrefix": "*", - "destinationPortRange": "[string(parameters('aiUnlimitedHttpPort'))]", + "destinationPortRange": "[string(parameters('aiUnlimitedAuthPort'))]", "direction": "Inbound", "priority": 701, "protocol": "Tcp", @@ -454,6 +462,82 @@ "dependsOn": [ "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" ] + }, + { + "condition": "[not(equals(parameters('aiUnlimitedUIHttpPort'), 0))]", + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-ui-http-allow', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "destinationApplicationSecurityGroups", + "count": "[length(parameters('detinationAppSecGroups'))]", + "input": { + "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + }, + { + "name": "sourceApplicationSecurityGroups", + "count": "[length(parameters('sourceAppSecGroups'))]", + "input": { + "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + } + ], + "access": "Allow", + "description": "allow http to the workspace ui instance", + "destinationAddressPrefix": "*", + "destinationPortRange": "[string(parameters('aiUnlimitedUIHttpPort'))]", + "direction": "Inbound", + "priority": 705, + "protocol": "Tcp", + "sourceAddressPrefixes": "[parameters('accessCidrs')]", + "sourcePortRange": "*" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" + ] + }, + { + "condition": "[not(equals(parameters('aiUnlimitedUIHttpsPort'), 0))]", + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', variables('uniqueSecurityGroupName'), format('{0}-workspace-ui-https-allow', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "destinationApplicationSecurityGroups", + "count": "[length(parameters('detinationAppSecGroups'))]", + "input": { + "id": "[parameters('detinationAppSecGroups')[copyIndex('destinationApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + }, + { + "name": "sourceApplicationSecurityGroups", + "count": "[length(parameters('sourceAppSecGroups'))]", + "input": { + "id": "[parameters('sourceAppSecGroups')[copyIndex('sourceApplicationSecurityGroups')]]", + "location": "[parameters('location')]" + } + } + ], + "access": "Allow", + "description": "allow https to the workspace ui instance", + "destinationAddressPrefix": "*", + "destinationPortRange": "[string(parameters('aiUnlimitedUIHttpsPort'))]", + "direction": "Inbound", + "priority": 706, + "protocol": "Tcp", + "sourceAddressPrefixes": "[parameters('accessCidrs')]", + "sourcePortRange": "*" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups', variables('uniqueSecurityGroupName'))]" + ] } ], "outputs": { @@ -528,8 +612,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14119123377490335719" + "version": "0.31.92.45157", + "templateHash": "11687747621230861550" } }, "parameters": { @@ -809,8 +893,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6759154498137298299" + "version": "0.31.92.45157", + "templateHash": "4325662974998231491" } }, "parameters": { @@ -910,4 +994,4 @@ "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', 'firewall'), '2022-09-01').outputs.Id.value]" } } -} +} \ No newline at end of file diff --git a/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-with-nlb.bicep b/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-with-nlb.bicep index 41026ec..9cfdb10 100644 --- a/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-with-nlb.bicep +++ b/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-with-nlb.bicep @@ -6,10 +6,6 @@ param ResourceGroupName string = 'ai-unlimited-workspace' @description('Name for the AI Unlimited service\'s virtual machine.') param AiUnlimitedName string -@description('SSH public key value') -@secure() -param PublicKey string - @description('The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version.') @allowed([ 'Ubuntu-1804' @@ -33,8 +29,8 @@ param SecurityGroup string = 'AiUnlimitedSecurityGroup' @description('The CIDR ranges that can be used to communicate with the AI Unlimited service instance.') param AccessCIDRs array = ['0.0.0.0/0'] -@description('port to access the AI Unlimited service UI.') -param AiUnlimitedHttpPort int = 3000 +@description('port to access the AI Unlimited auth service.') +param AiUnlimitedAuthPort int = 3000 @description('port to access the AI Unlimited service api.') param AiUnlimitedGrpcPort int = 3282 @@ -45,6 +41,12 @@ param AiUnlimitedGrpcPort int = 3282 // @description('port to access the AI Unlimited scheduler service grpc api.') var AiUnlimitedSchedulerHttpPort = 50061 +// @description('port to access the AI Unlimited service UI http.') +var AiUnlimitedUIHttpPort = 80 + +// @description('port to access the AI Unlimited service UI https.') +var AiUnlimitedUIHttpsPort = 443 + @description('Source Application Security Groups to access the AI Unlimited service api.') param SourceAppSecGroups array = [] @@ -54,9 +56,6 @@ param detinationAppSecGroups array = [] @description('GUID of the AI Unlimited Role') param RoleDefinitionId string -@description('allow access the AI Unlimited ssh port from the access cidr.') -param AllowPublicSSH bool = false - @description('should we create a new Azure Key Vault for bootstrapping the AI Unlimited Engine nodes.') @allowed(['New', 'None']) param UseKeyVault string = 'New' @@ -72,7 +71,10 @@ param PersistentVolumeSize int = 100 param ExistingPersistentVolume string = 'NONE' @description('Container Version of the AI Unlimited service') -param AiUnlimitedVersion string = 'v0.2.23' +param AiUnlimitedVersion string = 'v0.3.0' + +@description('Container Version of the AI Unlimited UI service') +param AiUnlimitedUIVersion string = 'v0.1.0' // @description('Container Version of the AI Unlimited scheduler service') var AiUnlimitedSchedulerVersion = 'latest' @@ -89,6 +91,8 @@ var nlbDnsLabelPrefix = 'td${dnsId}-nlb' var registry = 'teradata' var workspaceRepository = 'ai-unlimited-workspaces' var workspaceSchedulerRepository = 'ai-unlimited-scheduler' +var workspaceUIRepository = 'ai-unlimited-workspaces-ui' + var cloudInitData = base64(format( loadTextContent('../../../scripts/ai-unlimited.cloudinit.yaml'), @@ -97,7 +101,7 @@ var cloudInitData = base64(format( registry, workspaceRepository, AiUnlimitedVersion, - AiUnlimitedHttpPort, + AiUnlimitedAuthPort, AiUnlimitedGrpcPort, subscription().subscriptionId, subscription().tenantId, @@ -110,6 +114,16 @@ var cloudInitData = base64(format( AiUnlimitedSchedulerVersion, // AiUnlimitedSchedulerGrpcPort, AiUnlimitedSchedulerHttpPort + )), + base64(format( + loadTextContent('../../../scripts/ai-unlimited-ui.service'), + registry, + workspaceUIRepository, + AiUnlimitedUIVersion, + AiUnlimitedUIHttpPort, + AiUnlimitedAuthPort, + AiUnlimitedGrpcPort, + '--network-alias ${nlb.outputs.PublicDns}' )) )) @@ -178,13 +192,15 @@ module firewall '../modules/firewall.bicep' = { location: rg.location name: SecurityGroup accessCidrs: AccessCIDRs - aiUnlimitedHttpPort: AiUnlimitedHttpPort + aiUnlimitedAuthPort: AiUnlimitedAuthPort aiUnlimitedGrpcPort: AiUnlimitedGrpcPort aiUnlimitedSchedulerHttpPort: AiUnlimitedSchedulerHttpPort // aiUnlimitedSchedulerGrpcPort: AiUnlimitedSchedulerGrpcPort + aiUnlimitedUIHttpPort: AiUnlimitedUIHttpPort + aiUnlimitedUIHttpsPort: AiUnlimitedUIHttpsPort sourceAppSecGroups: SourceAppSecGroups detinationAppSecGroups: detinationAppSecGroups - sshAccess: AllowPublicSSH + sshAccess: false tags: Tags } } @@ -196,10 +212,12 @@ module nlb '../modules/nlb.bicep' = { name: AiUnlimitedName dnsPrefix: nlbDnsLabelPrefix location: rg.location - aiUnlimitedHttpPort: AiUnlimitedHttpPort + aiUnlimitedAuthPort: AiUnlimitedAuthPort aiUnlimitedGrpcPort: AiUnlimitedGrpcPort aiUnlimitedSchedulerHttpPort: AiUnlimitedSchedulerHttpPort // aiUnlimitedSchedulerGrpcPort: AiUnlimitedSchedulerGrpcPort + aiUnlimitedUIHttpPort: AiUnlimitedUIHttpPort + aiUnlimitedUIHttpsPort: AiUnlimitedUIHttpsPort tags: Tags } } @@ -211,7 +229,7 @@ module aiUnlimited '../modules/instance.bicep' = { location: rg.location name: AiUnlimitedName adminUsername: 'azureuser' - sshPublicKey: PublicKey + sshPublicKey: PublicKey.outputs.PublicKey dnsLabelPrefix: dnsLabelPrefix vmSize: InstanceType subnetId: subnet.id @@ -228,6 +246,17 @@ module aiUnlimited '../modules/instance.bicep' = { } } +module PublicKey '../modules/public-key.bicep' = { + scope: rg + name: 'Public-Key' + params: { + Name: AiUnlimitedName + Location: deployment().location + VaultName: vault.outputs.name + RoleID: RoleDefinitionId + } +} + resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { scope: subscription() name: roleAssignmentName @@ -239,10 +268,9 @@ resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { output PublicIP string = nlb.outputs.PublicIp output PrivateIP string = aiUnlimited.outputs.PrivateIP -output AiUnlimitedPublicHttpAccess string = 'http://${nlb.outputs.PublicDns}:${AiUnlimitedHttpPort}' -output AiUnlimitedPrivateHttpAccess string = 'http://${aiUnlimited.outputs.PrivateIP}:${AiUnlimitedHttpPort}' +output AiUnlimitedPublicHttpAccess string = concat('http://${nlb.outputs.PublicDns}', (AiUnlimitedUIHttpPort != 80 ? concat(':', string(AiUnlimitedUIHttpPort)) : '')) +output AiUnlimitedPrivateHttpAccess string = concat('http://${aiUnlimited.outputs.PrivateIP}', (AiUnlimitedUIHttpPort != 80 ? concat(':', string(AiUnlimitedUIHttpPort)) : '')) output AiUnlimitedPublicGrpcAccess string = 'http://${nlb.outputs.PublicDns}:${AiUnlimitedGrpcPort}' output AiUnlimitedPrivateGrpcAccess string = 'http://${aiUnlimited.outputs.PrivateIP}:${AiUnlimitedGrpcPort}' -output sshCommand string = 'ssh azureuser@${aiUnlimited.outputs.PrivateIP}' output KeyVaultName string = (UseKeyVault == 'New') ? vault.outputs.name : '' output NetworkSecurityGroupId string = firewall.outputs.Id diff --git a/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-with-nlb.bicepparam b/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-with-nlb.bicepparam index 34d98d9..a575181 100644 --- a/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-with-nlb.bicepparam +++ b/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-with-nlb.bicepparam @@ -2,7 +2,6 @@ using './ai-unlimited-with-nlb.bicep' param ResourceGroupName = 'ai-unlimited-workspace' param AiUnlimitedName = '' -param PublicKey = '' param OSVersion = 'Ubuntu-2004' param InstanceType = 'Standard_D2s_v3' param Network = '' @@ -11,10 +10,12 @@ param SecurityGroup = 'AiUnlimitedSecurityGroup' param AccessCIDRs = [ '0.0.0.0/0' ] -param AiUnlimitedHttpPort = 3000 +param AiUnlimitedAuthPort = 3000 param AiUnlimitedGrpcPort = 3282 // param AiUnlimitedSchedulerHttpPort = 50061 // param AiUnlimitedSchedulerGrpcPort = 50051 +// param AiUnlimitedUIHttpPort = 80 +// param AiUnlimitedUIHttpsPort = 443 param SourceAppSecGroups = [] param detinationAppSecGroups = [] param RoleDefinitionId = '' @@ -22,6 +23,7 @@ param UseKeyVault = 'New' param UsePersistentVolume = 'New' param PersistentVolumeSize = 100 param ExistingPersistentVolume = 'NONE' -param AiUnlimitedVersion = 'v0.2.23' +param AiUnlimitedVersion = 'v0.3.0' +param AiUnlimitedUIVersion = 'v0.1.0' // param AiUnlimitedSchedulerVersion = 'latest' param Tags = {} diff --git a/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-without-lb.bicep b/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-without-lb.bicep index eaf5f2d..66544ee 100644 --- a/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-without-lb.bicep +++ b/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-without-lb.bicep @@ -6,10 +6,6 @@ param ResourceGroupName string = 'ai-unlimited-workspace' @description('Name for the AI Unlimited service\'s virtual machine.') param AiUnlimitedName string -@description('SSH public key value') -@secure() -param PublicKey string - @description('The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version.') @allowed([ 'Ubuntu-1804' @@ -33,8 +29,8 @@ param SecurityGroup string = 'AiUnlimitedSecurityGroup' @description('The CIDR ranges that can be used to communicate with the AI Unlimited service instance.') param AccessCIDRs array = ['0.0.0.0/0'] -@description('port to access the AI Unlimited service UI.') -param AiUnlimitedHttpPort int = 3000 +@description('port to access the AI Unlimited auth service.') +param AiUnlimitedAuthPort int = 3000 @description('port to access the AI Unlimited service api.') param AiUnlimitedGrpcPort int = 3282 @@ -45,6 +41,12 @@ param AiUnlimitedGrpcPort int = 3282 // @description('port to access the AI Unlimited scheduler service api.') var AiUnlimitedSchedulerHttpPort = 50061 +// @description('port to access the AI Unlimited service UI http.') +var AiUnlimitedUIHttpPort = 80 + +// @description('port to access the AI Unlimited service UI https.') +var AiUnlimitedUIHttpsPort = 443 + @description('Source Application Security Groups to access the AI Unlimited service api.') param SourceAppSecGroups array = [] @@ -54,9 +56,6 @@ param detinationAppSecGroups array = [] @description('GUID of the AI Unlimited Role') param RoleDefinitionId string -@description('allow access the AI Unlimited ssh port from the access cidr.') -param AllowPublicSSH bool = true - @description('should we create a new Azure Key Vault for bootstrapping the AI Unlimited Engine nodes.') @allowed(['New', 'None']) param UseKeyVault string = 'New' @@ -72,7 +71,10 @@ param PersistentVolumeSize int = 100 param ExistingPersistentVolume string = 'NONE' @description('Container Version of the AI Unlimited service') -param AiUnlimitedVersion string = 'v0.2.23' +param AiUnlimitedVersion string = 'v0.3.0' + +@description('Container Version of the AI Unlimited UI service') +param AiUnlimitedUIVersion string = 'v0.1.0' // @description('Container Version of the AI Unlimited scheduler service') var AiUnlimitedSchedulerVersion = 'latest' @@ -87,6 +89,7 @@ var dnsLabelPrefix = 'td${uniqueString(rg.id, deployment().name, AiUnlimitedName var registry = 'teradata' var workspaceRepository = 'ai-unlimited-workspaces' var workspaceSchedulerRepository = 'ai-unlimited-scheduler' +var workspaceUIRepository = 'ai-unlimited-workspaces-ui' var cloudInitData = base64(format( loadTextContent('../../../scripts/ai-unlimited.cloudinit.yaml'), @@ -95,7 +98,7 @@ var cloudInitData = base64(format( registry, workspaceRepository, AiUnlimitedVersion, - AiUnlimitedHttpPort, + AiUnlimitedAuthPort, AiUnlimitedGrpcPort, subscription().subscriptionId, subscription().tenantId, @@ -108,6 +111,16 @@ var cloudInitData = base64(format( AiUnlimitedSchedulerVersion, // AiUnlimitedSchedulerGrpcPort, AiUnlimitedSchedulerHttpPort + )), + base64(format( + loadTextContent('../../../scripts/ai-unlimited-ui.service'), + registry, + workspaceUIRepository, + AiUnlimitedUIVersion, + AiUnlimitedUIHttpPort, + AiUnlimitedAuthPort, + AiUnlimitedGrpcPort, + '--network-alias ai-unlimited' )) )) @@ -176,11 +189,13 @@ module firewall '../modules/firewall.bicep' = { location: rg.location name: SecurityGroup accessCidrs: AccessCIDRs - sshAccess: AllowPublicSSH - aiUnlimitedHttpPort: AiUnlimitedHttpPort + sshAccess: false + aiUnlimitedAuthPort: AiUnlimitedAuthPort aiUnlimitedGrpcPort: AiUnlimitedGrpcPort aiUnlimitedSchedulerHttpPort: AiUnlimitedSchedulerHttpPort // aiUnlimitedSchedulerGrpcPort: AiUnlimitedSchedulerGrpcPort + aiUnlimitedUIHttpPort: AiUnlimitedUIHttpPort + aiUnlimitedUIHttpsPort: AiUnlimitedUIHttpsPort sourceAppSecGroups: SourceAppSecGroups detinationAppSecGroups: detinationAppSecGroups tags: Tags @@ -194,7 +209,7 @@ module aiUnlimited '../modules/instance.bicep' = { location: rg.location name: AiUnlimitedName adminUsername: 'azureuser' - sshPublicKey: PublicKey + sshPublicKey: PublicKey.outputs.PublicKey dnsLabelPrefix: dnsLabelPrefix vmSize: InstanceType subnetId: subnet.id @@ -209,6 +224,17 @@ module aiUnlimited '../modules/instance.bicep' = { } } +module PublicKey '../modules/public-key.bicep' = { + scope: rg + name: 'Public-Key' + params: { + Name: AiUnlimitedName + Location: deployment().location + VaultName: vault.outputs.name + RoleID: RoleDefinitionId + } +} + resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { scope: subscription() name: roleAssignmentName @@ -220,10 +246,9 @@ resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { output PublicIP string = aiUnlimited.outputs.PublicIP output PrivateIP string = aiUnlimited.outputs.PrivateIP -output AiUnlimitedPublicHttpAccess string = 'http://${aiUnlimited.outputs.PublicIP}:${AiUnlimitedHttpPort}' -output AiUnlimitedPrivateHttpAccess string = 'http://${aiUnlimited.outputs.PrivateIP}:${AiUnlimitedHttpPort}' +output AiUnlimitedPublicHttpAccess string = concat('http://${aiUnlimited.outputs.PublicIP}', (AiUnlimitedUIHttpPort != 80 ? concat(':', string(AiUnlimitedUIHttpPort)) : '')) +output AiUnlimitedPrivateHttpAccess string = concat('http://${aiUnlimited.outputs.PrivateIP}', (AiUnlimitedUIHttpPort != 80 ? concat(':', string(AiUnlimitedUIHttpPort)) : '')) output AiUnlimitedPublicGrpcAccess string = 'http://${aiUnlimited.outputs.PublicIP}:${AiUnlimitedGrpcPort}' output AiUnlimitedPrivateGrpcAccess string = 'http://${aiUnlimited.outputs.PrivateIP}:${AiUnlimitedGrpcPort}' -output sshCommand string = 'ssh azureuser@${aiUnlimited.outputs.PublicIP}' output KeyVaultName string = (UseKeyVault == 'New') ? vault.outputs.name : '' output NetworkSecurityGroupId string = firewall.outputs.Id diff --git a/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-without-lb.bicepparam b/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-without-lb.bicepparam index f7eb31f..881bdac 100644 --- a/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-without-lb.bicepparam +++ b/deployments/azure/templates/bicep/ai-unlimited/ai-unlimited-without-lb.bicepparam @@ -2,7 +2,6 @@ using './ai-unlimited-without-lb.bicep' param ResourceGroupName = 'ai-unlimited-workspace' param AiUnlimitedName = '' -param PublicKey = '' param OSVersion = 'Ubuntu-2004' param InstanceType = 'Standard_D2s_v3' param Network = '' @@ -11,18 +10,20 @@ param SecurityGroup = 'AiUnlimitedSecurityGroup' param AccessCIDRs = [ '0.0.0.0/0' ] -param AiUnlimitedHttpPort = 3000 +param AiUnlimitedAuthPort = 3000 param AiUnlimitedGrpcPort = 3282 // param AiUnlimitedSchedulerGrpcPort = 50051 // param AiUnlimitedSchedulerHttpPort = 50061 +// param AiUnlimitedUIHttpPort = 80 +// param AiUnlimitedUIHttpsPort = 443 param SourceAppSecGroups = [] param detinationAppSecGroups = [] param RoleDefinitionId = '' -param AllowPublicSSH = true param UseKeyVault = 'New' param UsePersistentVolume = 'New' param PersistentVolumeSize = 100 param ExistingPersistentVolume = 'NONE' -param AiUnlimitedVersion = 'v0.2.23' +param AiUnlimitedVersion = 'v0.3.0' +param AiUnlimitedUIVersion = 'v0.1.0' // param AiUnlimitedSchedulerVersion = 'latest' param Tags = {} diff --git a/deployments/azure/templates/bicep/all-in-one/all-in-one-with-nlb.bicep b/deployments/azure/templates/bicep/all-in-one/all-in-one-with-nlb.bicep deleted file mode 100644 index b75bdc5..0000000 --- a/deployments/azure/templates/bicep/all-in-one/all-in-one-with-nlb.bicep +++ /dev/null @@ -1,270 +0,0 @@ -targetScope = 'subscription' - -@description('name for the resource group.') -param ResourceGroupName string = 'ai-unlimited-workspace' - -@description('Name for the Workspace service\'s virtual machine.') -param AiUnlimitedName string - -@description('SSH public key value') -@secure() -param PublicKey string - -@description('The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version.') -@allowed([ - 'Ubuntu-1804' - 'Ubuntu-2004' - 'Ubuntu-2204' -]) -param OSVersion string = 'Ubuntu-2004' - -@description('The AI Unlimited VM type') -param InstanceType string = 'Standard_D2s_v3' - -@description('Name of the network to run the AI Unlimited service in') -param Network string - -@description('Name of the subnet to run the AI Unlimited service in') -param Subnet string - -@description('Name of the network security group') -param SecurityGroup string = 'AiUnlimitedSecurityGroup' - -@description('The CIDR ranges that can be used to communicate with the AI Unlimited service instance.') -param AccessCIDRs array = ['0.0.0.0/0'] - -@description('port to access the Jupyter Labs UI.') -param JupyterHttpPort int = 8888 - -@description('port to access the AI Unlimited service UI.') -param AiUnlimitedHttpPort int = 3000 - -@description('port to access the AI Unlimited service api.') -param AiUnlimitedGrpcPort int = 3282 - -// @description('port to access the AI Unlimited scheduler service grpc api.') -var AiUnlimitedSchedulerGrpcPort = 50051 - -// @description('port to access the AI Unlimited scheduler service http api.') -var AiUnlimitedSchedulerHttpPort = 50061 - -@description('Source Application Security Groups to access the AI Unlimited service api.') -param SourceAppSecGroups array = [] - -@description('Destination Application Security Groups to give access to AI Unlimited service instance.') -param detinationAppSecGroups array = [] - -@description('GUID of the AI Unlimited Role') -param RoleDefinitionId string - -@description('allow access the AI Unlimited ssh port from the access cidr.') -param AllowPublicSSH bool = false - -@description('should we create a new Azure Key Vault for bootstrapping the AI Unlimited Engine nodes.') -@allowed(['New', 'None']) -param UseKeyVault string = 'New' - -@description('should we use a new or existing volume for persistent data on the AI Unlimited server.') -@allowed(['New', 'Existing']) -param UsePersistentVolume string = 'New' - -@description('size of the optional persistent disk to the AI Unlimited server.') -param PersistentVolumeSize int = 100 - -@description('Name of the existing persistent volume to attach. Must be in the same region and resourcegroup zone as the AI Unlimited server.') -param ExistingPersistentVolume string = 'NONE' - -@description('Container Version of the AI Unlimited service') -param AiUnlimitedVersion string = 'v0.2.23' - -@description('Container Version of the Jupyter Labs service') -param JupyterVersion string = 'latest' - -// @description('Container Version of the AI Unlimited scheduler service') -var AiUnlimitedSchedulerVersion = 'latest' - -@description('Join token for the Jupyter Labs service') -@secure() -param JupyterToken string - -@description('Tags to apply to all newly created resources, in the form of {"key_one":"value_one","key_two":"value_two"}') -param Tags object = {} - -var roleAssignmentName = guid(subscription().id, AiUnlimitedName, rg.id, RoleDefinitionId) -var dnsId = uniqueString(rg.id, deployment().name, AiUnlimitedName) -var dnsLabelPrefix = 'td${dnsId}' -var nlbDnsLabelPrefix = 'td${dnsId}-nlb' - -// below are static and are not expected to be changed -var registry = 'teradata' -var workspaceRepository = 'ai-unlimited-workspaces' -var jupyterRepository = 'ai-unlimited-jupyter' -var workspaceSchedulerRepository = 'ai-unlimited-scheduler' - -var cloudInitData = base64(format( - loadTextContent('../../../scripts/all-in-one.cloudinit.yaml'), - base64(format( - loadTextContent('../../../scripts/ai-unlimited.service'), - registry, - workspaceRepository, - AiUnlimitedVersion, - AiUnlimitedHttpPort, - AiUnlimitedGrpcPort, - subscription().subscriptionId, - subscription().tenantId, - '--network-alias ${nlb.outputs.PublicDns}' - )), - base64(format( - loadTextContent('../../../scripts/jupyter.service'), - registry, - jupyterRepository, - JupyterVersion, - JupyterHttpPort, - JupyterToken - )), - base64(format( - loadTextContent('../../../scripts/ai-unlimited-scheduler.service'), - registry, - workspaceSchedulerRepository, - AiUnlimitedSchedulerVersion, - AiUnlimitedSchedulerGrpcPort, - AiUnlimitedSchedulerHttpPort - )) -)) - -resource rg 'Microsoft.Resources/resourceGroups@2022-09-01' existing = { - name: ResourceGroupName -} - -resource network 'Microsoft.Network/virtualNetworks@2022-11-01' existing = { - scope: rg - name: Network -} - -resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-11-01' existing = { - parent: network - name: Subnet -} - -module vault '../modules/vault/vault.bicep' = if (UseKeyVault == 'New') { - scope: rg - name: 'vault' - params: { - encryptVolumes: true - keyVaultName: AiUnlimitedName - location: rg.location - tags: Tags - } -} - -module vaultAccessPolicy '../modules/vault/access-policy.bicep' = if (UseKeyVault == 'New') { - scope: rg - name: 'vault-access-policy' - params: { - vaultName: vault.outputs.name - accessPolicy: { - tenantId: subscription().tenantId - objectId: aiUnlimited.outputs.PrincipleId - permissions: { - keys: [ - 'Create' - 'Delete' - 'Get' - 'List' - 'Update' - 'Purge' - 'Recover' - 'Decrypt' - 'Encrypt' - 'Sign' - 'UnwrapKey' - 'Verify' - 'WrapKey' - 'GetRotationPolicy' - 'SetRotationPolicy' - ] - secrets: ['Get', 'Set', 'Delete', 'List', 'Purge'] - storage: ['Get'] - } - } - } -} - -module firewall '../modules/firewall.bicep' = { - scope: rg - name: 'firewall' - params: { - location: rg.location - name: SecurityGroup - accessCidrs: AccessCIDRs - sshAccess: AllowPublicSSH - aiUnlimitedHttpPort: AiUnlimitedHttpPort - aiUnlimitedGrpcPort: AiUnlimitedGrpcPort - aiUnlimitedSchedulerHttpPort: AiUnlimitedSchedulerHttpPort - // aiUnlimitedSchedulerGrpcPort: AiUnlimitedSchedulerGrpcPort - jupyterHttpPort: JupyterHttpPort - sourceAppSecGroups: SourceAppSecGroups - detinationAppSecGroups: detinationAppSecGroups - tags: Tags - } -} - -module nlb '../modules/nlb.bicep' = { - scope: rg - name: 'loadbalancer' - params: { - name: AiUnlimitedName - location: rg.location - dnsPrefix: dnsLabelPrefix - aiUnlimitedHttpPort: AiUnlimitedHttpPort - aiUnlimitedGrpcPort: AiUnlimitedGrpcPort - aiUnlimitedSchedulerHttpPort: AiUnlimitedSchedulerHttpPort - // aiUnlimitedSchedulerGrpcPort: AiUnlimitedSchedulerGrpcPort - jupyterHttpPort: JupyterHttpPort - tags: Tags - } -} - -module aiUnlimited '../modules/instance.bicep' = { - scope: rg - name: 'ai-unlimited' - params: { - location: rg.location - name: AiUnlimitedName - adminUsername: 'azureuser' - sshPublicKey: PublicKey - dnsLabelPrefix: dnsLabelPrefix - vmSize: InstanceType - subnetId: subnet.id - networkSecurityGroupID: firewall.outputs.Id - osVersion: OSVersion - cloudInitData: cloudInitData - usePersistentVolume: UsePersistentVolume - persistentVolumeSize: PersistentVolumeSize - existingPersistentVolume: ExistingPersistentVolume - nlbName: AiUnlimitedName - nlbPoolNames: nlb.outputs.nlbPools - usePublicIp: false - tags: Tags - } -} - -resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - scope: subscription() - name: roleAssignmentName - properties: { - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', RoleDefinitionId) - principalId: aiUnlimited.outputs.PrincipleId - } -} - -output PublicIP string = nlb.outputs.PublicIp -output PrivateIP string = aiUnlimited.outputs.PrivateIP -output AiUnlimitedPublicHttpAccess string = 'http://${nlb.outputs.PublicDns}:${AiUnlimitedHttpPort}' -output AiUnlimitedPrivateHttpAccess string = 'http://${aiUnlimited.outputs.PrivateIP}:${AiUnlimitedHttpPort}' -output AiUnlimitedPublicGrpcAccess string = 'http://${nlb.outputs.PublicDns}:${AiUnlimitedGrpcPort}' -output AiUnlimitedPrivateGrpcAccess string = 'http://${aiUnlimited.outputs.PrivateIP}:${AiUnlimitedGrpcPort}' -output JupyterLabPublicHttpAccess string = 'http://${nlb.outputs.PublicDns}:${JupyterHttpPort}?token=${JupyterToken}' -output JupyterLabPrivateHttpAccess string = 'http://${aiUnlimited.outputs.PrivateIP}:${JupyterHttpPort}?token=${JupyterToken}' -output sshCommand string = 'ssh azureuser@${aiUnlimited.outputs.PrivateIP}' -output SecurityGroup string = firewall.outputs.Id diff --git a/deployments/azure/templates/bicep/all-in-one/all-in-one-with-nlb.bicepparam b/deployments/azure/templates/bicep/all-in-one/all-in-one-with-nlb.bicepparam deleted file mode 100644 index 1569ea1..0000000 --- a/deployments/azure/templates/bicep/all-in-one/all-in-one-with-nlb.bicepparam +++ /dev/null @@ -1,31 +0,0 @@ -using './all-in-one-with-nlb.bicep' - -param ResourceGroupName = 'ai-unlimited-workspace' -param AiUnlimitedName = '' -param PublicKey = '' -param OSVersion = 'Ubuntu-2004' -param InstanceType = 'Standard_D2s_v3' -param Network = '' -param Subnet = '' -param SecurityGroup = 'AiUnlimitedSecurityGroup' -param AccessCIDRs = [ - '0.0.0.0/0' -] -param JupyterHttpPort = 8888 -param AiUnlimitedHttpPort = 3000 -param AiUnlimitedGrpcPort = 3282 -// param AiUnlimitedSchedulerGrpcPort = 50051 -// param AiUnlimitedSchedulerHttpPort = 50061 -param SourceAppSecGroups = [] -param detinationAppSecGroups = [] -param RoleDefinitionId = '' -param AllowPublicSSH = true -param UseKeyVault = 'New' -param UsePersistentVolume = 'New' -param PersistentVolumeSize = 100 -param ExistingPersistentVolume = 'NONE' -param AiUnlimitedVersion = 'v0.2.23' -param JupyterVersion = 'latest' -// param AiUnlimitedSchedulerVersion = 'latest' -param JupyterToken = 'USE_A_SECURE_TOKEN' /* TODO : please fix the value assigned to this parameter `uniqueString()` */ -param Tags = {} diff --git a/deployments/azure/templates/bicep/all-in-one/all-in-one-without-lb.bicep b/deployments/azure/templates/bicep/all-in-one/all-in-one-without-lb.bicep deleted file mode 100644 index 745ddbe..0000000 --- a/deployments/azure/templates/bicep/all-in-one/all-in-one-without-lb.bicep +++ /dev/null @@ -1,250 +0,0 @@ -targetScope = 'subscription' - -@description('name for the resource group.') -param ResourceGroupName string = 'ai-unlimited-workspace' - -@description('Name for the Workspace service\'s virtual machine.') -param AiUnlimitedName string - -@description('SSH public key value') -@secure() -param PublicKey string - -@description('The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version.') -@allowed([ - 'Ubuntu-1804' - 'Ubuntu-2004' - 'Ubuntu-2204' -]) -param OSVersion string = 'Ubuntu-2004' - -@description('The AI Unlimited VM type') -param InstanceType string = 'Standard_D2s_v3' - -@description('Name of the network to run the AI Unlimited service in') -param Network string - -@description('Name of the subnet to run the AI Unlimited service in') -param Subnet string - -@description('Name of the network security group') -param SecurityGroup string = 'AiUnlimitedSecurityGroup' - -@description('The CIDR ranges that can be used to communicate with the AI Unlimited service instance.') -param AccessCIDRs array = ['0.0.0.0/0'] - -@description('port to access the Jupyter Labs UI.') -param JupyterHttpPort int = 8888 - -@description('port to access the AI Unlimited service UI.') -param AiUnlimitedHttpPort int = 3000 - -@description('port to access the AI Unlimited service api.') -param AiUnlimitedGrpcPort int = 3282 - -// @description('port to access the AI Unlimited scheduler service grpc api.') -var AiUnlimitedSchedulerGrpcPort = 50051 - -// @description('port to access the AI Unlimited scheduler service http api.') -var AiUnlimitedSchedulerHttpPort = 50061 - -@description('Source Application Security Groups to access the AI Unlimited service api.') -param SourceAppSecGroups array = [] - -@description('Destination Application Security Groups to give access to AI Unlimited service instance.') -param detinationAppSecGroups array = [] - -@description('GUID of the AI Unlimited Role') -param RoleDefinitionId string - -@description('allow access the AI Unlimited ssh port from the access cidr.') -param AllowPublicSSH bool = true - -@description('should we create a new Azure Key Vault for bootstrapping the AI Unlimited Engine nodes.') -@allowed(['New', 'None']) -param UseKeyVault string = 'New' - -@description('should we use a new or existing volume for persistent data on the AI Unlimited server.') -@allowed(['New', 'Existing']) -param UsePersistentVolume string = 'New' - -@description('size of the optional persistent disk to the AI Unlimited server.') -param PersistentVolumeSize int = 100 - -@description('Name of the existing persistent volume to attach. Must be in the same region and resourcegroup zone as the AI Unlimited server.') -param ExistingPersistentVolume string = 'NONE' - -@description('Container Version of the AI Unlimited service') -param AiUnlimitedVersion string = 'v0.2.23' - -@description('Container Version of the Jupyter Labs service') -param JupyterVersion string = 'latest' - -// @description('Container Version of the AI Unlimited scheduler service') -var AiUnlimitedSchedulerVersion = 'latest' - -@description('Join token for the Jupyter Labs service') -@secure() -param JupyterToken string - -@description('Tags to apply to all newly created resources, in the form of {"key_one":"value_one","key_two":"value_two"}') -param Tags object = {} - -var roleAssignmentName = guid(subscription().id, AiUnlimitedName, rg.id, RoleDefinitionId) -var dnsLabelPrefix = 'td${uniqueString(rg.id, deployment().name, AiUnlimitedName)}' - -// below are static and are not expected to be changed -var registry = 'teradata' -var workspaceRepository = 'ai-unlimited-workspaces' -var jupyterRepository = 'ai-unlimited-jupyter' -var workspaceSchedulerRepository = 'ai-unlimited-scheduler' - -var cloudInitData = base64(format( - loadTextContent('../../../scripts/all-in-one.cloudinit.yaml'), - base64(format( - loadTextContent('../../../scripts/ai-unlimited.service'), - registry, - workspaceRepository, - AiUnlimitedVersion, - AiUnlimitedHttpPort, - AiUnlimitedGrpcPort, - subscription().subscriptionId, - subscription().tenantId, - '--network-alias ai-unlimited' - )), - base64(format( - loadTextContent('../../../scripts/jupyter.service'), - registry, - jupyterRepository, - JupyterVersion, - JupyterHttpPort, - JupyterToken - )), - base64(format( - loadTextContent('../../../scripts/ai-unlimited-scheduler.service'), - registry, - workspaceSchedulerRepository, - AiUnlimitedSchedulerVersion, - AiUnlimitedSchedulerGrpcPort, - AiUnlimitedSchedulerHttpPort - )) -)) - -resource rg 'Microsoft.Resources/resourceGroups@2022-09-01' existing = { - name: ResourceGroupName -} - -resource network 'Microsoft.Network/virtualNetworks@2022-11-01' existing = { - scope: rg - name: Network -} - -resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-11-01' existing = { - parent: network - name: Subnet -} - -module vault '../modules/vault/vault.bicep' = if (UseKeyVault == 'New') { - scope: rg - name: 'vault' - params: { - encryptVolumes: true - keyVaultName: AiUnlimitedName - location: rg.location - tags: Tags - } -} - -module vaultAccessPolicy '../modules/vault/access-policy.bicep' = if (UseKeyVault == 'New') { - scope: rg - name: 'vault-access-policy' - params: { - vaultName: vault.outputs.name - accessPolicy: { - tenantId: subscription().tenantId - objectId: aiUnlimited.outputs.PrincipleId - permissions: { - keys: [ - 'Create' - 'Delete' - 'Get' - 'List' - 'Update' - 'Purge' - 'Recover' - 'Decrypt' - 'Encrypt' - 'Sign' - 'UnwrapKey' - 'Verify' - 'WrapKey' - 'GetRotationPolicy' - 'SetRotationPolicy' - ] - secrets: ['Get', 'Set', 'Delete', 'List', 'Purge'] - storage: ['Get'] - } - } - } -} - -module firewall '../modules/firewall.bicep' = { - scope: rg - name: 'firewall' - params: { - location: rg.location - name: SecurityGroup - accessCidrs: AccessCIDRs - sshAccess: AllowPublicSSH - aiUnlimitedHttpPort: AiUnlimitedHttpPort - aiUnlimitedGrpcPort: AiUnlimitedGrpcPort - aiUnlimitedSchedulerHttpPort: AiUnlimitedSchedulerHttpPort - // aiUnlimitedSchedulerGrpcPort: AiUnlimitedSchedulerGrpcPort - jupyterHttpPort: JupyterHttpPort - sourceAppSecGroups: SourceAppSecGroups - detinationAppSecGroups: detinationAppSecGroups - tags: Tags - } -} - -module aiUnlimited '../modules/instance.bicep' = { - scope: rg - name: 'ai-unlimited' - params: { - location: rg.location - name: AiUnlimitedName - adminUsername: 'azureuser' - sshPublicKey: PublicKey - dnsLabelPrefix: dnsLabelPrefix - vmSize: InstanceType - subnetId: subnet.id - networkSecurityGroupID: firewall.outputs.Id - osVersion: OSVersion - cloudInitData: cloudInitData - usePersistentVolume: UsePersistentVolume - persistentVolumeSize: PersistentVolumeSize - existingPersistentVolume: ExistingPersistentVolume - usePublicIp: true - tags: Tags - } -} - -resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - scope: subscription() - name: roleAssignmentName - properties: { - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', RoleDefinitionId) - principalId: aiUnlimited.outputs.PrincipleId - } -} - -output PublicIP string = aiUnlimited.outputs.PublicIP -output PrivateIP string = aiUnlimited.outputs.PrivateIP -output AiUnlimitedPublicHttpAccess string = 'http://${aiUnlimited.outputs.PublicIP}:${AiUnlimitedHttpPort}' -output AiUnlimitedPrivateHttpAccess string = 'http://${aiUnlimited.outputs.PrivateIP}:${AiUnlimitedHttpPort}' -output AiUnlimitedPublicGrpcAccess string = 'http://${aiUnlimited.outputs.PublicIP}:${AiUnlimitedGrpcPort}' -output AiUnlimitedPrivateGrpcAccess string = 'http://${aiUnlimited.outputs.PrivateIP}:${AiUnlimitedGrpcPort}' -output JupyterLabPublicHttpAccess string = 'http://${aiUnlimited.outputs.PublicIP}:${JupyterHttpPort}?token=${JupyterToken}' -output JupyterLabPrivateHttpAccess string = 'http://${aiUnlimited.outputs.PrivateIP}:${JupyterHttpPort}?token=${JupyterToken}' -output sshCommand string = 'ssh azureuser@${aiUnlimited.outputs.PublicIP}' -output SecurityGroup string = firewall.outputs.Id diff --git a/deployments/azure/templates/bicep/all-in-one/all-in-one-without-lb.bicepparam b/deployments/azure/templates/bicep/all-in-one/all-in-one-without-lb.bicepparam deleted file mode 100644 index 1524f91..0000000 --- a/deployments/azure/templates/bicep/all-in-one/all-in-one-without-lb.bicepparam +++ /dev/null @@ -1,31 +0,0 @@ -using './all-in-one-without-lb.bicep' - -param ResourceGroupName = 'ai-unlimited-workspace' -param AiUnlimitedName = '' -param PublicKey = '' -param OSVersion = 'Ubuntu-2004' -param InstanceType = 'Standard_D2s_v3' -param Network = '' -param Subnet = '' -param SecurityGroup = 'AiUnlimitedSecurityGroup' -param AccessCIDRs = [ - '0.0.0.0/0' -] -param JupyterHttpPort = 8888 -param AiUnlimitedHttpPort = 3000 -param AiUnlimitedGrpcPort = 3282 -// param AiUnlimitedSchedulerGrpcPort = 50051 -// param AiUnlimitedSchedulerHttpPort = 50061 -param SourceAppSecGroups = [] -param detinationAppSecGroups = [] -param RoleDefinitionId = '' -param AllowPublicSSH = true -param UseKeyVault = 'New' -param UsePersistentVolume = 'New' -param PersistentVolumeSize = 100 -param ExistingPersistentVolume = 'NONE' -param AiUnlimitedVersion = 'v0.2.23' -param JupyterVersion = 'latest' -// param AiUnlimitedSchedulerVersion = 'latest' -param JupyterToken = 'USE_A_SECURE_TOKEN' -param Tags = {} diff --git a/deployments/azure/templates/bicep/jupyter/jupyter-with-nlb.bicep b/deployments/azure/templates/bicep/jupyter/jupyter-with-nlb.bicep index 1cdc5ac..b6b94d0 100644 --- a/deployments/azure/templates/bicep/jupyter/jupyter-with-nlb.bicep +++ b/deployments/azure/templates/bicep/jupyter/jupyter-with-nlb.bicep @@ -56,7 +56,7 @@ param PersistentVolumeSize int = 100 param ExistingPersistentVolume string = 'NONE' @description('Container Version of the Jupyter Labs service') -param JupyterVersion string = 'latest' +param JupyterVersion string = 'v0.1.0' @description('Join token for the Jupyter Labs service') @secure() diff --git a/deployments/azure/templates/bicep/jupyter/jupyter-with-nlb.bicepparam b/deployments/azure/templates/bicep/jupyter/jupyter-with-nlb.bicepparam index c29bc89..efeb49b 100644 --- a/deployments/azure/templates/bicep/jupyter/jupyter-with-nlb.bicepparam +++ b/deployments/azure/templates/bicep/jupyter/jupyter-with-nlb.bicepparam @@ -17,7 +17,7 @@ param detinationAppSecGroups = [] param UsePersistentVolume = 'New' param PersistentVolumeSize = 100 param ExistingPersistentVolume = 'NONE' -param JupyterVersion = 'latest' +param JupyterVersion = 'v0.1.0' param JupyterToken = 'USE_A_SECURE_TOKEN' /* TODO : please fix the value assigned to this parameter `uniqueString()` */ param Tags = {} diff --git a/deployments/azure/templates/bicep/jupyter/jupyter-without-lb.bicep b/deployments/azure/templates/bicep/jupyter/jupyter-without-lb.bicep index c8739d1..f7fe4d6 100644 --- a/deployments/azure/templates/bicep/jupyter/jupyter-without-lb.bicep +++ b/deployments/azure/templates/bicep/jupyter/jupyter-without-lb.bicep @@ -56,7 +56,7 @@ param PersistentVolumeSize int = 100 param ExistingPersistentVolume string = 'NONE' @description('Container Version of the Jupyter Labs service') -param JupyterVersion string = 'latest' +param JupyterVersion string = 'v0.1.0' @description('Join token for the Jupyter Labs service') @secure() diff --git a/deployments/azure/templates/bicep/jupyter/jupyter-without-lb.bicepparam b/deployments/azure/templates/bicep/jupyter/jupyter-without-lb.bicepparam index f291ead..b6b52b9 100644 --- a/deployments/azure/templates/bicep/jupyter/jupyter-without-lb.bicepparam +++ b/deployments/azure/templates/bicep/jupyter/jupyter-without-lb.bicepparam @@ -18,7 +18,7 @@ param AllowPublicSSH = true param UsePersistentVolume = 'New' param PersistentVolumeSize = 100 param ExistingPersistentVolume = 'NONE' -param JupyterVersion = 'latest' +param JupyterVersion = 'v0.1.0' param JupyterToken = 'USE_A_SECURE_TOKEN' param Tags = {} diff --git a/deployments/azure/templates/bicep/modules/firewall.bicep b/deployments/azure/templates/bicep/modules/firewall.bicep index 54fe4da..dc9d96f 100644 --- a/deployments/azure/templates/bicep/modules/firewall.bicep +++ b/deployments/azure/templates/bicep/modules/firewall.bicep @@ -5,10 +5,12 @@ param accessCidrs array = [] param sourceAppSecGroups array = [] param detinationAppSecGroups array = [] param sshAccess bool = false -param aiUnlimitedHttpPort int = 0 +param aiUnlimitedAuthPort int = 0 param aiUnlimitedGrpcPort int = 0 param aiUnlimitedSchedulerHttpPort int = 0 // param aiUnlimitedSchedulerGrpcPort int = 0 +param aiUnlimitedUIHttpPort int = 0 +param aiUnlimitedUIHttpsPort int = 0 param jupyterHttpPort int = 0 param tags object = {} param uuid string = newGuid() @@ -73,8 +75,8 @@ resource sshAllow 'Microsoft.Network/networkSecurityGroups/securityRules@2023-04 // } // } -resource AiUnlimitedHTTP 'Microsoft.Network/networkSecurityGroups/securityRules@2023-04-01' = if (aiUnlimitedHttpPort != 0) { - name: '${uniqueSecurityGroupName}-workspace-http-allow' +resource AiUnlimitedAuth 'Microsoft.Network/networkSecurityGroups/securityRules@2023-04-01' = if (aiUnlimitedAuthPort != 0) { + name: '${uniqueSecurityGroupName}-workspace-auth-allow' parent: networkSecurityGroup properties: { @@ -87,7 +89,7 @@ resource AiUnlimitedHTTP 'Microsoft.Network/networkSecurityGroups/securityRules@ location: location } ] - destinationPortRange: string(aiUnlimitedHttpPort) // destinationPortRanges: [] + destinationPortRange: string(aiUnlimitedAuthPort) // destinationPortRanges: [] direction: 'Inbound' priority: 701 protocol: 'Tcp' @@ -189,6 +191,60 @@ resource AiUnlimitedSchedulerHTTP 'Microsoft.Network/networkSecurityGroups/secur } } +resource AiUnlimitedUIHTTP 'Microsoft.Network/networkSecurityGroups/securityRules@2023-04-01' = if (aiUnlimitedUIHttpPort != 0) { + name: '${name}-workspace-ui-http-allow' + parent: networkSecurityGroup + + properties: { + access: 'Allow' + description: 'allow http to the workspace ui instance' + destinationAddressPrefix: '*' // destinationAddressPrefixes: [] + destinationApplicationSecurityGroups: [for secgroup in detinationAppSecGroups: { + id: secgroup + location: location + } + ] + destinationPortRange: string(aiUnlimitedUIHttpPort) // destinationPortRanges: [] + direction: 'Inbound' + priority: 705 + protocol: 'Tcp' + sourceAddressPrefixes: accessCidrs // sourceAddressPrefix: 'string' + sourceApplicationSecurityGroups: [for secgroup in sourceAppSecGroups: { + id: secgroup + location: location + } + ] + sourcePortRange: '*' // sourcePortRanges: [] + } +} + +resource AiUnlimitedUIHTTPS 'Microsoft.Network/networkSecurityGroups/securityRules@2023-04-01' = if (aiUnlimitedUIHttpsPort != 0) { + name: '${name}-workspace-ui-https-allow' + parent: networkSecurityGroup + + properties: { + access: 'Allow' + description: 'allow https to the workspace ui instance' + destinationAddressPrefix: '*' // destinationAddressPrefixes: [] + destinationApplicationSecurityGroups: [for secgroup in detinationAppSecGroups: { + id: secgroup + location: location + } + ] + destinationPortRange: string(aiUnlimitedUIHttpsPort) // destinationPortRanges: [] + direction: 'Inbound' + priority: 706 + protocol: 'Tcp' + sourceAddressPrefixes: accessCidrs // sourceAddressPrefix: 'string' + sourceApplicationSecurityGroups: [for secgroup in sourceAppSecGroups: { + id: secgroup + location: location + } + ] + sourcePortRange: '*' // sourcePortRanges: [] + } +} + // resource AiUnlimitedSchedulerGRPC 'Microsoft.Network/networkSecurityGroups/securityRules@2023-04-01' = if (aiUnlimitedSchedulerGrpcPort != 0) { // name: '${uniqueSecurityGroupName}-scheduler-grpc-allow' // parent: networkSecurityGroup diff --git a/deployments/azure/templates/bicep/modules/nlb.bicep b/deployments/azure/templates/bicep/modules/nlb.bicep index aaf7f9b..8b42f17 100644 --- a/deployments/azure/templates/bicep/modules/nlb.bicep +++ b/deployments/azure/templates/bicep/modules/nlb.bicep @@ -1,11 +1,13 @@ param name string param location string param dnsPrefix string -param aiUnlimitedHttpPort int = 0 +param aiUnlimitedAuthPort int = 0 param aiUnlimitedGrpcPort int = 0 param aiUnlimitedSchedulerHttpPort int = 0 // param aiUnlimitedSchedulerGrpcPort int = 0 param jupyterHttpPort int = 0 +param aiUnlimitedUIHttpPort int = 0 +param aiUnlimitedUIHttpsPort int = 0 param tags object = {} module lbPublicIPAddress 'public-ip.bicep' = { @@ -62,10 +64,10 @@ resource lb 'Microsoft.Network/loadBalancers@2021-08-01' = { } ] loadBalancingRules: flatten([ - aiUnlimitedHttpPort != 0 + aiUnlimitedAuthPort != 0 ? [ { - name: 'AiUnlimitedUI' + name: 'AiUnlimitedAuth' properties: { frontendIPConfiguration: { id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', name, '${name}Inbound') @@ -77,8 +79,8 @@ resource lb 'Microsoft.Network/loadBalancers@2021-08-01' = { '${name}OutboundBackendPool' ) } - frontendPort: aiUnlimitedHttpPort - backendPort: aiUnlimitedHttpPort + frontendPort: aiUnlimitedAuthPort + backendPort: aiUnlimitedAuthPort enableFloatingIP: false idleTimeoutInMinutes: 15 protocol: 'Tcp' @@ -86,7 +88,7 @@ resource lb 'Microsoft.Network/loadBalancers@2021-08-01' = { loadDistribution: 'Default' disableOutboundSnat: true probe: { - id: resourceId('Microsoft.Network/loadBalancers/probes', name, '${name}UILbProbe') + id: resourceId('Microsoft.Network/loadBalancers/probes', name, '${name}AuthLbProbe') } } } @@ -212,15 +214,75 @@ resource lb 'Microsoft.Network/loadBalancers@2021-08-01' = { // } // ] // : [] + aiUnlimitedUIHttpPort != 0 + ? [ + { + name: 'AiUnlimitedUIHttp' + properties: { + frontendIPConfiguration: { + id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', name, '${name}Inbound') + } + backendAddressPool: { + id: resourceId( + 'Microsoft.Network/loadBalancers/backendAddressPools', + name, + '${name}OutboundBackendPool' + ) + } + frontendPort: aiUnlimitedUIHttpPort + backendPort: aiUnlimitedUIHttpPort + enableFloatingIP: false + idleTimeoutInMinutes: 15 + protocol: 'Tcp' + enableTcpReset: true + loadDistribution: 'Default' + disableOutboundSnat: true + probe: { + id: resourceId('Microsoft.Network/loadBalancers/probes', name, '${name}UIHttpLbProbe') + } + } + } + ] + : [] + aiUnlimitedUIHttpsPort != 0 + ? [ + { + name: 'AiUnlimitedUIHttps' + properties: { + frontendIPConfiguration: { + id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', name, '${name}Inbound') + } + backendAddressPool: { + id: resourceId( + 'Microsoft.Network/loadBalancers/backendAddressPools', + name, + '${name}OutboundBackendPool' + ) + } + frontendPort: aiUnlimitedUIHttpsPort + backendPort: aiUnlimitedUIHttpsPort + enableFloatingIP: false + idleTimeoutInMinutes: 15 + protocol: 'Tcp' + enableTcpReset: true + loadDistribution: 'Default' + disableOutboundSnat: true + probe: { + id: resourceId('Microsoft.Network/loadBalancers/probes', name, '${name}UIHttpsLbProbe') + } + } + } + ] + : [] ]) probes: flatten([ - aiUnlimitedHttpPort != 0 + aiUnlimitedAuthPort != 0 ? [ { - name: '${name}UILbProbe' + name: '${name}AuthLbProbe' properties: { protocol: 'Tcp' - port: aiUnlimitedHttpPort + port: aiUnlimitedAuthPort intervalInSeconds: 5 numberOfProbes: 2 } @@ -281,6 +343,34 @@ resource lb 'Microsoft.Network/loadBalancers@2021-08-01' = { // } // ] // : [] + aiUnlimitedUIHttpPort != 0 + ? [ + { + name: '${name}UIHttpLbProbe' + properties: { + protocol: 'Http' + port: aiUnlimitedUIHttpPort + requestPath: '/' + intervalInSeconds: 5 + numberOfProbes: 2 + } + } + ] + : [] + aiUnlimitedUIHttpsPort != 0 + ? [ + { + name: '${name}UIHttpsLbProbe' + properties: { + protocol: 'Https' + port: aiUnlimitedUIHttpsPort + requestPath: '/' + intervalInSeconds: 5 + numberOfProbes: 2 + } + } + ] + : [] ]) outboundRules: [ { diff --git a/deployments/azure/templates/bicep/modules/public-key.bicep b/deployments/azure/templates/bicep/modules/public-key.bicep new file mode 100644 index 0000000..9f4bd1c --- /dev/null +++ b/deployments/azure/templates/bicep/modules/public-key.bicep @@ -0,0 +1,70 @@ +param Location string +param Name string +param VaultName string +param RoleID string +param Uuid string = newGuid() + +var SecretName = format('{0}-PrivateKey', Name) +var ScriptName = format('{0}-createKeys', Name) +var IdentityName = format('{0}-scratch', Name) +var RoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', RoleID) +var RoleDefinitionName = guid(Identity.id, RoleDefinitionId, resourceGroup().id) + +resource Identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: IdentityName + location: Location +} + +resource RoleDefinition 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: RoleDefinitionName + properties: { + roleDefinitionId: RoleDefinitionId + principalId: Identity.properties.principalId + principalType: 'ServicePrincipal' + } +} + +resource Script 'Microsoft.Resources/deploymentScripts@2023-08-01' = { + name: ScriptName + location: Location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${Identity.id}': {} + } + } + kind: 'AzureCLI' + properties: { + forceUpdateTag: Uuid + azCliVersion: '2.0.80' + timeout: 'PT30M' + retentionInterval: 'P1D' + cleanupPreference: 'OnSuccess' + scriptContent: ''' + #/bin/bash -e + + echo -e 'y' | ssh-keygen -f scratch + + privateKey=$(cat scratch) + publicKey=$(cat 'scratch.pub') + + json="{\"keyinfo\":{\"privateKey\":\"$privateKey\",\"publicKey\":\"$publicKey\"}}" + + echo "$json" > $AZ_SCRIPTS_OUTPUT_PATH + ''' + // primaryScriptUri: 'https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.resources/deployment-script-ssh-key-gen/new-key.sh' + } + dependsOn: [ + RoleDefinition + ] +} + +resource SshKeyecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = { + name: '${VaultName}/${SecretName}' + properties: { + value: Script.properties.outputs.keyinfo.privateKey + } +} + +output PublicKey string = Script.properties.outputs.keyinfo.publicKey +output Status object = Script.properties.status diff --git a/deployments/azure/templates/init/resources.bicep b/deployments/azure/templates/init/resources.bicep index ae353db..30aeb66 100644 --- a/deployments/azure/templates/init/resources.bicep +++ b/deployments/azure/templates/init/resources.bicep @@ -53,6 +53,7 @@ resource roleDef 'Microsoft.Authorization/roleDefinitions@2022-04-01' = { 'Microsoft.KeyVault/vaults/read' 'Microsoft.KeyVault/vaults/write' 'Microsoft.KeyVault/vaults/delete' + 'Microsoft.KeyVault/vaults/secrets/write' 'Microsoft.KeyVault/vaults/accessPolicies/write' 'Microsoft.KeyVault/locations/operationResults/read' 'Microsoft.KeyVault/locations/deletedVaults/purge/action' diff --git a/deployments/docker/README.md b/deployments/docker/README.md index 1eaf259..d0a7e74 100644 --- a/deployments/docker/README.md +++ b/deployments/docker/README.md @@ -1,50 +1,155 @@ -# Deploy with Docker Compose +# Deploy AI Unlimited with Docker Compose -## AI Unlimited + -### With AWS + +- [1. Pre-requisite Steps](#1-pre-requisite-steps) + - [1.1 Cleanup](#11-cleanup) + - [1.2 Initial API Key](#12-initial-api-key) + - [On macOS/Linux](#on-macoslinux) + - [On Windows](#on-windows) + - [1.3 TLS/SSL Certificates](#13-tlsssl-certificates) + - [1.4 Debug mode](#14-debug-mode) +- [2. Deploy AI Unlimited with AWS](#2-deploy-ai-unlimited-with-aws) +- [3. Deploy AI Unlimited with Azure](#3-deploy-ai-unlimited-with-azure) +- [4. Deploy Jupyter](#4-deploy-jupyter) +- [5. Deploy Jupyter and AI Unlimited](#5-deploy-jupyter-and-ai-unlimited) + - [5.1 With AWS](#51-with-aws) + - [5.2 With Azure](#52-with-azure) + - [5.3 With AWS and Azure](#53-with-aws-and-azure) + + + +## 1. Pre-requisite Steps + +### 1.1 Cleanup + +If you spun up AI Unlimited previously on the same domain (i.e. `localhost` or `my.ai-unlimited.local`) we recommend to cleanup the browser cache. + +If you pulled or used the Docker images previously, and are still there, we recommend to delete them, like so: + +```bash +docker-compose -f ai-unlimited.yaml down +docker-compose -f ai-unlimited.yaml rm -fsv +docker rmi $(docker images 'teradata/ai-unlimited*' -qa | sort | uniq) || true +docker images 'teradata/ai-unlimited*' ``` -docker compose -f ai-unlimited.yaml -f aws-credentials-env-vars.yaml.yaml -``` -### With Azure -``` -docker compose -f ai-unlimited.yaml -f azure-credentials-env-vars.yaml.yaml + +If you'd like a new fresh installation with no previous users in AI Unlimited, TLS/SSL certificates, or configuration, we recommend to delete these files or the files with the configration you don't want to persist: + +```bash +rm -rf ./volumes +rm -rf ./ssl ``` -## Jupyter +### 1.2 Initial API Key +An Initial API Key is required in the `AI_UNLIMITED_INIT_API_KEY` environment variable to start the docker-compose stack. You can generate one by doing the following: + +#### On macOS/Linux + +```bash +source generate_api_key.sh ``` -docker compose -f jupyter.yaml up + +#### On Windows + +Execute this script and follow the instructions from the scripts output. + +```powershell +./generate_api_key.ps1 ``` -## Jupyter and AI Unlimited -### With AWS, using environment variables +### 1.3 TLS/SSL Certificates + +To execute AI Unlimited with TLS/SSL enabled you need a Certificate & Private Key pair and (optional but recommended) a CA certificate. There are many different ways to generate them for testing environments on localhost, here is one of them using `mkcert`: + +1. Install `mkcert` following the instructions according to your OS from its [Github repository](https://github.com/FiloSottile/mkcert). If you are on macOS execute: + + ```bash + brew install mkcert + brew install nss # if you use Firefox + ``` + +2. Generate and install the CA certificate, executing: + ```bash + mkcert -install + ``` + +3. Generate the Certificate and Private Key + - generate them in the desired directory, for example, in `./ssl/certs/` and `./ssl/private/` + - if you have a local custom DNS in your `/etc/hosts`, include the hostname at the end of the list, for example, `my.ai-unlimited.local` + + ```bash + mkdir -p ./ssl/{certs,private} + mkcert -cert-file ./ssl/certs/ai-unlimited.crt -key-file ./ssl/private/ai-unlimited.key localhost 127.0.0.1 ::1 my.ai-unlimited.local + ``` + +Feel free to use `mkcert` as you which - or other tool - as long as you have a valid Certificate & Private Key pair. + +### 1.4 Debug mode + +To start on debug mode, export the environment variable `AI_UNLIMITED_LOG_LEVEL` with `DEBUG`, like so: + +```bash +export AI_UNLIMITED_LOG_LEVEL=DEBUG ``` -docker compose -f ai-unlimited.yaml -f aws-credentials-env-vars.yaml -f jupyter.yaml up + +## 2. Deploy AI Unlimited with AWS + +```bash +docker-compose -f ai-unlimited.yaml -f aws-credentials-env-vars.yaml up ``` -### With AWS, using a local volume with credentials +## 3. Deploy AI Unlimited with Azure +```bash +docker-compose -f ai-unlimited.yaml -f azure-credentials-env-vars.yaml up ``` -docker compose -f ai-unlimited.yaml -f aws-credentials-local-volume.yaml -f jupyter.yaml up + +## 4. Deploy Jupyter + +```bash +docker-compose -f jupyter.yaml up ``` -### With Azure, using environment variables +## 5. Deploy Jupyter and AI Unlimited + +### 5.1 With AWS + +If you have the AWS credentials on environment variables, execute: +```bash +docker-compose -f ai-unlimited.yaml -f aws-credentials-env-vars.yaml -f jupyter.yaml up ``` -docker compose -f ai-unlimited.yaml -f azure-credentials-env-vars.yaml -f jupyter.yaml up + +Otherwise, if you are using the AWS configuration files (i.e. `~/.aws/`), execute: + +```bash +docker-compose -f ai-unlimited.yaml -f aws-credentials-local-volume.yaml -f jupyter.yaml up ``` -### With Azure, using a local volume with credentials +### 5.2 With Azure +If you have the Azure credentials on environment variables, execute: + +```bash +docker-compose -f ai-unlimited.yaml -f azure-credentials-env-vars.yaml -f jupyter.yaml up ``` -docker compose -f ai-unlimited.yaml -f azure-credentials-local-volume.yaml -f jupyter.yaml up + +Otherwise, if you are using the Azure configuration files (i.e. `~/.azure/`), execute: + +```bash +docker-compose -f ai-unlimited.yaml -f azure-credentials-local-volume.yaml -f jupyter.yaml up ``` -### With Aws and Azure, using two local credential volumes +### 5.3 With AWS and Azure + +Same as above but using both files, for AWS and Azure either with the credentials on environment variables or with the configuration files or a conbination of both. +In this example, both environments are using the credentials on environment variables: +```bash +docker-compose -f ai-unlimited.yaml -f aws-credentials-local-volume.yaml -f azure-credentials-local-volume.yaml -f jupyter.yaml up ``` -docker compose -f ai-unlimited.yaml -f aws-credentials-local-volume.yaml -f azure-credentials-local-volume.yaml -f jupyter.yaml up -``` \ No newline at end of file diff --git a/deployments/docker/ai-unlimited.yaml b/deployments/docker/ai-unlimited.yaml index e32544d..77d8598 100644 --- a/deployments/docker/ai-unlimited.yaml +++ b/deployments/docker/ai-unlimited.yaml @@ -1,25 +1,55 @@ -version: "3.9" - services: ai-unlimited: deploy: replicas: 1 platform: linux/amd64 container_name: ai-unlimited-workspaces - image: ${AI_UNLIMITED_IMAGE_NAME:-teradata/ai-unlimited-workspaces}:${AI_UNLIMITED_IMAGE_TAG:-v0.2.23} + image: ${AI_UNLIMITED_IMAGE_NAME:-teradata/ai-unlimited-workspaces}:${AI_UNLIMITED_IMAGE_TAG:-v0.3.0} command: workspaces serve -v restart: unless-stopped ports: - - "443:443/tcp" - - "3000:3000/tcp" - - "3282:3282/tcp" + - "${TD_VCD_AUTH_PORT:-3000}:${TD_VCD_AUTH_PORT:-3000}/tcp" + - "${TD_VCD_API_PORT:-3282}:${TD_VCD_API_PORT:-3282}/tcp" environment: accept_license: "Y" TZ: ${AI_UNLIMITED_TZ:-UTC} + TD_VCD_API_PORT: "${TD_VCD_API_PORT:-3282}" + TD_VCD_AUTH_PORT: "${TD_VCD_AUTH_PORT:-3000}" + TD_VCD_INIT_API_KEY: "${AI_UNLIMITED_INIT_API_KEY:?Initial API key is required, please run the generate api key script.}" + TD_VCD_LOG_LEVEL: "${AI_UNLIMITED_LOG_LEVEL:-INFO}" volumes: + - ssl_certs:/etc/td/ssl - ${AI_UNLIMITED_HOME:-./volumes/ai-unlimited-workspaces}:/etc/td networks: - - ai-unlimited + - ai-unlimited-network + + ai-unlimited-ui: + deploy: + replicas: 1 + platform: linux/amd64 + container_name: ai-unlimited-workspaces-ui + image: ${AI_UNLIMITED_UI_IMAGE_NAME:-teradata/ai-unlimited-workspaces-ui}:${AI_UNLIMITED_UI_IMAGE_TAG:-v0.1.0} + ports: + - "443:443/tcp" + - "80:80/tcp" + environment: + TD_VCD_USE_TLS: "${TD_VCD_USE_TLS:-false}" + TD_VCD_API_PORT: "${TD_VCD_API_PORT:-3282}" + TD_VCD_AUTH_PORT: "${TD_VCD_AUTH_PORT:-3000}" + TD_VCD_INIT_API_KEY: "${AI_UNLIMITED_INIT_API_KEY:?Initial API key is required, please run the generate api key script.}" + volumes: + - ssl_certs:/etc/ssl/td + networks: + - ai-unlimited-network + +volumes: + ssl_certs: + # external: true + +# make the volume external if you'd like to preload the certificate & private key in advance. Like so: +# docker volume create ssl_certs +# docker run --rm -v ${PWD}/ssl:/source -v ssl_certs:/dest busybox cp -r /source/certs /source/private /dest/ networks: - ai-unlimited: + ai-unlimited-network: + name: ai-unlimited-network diff --git a/deployments/docker/aws-credentials-env-vars.yaml b/deployments/docker/aws-credentials-env-vars.yaml index 69f49c9..d957270 100644 --- a/deployments/docker/aws-credentials-env-vars.yaml +++ b/deployments/docker/aws-credentials-env-vars.yaml @@ -1,8 +1,6 @@ -version: "3.9" - services: ai-unlimited: environment: - AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID:-''}" - AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY:-''}" - AWS_SESSION_TOKEN: "${AWS_SESSION_TOKEN:-''}" + AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID:?Required to have access to AWS}" + AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY:?Required to have access to AWS}" + AWS_SESSION_TOKEN: "${AWS_SESSION_TOKEN:?Required to have access to AWS}" diff --git a/deployments/docker/aws-credentials-local-volume.yaml b/deployments/docker/aws-credentials-local-volume.yaml index 941d20f..693f9dd 100644 --- a/deployments/docker/aws-credentials-local-volume.yaml +++ b/deployments/docker/aws-credentials-local-volume.yaml @@ -1,5 +1,3 @@ -version: "3.9" - services: ai-unlimited: volumes: diff --git a/deployments/docker/azure-credentials-env-vars.yaml b/deployments/docker/azure-credentials-env-vars.yaml index b5e712a..3f312ad 100644 --- a/deployments/docker/azure-credentials-env-vars.yaml +++ b/deployments/docker/azure-credentials-env-vars.yaml @@ -1,9 +1,7 @@ -version: "3.9" - services: ai-unlimited: environment: - ARM_SUBSCRIPTION_ID: "${ARM_SUBSCRIPTION_ID:-''}" - ARM_CLIENT_ID: "${ARM_CLIENT_ID:-''}" - ARM_CLIENT_SECRET: "${ARM_CLIENT_SECRET:-''}" - ARM_TENANT_ID: "${ARM_TENANT_ID:-''}" + ARM_SUBSCRIPTION_ID: "${ARM_SUBSCRIPTION_ID:?Required to have access to Azure}" + ARM_CLIENT_ID: "${ARM_CLIENT_ID:?Required to have access to Azure}" + ARM_CLIENT_SECRET: "${ARM_CLIENT_SECRET:?Required to have access to Azure}" + ARM_TENANT_ID: "${ARM_TENANT_ID:?Required to have access to Azure}" diff --git a/deployments/docker/azure-credentials-local-volume.yaml b/deployments/docker/azure-credentials-local-volume.yaml index 29efc58..2d20d4c 100644 --- a/deployments/docker/azure-credentials-local-volume.yaml +++ b/deployments/docker/azure-credentials-local-volume.yaml @@ -1,5 +1,3 @@ -version: "3.9" - services: ai-unlimited: volumes: diff --git a/deployments/docker/generate_api_key.ps1 b/deployments/docker/generate_api_key.ps1 new file mode 100644 index 0000000..e63f678 --- /dev/null +++ b/deployments/docker/generate_api_key.ps1 @@ -0,0 +1,14 @@ +Write-Host "Generating API key..." + +# Define character ranges for alphanumeric characters +$chars = @() +$chars += 48..57 # 0-9 +$chars += 65..90 # A-Z +$chars += 97..122 # a-z + +# Generate API key +$API_KEY = -join ($chars | Get-Random -Count 64 | ForEach-Object { [char]$_ }) +$env:AI_UNLIMITED_INIT_API_KEY = $API_KEY + +Write-Host "API Key is generated, please export it by running the following command: `n" +Write-Host '$env:AI_UNLIMITED_INIT_API_KEY = $API_KEY' diff --git a/deployments/docker/generate_api_key.sh b/deployments/docker/generate_api_key.sh new file mode 100644 index 0000000..c3c25d0 --- /dev/null +++ b/deployments/docker/generate_api_key.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +API_KEY=$(LC_ALL=C tr -dc A-Za-z0-9