|
| 1 | +Metadata: |
| 2 | + License: Apache-2.0 |
| 3 | +AWSTemplateFormatVersion: '2010-09-09' |
| 4 | +Description: 'AWS CloudFormation Template to create an EC2 instance with NICE-DCV and IGV pre installed.' |
| 5 | + |
| 6 | +Mappings: |
| 7 | + RegionMap: |
| 8 | + us-east-1: |
| 9 | + AmiID: "ami-0759d9ecf1fcb7be9" |
| 10 | + us-east-2: |
| 11 | + AmiID: "ami-018bd425554ab6cf4" |
| 12 | + us-west-1: |
| 13 | + AmiID: "ami-02258ab533d22fa75" |
| 14 | + us-west-2: |
| 15 | + AmiID: "ami-02d3838a1d948d3ca" |
| 16 | + ap-south-1: |
| 17 | + AmiID: "ami-0c33a5a105981b1fb" |
| 18 | + ap-southeast-1: |
| 19 | + AmiID: "ami-061f216d271a880ce" |
| 20 | + ap-southeast-2: |
| 21 | + AmiID: "ami-0d1748650516fb8a3" |
| 22 | + |
| 23 | +Parameters: |
| 24 | + Namespace: |
| 25 | + Type: String |
| 26 | + Description: An environment name that will be prefixed to resource names |
| 27 | + S3Mounts: |
| 28 | + Type: String |
| 29 | + Description: A JSON array of objects with name, bucket, and prefix properties used to mount data |
| 30 | + IamPolicyDocument: |
| 31 | + Type: String |
| 32 | + Description: The IAM policy to be associated with the launched workstation |
| 33 | + VPC: |
| 34 | + Description: The VPC in which the EC2 instance will reside |
| 35 | + Type: AWS::EC2::VPC::Id |
| 36 | + Subnet: |
| 37 | + Description: The VPC subnet in which the EC2 instance will reside |
| 38 | + Type: AWS::EC2::Subnet::Id |
| 39 | + EnvironmentInstanceFiles: |
| 40 | + Type: String |
| 41 | + Description: >- |
| 42 | + An S3 URI (starting with "s3://") that specifies the location of files to be copied to |
| 43 | + the environment instance, including any bootstrap scripts |
| 44 | + InstanceType: |
| 45 | + Description: Choose the instance type for this instance. e.g. t3.medium |
| 46 | + Type: String |
| 47 | + Default: t3.medium |
| 48 | + AllowedValues: [t3.medium, t3.xlarge, t3.2xlarge, g4dn.xlarge, g4dn.2xlarge, g4dn.4xlarge, g4dn.8xlarge, g4dn.12xlarge, g4dn.16xlarge, g4dn.metal, g3.4xlarge, g3.8xlarge, g3.16xlarge] |
| 49 | + ConstraintDescription: must be a valid EC2 instance type. |
| 50 | + KeyPair: |
| 51 | + Description: Name of an existing EC2 KeyPair to enable SSH access to the instance. If no key pairs exist, please create one from the button next to the dropdown. Please contact your Administrator if you are unable to create one. |
| 52 | + Type: AWS::EC2::KeyPair::KeyName |
| 53 | + ConstraintDescription: must be the name of an existing EC2 KeyPair. |
| 54 | + AllowedIpAddress: |
| 55 | + Description: The IP address range that can be used to SSH to instance and Connect to DCV |
| 56 | + Type: String |
| 57 | + MinLength: '9' |
| 58 | + MaxLength: '18' |
| 59 | + Default: 0.0.0.0/0 |
| 60 | + AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2}) |
| 61 | + ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. |
| 62 | + |
| 63 | +Conditions: |
| 64 | + IamPolicyEmpty: !Equals [!Ref IamPolicyDocument, '{}'] |
| 65 | + |
| 66 | +Resources: |
| 67 | + SSMPolicy: |
| 68 | + Type: AWS::IAM::ManagedPolicy |
| 69 | + Properties: |
| 70 | + ManagedPolicyName: !Join ['-', [Ref: Namespace, 'SSM-Policy']] |
| 71 | + PolicyDocument: |
| 72 | + Version: "2012-10-17" |
| 73 | + Statement: |
| 74 | + - Effect: Allow |
| 75 | + Action: |
| 76 | + - ssm:PutParameter |
| 77 | + - ssm:GetParameter |
| 78 | + - ssm:DescribeParameters |
| 79 | + Resource: "*" |
| 80 | + IAMRole: |
| 81 | + Type: 'AWS::IAM::Role' |
| 82 | + Properties: |
| 83 | + RoleName: !Join ['-', [Ref: Namespace, 'ec2-role']] |
| 84 | + Path: '/' |
| 85 | + AssumeRolePolicyDocument: |
| 86 | + Version: '2012-10-17' |
| 87 | + Statement: |
| 88 | + - Effect: 'Allow' |
| 89 | + Principal: |
| 90 | + Service: |
| 91 | + - 'ec2.amazonaws.com' |
| 92 | + Action: |
| 93 | + - 'sts:AssumeRole' |
| 94 | + Policies: |
| 95 | + - !If |
| 96 | + - IamPolicyEmpty |
| 97 | + - !Ref 'AWS::NoValue' |
| 98 | + - PolicyName: !Join ['-', [Ref: Namespace, 's3-studydata-policy']] |
| 99 | + PolicyDocument: !Ref IamPolicyDocument |
| 100 | + ManagedPolicyArns: |
| 101 | + - Ref: SSMPolicy |
| 102 | + |
| 103 | + InstanceProfile: |
| 104 | + Type: 'AWS::IAM::InstanceProfile' |
| 105 | + Properties: |
| 106 | + InstanceProfileName: !Join ['-', [Ref: Namespace, 'ec2-profile']] |
| 107 | + Path: '/' |
| 108 | + Roles: |
| 109 | + - Ref: IAMRole |
| 110 | + |
| 111 | + EC2Instance: |
| 112 | + Type: AWS::EC2::Instance |
| 113 | + CreationPolicy: |
| 114 | + ResourceSignal: |
| 115 | + Timeout: PT10M |
| 116 | + Metadata: |
| 117 | + AWS::CloudFormation::Init: |
| 118 | + configSets: |
| 119 | + default: |
| 120 | + - config1 |
| 121 | + - config2 |
| 122 | + config1: |
| 123 | + files: |
| 124 | + "/home/ec2-user/.config/autostart/igv.desktop": |
| 125 | + mode: "000644" |
| 126 | + owner: "ec2-user" |
| 127 | + group: "ec2-user" |
| 128 | + content: | |
| 129 | + [Desktop Entry] |
| 130 | + Type=Application |
| 131 | + Exec="/home/ec2-user/start_igv.sh" |
| 132 | + Hidden=false |
| 133 | + NoDisplay=false |
| 134 | + X-GNOME-Autostart-enabled=true |
| 135 | + Terminal=true |
| 136 | + Name=igv |
| 137 | + Comment=igv Desktop |
| 138 | + "/home/ec2-user/.config/autostart/studymount.desktop": |
| 139 | + mode: "000644" |
| 140 | + owner: "ec2-user" |
| 141 | + group: "ec2-user" |
| 142 | + content: | |
| 143 | + [Desktop Entry] |
| 144 | + Type=Application |
| 145 | + Exec="/home/ec2-user/mount_study.sh" |
| 146 | + Hidden=false |
| 147 | + NoDisplay=false |
| 148 | + X-GNOME-Autostart-enabled=true |
| 149 | + Terminal=true |
| 150 | + Name=Study-Mounting |
| 151 | + Comment=Study Mounting |
| 152 | + "/home/ec2-user/mount_study.sh": |
| 153 | + mode: "000755" |
| 154 | + owner: "ec2-user" |
| 155 | + group: "ec2-user" |
| 156 | + content: !Sub | |
| 157 | + #!/bin/sh |
| 158 | + set -x |
| 159 | + # Adding sleep to avoid mounting failure |
| 160 | + sleep 30 |
| 161 | + mount_s3.sh 2>&1 >> /home/ec2-user/mount_s3.log |
| 162 | + "/home/ec2-user/start_igv.sh": |
| 163 | + mode: "000755" |
| 164 | + owner: "ec2-user" |
| 165 | + group: "ec2-user" |
| 166 | + content: !Sub | |
| 167 | + #!/bin/sh |
| 168 | + set -x |
| 169 | + xhost +local:all |
| 170 | + # Adding sleep to wait for study mounting |
| 171 | + sleep 40 |
| 172 | + docker run --rm -ti -v /home/ec2-user:/home/ec2-user --net=host -e DISPLAY=:1 relevancelab/igv-ide:1.1.0 |
| 173 | + "/home/ec2-user/set_user_token.sh": |
| 174 | + mode: "000755" |
| 175 | + owner: "ec2-user" |
| 176 | + group: "ec2-user" |
| 177 | + content: !Sub | |
| 178 | + #!/bin/bash |
| 179 | + # trap '/opt/aws/bin/cfn-signal --exit-code 1 --resource EC2Instance --region ${AWS::Region} --stack ${AWS::StackName}' ERR |
| 180 | + region=$(curl -s "http://169.254.169.254/latest/meta-data/placement/region") |
| 181 | + instance_id=$(curl -s "http://169.254.169.254/latest/meta-data/instance-id") |
| 182 | + session_id="rg-session" |
| 183 | + auth_token=`uuidgen` |
| 184 | + parameter_name="/RL/RG/igv-viewer/auth-token/$instance_id" |
| 185 | + aws ssm put-parameter --name "$parameter_name" --type "String" --value '{"auth_token":"'$auth_token'","session_id":"'$session_id'"}' --region $region --overwrite |
| 186 | + echo "User token set successfully" |
| 187 | + "/home/ec2-user/supervisor/supervisord.conf": |
| 188 | + mode: "000644" |
| 189 | + owner: "ec2-user" |
| 190 | + group: "ec2-user" |
| 191 | + content: | |
| 192 | + [unix_http_server] |
| 193 | + file=/home/ec2-user/supervisor/supervisor.sock ; the path to the socket file |
| 194 | + |
| 195 | + [supervisord] |
| 196 | + logfile=/home/ec2-user/supervisor/supervisord.log ; main log file; default $CWD/supervisord.log |
| 197 | + logfile_maxbytes=50MB ; max main logfile bytes b4 rotation; default 50MB |
| 198 | + logfile_backups=10 ; # of main logfile backups; 0 means none, default 10 |
| 199 | + loglevel=info ; log level; default info; others: debug,warn,trace |
| 200 | + pidfile=/home/ec2-user/supervisor/supervisord.pid ; supervisord pidfile; default supervisord.pid |
| 201 | + nodaemon=false ; start in foreground if true; default false |
| 202 | + minfds=1024 ; min. avail startup file descriptors; default 1024 |
| 203 | + minprocs=200 ; min. avail process descriptors;default 200 |
| 204 | + |
| 205 | + [rpcinterface:supervisor] |
| 206 | + supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface |
| 207 | +
|
| 208 | + [supervisorctl] |
| 209 | + serverurl=unix:///home/ec2-user/supervisor/supervisor.sock ; use a unix:// URL for a unix socket |
| 210 | +
|
| 211 | + [program:dcv-custom-auth] |
| 212 | + command=docker run -p 8445:8445 -e PORT=8445 -e PARAMNAMEPREFIX=/RL/RG/igv-viewer/auth-token/ relevancelab/nice-dcv-auth-svc:1.0.0 ; the program (relative uses PATH, can take args) |
| 213 | + directory=/home/ec2-user ; directory to cwd to before exec (def no cwd) |
| 214 | + user=ec2-user ; setuid to this UNIX account to run the program |
| 215 | + redirect_stderr=true ; redirect proc stderr to stdout (default false) |
| 216 | + stdout_logfile=/home/ec2-user/supervisor/cust_auth.log |
| 217 | + "/home/ec2-user/supervisor/supervisord.log": |
| 218 | + content: "\n" |
| 219 | + mode: "000664" |
| 220 | + owner: "ec2-user" |
| 221 | + group: "ec2-user" |
| 222 | + "/home/ec2-user/supervisor/cust_auth.log": |
| 223 | + content: "\n" |
| 224 | + mode: "000664" |
| 225 | + owner: "ec2-user" |
| 226 | + group: "ec2-user" |
| 227 | + "/home/ec2-user/disable_lock.sh": |
| 228 | + content: !Sub | |
| 229 | + #!/bin/bash |
| 230 | + gsettings set org.gnome.desktop.lockdown disable-lock-screen 'true' |
| 231 | + echo "Lock disabled successfully" |
| 232 | + mode: "000755" |
| 233 | + owner: "ec2-user" |
| 234 | + group: "ec2-user" |
| 235 | + config2: |
| 236 | + commands: |
| 237 | + 01_start_ext_auth: |
| 238 | + cwd: "/home/ec2-user/supervisor" |
| 239 | + command: "/usr/local/bin/supervisord" |
| 240 | + 02_set_user_token: |
| 241 | + cwd: "/home/ec2-user" |
| 242 | + command: "/home/ec2-user/set_user_token.sh" |
| 243 | + Properties: |
| 244 | + UserData: |
| 245 | + Fn::Base64: !Sub | |
| 246 | + #!/usr/bin/env bash |
| 247 | + exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1 |
| 248 | + # trap '/opt/aws/bin/cfn-signal --exit-code 1 --resource EC2Instance --region ${AWS::Region} --stack ${AWS::StackName}' ERR |
| 249 | + |
| 250 | + # Install supervisor and start on boot |
| 251 | + pip3 install supervisor crudini |
| 252 | + |
| 253 | + # Route auth request to external authenticator and restart dcv |
| 254 | + /usr/local/bin/crudini --set /etc/dcv/dcv.conf security auth-token-verifier \"http://127.0.0.1:8445\" |
| 255 | + systemctl restart dcvserver |
| 256 | + |
| 257 | + # Create dcv session start script |
| 258 | + cat << EOF > /usr/local/bin/start-dcv-session |
| 259 | + dcv create-session rg-session --name rg-session --user ec2-user --owner ec2-user |
| 260 | + EOF |
| 261 | + |
| 262 | + # Create dcv session on reboot |
| 263 | + chown root: "/usr/local/bin/start-dcv-session" |
| 264 | + chmod 775 "/usr/local/bin/start-dcv-session" |
| 265 | + sh "/usr/local/bin/start-dcv-session" |
| 266 | + |
| 267 | + # Install Docker |
| 268 | + yum install -y docker |
| 269 | + systemctl enable docker.service |
| 270 | + systemctl enable containerd.service |
| 271 | + systemctl start docker |
| 272 | + usermod -a -G docker ec2-user |
| 273 | + |
| 274 | + # Pull docker image |
| 275 | + docker pull relevancelab/igv-ide:1.1.0 |
| 276 | + docker pull relevancelab/nice-dcv-auth-svc:1.0.0 |
| 277 | + |
| 278 | + # Run init script to create files |
| 279 | + /opt/aws/bin/cfn-init --verbose --stack ${AWS::StackName} --resource EC2Instance --region ${AWS::Region} |
| 280 | + |
| 281 | + # Add command to start on reboot |
| 282 | + crontab -l 2>/dev/null > "/tmp/crontab" |
| 283 | + sh "/usr/local/bin/start-dcv-session" |
| 284 | + echo '@reboot cd /home/ec2-user/supervisor && /usr/local/bin/supervisord 2>&1 >> /home/ec2-user/supervisor/cust_auth.log' >> "/tmp/crontab" |
| 285 | + echo '@reboot /usr/local/bin/start-dcv-session 2>&1 >> /var/log/start-dcv-session.log' >> "/tmp/crontab" |
| 286 | + echo '@reboot /home/ec2-user/set_user_token.sh 2>&1 >> /var/log/set_user_token.log' >> "/tmp/crontab" |
| 287 | + crontab "/tmp/crontab" |
| 288 | + # Restart DCV session to start igv |
| 289 | + dcv close-session rg-session |
| 290 | + sh "/usr/local/bin/start-dcv-session" |
| 291 | + |
| 292 | + # Remove password and lock for ec2-user |
| 293 | + passwd -l ec2-user |
| 294 | + dcv create-session lock-session --name lock-session --user ec2-user --owner ec2-user --init /home/ec2-user/disable_lock.sh |
| 295 | + |
| 296 | + # Download and execute bootstrap script |
| 297 | + aws s3 cp --region "${AWS::Region}" "${EnvironmentInstanceFiles}/get_bootstrap.sh" "/tmp" |
| 298 | + chmod 500 "/tmp/get_bootstrap.sh" |
| 299 | + /tmp/get_bootstrap.sh "${EnvironmentInstanceFiles}" '${S3Mounts}' "${AWS::Region}" |
| 300 | + |
| 301 | + /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource EC2Instance --region ${AWS::Region} |
| 302 | + |
| 303 | + |
| 304 | + |
| 305 | + |
| 306 | + InstanceType: !Ref 'InstanceType' |
| 307 | + SecurityGroups: [!Ref 'InstanceSecurityGroup'] |
| 308 | + KeyName: !Ref 'KeyPair' |
| 309 | + ImageId: !FindInMap |
| 310 | + - RegionMap |
| 311 | + - !Ref 'AWS::Region' |
| 312 | + - AmiID |
| 313 | + IamInstanceProfile: !Ref InstanceProfile |
| 314 | + PropagateTagsToVolumeOnCreation: true |
| 315 | + Tags: |
| 316 | + - Key: Name |
| 317 | + Value: !Join ['-', [Ref: Namespace, 'ec2-linux']] |
| 318 | + - Key: Description |
| 319 | + Value: EC2 workspace instance |
| 320 | + - Key: cost_resource |
| 321 | + Value: !Sub ${AWS::StackName} |
| 322 | + |
| 323 | + |
| 324 | + InstanceSecurityGroup: |
| 325 | + Type: AWS::EC2::SecurityGroup |
| 326 | + Properties: |
| 327 | + GroupDescription: Enable SSH access |
| 328 | + SecurityGroupIngress: |
| 329 | + - IpProtocol: tcp |
| 330 | + FromPort: '22' |
| 331 | + ToPort: '22' |
| 332 | + CidrIp: !Ref 'AllowedIpAddress' |
| 333 | + - IpProtocol: tcp |
| 334 | + FromPort: '8443' |
| 335 | + ToPort: '8443' |
| 336 | + CidrIp: !Ref 'AllowedIpAddress' |
| 337 | + |
| 338 | + |
| 339 | +Outputs: |
| 340 | + InstanceId: |
| 341 | + Description: InstanceId of the newly created EC2 instance |
| 342 | + Value: !Ref 'EC2Instance' |
| 343 | + InstanceIPAddress: |
| 344 | + Description: IP address of the newly created EC2 instance |
| 345 | + Value: !GetAtt [EC2Instance, PublicIp] |
| 346 | + InstanceDNSName: |
| 347 | + Description: DNS name of the newly created EC2 instance |
| 348 | + Value: !GetAtt [EC2Instance, PublicDnsName] |
| 349 | + ApplicationPort: |
| 350 | + Description: The Port in which the application is running |
| 351 | + Value: '8443' |
0 commit comments