diff --git a/docs/deployment_guide.md b/docs/deployment_guide.md index a214b3a..eaf9d02 100644 --- a/docs/deployment_guide.md +++ b/docs/deployment_guide.md @@ -19,6 +19,11 @@ aws ssm put-parameter --name "/mri-sched/dbpwd_ec2" --value "DATABASEPWD" --type sam deploy -g --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND CAPABILITY_NAMED_IAM ``` +To deploy the solution into an existent VPC please use the following command: +```bash +sam deploy -g -t template-novpc.yaml --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND CAPABILITY_NAMED_IAM +``` + 3. Provide the stack name, region and the key-pair name. For all the other questions, please accept the default answers or select *Y*. Below an example: ``` diff --git a/template-novpc.yaml b/template-novpc.yaml new file mode 100644 index 0000000..0826d0e --- /dev/null +++ b/template-novpc.yaml @@ -0,0 +1,597 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + "MRI Scheduler Project \n" + +Parameters: + VpcId: + Description: VpcId + Type: 'String' + + PrivateSubnetA: + Description: Private Subnet A + Type: 'String' + + PublicSubnetB: + Description: Private Subnet A + Type: 'String' + + DBInstanceTypeParameter: + Description: EC2 instance type for the database + Type: String + Default: t3.medium + AllowedValues: [t2.medium, t2.large, t3.medium, t3.large] + ConstraintDescription: must be a valid EC2 instance type. + + AMIID: + Type: 'AWS::SSM::Parameter::Value' + Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' + +Globals: + Function: + AutoPublishAlias: live + Handler: index.handler + MemorySize: 256 + Runtime: python3.7 + Timeout: 20 + Tracing: Active + +Resources: + # Roles + IAMEC2InstanceRole: + Type: 'AWS::IAM::Role' + Properties: + Description: The SSM Instance Profile + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - ec2.amazonaws.com + Action: + - 'sts:AssumeRole' + Policies: + - PolicyName: ssmPutParameter + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - 'ssm:PutParameter' + Resource: !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/mri*' + ManagedPolicyArns: + - arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM + - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore + + IAMEC2InstanceProfile: + Type: AWS::IAM::InstanceProfile + Properties: + InstanceProfileName: AWSEC2SSMProfile + Roles: + - !Ref IAMEC2InstanceRole + + DataSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Allow traffic to data resources for MRI SHED + VpcId: !Ref VpcId + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-DataSecurityGroup + + SelfReferenceRulePostGres: + Type: AWS::EC2::SecurityGroupIngress + Properties: + IpProtocol: tcp + FromPort: 5432 + ToPort: 5432 + SourceSecurityGroupId: !Ref DataSecurityGroup + GroupId: !Ref DataSecurityGroup + + SelfReferenceRuleSSH: + Type: AWS::EC2::SecurityGroupIngress + Properties: + IpProtocol: tcp + FromPort: 22 + ToPort: 22 + SourceSecurityGroupId: !Ref DataSecurityGroup + GroupId: !Ref DataSecurityGroup + + + AccessLogs: + Type: AWS::Logs::LogGroup + + DatabaseEC2Instance: + Type: AWS::EC2::Instance + Properties: + ImageId: !Ref AMIID + InstanceType: !Ref DBInstanceTypeParameter + IamInstanceProfile: !Ref IAMEC2InstanceProfile + NetworkInterfaces: + - GroupSet: + - Ref: "DataSecurityGroup" + SubnetId: + Ref: "PrivateSubnetA" + AssociatePublicIpAddress: false + DeviceIndex: "0" + BlockDeviceMappings: + - DeviceName: /dev/xvda + Ebs: + VolumeType: gp3 + VolumeSize: 80 + Encrypted: true + DeleteOnTermination: true + UserData: + Fn::Base64: !Sub | + #!/bin/bash -xe + cd /tmp + alias cp="cp -f" + sudo yum -y --security update + sudo yum -y update aws-cli + sudo yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm + sudo amazon-linux-extras install postgresql10 vim epel -y + sudo yum install -y git jq postgresql-server postgresql-devel + sudo /usr/bin/postgresql-setup --initdb + sudo systemctl enable postgresql + + sudo sed -i "s|#listen_addresses = 'localhost'|listen_addresses = '*'|g" /var/lib/pgsql/data/postgresql.conf + ORIG="host all all 127.0.0.1/32 ident" + DEST="host all all 0.0.0.0/0 md5" + sudo sed -i "s|$ORIG|$DEST|g" /var/lib/pgsql/data/pg_hba.conf + + AWSREGION=$(curl http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region) + + aws configure set default.region $AWSREGION + + DBUSER=$(aws ssm get-parameter --name "/mri-sched/dbuser_ec2" --with-decryption --query Parameter.Value --output text) + DBPWD=$(aws ssm get-parameter --name "/mri-sched/dbpwd_ec2" --with-decryption --query Parameter.Value --output text) + + sudo systemctl start postgresql + + sudo -u postgres psql -c "create database rules;" + sudo -u postgres psql -c "create role $DBUSER;" + sudo -u postgres psql -c "alter role $DBUSER with login;" + sudo -u postgres psql -c "alter user $DBUSER with password '$DBPWD';" + sudo -u postgres psql -c "grant all privileges on database rules to $DBUSER;" + + sudo git clone https://github.com/UBC-CIC/vch-mri.git + cd vch-mri + sudo cp ./src/backend/csv/thesaurus_medical.ths /usr/share/pgsql/tsearch_data/thesaurus_medical.ths + + sudo -u postgres psql -f ./src/backend/init_db.sql + sudo -u postgres psql -d rules -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO $DBUSER;" + sudo -u postgres psql -d rules -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO $DBUSER;" + sudo -u postgres psql -d rules -c "GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO $DBUSER;" + + sudo systemctl restart postgresql + + LOCALHOST=$(curl http://169.254.169.254/latest/meta-data/local-hostname) + aws ssm put-parameter --name "/mri-sched/dbserver_ec2" --type "SecureString" --value "$LOCALHOST" --overwrite + PUBLICHOST=$(curl http://169.254.169.254/latest/meta-data/public-hostname) + aws ssm put-parameter --name "/mri-sched/dbserver_ec2_public" --type "SecureString" --value "$PUBLICHOST" --overwrite + INSTANCEID=$(curl http://169.254.169.254/latest/meta-data/instance-id) + aws ssm put-parameter --name "/mri-sched/ec2" --value "$INSTANCEID" --type SecureString --overwrite + + aws ssm put-parameter --name "/mri-sched/dbname_ec2" --value "rules" --type SecureString --overwrite + + sudo systemctl start amazon-ssm-agent + + Tags: + - Key: 'Name' + Value: 'MriPostgresDB' + - Key: SSMManaged + Value: "Yes" + + HttpApi: + Type: AWS::Serverless::HttpApi + Properties: + AccessLogSettings: + DestinationArn: !GetAtt AccessLogs.Arn + Format: $context.requestId $context.requestTime $context.path $context.status $context.responseLatency $context.integrationLatency + DefaultRouteSettings: + ThrottlingBurstLimit: 200 + FailOnWarnings: True + CorsConfiguration: + AllowHeaders: + - "*" + AllowMethods: + - "*" + AllowOrigins: + - "*" + ExposeHeaders: + - "*" + + ThesaurusFileBucket: + Type: AWS::S3::Bucket + + copyFile: + Type: AWS::SSM::Document + Properties: + Content: + schemaVersion: '2.2' + description: 'Run a script to copy file from S3 onto Linux Instance.' + parameters: + fileName: + type: String + description: "(Required) Name of file in S3" + destPath: + type: String + description: "(Required) EC2 instance ID" + bucket: + type: String + description: "(Required) S3 bucket name" + mainSteps: + - action: aws:runShellScript + name: runCommands + inputs: + timeoutSeconds: '60' + runCommand: + - "aws s3 cp s3://{{bucket}}/{{fileName}} {{destPath}}{{fileName}}" + DocumentType: Command + Name: 'copyFile' + + SpellcheckerLayer: + Type: AWS::Serverless::LayerVersion + Properties: + Description: Spell Checker + ContentUri: ./src/backend/layers/spellchecker.zip + CompatibleRuntimes: + - python3.8 + - python3.7 + - python3.6 + RetentionPolicy: Delete + + Psycopg2Layer: + Type: AWS::Serverless::LayerVersion + Properties: + Description: Psycopg2 Library + ContentUri: ./src/backend/layers/psycopg2.zip + CompatibleRuntimes: + - python3.8 + - python3.7 + - python3.6 + RetentionPolicy: Delete + + PostgresLayer: + Type: AWS::Serverless::LayerVersion + Properties: + Description: Postgres Custom Functions + ContentUri: ./src/backend/layers/postgresql.zip + CompatibleRuntimes: + - python3.8 + - python3.7 + - python3.6 + RetentionPolicy: Delete + + Preprocess: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src/backend/lambdas/preprocess + Events: + ExplicitApi: # warning: creates a public endpoint + Type: HttpApi + Properties: + ApiId: !Ref HttpApi + Method: Post + Path: /parser + TimeoutInMillis: 29000 + PayloadFormatVersion: "2.0" + RouteSettings: + ThrottlingBurstLimit: 100 + Layers: + - !Ref SpellcheckerLayer + - !Ref Psycopg2Layer + - !Ref PostgresLayer + Policies: + - arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess + - arn:aws:iam::aws:policy/ComprehendFullAccess + - arn:aws:iam::aws:policy/ComprehendMedicalFullAccess + - SSMParameterReadPolicy: + ParameterName: "mri*" + - LambdaInvokePolicy: + FunctionName: + !Ref RuleProcessing + VpcConfig: + SecurityGroupIds: + - !Ref DataSecurityGroup + SubnetIds: + - !Ref PrivateSubnetA + Environment: + Variables: + RULE_PROCESSING_LAMBDA: !Ref RuleProcessing + + RuleProcessing: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src/backend/lambdas/rule_processing + Layers: + - !Ref Psycopg2Layer + - !Ref PostgresLayer + Policies: + - SSMParameterReadPolicy: + ParameterName: "mri*" + - arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess + VpcConfig: + SecurityGroupIds: + - !Ref DataSecurityGroup + SubnetIds: + - !Ref PrivateSubnetA + + QueryRules: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src/backend/lambdas/query_rules_table + Events: + ExplicitApi: # warning: creates a public endpoint + Type: HttpApi + Properties: + ApiId: !Ref HttpApi + Method: Post + Path: /rules + TimeoutInMillis: 29000 + PayloadFormatVersion: "2.0" + RouteSettings: + ThrottlingBurstLimit: 100 + Layers: + - !Ref Psycopg2Layer + - !Ref PostgresLayer + Policies: + - SSMParameterReadPolicy: + ParameterName: "mri*" + - LambdaInvokePolicy: + FunctionName: + !Ref UpdateWeights + VpcConfig: + SecurityGroupIds: + - !Ref DataSecurityGroup + SubnetIds: + - !Ref PrivateSubnetA + Environment: + Variables: + UPDATE_WEIGHTS_LAMBDA: !Ref UpdateWeights + + QuerySpellchecker: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src/backend/lambdas/query_spellchecker + Events: + ExplicitApi: # warning: creates a public endpoint + Type: HttpApi + Properties: + ApiId: !Ref HttpApi + Method: Post + Path: /spell + TimeoutInMillis: 29000 + PayloadFormatVersion: "2.0" + RouteSettings: + ThrottlingBurstLimit: 100 + Layers: + - !Ref Psycopg2Layer + - !Ref PostgresLayer + Policies: + - SSMParameterReadPolicy: + ParameterName: "mri*" + VpcConfig: + SecurityGroupIds: + - !Ref DataSecurityGroup + SubnetIds: + - !Ref PrivateSubnetA + + QueryConjunctions: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src/backend/lambdas/query_conjunctions + Events: + ExplicitApi: # warning: creates a public endpoint + Type: HttpApi + Properties: + ApiId: !Ref HttpApi + Method: Post + Path: /conjunctions + TimeoutInMillis: 29000 + PayloadFormatVersion: "2.0" + RouteSettings: + ThrottlingBurstLimit: 100 + Layers: + - !Ref Psycopg2Layer + - !Ref PostgresLayer + Policies: + - SSMParameterReadPolicy: + ParameterName: "mri*" + VpcConfig: + SecurityGroupIds: + - !Ref DataSecurityGroup + SubnetIds: + - !Ref PrivateSubnetA + + QueryWeights: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src/backend/lambdas/query_weights + Events: + ExplicitApi: # warning: creates a public endpoint + Type: HttpApi + Properties: + ApiId: !Ref HttpApi + Method: Post + Path: /weights + TimeoutInMillis: 29000 + PayloadFormatVersion: "2.0" + RouteSettings: + ThrottlingBurstLimit: 100 + Layers: + - !Ref Psycopg2Layer + - !Ref PostgresLayer + Policies: + - SSMParameterReadPolicy: + ParameterName: "mri*" + - LambdaInvokePolicy: + FunctionName: + !Ref UpdateWeights + VpcConfig: + SecurityGroupIds: + - !Ref DataSecurityGroup + SubnetIds: + - !Ref PrivateSubnetA + Environment: + Variables: + UPDATE_WEIGHTS_LAMBDA: !Ref UpdateWeights + + QuerySpecialtyTags: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src/backend/lambdas/query_specialty_tags + Events: + ExplicitApi: # warning: creates a public endpoint + Type: HttpApi + Properties: + ApiId: !Ref HttpApi + Method: Post + Path: /tags + TimeoutInMillis: 29000 + PayloadFormatVersion: "2.0" + RouteSettings: + ThrottlingBurstLimit: 100 + Layers: + - !Ref Psycopg2Layer + - !Ref PostgresLayer + Policies: + - SSMParameterReadPolicy: + ParameterName: "mri*" + VpcConfig: + SecurityGroupIds: + - !Ref DataSecurityGroup + SubnetIds: + - !Ref PrivateSubnetA + + UpdateWeights: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src/backend/lambdas/update_word_weights + Layers: + - !Ref Psycopg2Layer + - !Ref PostgresLayer + Policies: + - SSMParameterReadPolicy: + ParameterName: "mri*" + - arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess + VpcConfig: + SecurityGroupIds: + - !Ref DataSecurityGroup + SubnetIds: + - !Ref PrivateSubnetA + + DataResults: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src/backend/lambdas/results + Events: + ExplicitApi: # warning: creates a public endpoint + Type: HttpApi + Properties: + ApiId: !Ref HttpApi + Method: Post + Path: /results + TimeoutInMillis: 29000 + PayloadFormatVersion: "2.0" + RouteSettings: + ThrottlingBurstLimit: 100 + Layers: + - !Ref Psycopg2Layer + - !Ref PostgresLayer + Policies: + - SSMParameterReadPolicy: + ParameterName: "mri*" + VpcConfig: + SecurityGroupIds: + - !Ref DataSecurityGroup + SubnetIds: + - !Ref PrivateSubnetA + + Thesaurus: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src/backend/lambdas/thesaurus + Events: + FileUpload: + Type: S3 + Properties: + Bucket: !Ref ThesaurusFileBucket + Events: s3:ObjectCreated:* + Layers: + - !Ref Psycopg2Layer + - !Ref PostgresLayer + Policies: + - SSMParameterReadPolicy: + ParameterName: "mri*" + - S3ReadPolicy: + BucketName: '*' + - arn:aws:iam::aws:policy/AmazonSSMFullAccess + - arn:aws:iam::aws:policy/AWSResourceAccessManagerFullAccess + VpcConfig: + SecurityGroupIds: + - !Ref DataSecurityGroup + SubnetIds: + - !Ref PrivateSubnetA + Environment: + Variables: + DEST_PATH: "/usr/share/pgsql/tsearch_data/" # Path to shared postgresql directory + SEND_COMMAND_NAME: !Ref copyFile + SSM_PATH: "/mri-sched/ec2" + + HttpParameter: + Type: AWS::SSM::Parameter + Properties: + Name: /mri-sched/apiid + Type: String + Value: + !Ref HttpApi + Description: !Sub HTTP API Domain for mri-sched + + lambdaThesaurusParameter: + Type: AWS::SSM::Parameter + Properties: + Name: /mri-sched/lambdathesaurusarn + Type: String + Value: + !GetAtt Thesaurus.Arn + Description: LambdaArn Thesaurus + + + lambdaUpdateWeightsParameter: + Type: AWS::SSM::Parameter + Properties: + Name: /mri-sched/lambdaupdateweightsarn + Type: String + Value: + !GetAtt UpdateWeights.Arn + Description: LambdaArn UpdateWeights + +Outputs: + Preprocess: + Value: !GetAtt Preprocess.Arn + RuleProcessing: + Value: !GetAtt RuleProcessing.Arn + QueryRules: + Value: !GetAtt QueryRules.Arn + QuerySpellchecker: + Value: !GetAtt QuerySpellchecker.Arn + QueryConjunctions: + Value: !GetAtt QueryConjunctions.Arn + QueryWeights: + Value: !GetAtt QueryWeights.Arn + UpdateWeights: + Value: !GetAtt UpdateWeights.Arn + DataResults: + Value: !GetAtt DataResults.Arn + HttpApiUrl: + Description: URL of your API endpoint + Value: + Fn::Sub: 'https://${HttpApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/' + + DataSecurityGroup: + Description: Allow traffic to data subnets + Value: !Ref DataSecurityGroup + Export: + Name: !Sub ${AWS::StackName}-DataSecurityGroup \ No newline at end of file diff --git a/template.yaml b/template.yaml index 8a39b7a..7fc34e3 100644 --- a/template.yaml +++ b/template.yaml @@ -211,14 +211,15 @@ Resources: - Ref: "DataSecurityGroup" SubnetId: Ref: "PrivateSubnetA" - AssociatePublicIpAddress: "false" + AssociatePublicIpAddress: false DeviceIndex: "0" BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: - VolumeType: gp2 - VolumeSize: 40 - DeleteOnTermination: 'false' + VolumeType: gp3 + VolumeSize: 80 + Encrypted: true + DeleteOnTermination: true UserData: Fn::Base64: !Sub | #!/bin/bash -xe