@medplum/cdk 2.0.11 → 2.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -482,6 +482,37 @@ class BackEnd extends constructs.Construct {
482
482
  }
483
483
  }
484
484
 
485
+ /**
486
+ * Grants S3 bucket read access to the CloudFront Origin Access Identity (OAI).
487
+ *
488
+ * Under normal circumstances, where CDK creates both the S3 bucket and the OAI,
489
+ * you can achieve this same behavior by simply calling:
490
+ *
491
+ * bucket.grantRead(identity);
492
+ *
493
+ * However, if importing an S3 bucket via `s3.Bucket.fromBucketAttributes()`, that does not work.
494
+ *
495
+ * See: https://stackoverflow.com/a/60917015
496
+ *
497
+ * @param scope The CDK construct scope.
498
+ * @param bucket The S3 bucket.
499
+ * @param identity The CloudFront Origin Access Identity.
500
+ */
501
+ function grantBucketAccessToOriginAccessIdentity(scope, bucket, identity) {
502
+ const policyStatement = new awsCdkLib.aws_iam.PolicyStatement();
503
+ policyStatement.addActions('s3:GetObject*');
504
+ policyStatement.addActions('s3:GetBucket*');
505
+ policyStatement.addActions('s3:List*');
506
+ policyStatement.addResources(bucket.bucketArn);
507
+ policyStatement.addResources(`${bucket.bucketArn}/*`);
508
+ policyStatement.addCanonicalUserPrincipal(identity.cloudFrontOriginAccessIdentityS3CanonicalUserId);
509
+ let policy = bucket.policy;
510
+ if (!policy) {
511
+ policy = new awsCdkLib.aws_s3.BucketPolicy(scope, 'Policy', { bucket });
512
+ }
513
+ policy.document.addStatements(policyStatement);
514
+ }
515
+
485
516
  /**
486
517
  * Static app infrastructure, which deploys app content to an S3 bucket.
487
518
  *
@@ -570,7 +601,7 @@ class FrontEnd extends constructs.Construct {
570
601
  });
571
602
  // Origin access identity
572
603
  const originAccessIdentity = new awsCdkLib.aws_cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {});
573
- appBucket.grantRead(originAccessIdentity);
604
+ grantBucketAccessToOriginAccessIdentity(this, appBucket, originAccessIdentity);
574
605
  // CloudFront distribution
575
606
  const distribution = new awsCdkLib.aws_cloudfront.Distribution(this, 'AppDistribution', {
576
607
  defaultRootObject: 'index.html',
@@ -702,7 +733,7 @@ class Storage extends constructs.Construct {
702
733
  });
703
734
  // Origin access identity
704
735
  const originAccessIdentity = new awsCdkLib.aws_cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {});
705
- storageBucket.grantRead(originAccessIdentity);
736
+ grantBucketAccessToOriginAccessIdentity(this, storageBucket, originAccessIdentity);
706
737
  // CloudFront distribution
707
738
  const distribution = new awsCdkLib.aws_cloudfront.Distribution(this, 'StorageDistribution', {
708
739
  defaultBehavior: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../src/waf.ts","../../../src/backend.ts","../../../src/frontend.ts","../../../src/storage.ts","../../../src/index.ts"],"sourcesContent":["// Based on https://gist.github.com/statik/f1ac9d6227d98d30c7a7cec0c83f4e64\n\nimport { aws_wafv2 as wafv2 } from 'aws-cdk-lib';\n\nexport const awsManagedRules: wafv2.CfnWebACL.RuleProperty[] = [\n // Common Rule Set aligns with major portions of OWASP Core Rule Set\n // https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-baseline.html\n {\n name: 'AWS-AWSManagedRulesCommonRuleSet',\n priority: 10,\n statement: {\n managedRuleGroupStatement: {\n vendorName: 'AWS',\n name: 'AWSManagedRulesCommonRuleSet',\n // Excluding generic RFI body rule for sns notifications\n // https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html\n excludedRules: [\n { name: 'NoUserAgent_HEADER' },\n { name: 'UserAgent_BadBots_HEADER' },\n { name: 'SizeRestrictions_QUERYSTRING' },\n { name: 'SizeRestrictions_Cookie_HEADER' },\n { name: 'SizeRestrictions_BODY' },\n { name: 'SizeRestrictions_URIPATH' },\n { name: 'EC2MetaDataSSRF_BODY' },\n { name: 'EC2MetaDataSSRF_COOKIE' },\n { name: 'EC2MetaDataSSRF_URIPATH' },\n { name: 'EC2MetaDataSSRF_QUERYARGUMENTS' },\n { name: 'GenericLFI_QUERYARGUMENTS' },\n { name: 'GenericLFI_URIPATH' },\n { name: 'GenericLFI_BODY' },\n { name: 'RestrictedExtensions_URIPATH' },\n { name: 'RestrictedExtensions_QUERYARGUMENTS' },\n { name: 'GenericRFI_QUERYARGUMENTS' },\n { name: 'GenericRFI_BODY' },\n { name: 'GenericRFI_URIPATH' },\n { name: 'CrossSiteScripting_COOKIE' },\n { name: 'CrossSiteScripting_QUERYARGUMENTS' },\n { name: 'CrossSiteScripting_BODY' },\n { name: 'CrossSiteScripting_URIPATH' },\n ],\n },\n },\n overrideAction: {\n count: {},\n },\n visibilityConfig: {\n sampledRequestsEnabled: true,\n cloudWatchMetricsEnabled: true,\n metricName: 'AWS-AWSManagedRulesCommonRuleSet',\n },\n },\n // AWS IP Reputation list includes known malicious actors/bots and is regularly updated\n // https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-ip-rep.html\n {\n name: 'AWS-AWSManagedRulesAmazonIpReputationList',\n priority: 20,\n statement: {\n managedRuleGroupStatement: {\n vendorName: 'AWS',\n name: 'AWSManagedRulesAmazonIpReputationList',\n excludedRules: [{ name: 'AWSManagedIPReputationList' }, { name: 'AWSManagedReconnaissanceList' }],\n },\n },\n overrideAction: {\n count: {},\n },\n visibilityConfig: {\n sampledRequestsEnabled: true,\n cloudWatchMetricsEnabled: true,\n metricName: 'AWSManagedRulesAmazonIpReputationList',\n },\n },\n // Blocks common SQL Injection\n // https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-use-case.html#aws-managed-rule-groups-use-case-sql-db\n {\n name: 'AWSManagedRulesSQLiRuleSet',\n priority: 30,\n visibilityConfig: {\n sampledRequestsEnabled: true,\n cloudWatchMetricsEnabled: true,\n metricName: 'AWSManagedRulesSQLiRuleSet',\n },\n overrideAction: {\n count: {},\n },\n statement: {\n managedRuleGroupStatement: {\n vendorName: 'AWS',\n name: 'AWSManagedRulesSQLiRuleSet',\n excludedRules: [\n { name: 'SQLi_QUERYARGUMENTS' },\n { name: 'SQLiExtendedPatterns_QUERYARGUMENTS' },\n { name: 'SQLi_BODY' },\n { name: 'SQLiExtendedPatterns_BODY' },\n { name: 'SQLi_COOKIE' },\n { name: 'SQLi_URIPATH' },\n ],\n },\n },\n },\n // Blocks attacks targeting LFI(Local File Injection) for linux systems\n // https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-use-case.html#aws-managed-rule-groups-use-case-linux-os\n {\n name: 'AWSManagedRuleLinux',\n priority: 40,\n visibilityConfig: {\n sampledRequestsEnabled: true,\n cloudWatchMetricsEnabled: true,\n metricName: 'AWSManagedRuleLinux',\n },\n overrideAction: {\n count: {},\n },\n statement: {\n managedRuleGroupStatement: {\n vendorName: 'AWS',\n name: 'AWSManagedRulesLinuxRuleSet',\n excludedRules: [{ name: 'LFI_URIPATH' }, { name: 'LFI_QUERYSTRING' }, { name: 'LFI_COOKIE' }],\n },\n },\n },\n];\n","import {\n aws_ec2 as ec2,\n aws_ecs as ecs,\n aws_elasticache as elasticache,\n aws_elasticloadbalancingv2 as elbv2,\n aws_iam as iam,\n aws_logs as logs,\n aws_rds as rds,\n aws_route53 as route53,\n aws_route53_targets as targets,\n aws_s3 as s3,\n aws_secretsmanager as secretsmanager,\n aws_ssm as ssm,\n aws_wafv2 as wafv2,\n Duration,\n RemovalPolicy,\n} from 'aws-cdk-lib';\nimport { Repository } from 'aws-cdk-lib/aws-ecr';\nimport { Construct } from 'constructs';\nimport { MedplumInfraConfig } from './config';\nimport { awsManagedRules } from './waf';\n\n/**\n * Based on: https://github.com/aws-samples/http-api-aws-fargate-cdk/blob/master/cdk/singleAccount/lib/fargate-vpclink-stack.ts\n *\n * RDS config: https://docs.aws.amazon.com/cdk/api/latest/docs/aws-rds-readme.html\n */\nexport class BackEnd extends Construct {\n constructor(scope: Construct, config: MedplumInfraConfig) {\n super(scope, 'BackEnd');\n\n const name = config.name;\n\n // VPC\n let vpc: ec2.IVpc;\n\n if (config.vpcId) {\n // Lookup VPC by ARN\n vpc = ec2.Vpc.fromLookup(this, 'VPC', { vpcId: config.vpcId });\n } else {\n // VPC Flow Logs\n const vpcFlowLogs = new logs.LogGroup(this, 'VpcFlowLogs', {\n logGroupName: '/medplum/flowlogs/' + name,\n removalPolicy: RemovalPolicy.DESTROY,\n });\n\n // Create VPC\n vpc = new ec2.Vpc(this, 'VPC', {\n maxAzs: config.maxAzs,\n flowLogs: {\n cloudwatch: {\n destination: ec2.FlowLogDestination.toCloudWatchLogs(vpcFlowLogs),\n trafficType: ec2.FlowLogTrafficType.ALL,\n },\n },\n });\n }\n\n // Bot Lambda Role\n const botLambdaRole = new iam.Role(this, 'BotLambdaRole', {\n assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),\n });\n\n // RDS\n let rdsCluster = undefined;\n let rdsSecretsArn = config.rdsSecretsArn;\n if (!rdsSecretsArn) {\n rdsCluster = new rds.DatabaseCluster(this, 'DatabaseCluster', {\n engine: rds.DatabaseClusterEngine.auroraPostgres({\n version: rds.AuroraPostgresEngineVersion.VER_12_9,\n }),\n credentials: rds.Credentials.fromGeneratedSecret('clusteradmin'),\n defaultDatabaseName: 'medplum',\n storageEncrypted: true,\n instances: config.rdsInstances,\n instanceProps: {\n vpc: vpc,\n vpcSubnets: {\n subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,\n },\n instanceType: config.rdsInstanceType ? new ec2.InstanceType(config.rdsInstanceType) : undefined,\n enablePerformanceInsights: true,\n },\n backup: {\n retention: Duration.days(7),\n },\n cloudwatchLogsExports: ['postgresql'],\n instanceUpdateBehaviour: rds.InstanceUpdateBehaviour.ROLLING,\n });\n\n rdsSecretsArn = (rdsCluster.secret as secretsmanager.ISecret).secretArn;\n }\n\n // Redis\n // Important: For HIPAA compliance, you must specify TransitEncryptionEnabled as true, an AuthToken, and a CacheSubnetGroup.\n const redisSubnetGroup = new elasticache.CfnSubnetGroup(this, 'RedisSubnetGroup', {\n description: 'Redis Subnet Group',\n subnetIds: vpc.privateSubnets.map((subnet) => subnet.subnetId),\n });\n\n const redisSecurityGroup = new ec2.SecurityGroup(this, 'RedisSecurityGroup', {\n vpc,\n description: 'Redis Security Group',\n allowAllOutbound: false,\n });\n\n const redisPassword = new secretsmanager.Secret(this, 'RedisPassword', {\n generateSecretString: {\n secretStringTemplate: '{}',\n generateStringKey: 'password',\n excludeCharacters: '@%*()_+=`~{}|[]\\\\:\";\\'?,./',\n },\n });\n\n const redisCluster = new elasticache.CfnReplicationGroup(this, 'RedisCluster', {\n engine: 'Redis',\n engineVersion: '6.x',\n cacheNodeType: 'cache.t2.medium',\n replicationGroupDescription: 'RedisReplicationGroup',\n authToken: redisPassword.secretValueFromJson('password').toString(),\n transitEncryptionEnabled: true,\n atRestEncryptionEnabled: true,\n multiAzEnabled: true,\n cacheSubnetGroupName: redisSubnetGroup.ref,\n numNodeGroups: 1,\n replicasPerNodeGroup: 1,\n securityGroupIds: [redisSecurityGroup.securityGroupId],\n });\n redisCluster.node.addDependency(redisPassword);\n\n const redisSecrets = new secretsmanager.Secret(this, 'RedisSecrets', {\n generateSecretString: {\n secretStringTemplate: JSON.stringify({\n host: redisCluster.attrPrimaryEndPointAddress,\n port: redisCluster.attrPrimaryEndPointPort,\n password: redisPassword.secretValueFromJson('password').toString(),\n tls: {},\n }),\n generateStringKey: 'unused',\n },\n });\n redisSecrets.node.addDependency(redisPassword);\n redisSecrets.node.addDependency(redisCluster);\n\n // ECS Cluster\n const cluster = new ecs.Cluster(this, 'Cluster', {\n vpc: vpc,\n });\n\n // Task Policies\n const taskRolePolicies = new iam.PolicyDocument({\n statements: [\n // CloudWatch Logs: Create streams and put events\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: ['logs:CreateLogStream', 'logs:PutLogEvents'],\n resources: ['arn:aws:logs:*'],\n }),\n\n // Secrets Manager: Read only access to secrets\n // https://docs.aws.amazon.com/mediaconnect/latest/ug/iam-policy-examples-asm-secrets.html\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: [\n 'secretsmanager:GetResourcePolicy',\n 'secretsmanager:GetSecretValue',\n 'secretsmanager:DescribeSecret',\n 'secretsmanager:ListSecrets',\n 'secretsmanager:ListSecretVersionIds',\n ],\n resources: ['arn:aws:secretsmanager:*'],\n }),\n\n // Parameter Store: Read only access\n // https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-paramstore-access.html\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: ['ssm:GetParametersByPath', 'ssm:GetParameters', 'ssm:GetParameter', 'ssm:DescribeParameters'],\n resources: ['arn:aws:ssm:*'],\n }),\n\n // SES: Send emails\n // https://docs.aws.amazon.com/ses/latest/dg/sending-authorization-policy-examples.html\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: ['ses:SendEmail', 'ses:SendRawEmail'],\n resources: ['arn:aws:ses:*'],\n }),\n\n // S3: Read and write access to buckets\n // https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazons3.html\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: ['s3:ListBucket', 's3:GetObject', 's3:PutObject', 's3:DeleteObject'],\n resources: ['arn:aws:s3:::*'],\n }),\n\n // IAM: Pass role to innvoke lambda functions\n // https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_passrole.html\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: ['iam:ListRoles', 'iam:GetRole', 'iam:PassRole'],\n resources: [botLambdaRole.roleArn],\n }),\n\n // Lambda: Create, read, update, delete, and invoke functions\n // https://docs.aws.amazon.com/lambda/latest/dg/access-control-identity-based.html\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: [\n 'lambda:CreateFunction',\n 'lambda:GetFunction',\n 'lambda:GetFunctionConfiguration',\n 'lambda:UpdateFunctionCode',\n 'lambda:UpdateFunctionConfiguration',\n 'lambda:ListLayerVersions',\n 'lambda:GetLayerVersion',\n 'lambda:InvokeFunction',\n ],\n resources: ['arn:aws:lambda:*'],\n }),\n ],\n });\n\n // Task Role\n const taskRole = new iam.Role(this, 'TaskExecutionRole', {\n assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),\n description: 'Medplum Server Task Execution Role',\n inlinePolicies: {\n TaskExecutionPolicies: taskRolePolicies,\n },\n });\n\n // Task Definitions\n const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDefinition', {\n memoryLimitMiB: config.serverMemory,\n cpu: config.serverCpu,\n taskRole: taskRole,\n });\n\n // Log Groups\n const logGroup = new logs.LogGroup(this, 'LogGroup', {\n logGroupName: '/ecs/medplum/' + name,\n removalPolicy: RemovalPolicy.DESTROY,\n });\n\n const logDriver = new ecs.AwsLogDriver({\n logGroup: logGroup,\n streamPrefix: 'Medplum',\n });\n\n // Task Containers\n let serverImage: ecs.ContainerImage | undefined = undefined;\n // Pull out the image name and tag from the image URI if it's an ECR image\n const ecrImageUriRegex = new RegExp(\n `^${config.accountNumber}\\\\.dkr\\\\.ecr\\\\.${config.region}\\\\.amazonaws\\\\.com/(.*)[:@](.*)$`\n );\n const nameTagMatches = config.serverImage.match(ecrImageUriRegex);\n const serverImageName = nameTagMatches?.[1];\n const serverImageTag = nameTagMatches?.[2];\n if (serverImageName && serverImageTag) {\n // Creating an ecr repository image will automatically grant fine-grained permissions to ecs to access the image\n const ecrRepo = Repository.fromRepositoryArn(\n this,\n 'ServerImageRepo',\n `arn:aws:ecr:${config.region}:${config.accountNumber}:repository/${serverImageName}`\n );\n serverImage = ecs.ContainerImage.fromEcrRepository(ecrRepo, serverImageTag);\n } else {\n // Otherwise, use the standard container image\n serverImage = ecs.ContainerImage.fromRegistry(config.serverImage);\n }\n const serviceContainer = taskDefinition.addContainer('MedplumTaskDefinition', {\n image: serverImage,\n command: [config.region === 'us-east-1' ? `aws:/medplum/${name}/` : `aws:${config.region}:/medplum/${name}/`],\n logging: logDriver,\n });\n\n serviceContainer.addPortMappings({\n containerPort: config.apiPort,\n hostPort: config.apiPort,\n });\n\n // Security Groups\n const fargateSecurityGroup = new ec2.SecurityGroup(this, 'ServiceSecurityGroup', {\n allowAllOutbound: true,\n securityGroupName: 'MedplumSecurityGroup',\n vpc: vpc,\n });\n\n // Fargate Services\n const fargateService = new ecs.FargateService(this, 'FargateService', {\n cluster: cluster,\n taskDefinition: taskDefinition,\n assignPublicIp: false,\n vpcSubnets: {\n subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,\n },\n desiredCount: config.desiredServerCount,\n securityGroups: [fargateSecurityGroup],\n });\n\n // Add dependencies - make sure Fargate service is created after RDS and Redis\n if (rdsCluster) {\n fargateService.node.addDependency(rdsCluster);\n }\n fargateService.node.addDependency(redisCluster);\n\n // Load Balancer Target Group\n const targetGroup = new elbv2.ApplicationTargetGroup(this, 'TargetGroup', {\n vpc: vpc,\n port: config.apiPort,\n protocol: elbv2.ApplicationProtocol.HTTP,\n healthCheck: {\n path: '/healthcheck',\n interval: Duration.seconds(30),\n timeout: Duration.seconds(3),\n healthyThresholdCount: 2,\n unhealthyThresholdCount: 5,\n },\n targets: [fargateService],\n });\n\n // Load Balancer\n const loadBalancer = new elbv2.ApplicationLoadBalancer(this, 'LoadBalancer', {\n vpc: vpc,\n internetFacing: true,\n http2Enabled: true,\n });\n\n if (config.loadBalancerLoggingEnabled) {\n // Load Balancer logging\n loadBalancer.logAccessLogs(\n s3.Bucket.fromBucketName(this, 'LoggingBucket', config.loadBalancerLoggingBucket),\n config.loadBalancerLoggingPrefix\n );\n }\n\n // HTTPS Listener\n // Forward to the target group\n loadBalancer.addListener('HttpsListener', {\n port: 443,\n certificates: [\n {\n certificateArn: config.apiSslCertArn,\n },\n ],\n sslPolicy: elbv2.SslPolicy.FORWARD_SECRECY_TLS12_RES_GCM,\n defaultAction: elbv2.ListenerAction.forward([targetGroup]),\n });\n\n // WAF\n const waf = new wafv2.CfnWebACL(this, 'BackEndWAF', {\n defaultAction: { allow: {} },\n scope: 'REGIONAL',\n name: `${config.stackName}-BackEndWAF`,\n rules: awsManagedRules,\n visibilityConfig: {\n cloudWatchMetricsEnabled: true,\n metricName: `${config.stackName}-BackEndWAF-Metric`,\n sampledRequestsEnabled: false,\n },\n });\n\n // Create an association between the load balancer and the WAF\n const wafAssociation = new wafv2.CfnWebACLAssociation(this, 'LoadBalancerAssociation', {\n resourceArn: loadBalancer.loadBalancerArn,\n webAclArn: waf.attrArn,\n });\n\n // Grant RDS access to the fargate group\n if (rdsCluster) {\n rdsCluster.connections.allowDefaultPortFrom(fargateSecurityGroup);\n }\n\n // Grant Redis access to the fargate group\n redisSecurityGroup.addIngressRule(fargateSecurityGroup, ec2.Port.tcp(6379));\n\n // DNS\n let record = undefined;\n if (!config.skipDns) {\n // Route 53\n const zone = route53.HostedZone.fromLookup(this, 'Zone', {\n domainName: config.domainName.split('.').slice(-2).join('.'),\n });\n\n // Route53 alias record for the load balancer\n record = new route53.ARecord(this, 'LoadBalancerAliasRecord', {\n recordName: config.apiDomainName,\n target: route53.RecordTarget.fromAlias(new targets.LoadBalancerTarget(loadBalancer)),\n zone: zone,\n });\n }\n\n // SSM Parameters\n const databaseSecrets = new ssm.StringParameter(this, 'DatabaseSecretsParameter', {\n tier: ssm.ParameterTier.STANDARD,\n parameterName: `/medplum/${name}/DatabaseSecrets`,\n description: 'Database secrets ARN',\n stringValue: rdsSecretsArn,\n });\n\n const redisSecretsParameter = new ssm.StringParameter(this, 'RedisSecretsParameter', {\n tier: ssm.ParameterTier.STANDARD,\n parameterName: `/medplum/${name}/RedisSecrets`,\n description: 'Redis secrets ARN',\n stringValue: redisSecrets.secretArn,\n });\n\n const botLambdaRoleParameter = new ssm.StringParameter(this, 'BotLambdaRoleParameter', {\n tier: ssm.ParameterTier.STANDARD,\n parameterName: `/medplum/${name}/botLambdaRoleArn`,\n description: 'Bot lambda execution role ARN',\n stringValue: botLambdaRole.roleArn,\n });\n\n // Debug\n console.log('ARecord', record?.domainName);\n console.log('DatabaseSecretsParameter', databaseSecrets.parameterArn);\n console.log('RedisSecretsParameter', redisSecretsParameter.parameterArn);\n console.log('RedisCluster', redisCluster.attrPrimaryEndPointAddress);\n console.log('BotLambdaRole', botLambdaRoleParameter.stringValue);\n console.log('WAF', waf.attrArn);\n console.log('WAF Association', wafAssociation.node.id);\n }\n}\n","import {\n aws_certificatemanager as acm,\n aws_cloudfront as cloudfront,\n aws_cloudfront_origins as origins,\n aws_route53 as route53,\n aws_route53_targets as targets,\n aws_s3 as s3,\n aws_wafv2 as wafv2,\n Duration,\n RemovalPolicy,\n} from 'aws-cdk-lib';\nimport { Construct } from 'constructs';\nimport { MedplumInfraConfig } from './config';\nimport { awsManagedRules } from './waf';\n\n/**\n * Static app infrastructure, which deploys app content to an S3 bucket.\n *\n * The app redirects from HTTP to HTTPS, using a CloudFront distribution,\n * Route53 alias record, and ACM certificate.\n */\nexport class FrontEnd extends Construct {\n constructor(parent: Construct, config: MedplumInfraConfig, region: string) {\n super(parent, 'FrontEnd');\n\n let appBucket: s3.IBucket;\n\n if (region === config.region) {\n // S3 bucket\n appBucket = new s3.Bucket(this, 'AppBucket', {\n bucketName: config.appDomainName,\n publicReadAccess: false,\n blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,\n removalPolicy: RemovalPolicy.DESTROY,\n encryption: s3.BucketEncryption.S3_MANAGED,\n enforceSSL: true,\n versioned: false,\n });\n } else {\n // Otherwise, reference the bucket by name and region\n appBucket = s3.Bucket.fromBucketAttributes(this, 'AppBucket', {\n bucketName: config.appDomainName,\n region: config.region,\n });\n }\n\n if (region === 'us-east-1') {\n // HTTP response headers policy\n const responseHeadersPolicy = new cloudfront.ResponseHeadersPolicy(this, 'ResponseHeadersPolicy', {\n securityHeadersBehavior: {\n contentSecurityPolicy: {\n contentSecurityPolicy: [\n `default-src 'none'`,\n `base-uri 'self'`,\n `child-src 'self'`,\n `connect-src 'self' ${config.apiDomainName} *.google.com`,\n `font-src 'self' fonts.gstatic.com`,\n `form-action 'self' *.gstatic.com *.google.com`,\n `frame-ancestors 'none'`,\n `frame-src 'self' *.medplum.com *.gstatic.com *.google.com`,\n `img-src 'self' data: ${config.storageDomainName} *.gstatic.com *.google.com *.googleapis.com`,\n `manifest-src 'self'`,\n `media-src 'self' ${config.storageDomainName}`,\n `script-src 'self' *.medplum.com *.gstatic.com *.google.com`,\n `style-src 'self' 'unsafe-inline' *.medplum.com *.gstatic.com *.google.com`,\n `worker-src 'self' blob: *.gstatic.com *.google.com`,\n `upgrade-insecure-requests`,\n ].join('; '),\n override: true,\n },\n contentTypeOptions: { override: true },\n frameOptions: { frameOption: cloudfront.HeadersFrameOption.DENY, override: true },\n strictTransportSecurity: {\n accessControlMaxAge: Duration.seconds(63072000),\n includeSubdomains: true,\n override: true,\n },\n xssProtection: {\n protection: true,\n modeBlock: true,\n override: true,\n },\n },\n });\n\n // WAF\n const waf = new wafv2.CfnWebACL(this, 'FrontEndWAF', {\n defaultAction: { allow: {} },\n scope: 'CLOUDFRONT',\n name: `${config.stackName}-FrontEndWAF`,\n rules: awsManagedRules,\n visibilityConfig: {\n cloudWatchMetricsEnabled: true,\n metricName: `${config.stackName}-FrontEndWAF-Metric`,\n sampledRequestsEnabled: false,\n },\n });\n\n // API Origin Cache Policy\n const apiOriginCachePolicy = new cloudfront.CachePolicy(this, 'ApiOriginCachePolicy', {\n cachePolicyName: `${config.stackName}-ApiOriginCachePolicy`,\n cookieBehavior: cloudfront.CacheCookieBehavior.all(),\n headerBehavior: cloudfront.CacheHeaderBehavior.allowList(\n 'Authorization',\n 'Content-Encoding',\n 'Content-Type',\n 'If-None-Match',\n 'Origin',\n 'Referer',\n 'User-Agent',\n 'X-Medplum'\n ),\n queryStringBehavior: cloudfront.CacheQueryStringBehavior.all(),\n });\n\n // Origin access identity\n const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {});\n appBucket.grantRead(originAccessIdentity);\n\n // CloudFront distribution\n const distribution = new cloudfront.Distribution(this, 'AppDistribution', {\n defaultRootObject: 'index.html',\n defaultBehavior: {\n origin: new origins.S3Origin(appBucket, { originAccessIdentity }),\n responseHeadersPolicy,\n viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,\n },\n additionalBehaviors: {\n '/api/*': {\n origin: new origins.HttpOrigin(config.apiDomainName),\n allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,\n cachePolicy: apiOriginCachePolicy,\n viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,\n },\n },\n certificate: acm.Certificate.fromCertificateArn(this, 'AppCertificate', config.appSslCertArn),\n domainNames: [config.appDomainName],\n errorResponses: [\n {\n httpStatus: 403,\n responseHttpStatus: 200,\n responsePagePath: '/index.html',\n },\n {\n httpStatus: 404,\n responseHttpStatus: 200,\n responsePagePath: '/index.html',\n },\n ],\n webAclId: waf.attrArn,\n });\n\n // DNS\n let record = undefined;\n if (!config.skipDns) {\n const zone = route53.HostedZone.fromLookup(this, 'Zone', {\n domainName: config.domainName.split('.').slice(-2).join('.'),\n });\n\n // Route53 alias record for the CloudFront distribution\n record = new route53.ARecord(this, 'AppAliasRecord', {\n recordName: config.appDomainName,\n target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),\n zone,\n });\n }\n\n // Debug\n console.log('ARecord', record?.domainName);\n }\n }\n}\n","import {\n aws_certificatemanager as acm,\n aws_cloudfront as cloudfront,\n aws_cloudfront_origins as origins,\n aws_route53 as route53,\n aws_route53_targets as targets,\n aws_s3 as s3,\n aws_wafv2 as wafv2,\n Duration,\n} from 'aws-cdk-lib';\nimport { ServerlessClamscan } from 'cdk-serverless-clamscan';\nimport { Construct } from 'constructs';\nimport { MedplumInfraConfig } from './config';\nimport { awsManagedRules } from './waf';\n\n/**\n * Binary storage bucket and CloudFront distribution.\n */\nexport class Storage extends Construct {\n constructor(parent: Construct, config: MedplumInfraConfig, region: string) {\n super(parent, 'Storage');\n\n let storageBucket: s3.IBucket;\n\n if (region === config.region) {\n // S3 bucket\n storageBucket = new s3.Bucket(this, 'StorageBucket', {\n bucketName: config.storageBucketName,\n publicReadAccess: false,\n blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,\n encryption: s3.BucketEncryption.S3_MANAGED,\n enforceSSL: true,\n versioned: false,\n });\n\n if (config.clamscanEnabled) {\n // ClamAV serverless scan\n const sc = new ServerlessClamscan(this, 'ServerlessClamscan', {\n defsBucketAccessLogsConfig: {\n logsBucket: s3.Bucket.fromBucketName(this, 'LoggingBucket', config.clamscanLoggingBucket),\n logsPrefix: config.clamscanLoggingPrefix,\n },\n });\n sc.addSourceBucket(storageBucket);\n }\n } else {\n // Otherwise, reference the bucket by name\n storageBucket = s3.Bucket.fromBucketAttributes(this, 'StorageBucket', {\n bucketName: config.storageBucketName,\n region: config.region,\n });\n }\n\n if (region === 'us-east-1') {\n // Public key in PEM format\n const publicKey = new cloudfront.PublicKey(this, 'StoragePublicKey', {\n encodedKey: config.storagePublicKey,\n });\n\n // Authorized key group for presigned URLs\n const keyGroup = new cloudfront.KeyGroup(this, 'StorageKeyGroup', {\n items: [publicKey],\n });\n\n // HTTP response headers policy\n const responseHeadersPolicy = new cloudfront.ResponseHeadersPolicy(this, 'ResponseHeadersPolicy', {\n securityHeadersBehavior: {\n contentSecurityPolicy: {\n contentSecurityPolicy:\n \"default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors *.medplum.com;\",\n override: true,\n },\n contentTypeOptions: { override: true },\n frameOptions: { frameOption: cloudfront.HeadersFrameOption.DENY, override: true },\n referrerPolicy: { referrerPolicy: cloudfront.HeadersReferrerPolicy.NO_REFERRER, override: true },\n strictTransportSecurity: {\n accessControlMaxAge: Duration.seconds(63072000),\n includeSubdomains: true,\n override: true,\n },\n xssProtection: {\n protection: true,\n modeBlock: true,\n override: true,\n },\n },\n });\n\n // WAF\n const waf = new wafv2.CfnWebACL(this, 'StorageWAF', {\n defaultAction: { allow: {} },\n scope: 'CLOUDFRONT',\n name: `${config.stackName}-StorageWAF`,\n rules: awsManagedRules,\n visibilityConfig: {\n cloudWatchMetricsEnabled: true,\n metricName: `${config.stackName}-StorageWAF-Metric`,\n sampledRequestsEnabled: false,\n },\n });\n\n // Origin access identity\n const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {});\n storageBucket.grantRead(originAccessIdentity);\n\n // CloudFront distribution\n const distribution = new cloudfront.Distribution(this, 'StorageDistribution', {\n defaultBehavior: {\n origin: new origins.S3Origin(storageBucket, { originAccessIdentity }),\n responseHeadersPolicy,\n viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,\n trustedKeyGroups: [keyGroup],\n },\n certificate: acm.Certificate.fromCertificateArn(this, 'StorageCertificate', config.storageSslCertArn),\n domainNames: [config.storageDomainName],\n webAclId: waf.attrArn,\n });\n\n // DNS\n let record = undefined;\n if (!config.skipDns) {\n const zone = route53.HostedZone.fromLookup(this, 'Zone', {\n domainName: config.domainName.split('.').slice(-2).join('.'),\n });\n\n // Route53 alias record for the CloudFront distribution\n record = new route53.ARecord(this, 'StorageAliasRecord', {\n recordName: config.storageDomainName,\n target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),\n zone,\n });\n }\n\n // Debug\n console.log('ARecord', record?.domainName);\n }\n }\n}\n","import { App, Stack } from 'aws-cdk-lib';\nimport { readFileSync } from 'fs';\nimport { resolve } from 'path';\nimport { BackEnd } from './backend';\nimport { MedplumInfraConfig } from './config';\nimport { FrontEnd } from './frontend';\nimport { Storage } from './storage';\n\nclass MedplumStack {\n primaryStack: Stack;\n backEnd: BackEnd;\n frontEnd: FrontEnd;\n storage: Storage;\n\n constructor(scope: App, config: MedplumInfraConfig) {\n this.primaryStack = new Stack(scope, config.stackName, {\n env: {\n region: config.region,\n account: config.accountNumber,\n },\n });\n\n this.backEnd = new BackEnd(this.primaryStack, config);\n this.frontEnd = new FrontEnd(this.primaryStack, config, config.region);\n this.storage = new Storage(this.primaryStack, config, config.region);\n\n if (config.region !== 'us-east-1') {\n // Some resources must be created in us-east-1\n // For example, CloudFront distributions and ACM certificates\n // If the primary region is not us-east-1, create these resources in us-east-1\n const usEast1Stack = new Stack(scope, config.stackName + '-us-east-1', {\n env: {\n region: 'us-east-1',\n account: config.accountNumber,\n },\n });\n\n this.frontEnd = new FrontEnd(usEast1Stack, config, 'us-east-1');\n this.storage = new Storage(usEast1Stack, config, 'us-east-1');\n }\n }\n}\n\nexport function main(context?: Record<string, string>): void {\n const app = new App({ context });\n\n const configFileName = app.node.tryGetContext('config');\n if (!configFileName) {\n console.log('Missing \"config\" context variable');\n console.log('Usage: cdk deploy -c config=my-config.json');\n return;\n }\n\n const config = JSON.parse(readFileSync(resolve(configFileName), 'utf-8')) as MedplumInfraConfig;\n\n const stack = new MedplumStack(app, config);\n\n console.log('Stack', stack.primaryStack.stackId);\n console.log('BackEnd', stack.backEnd.node.id);\n console.log('FrontEnd', stack.frontEnd.node.id);\n console.log('Storage', stack.storage.node.id);\n\n app.synth();\n}\n\nif (require.main === module) {\n main();\n}\n"],"names":["Construct","ec2","logs","RemovalPolicy","iam","rds","Duration","elasticache","secretsmanager","ecs","Repository","elbv2","s3","wafv2","route53","targets","ssm","cloudfront","origins","acm","ServerlessClamscan","Stack","App","readFileSync","resolve"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../src/waf.ts","../../../src/backend.ts","../../../src/oai.ts","../../../src/frontend.ts","../../../src/storage.ts","../../../src/index.ts"],"sourcesContent":["// Based on https://gist.github.com/statik/f1ac9d6227d98d30c7a7cec0c83f4e64\n\nimport { aws_wafv2 as wafv2 } from 'aws-cdk-lib';\n\nexport const awsManagedRules: wafv2.CfnWebACL.RuleProperty[] = [\n // Common Rule Set aligns with major portions of OWASP Core Rule Set\n // https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-baseline.html\n {\n name: 'AWS-AWSManagedRulesCommonRuleSet',\n priority: 10,\n statement: {\n managedRuleGroupStatement: {\n vendorName: 'AWS',\n name: 'AWSManagedRulesCommonRuleSet',\n // Excluding generic RFI body rule for sns notifications\n // https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html\n excludedRules: [\n { name: 'NoUserAgent_HEADER' },\n { name: 'UserAgent_BadBots_HEADER' },\n { name: 'SizeRestrictions_QUERYSTRING' },\n { name: 'SizeRestrictions_Cookie_HEADER' },\n { name: 'SizeRestrictions_BODY' },\n { name: 'SizeRestrictions_URIPATH' },\n { name: 'EC2MetaDataSSRF_BODY' },\n { name: 'EC2MetaDataSSRF_COOKIE' },\n { name: 'EC2MetaDataSSRF_URIPATH' },\n { name: 'EC2MetaDataSSRF_QUERYARGUMENTS' },\n { name: 'GenericLFI_QUERYARGUMENTS' },\n { name: 'GenericLFI_URIPATH' },\n { name: 'GenericLFI_BODY' },\n { name: 'RestrictedExtensions_URIPATH' },\n { name: 'RestrictedExtensions_QUERYARGUMENTS' },\n { name: 'GenericRFI_QUERYARGUMENTS' },\n { name: 'GenericRFI_BODY' },\n { name: 'GenericRFI_URIPATH' },\n { name: 'CrossSiteScripting_COOKIE' },\n { name: 'CrossSiteScripting_QUERYARGUMENTS' },\n { name: 'CrossSiteScripting_BODY' },\n { name: 'CrossSiteScripting_URIPATH' },\n ],\n },\n },\n overrideAction: {\n count: {},\n },\n visibilityConfig: {\n sampledRequestsEnabled: true,\n cloudWatchMetricsEnabled: true,\n metricName: 'AWS-AWSManagedRulesCommonRuleSet',\n },\n },\n // AWS IP Reputation list includes known malicious actors/bots and is regularly updated\n // https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-ip-rep.html\n {\n name: 'AWS-AWSManagedRulesAmazonIpReputationList',\n priority: 20,\n statement: {\n managedRuleGroupStatement: {\n vendorName: 'AWS',\n name: 'AWSManagedRulesAmazonIpReputationList',\n excludedRules: [{ name: 'AWSManagedIPReputationList' }, { name: 'AWSManagedReconnaissanceList' }],\n },\n },\n overrideAction: {\n count: {},\n },\n visibilityConfig: {\n sampledRequestsEnabled: true,\n cloudWatchMetricsEnabled: true,\n metricName: 'AWSManagedRulesAmazonIpReputationList',\n },\n },\n // Blocks common SQL Injection\n // https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-use-case.html#aws-managed-rule-groups-use-case-sql-db\n {\n name: 'AWSManagedRulesSQLiRuleSet',\n priority: 30,\n visibilityConfig: {\n sampledRequestsEnabled: true,\n cloudWatchMetricsEnabled: true,\n metricName: 'AWSManagedRulesSQLiRuleSet',\n },\n overrideAction: {\n count: {},\n },\n statement: {\n managedRuleGroupStatement: {\n vendorName: 'AWS',\n name: 'AWSManagedRulesSQLiRuleSet',\n excludedRules: [\n { name: 'SQLi_QUERYARGUMENTS' },\n { name: 'SQLiExtendedPatterns_QUERYARGUMENTS' },\n { name: 'SQLi_BODY' },\n { name: 'SQLiExtendedPatterns_BODY' },\n { name: 'SQLi_COOKIE' },\n { name: 'SQLi_URIPATH' },\n ],\n },\n },\n },\n // Blocks attacks targeting LFI(Local File Injection) for linux systems\n // https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-use-case.html#aws-managed-rule-groups-use-case-linux-os\n {\n name: 'AWSManagedRuleLinux',\n priority: 40,\n visibilityConfig: {\n sampledRequestsEnabled: true,\n cloudWatchMetricsEnabled: true,\n metricName: 'AWSManagedRuleLinux',\n },\n overrideAction: {\n count: {},\n },\n statement: {\n managedRuleGroupStatement: {\n vendorName: 'AWS',\n name: 'AWSManagedRulesLinuxRuleSet',\n excludedRules: [{ name: 'LFI_URIPATH' }, { name: 'LFI_QUERYSTRING' }, { name: 'LFI_COOKIE' }],\n },\n },\n },\n];\n","import {\n aws_ec2 as ec2,\n aws_ecs as ecs,\n aws_elasticache as elasticache,\n aws_elasticloadbalancingv2 as elbv2,\n aws_iam as iam,\n aws_logs as logs,\n aws_rds as rds,\n aws_route53 as route53,\n aws_route53_targets as targets,\n aws_s3 as s3,\n aws_secretsmanager as secretsmanager,\n aws_ssm as ssm,\n aws_wafv2 as wafv2,\n Duration,\n RemovalPolicy,\n} from 'aws-cdk-lib';\nimport { Repository } from 'aws-cdk-lib/aws-ecr';\nimport { Construct } from 'constructs';\nimport { MedplumInfraConfig } from './config';\nimport { awsManagedRules } from './waf';\n\n/**\n * Based on: https://github.com/aws-samples/http-api-aws-fargate-cdk/blob/master/cdk/singleAccount/lib/fargate-vpclink-stack.ts\n *\n * RDS config: https://docs.aws.amazon.com/cdk/api/latest/docs/aws-rds-readme.html\n */\nexport class BackEnd extends Construct {\n constructor(scope: Construct, config: MedplumInfraConfig) {\n super(scope, 'BackEnd');\n\n const name = config.name;\n\n // VPC\n let vpc: ec2.IVpc;\n\n if (config.vpcId) {\n // Lookup VPC by ARN\n vpc = ec2.Vpc.fromLookup(this, 'VPC', { vpcId: config.vpcId });\n } else {\n // VPC Flow Logs\n const vpcFlowLogs = new logs.LogGroup(this, 'VpcFlowLogs', {\n logGroupName: '/medplum/flowlogs/' + name,\n removalPolicy: RemovalPolicy.DESTROY,\n });\n\n // Create VPC\n vpc = new ec2.Vpc(this, 'VPC', {\n maxAzs: config.maxAzs,\n flowLogs: {\n cloudwatch: {\n destination: ec2.FlowLogDestination.toCloudWatchLogs(vpcFlowLogs),\n trafficType: ec2.FlowLogTrafficType.ALL,\n },\n },\n });\n }\n\n // Bot Lambda Role\n const botLambdaRole = new iam.Role(this, 'BotLambdaRole', {\n assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),\n });\n\n // RDS\n let rdsCluster = undefined;\n let rdsSecretsArn = config.rdsSecretsArn;\n if (!rdsSecretsArn) {\n rdsCluster = new rds.DatabaseCluster(this, 'DatabaseCluster', {\n engine: rds.DatabaseClusterEngine.auroraPostgres({\n version: rds.AuroraPostgresEngineVersion.VER_12_9,\n }),\n credentials: rds.Credentials.fromGeneratedSecret('clusteradmin'),\n defaultDatabaseName: 'medplum',\n storageEncrypted: true,\n instances: config.rdsInstances,\n instanceProps: {\n vpc: vpc,\n vpcSubnets: {\n subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,\n },\n instanceType: config.rdsInstanceType ? new ec2.InstanceType(config.rdsInstanceType) : undefined,\n enablePerformanceInsights: true,\n },\n backup: {\n retention: Duration.days(7),\n },\n cloudwatchLogsExports: ['postgresql'],\n instanceUpdateBehaviour: rds.InstanceUpdateBehaviour.ROLLING,\n });\n\n rdsSecretsArn = (rdsCluster.secret as secretsmanager.ISecret).secretArn;\n }\n\n // Redis\n // Important: For HIPAA compliance, you must specify TransitEncryptionEnabled as true, an AuthToken, and a CacheSubnetGroup.\n const redisSubnetGroup = new elasticache.CfnSubnetGroup(this, 'RedisSubnetGroup', {\n description: 'Redis Subnet Group',\n subnetIds: vpc.privateSubnets.map((subnet) => subnet.subnetId),\n });\n\n const redisSecurityGroup = new ec2.SecurityGroup(this, 'RedisSecurityGroup', {\n vpc,\n description: 'Redis Security Group',\n allowAllOutbound: false,\n });\n\n const redisPassword = new secretsmanager.Secret(this, 'RedisPassword', {\n generateSecretString: {\n secretStringTemplate: '{}',\n generateStringKey: 'password',\n excludeCharacters: '@%*()_+=`~{}|[]\\\\:\";\\'?,./',\n },\n });\n\n const redisCluster = new elasticache.CfnReplicationGroup(this, 'RedisCluster', {\n engine: 'Redis',\n engineVersion: '6.x',\n cacheNodeType: 'cache.t2.medium',\n replicationGroupDescription: 'RedisReplicationGroup',\n authToken: redisPassword.secretValueFromJson('password').toString(),\n transitEncryptionEnabled: true,\n atRestEncryptionEnabled: true,\n multiAzEnabled: true,\n cacheSubnetGroupName: redisSubnetGroup.ref,\n numNodeGroups: 1,\n replicasPerNodeGroup: 1,\n securityGroupIds: [redisSecurityGroup.securityGroupId],\n });\n redisCluster.node.addDependency(redisPassword);\n\n const redisSecrets = new secretsmanager.Secret(this, 'RedisSecrets', {\n generateSecretString: {\n secretStringTemplate: JSON.stringify({\n host: redisCluster.attrPrimaryEndPointAddress,\n port: redisCluster.attrPrimaryEndPointPort,\n password: redisPassword.secretValueFromJson('password').toString(),\n tls: {},\n }),\n generateStringKey: 'unused',\n },\n });\n redisSecrets.node.addDependency(redisPassword);\n redisSecrets.node.addDependency(redisCluster);\n\n // ECS Cluster\n const cluster = new ecs.Cluster(this, 'Cluster', {\n vpc: vpc,\n });\n\n // Task Policies\n const taskRolePolicies = new iam.PolicyDocument({\n statements: [\n // CloudWatch Logs: Create streams and put events\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: ['logs:CreateLogStream', 'logs:PutLogEvents'],\n resources: ['arn:aws:logs:*'],\n }),\n\n // Secrets Manager: Read only access to secrets\n // https://docs.aws.amazon.com/mediaconnect/latest/ug/iam-policy-examples-asm-secrets.html\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: [\n 'secretsmanager:GetResourcePolicy',\n 'secretsmanager:GetSecretValue',\n 'secretsmanager:DescribeSecret',\n 'secretsmanager:ListSecrets',\n 'secretsmanager:ListSecretVersionIds',\n ],\n resources: ['arn:aws:secretsmanager:*'],\n }),\n\n // Parameter Store: Read only access\n // https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-paramstore-access.html\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: ['ssm:GetParametersByPath', 'ssm:GetParameters', 'ssm:GetParameter', 'ssm:DescribeParameters'],\n resources: ['arn:aws:ssm:*'],\n }),\n\n // SES: Send emails\n // https://docs.aws.amazon.com/ses/latest/dg/sending-authorization-policy-examples.html\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: ['ses:SendEmail', 'ses:SendRawEmail'],\n resources: ['arn:aws:ses:*'],\n }),\n\n // S3: Read and write access to buckets\n // https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazons3.html\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: ['s3:ListBucket', 's3:GetObject', 's3:PutObject', 's3:DeleteObject'],\n resources: ['arn:aws:s3:::*'],\n }),\n\n // IAM: Pass role to innvoke lambda functions\n // https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_passrole.html\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: ['iam:ListRoles', 'iam:GetRole', 'iam:PassRole'],\n resources: [botLambdaRole.roleArn],\n }),\n\n // Lambda: Create, read, update, delete, and invoke functions\n // https://docs.aws.amazon.com/lambda/latest/dg/access-control-identity-based.html\n new iam.PolicyStatement({\n effect: iam.Effect.ALLOW,\n actions: [\n 'lambda:CreateFunction',\n 'lambda:GetFunction',\n 'lambda:GetFunctionConfiguration',\n 'lambda:UpdateFunctionCode',\n 'lambda:UpdateFunctionConfiguration',\n 'lambda:ListLayerVersions',\n 'lambda:GetLayerVersion',\n 'lambda:InvokeFunction',\n ],\n resources: ['arn:aws:lambda:*'],\n }),\n ],\n });\n\n // Task Role\n const taskRole = new iam.Role(this, 'TaskExecutionRole', {\n assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),\n description: 'Medplum Server Task Execution Role',\n inlinePolicies: {\n TaskExecutionPolicies: taskRolePolicies,\n },\n });\n\n // Task Definitions\n const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDefinition', {\n memoryLimitMiB: config.serverMemory,\n cpu: config.serverCpu,\n taskRole: taskRole,\n });\n\n // Log Groups\n const logGroup = new logs.LogGroup(this, 'LogGroup', {\n logGroupName: '/ecs/medplum/' + name,\n removalPolicy: RemovalPolicy.DESTROY,\n });\n\n const logDriver = new ecs.AwsLogDriver({\n logGroup: logGroup,\n streamPrefix: 'Medplum',\n });\n\n // Task Containers\n let serverImage: ecs.ContainerImage | undefined = undefined;\n // Pull out the image name and tag from the image URI if it's an ECR image\n const ecrImageUriRegex = new RegExp(\n `^${config.accountNumber}\\\\.dkr\\\\.ecr\\\\.${config.region}\\\\.amazonaws\\\\.com/(.*)[:@](.*)$`\n );\n const nameTagMatches = config.serverImage.match(ecrImageUriRegex);\n const serverImageName = nameTagMatches?.[1];\n const serverImageTag = nameTagMatches?.[2];\n if (serverImageName && serverImageTag) {\n // Creating an ecr repository image will automatically grant fine-grained permissions to ecs to access the image\n const ecrRepo = Repository.fromRepositoryArn(\n this,\n 'ServerImageRepo',\n `arn:aws:ecr:${config.region}:${config.accountNumber}:repository/${serverImageName}`\n );\n serverImage = ecs.ContainerImage.fromEcrRepository(ecrRepo, serverImageTag);\n } else {\n // Otherwise, use the standard container image\n serverImage = ecs.ContainerImage.fromRegistry(config.serverImage);\n }\n const serviceContainer = taskDefinition.addContainer('MedplumTaskDefinition', {\n image: serverImage,\n command: [config.region === 'us-east-1' ? `aws:/medplum/${name}/` : `aws:${config.region}:/medplum/${name}/`],\n logging: logDriver,\n });\n\n serviceContainer.addPortMappings({\n containerPort: config.apiPort,\n hostPort: config.apiPort,\n });\n\n // Security Groups\n const fargateSecurityGroup = new ec2.SecurityGroup(this, 'ServiceSecurityGroup', {\n allowAllOutbound: true,\n securityGroupName: 'MedplumSecurityGroup',\n vpc: vpc,\n });\n\n // Fargate Services\n const fargateService = new ecs.FargateService(this, 'FargateService', {\n cluster: cluster,\n taskDefinition: taskDefinition,\n assignPublicIp: false,\n vpcSubnets: {\n subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,\n },\n desiredCount: config.desiredServerCount,\n securityGroups: [fargateSecurityGroup],\n });\n\n // Add dependencies - make sure Fargate service is created after RDS and Redis\n if (rdsCluster) {\n fargateService.node.addDependency(rdsCluster);\n }\n fargateService.node.addDependency(redisCluster);\n\n // Load Balancer Target Group\n const targetGroup = new elbv2.ApplicationTargetGroup(this, 'TargetGroup', {\n vpc: vpc,\n port: config.apiPort,\n protocol: elbv2.ApplicationProtocol.HTTP,\n healthCheck: {\n path: '/healthcheck',\n interval: Duration.seconds(30),\n timeout: Duration.seconds(3),\n healthyThresholdCount: 2,\n unhealthyThresholdCount: 5,\n },\n targets: [fargateService],\n });\n\n // Load Balancer\n const loadBalancer = new elbv2.ApplicationLoadBalancer(this, 'LoadBalancer', {\n vpc: vpc,\n internetFacing: true,\n http2Enabled: true,\n });\n\n if (config.loadBalancerLoggingEnabled) {\n // Load Balancer logging\n loadBalancer.logAccessLogs(\n s3.Bucket.fromBucketName(this, 'LoggingBucket', config.loadBalancerLoggingBucket),\n config.loadBalancerLoggingPrefix\n );\n }\n\n // HTTPS Listener\n // Forward to the target group\n loadBalancer.addListener('HttpsListener', {\n port: 443,\n certificates: [\n {\n certificateArn: config.apiSslCertArn,\n },\n ],\n sslPolicy: elbv2.SslPolicy.FORWARD_SECRECY_TLS12_RES_GCM,\n defaultAction: elbv2.ListenerAction.forward([targetGroup]),\n });\n\n // WAF\n const waf = new wafv2.CfnWebACL(this, 'BackEndWAF', {\n defaultAction: { allow: {} },\n scope: 'REGIONAL',\n name: `${config.stackName}-BackEndWAF`,\n rules: awsManagedRules,\n visibilityConfig: {\n cloudWatchMetricsEnabled: true,\n metricName: `${config.stackName}-BackEndWAF-Metric`,\n sampledRequestsEnabled: false,\n },\n });\n\n // Create an association between the load balancer and the WAF\n const wafAssociation = new wafv2.CfnWebACLAssociation(this, 'LoadBalancerAssociation', {\n resourceArn: loadBalancer.loadBalancerArn,\n webAclArn: waf.attrArn,\n });\n\n // Grant RDS access to the fargate group\n if (rdsCluster) {\n rdsCluster.connections.allowDefaultPortFrom(fargateSecurityGroup);\n }\n\n // Grant Redis access to the fargate group\n redisSecurityGroup.addIngressRule(fargateSecurityGroup, ec2.Port.tcp(6379));\n\n // DNS\n let record = undefined;\n if (!config.skipDns) {\n // Route 53\n const zone = route53.HostedZone.fromLookup(this, 'Zone', {\n domainName: config.domainName.split('.').slice(-2).join('.'),\n });\n\n // Route53 alias record for the load balancer\n record = new route53.ARecord(this, 'LoadBalancerAliasRecord', {\n recordName: config.apiDomainName,\n target: route53.RecordTarget.fromAlias(new targets.LoadBalancerTarget(loadBalancer)),\n zone: zone,\n });\n }\n\n // SSM Parameters\n const databaseSecrets = new ssm.StringParameter(this, 'DatabaseSecretsParameter', {\n tier: ssm.ParameterTier.STANDARD,\n parameterName: `/medplum/${name}/DatabaseSecrets`,\n description: 'Database secrets ARN',\n stringValue: rdsSecretsArn,\n });\n\n const redisSecretsParameter = new ssm.StringParameter(this, 'RedisSecretsParameter', {\n tier: ssm.ParameterTier.STANDARD,\n parameterName: `/medplum/${name}/RedisSecrets`,\n description: 'Redis secrets ARN',\n stringValue: redisSecrets.secretArn,\n });\n\n const botLambdaRoleParameter = new ssm.StringParameter(this, 'BotLambdaRoleParameter', {\n tier: ssm.ParameterTier.STANDARD,\n parameterName: `/medplum/${name}/botLambdaRoleArn`,\n description: 'Bot lambda execution role ARN',\n stringValue: botLambdaRole.roleArn,\n });\n\n // Debug\n console.log('ARecord', record?.domainName);\n console.log('DatabaseSecretsParameter', databaseSecrets.parameterArn);\n console.log('RedisSecretsParameter', redisSecretsParameter.parameterArn);\n console.log('RedisCluster', redisCluster.attrPrimaryEndPointAddress);\n console.log('BotLambdaRole', botLambdaRoleParameter.stringValue);\n console.log('WAF', waf.attrArn);\n console.log('WAF Association', wafAssociation.node.id);\n }\n}\n","import { aws_cloudfront as cloudfront, aws_iam as iam, aws_s3 as s3 } from 'aws-cdk-lib';\nimport { Construct } from 'constructs';\n\n/**\n * Grants S3 bucket read access to the CloudFront Origin Access Identity (OAI).\n *\n * Under normal circumstances, where CDK creates both the S3 bucket and the OAI,\n * you can achieve this same behavior by simply calling:\n *\n * bucket.grantRead(identity);\n *\n * However, if importing an S3 bucket via `s3.Bucket.fromBucketAttributes()`, that does not work.\n *\n * See: https://stackoverflow.com/a/60917015\n *\n * @param scope The CDK construct scope.\n * @param bucket The S3 bucket.\n * @param identity The CloudFront Origin Access Identity.\n */\nexport function grantBucketAccessToOriginAccessIdentity(\n scope: Construct,\n bucket: s3.IBucket,\n identity: cloudfront.OriginAccessIdentity\n): void {\n const policyStatement = new iam.PolicyStatement();\n policyStatement.addActions('s3:GetObject*');\n policyStatement.addActions('s3:GetBucket*');\n policyStatement.addActions('s3:List*');\n policyStatement.addResources(bucket.bucketArn);\n policyStatement.addResources(`${bucket.bucketArn}/*`);\n policyStatement.addCanonicalUserPrincipal(identity.cloudFrontOriginAccessIdentityS3CanonicalUserId);\n\n let policy = bucket.policy;\n if (!policy) {\n policy = new s3.BucketPolicy(scope, 'Policy', { bucket });\n }\n\n policy.document.addStatements(policyStatement);\n}\n","import {\n aws_certificatemanager as acm,\n aws_cloudfront as cloudfront,\n aws_cloudfront_origins as origins,\n aws_route53 as route53,\n aws_route53_targets as targets,\n aws_s3 as s3,\n aws_wafv2 as wafv2,\n Duration,\n RemovalPolicy,\n} from 'aws-cdk-lib';\nimport { Construct } from 'constructs';\nimport { MedplumInfraConfig } from './config';\nimport { grantBucketAccessToOriginAccessIdentity } from './oai';\nimport { awsManagedRules } from './waf';\n\n/**\n * Static app infrastructure, which deploys app content to an S3 bucket.\n *\n * The app redirects from HTTP to HTTPS, using a CloudFront distribution,\n * Route53 alias record, and ACM certificate.\n */\nexport class FrontEnd extends Construct {\n constructor(parent: Construct, config: MedplumInfraConfig, region: string) {\n super(parent, 'FrontEnd');\n\n let appBucket: s3.IBucket;\n\n if (region === config.region) {\n // S3 bucket\n appBucket = new s3.Bucket(this, 'AppBucket', {\n bucketName: config.appDomainName,\n publicReadAccess: false,\n blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,\n removalPolicy: RemovalPolicy.DESTROY,\n encryption: s3.BucketEncryption.S3_MANAGED,\n enforceSSL: true,\n versioned: false,\n });\n } else {\n // Otherwise, reference the bucket by name and region\n appBucket = s3.Bucket.fromBucketAttributes(this, 'AppBucket', {\n bucketName: config.appDomainName,\n region: config.region,\n });\n }\n\n if (region === 'us-east-1') {\n // HTTP response headers policy\n const responseHeadersPolicy = new cloudfront.ResponseHeadersPolicy(this, 'ResponseHeadersPolicy', {\n securityHeadersBehavior: {\n contentSecurityPolicy: {\n contentSecurityPolicy: [\n `default-src 'none'`,\n `base-uri 'self'`,\n `child-src 'self'`,\n `connect-src 'self' ${config.apiDomainName} *.google.com`,\n `font-src 'self' fonts.gstatic.com`,\n `form-action 'self' *.gstatic.com *.google.com`,\n `frame-ancestors 'none'`,\n `frame-src 'self' *.medplum.com *.gstatic.com *.google.com`,\n `img-src 'self' data: ${config.storageDomainName} *.gstatic.com *.google.com *.googleapis.com`,\n `manifest-src 'self'`,\n `media-src 'self' ${config.storageDomainName}`,\n `script-src 'self' *.medplum.com *.gstatic.com *.google.com`,\n `style-src 'self' 'unsafe-inline' *.medplum.com *.gstatic.com *.google.com`,\n `worker-src 'self' blob: *.gstatic.com *.google.com`,\n `upgrade-insecure-requests`,\n ].join('; '),\n override: true,\n },\n contentTypeOptions: { override: true },\n frameOptions: { frameOption: cloudfront.HeadersFrameOption.DENY, override: true },\n strictTransportSecurity: {\n accessControlMaxAge: Duration.seconds(63072000),\n includeSubdomains: true,\n override: true,\n },\n xssProtection: {\n protection: true,\n modeBlock: true,\n override: true,\n },\n },\n });\n\n // WAF\n const waf = new wafv2.CfnWebACL(this, 'FrontEndWAF', {\n defaultAction: { allow: {} },\n scope: 'CLOUDFRONT',\n name: `${config.stackName}-FrontEndWAF`,\n rules: awsManagedRules,\n visibilityConfig: {\n cloudWatchMetricsEnabled: true,\n metricName: `${config.stackName}-FrontEndWAF-Metric`,\n sampledRequestsEnabled: false,\n },\n });\n\n // API Origin Cache Policy\n const apiOriginCachePolicy = new cloudfront.CachePolicy(this, 'ApiOriginCachePolicy', {\n cachePolicyName: `${config.stackName}-ApiOriginCachePolicy`,\n cookieBehavior: cloudfront.CacheCookieBehavior.all(),\n headerBehavior: cloudfront.CacheHeaderBehavior.allowList(\n 'Authorization',\n 'Content-Encoding',\n 'Content-Type',\n 'If-None-Match',\n 'Origin',\n 'Referer',\n 'User-Agent',\n 'X-Medplum'\n ),\n queryStringBehavior: cloudfront.CacheQueryStringBehavior.all(),\n });\n\n // Origin access identity\n const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {});\n grantBucketAccessToOriginAccessIdentity(this, appBucket, originAccessIdentity);\n\n // CloudFront distribution\n const distribution = new cloudfront.Distribution(this, 'AppDistribution', {\n defaultRootObject: 'index.html',\n defaultBehavior: {\n origin: new origins.S3Origin(appBucket, { originAccessIdentity }),\n responseHeadersPolicy,\n viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,\n },\n additionalBehaviors: {\n '/api/*': {\n origin: new origins.HttpOrigin(config.apiDomainName),\n allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,\n cachePolicy: apiOriginCachePolicy,\n viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,\n },\n },\n certificate: acm.Certificate.fromCertificateArn(this, 'AppCertificate', config.appSslCertArn),\n domainNames: [config.appDomainName],\n errorResponses: [\n {\n httpStatus: 403,\n responseHttpStatus: 200,\n responsePagePath: '/index.html',\n },\n {\n httpStatus: 404,\n responseHttpStatus: 200,\n responsePagePath: '/index.html',\n },\n ],\n webAclId: waf.attrArn,\n });\n\n // DNS\n let record = undefined;\n if (!config.skipDns) {\n const zone = route53.HostedZone.fromLookup(this, 'Zone', {\n domainName: config.domainName.split('.').slice(-2).join('.'),\n });\n\n // Route53 alias record for the CloudFront distribution\n record = new route53.ARecord(this, 'AppAliasRecord', {\n recordName: config.appDomainName,\n target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),\n zone,\n });\n }\n\n // Debug\n console.log('ARecord', record?.domainName);\n }\n }\n}\n","import {\n aws_certificatemanager as acm,\n aws_cloudfront as cloudfront,\n aws_cloudfront_origins as origins,\n aws_route53 as route53,\n aws_route53_targets as targets,\n aws_s3 as s3,\n aws_wafv2 as wafv2,\n Duration,\n} from 'aws-cdk-lib';\nimport { ServerlessClamscan } from 'cdk-serverless-clamscan';\nimport { Construct } from 'constructs';\nimport { MedplumInfraConfig } from './config';\nimport { grantBucketAccessToOriginAccessIdentity } from './oai';\nimport { awsManagedRules } from './waf';\n\n/**\n * Binary storage bucket and CloudFront distribution.\n */\nexport class Storage extends Construct {\n constructor(parent: Construct, config: MedplumInfraConfig, region: string) {\n super(parent, 'Storage');\n\n let storageBucket: s3.IBucket;\n\n if (region === config.region) {\n // S3 bucket\n storageBucket = new s3.Bucket(this, 'StorageBucket', {\n bucketName: config.storageBucketName,\n publicReadAccess: false,\n blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,\n encryption: s3.BucketEncryption.S3_MANAGED,\n enforceSSL: true,\n versioned: false,\n });\n\n if (config.clamscanEnabled) {\n // ClamAV serverless scan\n const sc = new ServerlessClamscan(this, 'ServerlessClamscan', {\n defsBucketAccessLogsConfig: {\n logsBucket: s3.Bucket.fromBucketName(this, 'LoggingBucket', config.clamscanLoggingBucket),\n logsPrefix: config.clamscanLoggingPrefix,\n },\n });\n sc.addSourceBucket(storageBucket);\n }\n } else {\n // Otherwise, reference the bucket by name\n storageBucket = s3.Bucket.fromBucketAttributes(this, 'StorageBucket', {\n bucketName: config.storageBucketName,\n region: config.region,\n });\n }\n\n if (region === 'us-east-1') {\n // Public key in PEM format\n const publicKey = new cloudfront.PublicKey(this, 'StoragePublicKey', {\n encodedKey: config.storagePublicKey,\n });\n\n // Authorized key group for presigned URLs\n const keyGroup = new cloudfront.KeyGroup(this, 'StorageKeyGroup', {\n items: [publicKey],\n });\n\n // HTTP response headers policy\n const responseHeadersPolicy = new cloudfront.ResponseHeadersPolicy(this, 'ResponseHeadersPolicy', {\n securityHeadersBehavior: {\n contentSecurityPolicy: {\n contentSecurityPolicy:\n \"default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors *.medplum.com;\",\n override: true,\n },\n contentTypeOptions: { override: true },\n frameOptions: { frameOption: cloudfront.HeadersFrameOption.DENY, override: true },\n referrerPolicy: { referrerPolicy: cloudfront.HeadersReferrerPolicy.NO_REFERRER, override: true },\n strictTransportSecurity: {\n accessControlMaxAge: Duration.seconds(63072000),\n includeSubdomains: true,\n override: true,\n },\n xssProtection: {\n protection: true,\n modeBlock: true,\n override: true,\n },\n },\n });\n\n // WAF\n const waf = new wafv2.CfnWebACL(this, 'StorageWAF', {\n defaultAction: { allow: {} },\n scope: 'CLOUDFRONT',\n name: `${config.stackName}-StorageWAF`,\n rules: awsManagedRules,\n visibilityConfig: {\n cloudWatchMetricsEnabled: true,\n metricName: `${config.stackName}-StorageWAF-Metric`,\n sampledRequestsEnabled: false,\n },\n });\n\n // Origin access identity\n const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {});\n grantBucketAccessToOriginAccessIdentity(this, storageBucket, originAccessIdentity);\n\n // CloudFront distribution\n const distribution = new cloudfront.Distribution(this, 'StorageDistribution', {\n defaultBehavior: {\n origin: new origins.S3Origin(storageBucket, { originAccessIdentity }),\n responseHeadersPolicy,\n viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,\n trustedKeyGroups: [keyGroup],\n },\n certificate: acm.Certificate.fromCertificateArn(this, 'StorageCertificate', config.storageSslCertArn),\n domainNames: [config.storageDomainName],\n webAclId: waf.attrArn,\n });\n\n // DNS\n let record = undefined;\n if (!config.skipDns) {\n const zone = route53.HostedZone.fromLookup(this, 'Zone', {\n domainName: config.domainName.split('.').slice(-2).join('.'),\n });\n\n // Route53 alias record for the CloudFront distribution\n record = new route53.ARecord(this, 'StorageAliasRecord', {\n recordName: config.storageDomainName,\n target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),\n zone,\n });\n }\n\n // Debug\n console.log('ARecord', record?.domainName);\n }\n }\n}\n","import { App, Stack } from 'aws-cdk-lib';\nimport { readFileSync } from 'fs';\nimport { resolve } from 'path';\nimport { BackEnd } from './backend';\nimport { MedplumInfraConfig } from './config';\nimport { FrontEnd } from './frontend';\nimport { Storage } from './storage';\n\nclass MedplumStack {\n primaryStack: Stack;\n backEnd: BackEnd;\n frontEnd: FrontEnd;\n storage: Storage;\n\n constructor(scope: App, config: MedplumInfraConfig) {\n this.primaryStack = new Stack(scope, config.stackName, {\n env: {\n region: config.region,\n account: config.accountNumber,\n },\n });\n\n this.backEnd = new BackEnd(this.primaryStack, config);\n this.frontEnd = new FrontEnd(this.primaryStack, config, config.region);\n this.storage = new Storage(this.primaryStack, config, config.region);\n\n if (config.region !== 'us-east-1') {\n // Some resources must be created in us-east-1\n // For example, CloudFront distributions and ACM certificates\n // If the primary region is not us-east-1, create these resources in us-east-1\n const usEast1Stack = new Stack(scope, config.stackName + '-us-east-1', {\n env: {\n region: 'us-east-1',\n account: config.accountNumber,\n },\n });\n\n this.frontEnd = new FrontEnd(usEast1Stack, config, 'us-east-1');\n this.storage = new Storage(usEast1Stack, config, 'us-east-1');\n }\n }\n}\n\nexport function main(context?: Record<string, string>): void {\n const app = new App({ context });\n\n const configFileName = app.node.tryGetContext('config');\n if (!configFileName) {\n console.log('Missing \"config\" context variable');\n console.log('Usage: cdk deploy -c config=my-config.json');\n return;\n }\n\n const config = JSON.parse(readFileSync(resolve(configFileName), 'utf-8')) as MedplumInfraConfig;\n\n const stack = new MedplumStack(app, config);\n\n console.log('Stack', stack.primaryStack.stackId);\n console.log('BackEnd', stack.backEnd.node.id);\n console.log('FrontEnd', stack.frontEnd.node.id);\n console.log('Storage', stack.storage.node.id);\n\n app.synth();\n}\n\nif (require.main === module) {\n main();\n}\n"],"names":["Construct","ec2","logs","RemovalPolicy","iam","rds","Duration","elasticache","secretsmanager","ecs","Repository","elbv2","s3","wafv2","route53","targets","ssm","cloudfront","origins","acm","ServerlessClamscan","Stack","App","readFileSync","resolve"],"mappings":"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"}
package/dist/cjs/init.cjs CHANGED
@@ -208,8 +208,14 @@ async function main(t) {
208
208
  print('You can now proceed to deploying the Medplum infrastructure with CDK.');
209
209
  print('Run:');
210
210
  print('');
211
+ print(` npx cdk bootstrap -c config=${configFileName}`);
211
212
  print(` npx cdk synth -c config=${configFileName}`);
212
- print(` npx cdk deploy -c config=${configFileName}`);
213
+ if (config.region === 'us-east-1') {
214
+ print(` npx cdk deploy -c config=${configFileName}`);
215
+ }
216
+ else {
217
+ print(` npx cdk deploy -c config=${configFileName} --all`);
218
+ }
213
219
  print('');
214
220
  print('See Medplum documentation for more information:');
215
221
  print('');
@@ -427,7 +433,10 @@ async function writeParameter(region, key, value) {
427
433
  */
428
434
  async function writeParameters(region, prefix, params) {
429
435
  for (const [key, value] of Object.entries(params)) {
430
- await writeParameter(region, prefix + key, value.toString());
436
+ const valueStr = value.toString();
437
+ if (valueStr) {
438
+ await writeParameter(region, prefix + key, valueStr);
439
+ }
431
440
  }
432
441
  }
433
442
  if (require.main === module) {
@@ -1 +1 @@
1
- {"version":3,"file":"init.cjs","sources":["../../../src/init.ts"],"sourcesContent":["import { ACMClient, CertificateSummary, ListCertificatesCommand, RequestCertificateCommand } from '@aws-sdk/client-acm';\nimport { PutParameterCommand, SSMClient } from '@aws-sdk/client-ssm';\nimport { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';\nimport { generateKeyPairSync, randomUUID } from 'crypto';\nimport { existsSync, writeFileSync } from 'fs';\nimport { resolve } from 'path';\nimport readline from 'readline';\nimport { MedplumInfraConfig } from './config';\n\ntype MedplumDomainType = 'api' | 'app' | 'storage';\ntype MedplumDomainSetting = `${MedplumDomainType}DomainName`;\ntype MedplumDomainCertSetting = `${MedplumDomainType}SslCertArn`;\n\nconst getDomainSetting = (domain: MedplumDomainType): MedplumDomainSetting => `${domain}DomainName`;\nconst getDomainCertSetting = (domain: MedplumDomainType): MedplumDomainCertSetting => `${domain}SslCertArn`;\n\nlet terminal: readline.Interface;\n\nexport async function main(t: readline.Interface): Promise<void> {\n const config = { apiPort: 8103, region: 'us-east-1' } as MedplumInfraConfig;\n terminal = t;\n header('MEDPLUM');\n print('This tool prepares the necessary prerequisites for deploying Medplum in your AWS account.');\n print('');\n print('Most Medplum infrastructure is deployed using the AWS CDK.');\n print('However, some AWS resources must be created manually, such as email addresses and SSL certificates.');\n print('This tool will help you create those resources.');\n print('');\n print('Upon completion, this tool will:');\n print(' 1. Generate a Medplum CDK config file (i.e., medplum.demo.config.json)');\n print(' 2. Optionally generate an AWS CloudFront signing key');\n print(' 3. Optionally request SSL certificates from AWS Certificate Manager');\n print(' 4. Optionally write server config settings to AWS Parameter Store');\n print('');\n print('The Medplum infra config file is an input to the Medplum CDK.');\n print('The Medplum CDK will create and manage the necessary AWS resources.');\n print('');\n print('We will ask a series of questions to generate your infra config file.');\n print('Some questions have predefined options in [square brackets].');\n print('Some questions have default values in (parentheses), which you can accept by pressing Enter.');\n print('Press Ctrl+C at any time to exit.');\n\n header('ENVIRONMENT NAME');\n print('Medplum deployments have a short environment name such as \"prod\", \"staging\", \"alice\", or \"demo\".');\n print('The environment name is used in multiple places:');\n print(' 1. As part of config file names (i.e., medplum.demo.config.json)');\n print(' 2. As the base of CloudFormation stack names (i.e., MedplumDemo)');\n print(' 3. AWS Parameter Store keys (i.e., /medplum/demo/...)');\n config.name = await ask('What is your environment name?', 'demo');\n print('Using environment name \"' + config.name + '\"...');\n\n header('CONFIG FILE');\n print('Medplum Infrastructure will create a config file in the current directory.');\n const configFileName = await ask('What is the config file name?', `medplum.${config.name}.config.json`);\n if (existsSync(configFileName)) {\n print('Config file already exists.');\n await checkOk('Do you want to overwrite the config file?');\n }\n print('Using config file \"' + configFileName + '\"...');\n writeConfig(configFileName, config);\n\n header('AWS REGION');\n print('Most Medplum resources will be created in a single AWS region.');\n config.region = await ask('Enter your AWS region:', 'us-east-1');\n writeConfig(configFileName, config);\n\n header('AWS ACCOUNT NUMBER');\n print('Medplum Infrastructure will use your AWS account number to create AWS resources.');\n const currentAccountId = await getAccountId(config.region);\n print('Using the AWS CLI, your current account ID is: ' + currentAccountId);\n config.accountNumber = await ask('What is your AWS account number?', currentAccountId);\n writeConfig(configFileName, config);\n\n header('STACK NAME');\n print('Medplum will create a CloudFormation stack to manage AWS resources.');\n const defaultStackName = 'Medplum' + config.name.charAt(0).toUpperCase() + config.name.slice(1);\n config.stackName = await ask('Enter your CloudFormation stack name?', defaultStackName);\n writeConfig(configFileName, config);\n\n header('BASE DOMAIN NAME');\n print('Medplum deploys multiple subdomains for various services.');\n print('');\n print('For example, \"api.\" for the REST API and \"app.\" for the web application.');\n print('The base domain name is the common suffix for all subdomains.');\n print('');\n print('For example, if your base domain name is \"example.com\",');\n print('then the REST API will be \"api.example.com\".');\n print('');\n print('Note that you must own the base domain, and it must use Route53 DNS.');\n print('Medplum will create subdomains for you, but you must configure the base domain.');\n while (!config.domainName) {\n config.domainName = await ask('Enter your base domain name:');\n }\n writeConfig(configFileName, config);\n\n header('SUPPORT EMAIL');\n print('Medplum sends transactional emails to users.');\n print('For example, emails to new users or for password reset.');\n print('Medplum will use the support email address to send these emails.');\n print('Note that you must verify the support email address in SES.');\n const supportEmail = await ask('Enter your support email address:');\n\n header('API DOMAIN NAME');\n print('Medplum deploys a REST API for the backend services.');\n config.apiDomainName = await ask('Enter your REST API domain name:', 'api.' + config.domainName);\n writeConfig(configFileName, config);\n\n header('APP DOMAIN NAME');\n print('Medplum deploys a web application for the user interface.');\n config.appDomainName = await ask('Enter your web application domain name:', 'app.' + config.domainName);\n writeConfig(configFileName, config);\n\n header('STORAGE DOMAIN NAME');\n print('Medplum deploys a storage service for file uploads.');\n config.storageDomainName = await ask('Enter your storage domain name:', 'storage.' + config.domainName);\n writeConfig(configFileName, config);\n\n header('STORAGE BUCKET');\n print('Medplum uses an S3 bucket to store binary content such as file uploads.');\n print('Medplum will create a the S3 bucket as part of the CloudFormation stack.');\n config.storageBucketName = await ask('Enter your storage bucket name:', 'medplum-' + config.name + '-storage');\n writeConfig(configFileName, config);\n\n header('MAX AVAILABILITY ZONES');\n print('Medplum API servers can be deployed in multiple availability zones.');\n print('This provides redundancy and high availability.');\n print('However, it also increases the cost of the deployment.');\n print('If you want to use all availability zones, choose a large number such as 99.');\n print('If you want to restrict the number, for example to manage EIP limits,');\n print('then choose a small number such as 1 or 2.');\n config.maxAzs = await chooseInt('Enter the maximum number of availability zones:', [1, 2, 3, 99], 2);\n\n header('DATABASE INSTANCES');\n print('Medplum uses a relational database to store data.');\n print('You can set up your own database,');\n print('or Medplum can create a new RDS database as part of the CloudFormation stack.');\n if (await yesOrNo('Do you want to create a new RDS database as part of the CloudFormation stack?')) {\n print('Medplum will create a new RDS database as part of the CloudFormation stack.');\n print('');\n print('If you need high availability, you can choose multiple instances.');\n print('Use 1 for a single instance, or 2 for a primary and a standby.');\n config.rdsInstances = await chooseInt('Enter the number of database instances:', [1, 2], 1);\n } else {\n print('Medplum will not create a new RDS database.');\n print('Please create a new RDS database and enter the database name, username, and password.');\n print('Set the AWS Secrets Manager secret ARN in the config file in the \"rdsSecretsArn\" setting.');\n config.rdsSecretsArn = 'TODO';\n }\n writeConfig(configFileName, config);\n\n header('SERVER INSTANCES');\n print('Medplum uses AWS Fargate to run the API servers.');\n print('Medplum will create a new Fargate cluster as part of the CloudFormation stack.');\n print('Fargate will automatically scale the number of servers up and down.');\n print('If you need high availability, you can choose multiple instances.');\n config.desiredServerCount = await chooseInt('Enter the number of server instances:', [1, 2, 3, 4, 6, 8], 1);\n writeConfig(configFileName, config);\n\n header('SERVER MEMORY');\n print('You can choose the amount of memory for each server instance.');\n print('The default is 512 MB, which is sufficient for getting started.');\n print('Note that only certain CPU units are compatible with memory units.');\n print('Consult AWS Fargate \"Task Definition Parameters\" for more information.');\n config.serverMemory = await chooseInt('Enter the server memory (MB):', [512, 1024, 2048, 4096, 8192, 16384], 512);\n writeConfig(configFileName, config);\n\n header('SERVER CPU');\n print('You can choose the amount of CPU for each server instance.');\n print('CPU is expressed as an integer using AWS CPU units');\n print('The default is 256, which is sufficient for getting started.');\n print('Note that only certain CPU units are compatible with memory units.');\n print('Consult AWS Fargate \"Task Definition Parameters\" for more information.');\n config.serverCpu = await chooseInt('Enter the server CPU:', [256, 512, 1024, 2048, 4096, 8192, 16384], 256);\n writeConfig(configFileName, config);\n\n header('SERVER IMAGE');\n print('Medplum uses Docker images for the API servers.');\n print('You can choose the image to use for the servers.');\n print('Docker images can be loaded from either Docker Hub or AWS ECR.');\n print('The default is the latest Medplum release.');\n config.serverImage = await ask('Enter the server image:', 'medplum/medplum-server:latest');\n writeConfig(configFileName, config);\n\n header('SIGNING KEY');\n print('Medplum uses AWS CloudFront Presigned URLs for binary content such as file uploads.');\n const { privateKey, publicKey, passphrase } = generateSigningKey();\n config.storagePublicKey = publicKey;\n writeConfig(configFileName, config);\n\n header('SSL CERTIFICATES');\n print(`Medplum will now check for existing SSL certificates for the subdomains.`);\n const allCerts = await listAllCertificates(config.region);\n print('Found ' + allCerts.length + ' certificate(s).');\n\n // Process certificates for each subdomain\n // Note: The \"api\" certificate must be created in the same region as the API\n // Note: The \"app\" and \"storage\" certificates must be created in us-east-1\n for (const { region, certName } of [\n { region: config.region, certName: 'api' },\n { region: 'us-east-1', certName: 'app' },\n { region: 'us-east-1', certName: 'storage' },\n ] as const) {\n print('');\n const arn = await processCert(config, allCerts, region, certName);\n config[getDomainCertSetting(certName)] = arn;\n writeConfig(configFileName, config);\n }\n\n header('AWS PARAMETER STORE');\n print('Medplum uses AWS Parameter Store to store sensitive configuration values.');\n print('These values will be encrypted at rest.');\n print(`The values will be stored in the \"/medplum/${config.name}\" path.`);\n\n const serverParams = {\n port: config.apiPort,\n baseUrl: `https://${config.apiDomainName}/`,\n appBaseUrl: `https://${config.appDomainName}/`,\n storageBaseUrl: `https://${config.storageDomainName}/binary/`,\n binaryStorage: `s3:${config.storageBucketName}`,\n signingKey: privateKey,\n signingKeyPassphrase: passphrase,\n supportEmail: supportEmail,\n };\n\n print(\n JSON.stringify(\n {\n ...serverParams,\n signingKey: '****',\n signingKeyPassphrase: '****',\n },\n null,\n 2\n )\n );\n\n await checkOk('Do you want to store these values in AWS Parameter Store?');\n await writeParameters(config.region, `/medplum/${config.name}/`, serverParams);\n\n header('DONE!');\n print('Medplum configuration complete.');\n print('You can now proceed to deploying the Medplum infrastructure with CDK.');\n print('Run:');\n print('');\n print(` npx cdk synth -c config=${configFileName}`);\n print(` npx cdk deploy -c config=${configFileName}`);\n print('');\n print('See Medplum documentation for more information:');\n print('');\n print(' https://www.medplum.com/docs/self-hosting/install-on-aws');\n print('');\n}\n\n/** Prints to stdout. */\nfunction print(text: string): void {\n terminal.write(text + '\\n');\n}\n\n/** Prints a header with extra line spacing. */\nfunction header(text: string): void {\n print('\\n' + text + '\\n');\n}\n\n/** Prints a question and waits for user input. */\nfunction ask(text: string, defaultValue?: string | number): Promise<string> {\n return new Promise((resolve) => {\n terminal.question(text + (defaultValue ? ' (' + defaultValue + ')' : '') + ' ', (answer: string) => {\n resolve(answer || defaultValue?.toString() || '');\n });\n });\n}\n\n/** Prints a question and waits for user to choose one of the provided options. */\nasync function choose(text: string, options: (string | number)[], defaultValue?: string): Promise<string> {\n const str = text + ' [' + options.map((o) => (o === defaultValue ? '(' + o + ')' : o)).join('|') + ']';\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const answer = (await ask(str)) || defaultValue || '';\n if (options.includes(answer)) {\n return answer;\n }\n print('Please choose one of the following options: ' + options.join(', '));\n }\n}\n\n/** Prints a question and waits for the user to choose a valid integer option. */\nasync function chooseInt(text: string, options: number[], defaultValue?: number): Promise<number> {\n return parseInt(\n await choose(\n text,\n options.map((o) => o.toString()),\n defaultValue?.toString() || '0'\n )\n );\n}\n\n/** Prints a question and waits for the user to choose yes or no. */\nasync function yesOrNo(text: string): Promise<boolean> {\n return (await choose(text, ['y', 'n'])).toLowerCase() === 'y';\n}\n\n/** Prints a question and waits for the user to confirm yes. Throws error on no, and exits the program. */\nasync function checkOk(text: string): Promise<void> {\n if (!(await yesOrNo(text))) {\n print('Exiting...');\n throw new Error('User cancelled');\n }\n}\n\n/**\n * Writes a config file to disk.\n * @param configFileName The config file name.\n * @param config The config file contents.\n */\nfunction writeConfig(configFileName: string, config: MedplumInfraConfig): void {\n writeFileSync(resolve(configFileName), JSON.stringify(config, undefined, 2), 'utf-8');\n}\n\n/**\n * Returns the current AWS account ID.\n * This is used as the default value for the \"accountNumber\" config setting.\n * @param region The AWS region.\n * @returns The AWS account ID.\n */\nasync function getAccountId(region: string): Promise<string | undefined> {\n try {\n const client = new STSClient({ region });\n const command = new GetCallerIdentityCommand({});\n const response = await client.send(command);\n return response.Account as string;\n } catch (err) {\n console.log('Warning: Unable to get AWS account ID', (err as Error).message);\n return undefined;\n }\n}\n\n/**\n * Returns a list of all AWS certificates.\n * This is used to find existing certificates for the subdomains.\n * If the primary region is not us-east-1, then certificates in us-east-1 will also be returned.\n * @param region The AWS region.\n * @returns The list of AWS Certificates.\n */\nasync function listAllCertificates(region: string): Promise<CertificateSummary[]> {\n const result = await listCertificates(region);\n if (region !== 'us-east-1') {\n const usEast1Result = await listCertificates('us-east-1');\n result.push(...usEast1Result);\n }\n return result;\n}\n\n/**\n * Returns a list of AWS Certificates.\n * This is used to find existing certificates for the subdomains.\n * @param region The AWS region.\n * @returns The list of AWS Certificates.\n */\nasync function listCertificates(region: string): Promise<CertificateSummary[]> {\n try {\n const client = new ACMClient({ region });\n const command = new ListCertificatesCommand({ MaxItems: 1000 });\n const response = await client.send(command);\n return response.CertificateSummaryList as CertificateSummary[];\n } catch (err) {\n console.log('Warning: Unable to list certificates', (err as Error).message);\n return [];\n }\n}\n\n/**\n * Processes a required certificate.\n *\n * 1. If the certificate already exists, return the ARN.\n * 2. If the certificate does not exist, and the user wants to create a new certificate, create it and return the ARN.\n * 3. If the certificate does not exist, and the user does not want to create a new certificate, return a placeholder.\n *\n * @param config In-progress config settings.\n * @param allCerts List of all existing certificates.\n * @param region The AWS region where the certificate is needed.\n * @param certName The name of the certificate (api, app, or storage).\n * @returns The ARN of the certificate or placeholder if a new certificate is needed.\n */\nasync function processCert(\n config: MedplumInfraConfig,\n allCerts: CertificateSummary[],\n region: string,\n certName: 'api' | 'app' | 'storage'\n): Promise<string> {\n const domainName = config[getDomainSetting(certName)];\n const existingCert = allCerts.find((cert) => cert.CertificateArn?.includes(region) && cert.DomainName === domainName);\n if (existingCert) {\n print(`Found existing certificate for \"${domainName}\" in \"${region}.`);\n return existingCert.CertificateArn as string;\n }\n\n print(`No existing certificate found for \"${domainName}\" in \"${region}.`);\n if (!(await yesOrNo('Do you want to request a new certificate?'))) {\n print(`Please add your certificate ARN to the config file in the \"${getDomainCertSetting(certName)}\" setting.`);\n return 'TODO';\n }\n\n const arn = await requestCert(region, domainName);\n print('Certificate ARN: ' + arn);\n return arn;\n}\n\n/**\n * Requests an AWS Certificate.\n * @param region The AWS region.\n * @param domain The domain name.\n * @returns The AWS Certificate ARN on success, or undefined on failure.\n */\nasync function requestCert(region: string, domain: string): Promise<string> {\n try {\n const validationMethod = await choose(\n 'Validate certificate using DNS or email validation?',\n ['dns', 'email'],\n 'dns'\n );\n const client = new ACMClient({ region });\n const command = new RequestCertificateCommand({\n DomainName: domain,\n ValidationMethod: validationMethod.toUpperCase(),\n });\n const response = await client.send(command);\n return response.CertificateArn as string;\n } catch (err) {\n console.log('Error: Unable to request certificate', (err as Error).message);\n return 'TODO';\n }\n}\n\n/**\n * Generates an AWS CloudFront signing key.\n *\n * Requirements:\n *\n * 1. It must be an SSH-2 RSA key pair.\n * 2. It must be in base64-encoded PEM format.\n * 3. It must be a 2048-bit key pair.\n *\n * See: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html#private-content-creating-cloudfront-key-pairs\n *\n * @returns A new signing key.\n */\nfunction generateSigningKey(): { publicKey: string; privateKey: string; passphrase: string } {\n const passphrase = randomUUID();\n const signingKey = generateKeyPairSync('rsa', {\n modulusLength: 2048,\n publicKeyEncoding: {\n type: 'spki',\n format: 'pem',\n },\n privateKeyEncoding: {\n type: 'pkcs1',\n format: 'pem',\n cipher: 'aes-256-cbc',\n passphrase,\n },\n });\n return {\n publicKey: signingKey.publicKey,\n privateKey: signingKey.privateKey,\n passphrase,\n };\n}\n\n/**\n * Writes a parameter to AWS Parameter Store.\n * @param region The AWS region.\n * @param key The parameter key.\n * @param value The parameter value.\n */\nasync function writeParameter(region: string, key: string, value: string): Promise<void> {\n const client = new SSMClient({ region });\n const command = new PutParameterCommand({\n Name: key,\n Value: value,\n Type: 'SecureString',\n Overwrite: true,\n });\n await client.send(command);\n}\n\n/**\n * Writes a collection of parameters to AWS Parameter Store.\n * @param region The AWS region.\n * @param prefix The AWS Parameter Store prefix.\n * @param params The parameters to write.\n */\nasync function writeParameters(region: string, prefix: string, params: Record<string, string | number>): Promise<void> {\n for (const [key, value] of Object.entries(params)) {\n await writeParameter(region, prefix + key, value.toString());\n }\n}\n\nif (require.main === module) {\n main(readline.createInterface({ input: process.stdin, output: process.stdout }))\n .then(() => process.exit(0))\n .catch((err) => {\n console.error((err as Error).message);\n process.exit(1);\n });\n}\n"],"names":["existsSync","writeFileSync","resolve","STSClient","GetCallerIdentityCommand","ACMClient","ListCertificatesCommand","RequestCertificateCommand","randomUUID","generateKeyPairSync","SSMClient","PutParameterCommand"],"mappings":";;;;;;;;;;;AAaA,MAAM,gBAAgB,GAAG,CAAC,MAAyB,KAA2B,CAAA,EAAG,MAAM,CAAA,UAAA,CAAY,CAAC;AACpG,MAAM,oBAAoB,GAAG,CAAC,MAAyB,KAA+B,CAAA,EAAG,MAAM,CAAA,UAAA,CAAY,CAAC;AAE5G,IAAI,QAA4B,CAAC;AAE1B,eAAe,IAAI,CAAC,CAAqB,EAAA;IAC9C,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAwB,CAAC;IAC5E,QAAQ,GAAG,CAAC,CAAC;IACb,MAAM,CAAC,SAAS,CAAC,CAAC;IAClB,KAAK,CAAC,2FAA2F,CAAC,CAAC;IACnG,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,KAAK,CAAC,4DAA4D,CAAC,CAAC;IACpE,KAAK,CAAC,qGAAqG,CAAC,CAAC;IAC7G,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACzD,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC1C,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAClF,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAChE,KAAK,CAAC,uEAAuE,CAAC,CAAC;IAC/E,KAAK,CAAC,qEAAqE,CAAC,CAAC;IAC7E,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,KAAK,CAAC,+DAA+D,CAAC,CAAC;IACvE,KAAK,CAAC,qEAAqE,CAAC,CAAC;IAC7E,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,KAAK,CAAC,uEAAuE,CAAC,CAAC;IAC/E,KAAK,CAAC,8DAA8D,CAAC,CAAC;IACtE,KAAK,CAAC,8FAA8F,CAAC,CAAC;IACtG,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAE3C,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAC3B,KAAK,CAAC,kGAAkG,CAAC,CAAC;IAC1G,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAC1D,KAAK,CAAC,oEAAoE,CAAC,CAAC;IAC5E,KAAK,CAAC,oEAAoE,CAAC,CAAC;IAC5E,KAAK,CAAC,yDAAyD,CAAC,CAAC;IACjE,MAAM,CAAC,IAAI,GAAG,MAAM,GAAG,CAAC,gCAAgC,EAAE,MAAM,CAAC,CAAC;IAClE,KAAK,CAAC,0BAA0B,GAAG,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IAEzD,MAAM,CAAC,aAAa,CAAC,CAAC;IACtB,KAAK,CAAC,4EAA4E,CAAC,CAAC;AACpF,IAAA,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,+BAA+B,EAAE,CAAA,QAAA,EAAW,MAAM,CAAC,IAAI,CAAA,YAAA,CAAc,CAAC,CAAC;AACxG,IAAA,IAAIA,aAAU,CAAC,cAAc,CAAC,EAAE;QAC9B,KAAK,CAAC,6BAA6B,CAAC,CAAC;AACrC,QAAA,MAAM,OAAO,CAAC,2CAA2C,CAAC,CAAC;AAC5D,KAAA;AACD,IAAA,KAAK,CAAC,qBAAqB,GAAG,cAAc,GAAG,MAAM,CAAC,CAAC;AACvD,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,YAAY,CAAC,CAAC;IACrB,KAAK,CAAC,gEAAgE,CAAC,CAAC;IACxE,MAAM,CAAC,MAAM,GAAG,MAAM,GAAG,CAAC,wBAAwB,EAAE,WAAW,CAAC,CAAC;AACjE,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAC7B,KAAK,CAAC,kFAAkF,CAAC,CAAC;IAC1F,MAAM,gBAAgB,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAC3D,IAAA,KAAK,CAAC,iDAAiD,GAAG,gBAAgB,CAAC,CAAC;IAC5E,MAAM,CAAC,aAAa,GAAG,MAAM,GAAG,CAAC,kCAAkC,EAAE,gBAAgB,CAAC,CAAC;AACvF,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,YAAY,CAAC,CAAC;IACrB,KAAK,CAAC,qEAAqE,CAAC,CAAC;IAC7E,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAChG,MAAM,CAAC,SAAS,GAAG,MAAM,GAAG,CAAC,uCAAuC,EAAE,gBAAgB,CAAC,CAAC;AACxF,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAC3B,KAAK,CAAC,2DAA2D,CAAC,CAAC;IACnE,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAClF,KAAK,CAAC,+DAA+D,CAAC,CAAC;IACvE,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,KAAK,CAAC,yDAAyD,CAAC,CAAC;IACjE,KAAK,CAAC,8CAA8C,CAAC,CAAC;IACtD,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,KAAK,CAAC,sEAAsE,CAAC,CAAC;IAC9E,KAAK,CAAC,iFAAiF,CAAC,CAAC;AACzF,IAAA,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE;QACzB,MAAM,CAAC,UAAU,GAAG,MAAM,GAAG,CAAC,8BAA8B,CAAC,CAAC;AAC/D,KAAA;AACD,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,eAAe,CAAC,CAAC;IACxB,KAAK,CAAC,8CAA8C,CAAC,CAAC;IACtD,KAAK,CAAC,yDAAyD,CAAC,CAAC;IACjE,KAAK,CAAC,kEAAkE,CAAC,CAAC;IAC1E,KAAK,CAAC,6DAA6D,CAAC,CAAC;AACrE,IAAA,MAAM,YAAY,GAAG,MAAM,GAAG,CAAC,mCAAmC,CAAC,CAAC;IAEpE,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC1B,KAAK,CAAC,sDAAsD,CAAC,CAAC;AAC9D,IAAA,MAAM,CAAC,aAAa,GAAG,MAAM,GAAG,CAAC,kCAAkC,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;AACjG,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC1B,KAAK,CAAC,2DAA2D,CAAC,CAAC;AACnE,IAAA,MAAM,CAAC,aAAa,GAAG,MAAM,GAAG,CAAC,yCAAyC,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;AACxG,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAC9B,KAAK,CAAC,qDAAqD,CAAC,CAAC;AAC7D,IAAA,MAAM,CAAC,iBAAiB,GAAG,MAAM,GAAG,CAAC,iCAAiC,EAAE,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;AACxG,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACzB,KAAK,CAAC,yEAAyE,CAAC,CAAC;IACjF,KAAK,CAAC,0EAA0E,CAAC,CAAC;AAClF,IAAA,MAAM,CAAC,iBAAiB,GAAG,MAAM,GAAG,CAAC,iCAAiC,EAAE,UAAU,GAAG,MAAM,CAAC,IAAI,GAAG,UAAU,CAAC,CAAC;AAC/G,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,wBAAwB,CAAC,CAAC;IACjC,KAAK,CAAC,qEAAqE,CAAC,CAAC;IAC7E,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACzD,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAChE,KAAK,CAAC,8EAA8E,CAAC,CAAC;IACtF,KAAK,CAAC,uEAAuE,CAAC,CAAC;IAC/E,KAAK,CAAC,4CAA4C,CAAC,CAAC;IACpD,MAAM,CAAC,MAAM,GAAG,MAAM,SAAS,CAAC,iDAAiD,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAErG,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAC7B,KAAK,CAAC,mDAAmD,CAAC,CAAC;IAC3D,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAC3C,KAAK,CAAC,+EAA+E,CAAC,CAAC;AACvF,IAAA,IAAI,MAAM,OAAO,CAAC,+EAA+E,CAAC,EAAE;QAClG,KAAK,CAAC,6EAA6E,CAAC,CAAC;QACrF,KAAK,CAAC,EAAE,CAAC,CAAC;QACV,KAAK,CAAC,mEAAmE,CAAC,CAAC;QAC3E,KAAK,CAAC,gEAAgE,CAAC,CAAC;AACxE,QAAA,MAAM,CAAC,YAAY,GAAG,MAAM,SAAS,CAAC,yCAAyC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7F,KAAA;AAAM,SAAA;QACL,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACrD,KAAK,CAAC,uFAAuF,CAAC,CAAC;QAC/F,KAAK,CAAC,2FAA2F,CAAC,CAAC;AACnG,QAAA,MAAM,CAAC,aAAa,GAAG,MAAM,CAAC;AAC/B,KAAA;AACD,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAC3B,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAC1D,KAAK,CAAC,gFAAgF,CAAC,CAAC;IACxF,KAAK,CAAC,qEAAqE,CAAC,CAAC;IAC7E,KAAK,CAAC,mEAAmE,CAAC,CAAC;IAC3E,MAAM,CAAC,kBAAkB,GAAG,MAAM,SAAS,CAAC,uCAAuC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC5G,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,eAAe,CAAC,CAAC;IACxB,KAAK,CAAC,+DAA+D,CAAC,CAAC;IACvE,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACzE,KAAK,CAAC,oEAAoE,CAAC,CAAC;IAC5E,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAChF,MAAM,CAAC,YAAY,GAAG,MAAM,SAAS,CAAC,+BAA+B,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;AAClH,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,YAAY,CAAC,CAAC;IACrB,KAAK,CAAC,4DAA4D,CAAC,CAAC;IACpE,KAAK,CAAC,oDAAoD,CAAC,CAAC;IAC5D,KAAK,CAAC,8DAA8D,CAAC,CAAC;IACtE,KAAK,CAAC,oEAAoE,CAAC,CAAC;IAC5E,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAChF,MAAM,CAAC,SAAS,GAAG,MAAM,SAAS,CAAC,uBAAuB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;AAC5G,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,cAAc,CAAC,CAAC;IACvB,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACzD,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAC1D,KAAK,CAAC,gEAAgE,CAAC,CAAC;IACxE,KAAK,CAAC,4CAA4C,CAAC,CAAC;IACpD,MAAM,CAAC,WAAW,GAAG,MAAM,GAAG,CAAC,yBAAyB,EAAE,+BAA+B,CAAC,CAAC;AAC3F,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,aAAa,CAAC,CAAC;IACtB,KAAK,CAAC,qFAAqF,CAAC,CAAC;IAC7F,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,kBAAkB,EAAE,CAAC;AACnE,IAAA,MAAM,CAAC,gBAAgB,GAAG,SAAS,CAAC;AACpC,IAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAEpC,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAC3B,KAAK,CAAC,CAA0E,wEAAA,CAAA,CAAC,CAAC;IAClF,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1D,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC,MAAM,GAAG,kBAAkB,CAAC,CAAC;;;;AAKvD,IAAA,KAAK,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI;QACjC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;AAC1C,QAAA,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE;AACxC,QAAA,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE;KACpC,EAAE;QACV,KAAK,CAAC,EAAE,CAAC,CAAC;AACV,QAAA,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAClE,MAAM,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,GAAG,GAAG,CAAC;AAC7C,QAAA,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;AACrC,KAAA;IAED,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAC9B,KAAK,CAAC,2EAA2E,CAAC,CAAC;IACnF,KAAK,CAAC,yCAAyC,CAAC,CAAC;AACjD,IAAA,KAAK,CAAC,CAA8C,2CAAA,EAAA,MAAM,CAAC,IAAI,CAAA,OAAA,CAAS,CAAC,CAAC;AAE1E,IAAA,MAAM,YAAY,GAAG;QACnB,IAAI,EAAE,MAAM,CAAC,OAAO;AACpB,QAAA,OAAO,EAAE,CAAA,QAAA,EAAW,MAAM,CAAC,aAAa,CAAG,CAAA,CAAA;AAC3C,QAAA,UAAU,EAAE,CAAA,QAAA,EAAW,MAAM,CAAC,aAAa,CAAG,CAAA,CAAA;AAC9C,QAAA,cAAc,EAAE,CAAA,QAAA,EAAW,MAAM,CAAC,iBAAiB,CAAU,QAAA,CAAA;AAC7D,QAAA,aAAa,EAAE,CAAA,GAAA,EAAM,MAAM,CAAC,iBAAiB,CAAE,CAAA;AAC/C,QAAA,UAAU,EAAE,UAAU;AACtB,QAAA,oBAAoB,EAAE,UAAU;AAChC,QAAA,YAAY,EAAE,YAAY;KAC3B,CAAC;AAEF,IAAA,KAAK,CACH,IAAI,CAAC,SAAS,CACZ;AACE,QAAA,GAAG,YAAY;AACf,QAAA,UAAU,EAAE,MAAM;AAClB,QAAA,oBAAoB,EAAE,MAAM;AAC7B,KAAA,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;AAEF,IAAA,MAAM,OAAO,CAAC,2DAA2D,CAAC,CAAC;AAC3E,IAAA,MAAM,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,CAAA,SAAA,EAAY,MAAM,CAAC,IAAI,CAAA,CAAA,CAAG,EAAE,YAAY,CAAC,CAAC;IAE/E,MAAM,CAAC,OAAO,CAAC,CAAC;IAChB,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACzC,KAAK,CAAC,uEAAuE,CAAC,CAAC;IAC/E,KAAK,CAAC,MAAM,CAAC,CAAC;IACd,KAAK,CAAC,EAAE,CAAC,CAAC;AACV,IAAA,KAAK,CAAC,CAAA,4BAAA,EAA+B,cAAc,CAAA,CAAE,CAAC,CAAC;AACvD,IAAA,KAAK,CAAC,CAAA,6BAAA,EAAgC,cAAc,CAAA,CAAE,CAAC,CAAC;IACxD,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACzD,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,KAAK,CAAC,8DAA8D,CAAC,CAAC;IACtE,KAAK,CAAC,EAAE,CAAC,CAAC;AACZ,CAAC;AAED;AACA,SAAS,KAAK,CAAC,IAAY,EAAA;AACzB,IAAA,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED;AACA,SAAS,MAAM,CAAC,IAAY,EAAA;AAC1B,IAAA,KAAK,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED;AACA,SAAS,GAAG,CAAC,IAAY,EAAE,YAA8B,EAAA;AACvD,IAAA,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,KAAI;QAC7B,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,YAAY,GAAG,IAAI,GAAG,YAAY,GAAG,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,MAAc,KAAI;YACjG,OAAO,CAAC,MAAM,IAAI,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AACpD,SAAC,CAAC,CAAC;AACL,KAAC,CAAC,CAAC;AACL,CAAC;AAED;AACA,eAAe,MAAM,CAAC,IAAY,EAAE,OAA4B,EAAE,YAAqB,EAAA;AACrF,IAAA,MAAM,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,YAAY,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;;AAEvG,IAAA,OAAO,IAAI,EAAE;AACX,QAAA,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,KAAK,YAAY,IAAI,EAAE,CAAC;AACtD,QAAA,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE;AAC5B,YAAA,OAAO,MAAM,CAAC;AACf,SAAA;QACD,KAAK,CAAC,8CAA8C,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5E,KAAA;AACH,CAAC;AAED;AACA,eAAe,SAAS,CAAC,IAAY,EAAE,OAAiB,EAAE,YAAqB,EAAA;AAC7E,IAAA,OAAO,QAAQ,CACb,MAAM,MAAM,CACV,IAAI,EACJ,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC,EAChC,YAAY,EAAE,QAAQ,EAAE,IAAI,GAAG,CAChC,CACF,CAAC;AACJ,CAAC;AAED;AACA,eAAe,OAAO,CAAC,IAAY,EAAA;AACjC,IAAA,OAAO,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,GAAG,CAAC;AAChE,CAAC;AAED;AACA,eAAe,OAAO,CAAC,IAAY,EAAA;IACjC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE;QAC1B,KAAK,CAAC,YAAY,CAAC,CAAC;AACpB,QAAA,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;AACnC,KAAA;AACH,CAAC;AAED;;;;AAIG;AACH,SAAS,WAAW,CAAC,cAAsB,EAAE,MAA0B,EAAA;AACrE,IAAAC,gBAAa,CAACC,YAAO,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACxF,CAAC;AAED;;;;;AAKG;AACH,eAAe,YAAY,CAAC,MAAc,EAAA;IACxC,IAAI;QACF,MAAM,MAAM,GAAG,IAAIC,mBAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AACzC,QAAA,MAAM,OAAO,GAAG,IAAIC,kCAAwB,CAAC,EAAE,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,QAAQ,CAAC,OAAiB,CAAC;AACnC,KAAA;AAAC,IAAA,OAAO,GAAG,EAAE;QACZ,OAAO,CAAC,GAAG,CAAC,uCAAuC,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;AAC7E,QAAA,OAAO,SAAS,CAAC;AAClB,KAAA;AACH,CAAC;AAED;;;;;;AAMG;AACH,eAAe,mBAAmB,CAAC,MAAc,EAAA;AAC/C,IAAA,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC9C,IAAI,MAAM,KAAK,WAAW,EAAE;AAC1B,QAAA,MAAM,aAAa,GAAG,MAAM,gBAAgB,CAAC,WAAW,CAAC,CAAC;AAC1D,QAAA,MAAM,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,CAAC;AAC/B,KAAA;AACD,IAAA,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;AAKG;AACH,eAAe,gBAAgB,CAAC,MAAc,EAAA;IAC5C,IAAI;QACF,MAAM,MAAM,GAAG,IAAIC,mBAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,IAAIC,iCAAuB,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,QAAQ,CAAC,sBAA8C,CAAC;AAChE,KAAA;AAAC,IAAA,OAAO,GAAG,EAAE;QACZ,OAAO,CAAC,GAAG,CAAC,sCAAsC,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;AAC5E,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;AACH,CAAC;AAED;;;;;;;;;;;;AAYG;AACH,eAAe,WAAW,CACxB,MAA0B,EAC1B,QAA8B,EAC9B,MAAc,EACd,QAAmC,EAAA;IAEnC,MAAM,UAAU,GAAG,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtD,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;AACtH,IAAA,IAAI,YAAY,EAAE;AAChB,QAAA,KAAK,CAAC,CAAmC,gCAAA,EAAA,UAAU,SAAS,MAAM,CAAA,CAAA,CAAG,CAAC,CAAC;QACvE,OAAO,YAAY,CAAC,cAAwB,CAAC;AAC9C,KAAA;AAED,IAAA,KAAK,CAAC,CAAsC,mCAAA,EAAA,UAAU,SAAS,MAAM,CAAA,CAAA,CAAG,CAAC,CAAC;IAC1E,IAAI,EAAE,MAAM,OAAO,CAAC,2CAA2C,CAAC,CAAC,EAAE;QACjE,KAAK,CAAC,8DAA8D,oBAAoB,CAAC,QAAQ,CAAC,CAAA,UAAA,CAAY,CAAC,CAAC;AAChH,QAAA,OAAO,MAAM,CAAC;AACf,KAAA;IAED,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAClD,IAAA,KAAK,CAAC,mBAAmB,GAAG,GAAG,CAAC,CAAC;AACjC,IAAA,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;AAKG;AACH,eAAe,WAAW,CAAC,MAAc,EAAE,MAAc,EAAA;IACvD,IAAI;AACF,QAAA,MAAM,gBAAgB,GAAG,MAAM,MAAM,CACnC,qDAAqD,EACrD,CAAC,KAAK,EAAE,OAAO,CAAC,EAChB,KAAK,CACN,CAAC;QACF,MAAM,MAAM,GAAG,IAAID,mBAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AACzC,QAAA,MAAM,OAAO,GAAG,IAAIE,mCAAyB,CAAC;AAC5C,YAAA,UAAU,EAAE,MAAM;AAClB,YAAA,gBAAgB,EAAE,gBAAgB,CAAC,WAAW,EAAE;AACjD,SAAA,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,QAAQ,CAAC,cAAwB,CAAC;AAC1C,KAAA;AAAC,IAAA,OAAO,GAAG,EAAE;QACZ,OAAO,CAAC,GAAG,CAAC,sCAAsC,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;AAC5E,QAAA,OAAO,MAAM,CAAC;AACf,KAAA;AACH,CAAC;AAED;;;;;;;;;;;;AAYG;AACH,SAAS,kBAAkB,GAAA;AACzB,IAAA,MAAM,UAAU,GAAGC,iBAAU,EAAE,CAAC;AAChC,IAAA,MAAM,UAAU,GAAGC,0BAAmB,CAAC,KAAK,EAAE;AAC5C,QAAA,aAAa,EAAE,IAAI;AACnB,QAAA,iBAAiB,EAAE;AACjB,YAAA,IAAI,EAAE,MAAM;AACZ,YAAA,MAAM,EAAE,KAAK;AACd,SAAA;AACD,QAAA,kBAAkB,EAAE;AAClB,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,MAAM,EAAE,KAAK;AACb,YAAA,MAAM,EAAE,aAAa;YACrB,UAAU;AACX,SAAA;AACF,KAAA,CAAC,CAAC;IACH,OAAO;QACL,SAAS,EAAE,UAAU,CAAC,SAAS;QAC/B,UAAU,EAAE,UAAU,CAAC,UAAU;QACjC,UAAU;KACX,CAAC;AACJ,CAAC;AAED;;;;;AAKG;AACH,eAAe,cAAc,CAAC,MAAc,EAAE,GAAW,EAAE,KAAa,EAAA;IACtE,MAAM,MAAM,GAAG,IAAIC,mBAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AACzC,IAAA,MAAM,OAAO,GAAG,IAAIC,6BAAmB,CAAC;AACtC,QAAA,IAAI,EAAE,GAAG;AACT,QAAA,KAAK,EAAE,KAAK;AACZ,QAAA,IAAI,EAAE,cAAc;AACpB,QAAA,SAAS,EAAE,IAAI;AAChB,KAAA,CAAC,CAAC;AACH,IAAA,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;AAKG;AACH,eAAe,eAAe,CAAC,MAAc,EAAE,MAAc,EAAE,MAAuC,EAAA;AACpG,IAAA,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;AACjD,QAAA,MAAM,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC9D,KAAA;AACH,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE;AAC3B,IAAA,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;SAC7E,IAAI,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC3B,SAAA,KAAK,CAAC,CAAC,GAAG,KAAI;AACb,QAAA,OAAO,CAAC,KAAK,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;AACtC,QAAA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,KAAC,CAAC,CAAC;AACN;;;;"}
1
+ {"version":3,"file":"init.cjs","sources":["../../../src/init.ts"],"sourcesContent":["import { ACMClient, CertificateSummary, ListCertificatesCommand, RequestCertificateCommand } from '@aws-sdk/client-acm';\nimport { PutParameterCommand, SSMClient } from '@aws-sdk/client-ssm';\nimport { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';\nimport { generateKeyPairSync, randomUUID } from 'crypto';\nimport { existsSync, writeFileSync } from 'fs';\nimport { resolve } from 'path';\nimport readline from 'readline';\nimport { MedplumInfraConfig } from './config';\n\ntype MedplumDomainType = 'api' | 'app' | 'storage';\ntype MedplumDomainSetting = `${MedplumDomainType}DomainName`;\ntype MedplumDomainCertSetting = `${MedplumDomainType}SslCertArn`;\n\nconst getDomainSetting = (domain: MedplumDomainType): MedplumDomainSetting => `${domain}DomainName`;\nconst getDomainCertSetting = (domain: MedplumDomainType): MedplumDomainCertSetting => `${domain}SslCertArn`;\n\nlet terminal: readline.Interface;\n\nexport async function main(t: readline.Interface): Promise<void> {\n const config = { apiPort: 8103, region: 'us-east-1' } as MedplumInfraConfig;\n terminal = t;\n header('MEDPLUM');\n print('This tool prepares the necessary prerequisites for deploying Medplum in your AWS account.');\n print('');\n print('Most Medplum infrastructure is deployed using the AWS CDK.');\n print('However, some AWS resources must be created manually, such as email addresses and SSL certificates.');\n print('This tool will help you create those resources.');\n print('');\n print('Upon completion, this tool will:');\n print(' 1. Generate a Medplum CDK config file (i.e., medplum.demo.config.json)');\n print(' 2. Optionally generate an AWS CloudFront signing key');\n print(' 3. Optionally request SSL certificates from AWS Certificate Manager');\n print(' 4. Optionally write server config settings to AWS Parameter Store');\n print('');\n print('The Medplum infra config file is an input to the Medplum CDK.');\n print('The Medplum CDK will create and manage the necessary AWS resources.');\n print('');\n print('We will ask a series of questions to generate your infra config file.');\n print('Some questions have predefined options in [square brackets].');\n print('Some questions have default values in (parentheses), which you can accept by pressing Enter.');\n print('Press Ctrl+C at any time to exit.');\n\n header('ENVIRONMENT NAME');\n print('Medplum deployments have a short environment name such as \"prod\", \"staging\", \"alice\", or \"demo\".');\n print('The environment name is used in multiple places:');\n print(' 1. As part of config file names (i.e., medplum.demo.config.json)');\n print(' 2. As the base of CloudFormation stack names (i.e., MedplumDemo)');\n print(' 3. AWS Parameter Store keys (i.e., /medplum/demo/...)');\n config.name = await ask('What is your environment name?', 'demo');\n print('Using environment name \"' + config.name + '\"...');\n\n header('CONFIG FILE');\n print('Medplum Infrastructure will create a config file in the current directory.');\n const configFileName = await ask('What is the config file name?', `medplum.${config.name}.config.json`);\n if (existsSync(configFileName)) {\n print('Config file already exists.');\n await checkOk('Do you want to overwrite the config file?');\n }\n print('Using config file \"' + configFileName + '\"...');\n writeConfig(configFileName, config);\n\n header('AWS REGION');\n print('Most Medplum resources will be created in a single AWS region.');\n config.region = await ask('Enter your AWS region:', 'us-east-1');\n writeConfig(configFileName, config);\n\n header('AWS ACCOUNT NUMBER');\n print('Medplum Infrastructure will use your AWS account number to create AWS resources.');\n const currentAccountId = await getAccountId(config.region);\n print('Using the AWS CLI, your current account ID is: ' + currentAccountId);\n config.accountNumber = await ask('What is your AWS account number?', currentAccountId);\n writeConfig(configFileName, config);\n\n header('STACK NAME');\n print('Medplum will create a CloudFormation stack to manage AWS resources.');\n const defaultStackName = 'Medplum' + config.name.charAt(0).toUpperCase() + config.name.slice(1);\n config.stackName = await ask('Enter your CloudFormation stack name?', defaultStackName);\n writeConfig(configFileName, config);\n\n header('BASE DOMAIN NAME');\n print('Medplum deploys multiple subdomains for various services.');\n print('');\n print('For example, \"api.\" for the REST API and \"app.\" for the web application.');\n print('The base domain name is the common suffix for all subdomains.');\n print('');\n print('For example, if your base domain name is \"example.com\",');\n print('then the REST API will be \"api.example.com\".');\n print('');\n print('Note that you must own the base domain, and it must use Route53 DNS.');\n print('Medplum will create subdomains for you, but you must configure the base domain.');\n while (!config.domainName) {\n config.domainName = await ask('Enter your base domain name:');\n }\n writeConfig(configFileName, config);\n\n header('SUPPORT EMAIL');\n print('Medplum sends transactional emails to users.');\n print('For example, emails to new users or for password reset.');\n print('Medplum will use the support email address to send these emails.');\n print('Note that you must verify the support email address in SES.');\n const supportEmail = await ask('Enter your support email address:');\n\n header('API DOMAIN NAME');\n print('Medplum deploys a REST API for the backend services.');\n config.apiDomainName = await ask('Enter your REST API domain name:', 'api.' + config.domainName);\n writeConfig(configFileName, config);\n\n header('APP DOMAIN NAME');\n print('Medplum deploys a web application for the user interface.');\n config.appDomainName = await ask('Enter your web application domain name:', 'app.' + config.domainName);\n writeConfig(configFileName, config);\n\n header('STORAGE DOMAIN NAME');\n print('Medplum deploys a storage service for file uploads.');\n config.storageDomainName = await ask('Enter your storage domain name:', 'storage.' + config.domainName);\n writeConfig(configFileName, config);\n\n header('STORAGE BUCKET');\n print('Medplum uses an S3 bucket to store binary content such as file uploads.');\n print('Medplum will create a the S3 bucket as part of the CloudFormation stack.');\n config.storageBucketName = await ask('Enter your storage bucket name:', 'medplum-' + config.name + '-storage');\n writeConfig(configFileName, config);\n\n header('MAX AVAILABILITY ZONES');\n print('Medplum API servers can be deployed in multiple availability zones.');\n print('This provides redundancy and high availability.');\n print('However, it also increases the cost of the deployment.');\n print('If you want to use all availability zones, choose a large number such as 99.');\n print('If you want to restrict the number, for example to manage EIP limits,');\n print('then choose a small number such as 1 or 2.');\n config.maxAzs = await chooseInt('Enter the maximum number of availability zones:', [1, 2, 3, 99], 2);\n\n header('DATABASE INSTANCES');\n print('Medplum uses a relational database to store data.');\n print('You can set up your own database,');\n print('or Medplum can create a new RDS database as part of the CloudFormation stack.');\n if (await yesOrNo('Do you want to create a new RDS database as part of the CloudFormation stack?')) {\n print('Medplum will create a new RDS database as part of the CloudFormation stack.');\n print('');\n print('If you need high availability, you can choose multiple instances.');\n print('Use 1 for a single instance, or 2 for a primary and a standby.');\n config.rdsInstances = await chooseInt('Enter the number of database instances:', [1, 2], 1);\n } else {\n print('Medplum will not create a new RDS database.');\n print('Please create a new RDS database and enter the database name, username, and password.');\n print('Set the AWS Secrets Manager secret ARN in the config file in the \"rdsSecretsArn\" setting.');\n config.rdsSecretsArn = 'TODO';\n }\n writeConfig(configFileName, config);\n\n header('SERVER INSTANCES');\n print('Medplum uses AWS Fargate to run the API servers.');\n print('Medplum will create a new Fargate cluster as part of the CloudFormation stack.');\n print('Fargate will automatically scale the number of servers up and down.');\n print('If you need high availability, you can choose multiple instances.');\n config.desiredServerCount = await chooseInt('Enter the number of server instances:', [1, 2, 3, 4, 6, 8], 1);\n writeConfig(configFileName, config);\n\n header('SERVER MEMORY');\n print('You can choose the amount of memory for each server instance.');\n print('The default is 512 MB, which is sufficient for getting started.');\n print('Note that only certain CPU units are compatible with memory units.');\n print('Consult AWS Fargate \"Task Definition Parameters\" for more information.');\n config.serverMemory = await chooseInt('Enter the server memory (MB):', [512, 1024, 2048, 4096, 8192, 16384], 512);\n writeConfig(configFileName, config);\n\n header('SERVER CPU');\n print('You can choose the amount of CPU for each server instance.');\n print('CPU is expressed as an integer using AWS CPU units');\n print('The default is 256, which is sufficient for getting started.');\n print('Note that only certain CPU units are compatible with memory units.');\n print('Consult AWS Fargate \"Task Definition Parameters\" for more information.');\n config.serverCpu = await chooseInt('Enter the server CPU:', [256, 512, 1024, 2048, 4096, 8192, 16384], 256);\n writeConfig(configFileName, config);\n\n header('SERVER IMAGE');\n print('Medplum uses Docker images for the API servers.');\n print('You can choose the image to use for the servers.');\n print('Docker images can be loaded from either Docker Hub or AWS ECR.');\n print('The default is the latest Medplum release.');\n config.serverImage = await ask('Enter the server image:', 'medplum/medplum-server:latest');\n writeConfig(configFileName, config);\n\n header('SIGNING KEY');\n print('Medplum uses AWS CloudFront Presigned URLs for binary content such as file uploads.');\n const { privateKey, publicKey, passphrase } = generateSigningKey();\n config.storagePublicKey = publicKey;\n writeConfig(configFileName, config);\n\n header('SSL CERTIFICATES');\n print(`Medplum will now check for existing SSL certificates for the subdomains.`);\n const allCerts = await listAllCertificates(config.region);\n print('Found ' + allCerts.length + ' certificate(s).');\n\n // Process certificates for each subdomain\n // Note: The \"api\" certificate must be created in the same region as the API\n // Note: The \"app\" and \"storage\" certificates must be created in us-east-1\n for (const { region, certName } of [\n { region: config.region, certName: 'api' },\n { region: 'us-east-1', certName: 'app' },\n { region: 'us-east-1', certName: 'storage' },\n ] as const) {\n print('');\n const arn = await processCert(config, allCerts, region, certName);\n config[getDomainCertSetting(certName)] = arn;\n writeConfig(configFileName, config);\n }\n\n header('AWS PARAMETER STORE');\n print('Medplum uses AWS Parameter Store to store sensitive configuration values.');\n print('These values will be encrypted at rest.');\n print(`The values will be stored in the \"/medplum/${config.name}\" path.`);\n\n const serverParams = {\n port: config.apiPort,\n baseUrl: `https://${config.apiDomainName}/`,\n appBaseUrl: `https://${config.appDomainName}/`,\n storageBaseUrl: `https://${config.storageDomainName}/binary/`,\n binaryStorage: `s3:${config.storageBucketName}`,\n signingKey: privateKey,\n signingKeyPassphrase: passphrase,\n supportEmail: supportEmail,\n };\n\n print(\n JSON.stringify(\n {\n ...serverParams,\n signingKey: '****',\n signingKeyPassphrase: '****',\n },\n null,\n 2\n )\n );\n\n await checkOk('Do you want to store these values in AWS Parameter Store?');\n await writeParameters(config.region, `/medplum/${config.name}/`, serverParams);\n\n header('DONE!');\n print('Medplum configuration complete.');\n print('You can now proceed to deploying the Medplum infrastructure with CDK.');\n print('Run:');\n print('');\n print(` npx cdk bootstrap -c config=${configFileName}`);\n print(` npx cdk synth -c config=${configFileName}`);\n if (config.region === 'us-east-1') {\n print(` npx cdk deploy -c config=${configFileName}`);\n } else {\n print(` npx cdk deploy -c config=${configFileName} --all`);\n }\n print('');\n print('See Medplum documentation for more information:');\n print('');\n print(' https://www.medplum.com/docs/self-hosting/install-on-aws');\n print('');\n}\n\n/** Prints to stdout. */\nfunction print(text: string): void {\n terminal.write(text + '\\n');\n}\n\n/** Prints a header with extra line spacing. */\nfunction header(text: string): void {\n print('\\n' + text + '\\n');\n}\n\n/** Prints a question and waits for user input. */\nfunction ask(text: string, defaultValue?: string | number): Promise<string> {\n return new Promise((resolve) => {\n terminal.question(text + (defaultValue ? ' (' + defaultValue + ')' : '') + ' ', (answer: string) => {\n resolve(answer || defaultValue?.toString() || '');\n });\n });\n}\n\n/** Prints a question and waits for user to choose one of the provided options. */\nasync function choose(text: string, options: (string | number)[], defaultValue?: string): Promise<string> {\n const str = text + ' [' + options.map((o) => (o === defaultValue ? '(' + o + ')' : o)).join('|') + ']';\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const answer = (await ask(str)) || defaultValue || '';\n if (options.includes(answer)) {\n return answer;\n }\n print('Please choose one of the following options: ' + options.join(', '));\n }\n}\n\n/** Prints a question and waits for the user to choose a valid integer option. */\nasync function chooseInt(text: string, options: number[], defaultValue?: number): Promise<number> {\n return parseInt(\n await choose(\n text,\n options.map((o) => o.toString()),\n defaultValue?.toString() || '0'\n )\n );\n}\n\n/** Prints a question and waits for the user to choose yes or no. */\nasync function yesOrNo(text: string): Promise<boolean> {\n return (await choose(text, ['y', 'n'])).toLowerCase() === 'y';\n}\n\n/** Prints a question and waits for the user to confirm yes. Throws error on no, and exits the program. */\nasync function checkOk(text: string): Promise<void> {\n if (!(await yesOrNo(text))) {\n print('Exiting...');\n throw new Error('User cancelled');\n }\n}\n\n/**\n * Writes a config file to disk.\n * @param configFileName The config file name.\n * @param config The config file contents.\n */\nfunction writeConfig(configFileName: string, config: MedplumInfraConfig): void {\n writeFileSync(resolve(configFileName), JSON.stringify(config, undefined, 2), 'utf-8');\n}\n\n/**\n * Returns the current AWS account ID.\n * This is used as the default value for the \"accountNumber\" config setting.\n * @param region The AWS region.\n * @returns The AWS account ID.\n */\nasync function getAccountId(region: string): Promise<string | undefined> {\n try {\n const client = new STSClient({ region });\n const command = new GetCallerIdentityCommand({});\n const response = await client.send(command);\n return response.Account as string;\n } catch (err) {\n console.log('Warning: Unable to get AWS account ID', (err as Error).message);\n return undefined;\n }\n}\n\n/**\n * Returns a list of all AWS certificates.\n * This is used to find existing certificates for the subdomains.\n * If the primary region is not us-east-1, then certificates in us-east-1 will also be returned.\n * @param region The AWS region.\n * @returns The list of AWS Certificates.\n */\nasync function listAllCertificates(region: string): Promise<CertificateSummary[]> {\n const result = await listCertificates(region);\n if (region !== 'us-east-1') {\n const usEast1Result = await listCertificates('us-east-1');\n result.push(...usEast1Result);\n }\n return result;\n}\n\n/**\n * Returns a list of AWS Certificates.\n * This is used to find existing certificates for the subdomains.\n * @param region The AWS region.\n * @returns The list of AWS Certificates.\n */\nasync function listCertificates(region: string): Promise<CertificateSummary[]> {\n try {\n const client = new ACMClient({ region });\n const command = new ListCertificatesCommand({ MaxItems: 1000 });\n const response = await client.send(command);\n return response.CertificateSummaryList as CertificateSummary[];\n } catch (err) {\n console.log('Warning: Unable to list certificates', (err as Error).message);\n return [];\n }\n}\n\n/**\n * Processes a required certificate.\n *\n * 1. If the certificate already exists, return the ARN.\n * 2. If the certificate does not exist, and the user wants to create a new certificate, create it and return the ARN.\n * 3. If the certificate does not exist, and the user does not want to create a new certificate, return a placeholder.\n *\n * @param config In-progress config settings.\n * @param allCerts List of all existing certificates.\n * @param region The AWS region where the certificate is needed.\n * @param certName The name of the certificate (api, app, or storage).\n * @returns The ARN of the certificate or placeholder if a new certificate is needed.\n */\nasync function processCert(\n config: MedplumInfraConfig,\n allCerts: CertificateSummary[],\n region: string,\n certName: 'api' | 'app' | 'storage'\n): Promise<string> {\n const domainName = config[getDomainSetting(certName)];\n const existingCert = allCerts.find((cert) => cert.CertificateArn?.includes(region) && cert.DomainName === domainName);\n if (existingCert) {\n print(`Found existing certificate for \"${domainName}\" in \"${region}.`);\n return existingCert.CertificateArn as string;\n }\n\n print(`No existing certificate found for \"${domainName}\" in \"${region}.`);\n if (!(await yesOrNo('Do you want to request a new certificate?'))) {\n print(`Please add your certificate ARN to the config file in the \"${getDomainCertSetting(certName)}\" setting.`);\n return 'TODO';\n }\n\n const arn = await requestCert(region, domainName);\n print('Certificate ARN: ' + arn);\n return arn;\n}\n\n/**\n * Requests an AWS Certificate.\n * @param region The AWS region.\n * @param domain The domain name.\n * @returns The AWS Certificate ARN on success, or undefined on failure.\n */\nasync function requestCert(region: string, domain: string): Promise<string> {\n try {\n const validationMethod = await choose(\n 'Validate certificate using DNS or email validation?',\n ['dns', 'email'],\n 'dns'\n );\n const client = new ACMClient({ region });\n const command = new RequestCertificateCommand({\n DomainName: domain,\n ValidationMethod: validationMethod.toUpperCase(),\n });\n const response = await client.send(command);\n return response.CertificateArn as string;\n } catch (err) {\n console.log('Error: Unable to request certificate', (err as Error).message);\n return 'TODO';\n }\n}\n\n/**\n * Generates an AWS CloudFront signing key.\n *\n * Requirements:\n *\n * 1. It must be an SSH-2 RSA key pair.\n * 2. It must be in base64-encoded PEM format.\n * 3. It must be a 2048-bit key pair.\n *\n * See: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html#private-content-creating-cloudfront-key-pairs\n *\n * @returns A new signing key.\n */\nfunction generateSigningKey(): { publicKey: string; privateKey: string; passphrase: string } {\n const passphrase = randomUUID();\n const signingKey = generateKeyPairSync('rsa', {\n modulusLength: 2048,\n publicKeyEncoding: {\n type: 'spki',\n format: 'pem',\n },\n privateKeyEncoding: {\n type: 'pkcs1',\n format: 'pem',\n cipher: 'aes-256-cbc',\n passphrase,\n },\n });\n return {\n publicKey: signingKey.publicKey,\n privateKey: signingKey.privateKey,\n passphrase,\n };\n}\n\n/**\n * Writes a parameter to AWS Parameter Store.\n * @param region The AWS region.\n * @param key The parameter key.\n * @param value The parameter value.\n */\nasync function writeParameter(region: string, key: string, value: string): Promise<void> {\n const client = new SSMClient({ region });\n const command = new PutParameterCommand({\n Name: key,\n Value: value,\n Type: 'SecureString',\n Overwrite: true,\n });\n await client.send(command);\n}\n\n/**\n * Writes a collection of parameters to AWS Parameter Store.\n * @param region The AWS region.\n * @param prefix The AWS Parameter Store prefix.\n * @param params The parameters to write.\n */\nasync function writeParameters(region: string, prefix: string, params: Record<string, string | number>): Promise<void> {\n for (const [key, value] of Object.entries(params)) {\n const valueStr = value.toString();\n if (valueStr) {\n await writeParameter(region, prefix + key, valueStr);\n }\n }\n}\n\nif (require.main === module) {\n main(readline.createInterface({ input: process.stdin, output: process.stdout }))\n .then(() => process.exit(0))\n .catch((err) => {\n console.error((err as Error).message);\n process.exit(1);\n });\n}\n"],"names":["existsSync","writeFileSync","resolve","STSClient","GetCallerIdentityCommand","ACMClient","ListCertificatesCommand","RequestCertificateCommand","randomUUID","generateKeyPairSync","SSMClient","PutParameterCommand"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medplum/cdk",
3
- "version": "2.0.11",
3
+ "version": "2.0.13",
4
4
  "description": "Medplum CDK Infra as Code",
5
5
  "author": "Medplum <hello@medplum.com>",
6
6
  "license": "Apache-2.0",
@@ -18,14 +18,14 @@
18
18
  "test": "jest --runInBand"
19
19
  },
20
20
  "dependencies": {
21
- "@aws-sdk/client-acm": "3.287.0",
22
- "@aws-sdk/client-ssm": "3.287.0",
23
- "@aws-sdk/client-sts": "3.287.0",
24
- "aws-cdk-lib": "2.68.0",
25
- "cdk": "2.68.0",
26
- "cdk-nag": "2.22.30",
27
- "cdk-serverless-clamscan": "2.4.195",
28
- "constructs": "10.1.272"
21
+ "@aws-sdk/client-acm": "3.299.0",
22
+ "@aws-sdk/client-ssm": "3.299.0",
23
+ "@aws-sdk/client-sts": "3.299.0",
24
+ "aws-cdk-lib": "2.70.0",
25
+ "cdk": "2.70.0",
26
+ "cdk-nag": "2.23.5",
27
+ "cdk-serverless-clamscan": "2.4.207",
28
+ "constructs": "10.1.291"
29
29
  },
30
30
  "bin": {
31
31
  "medplum-cdk-init": "./dist/cjs/init.cjs"
package/src/frontend.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  } from 'aws-cdk-lib';
12
12
  import { Construct } from 'constructs';
13
13
  import { MedplumInfraConfig } from './config';
14
+ import { grantBucketAccessToOriginAccessIdentity } from './oai';
14
15
  import { awsManagedRules } from './waf';
15
16
 
16
17
  /**
@@ -115,7 +116,7 @@ export class FrontEnd extends Construct {
115
116
 
116
117
  // Origin access identity
117
118
  const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {});
118
- appBucket.grantRead(originAccessIdentity);
119
+ grantBucketAccessToOriginAccessIdentity(this, appBucket, originAccessIdentity);
119
120
 
120
121
  // CloudFront distribution
121
122
  const distribution = new cloudfront.Distribution(this, 'AppDistribution', {
package/src/init.test.ts CHANGED
@@ -363,6 +363,65 @@ test('Existing SSL certificates', async () => {
363
363
  unlinkSync(filename);
364
364
  });
365
365
 
366
+ test('Handle empty support email', async () => {
367
+ const filename = `test-${randomUUID()}.json`;
368
+
369
+ await main(
370
+ mockReadline(
371
+ 'foo',
372
+ filename,
373
+ 'us-east-1',
374
+ 'account-123',
375
+ 'TestStack',
376
+ 'test.example.com',
377
+ '', // Empty support email -- user will have to set manually later, but don't crash
378
+ '', // default API domain
379
+ '', // default app domain
380
+ '', // default storage domain
381
+ '', // default storage bucket
382
+ '', // default availability zones
383
+ 'y', // Yes, create a database
384
+ '', // default database instances
385
+ '', // default server instances
386
+ '', // default server memory
387
+ '', // default server cpu
388
+ '', // default server image
389
+ 'y', // Yes, request api certificate
390
+ '', // default DNS validation
391
+ 'y', // Yes, request app certificate
392
+ '', // default DNS validation
393
+ 'y', // Yes, request storage certificate
394
+ '', // default DNS validation
395
+ 'y' // Yes, write to Parameter Store
396
+ )
397
+ );
398
+
399
+ const config = JSON.parse(readFileSync(filename, 'utf8'));
400
+ expect(config).toMatchObject({
401
+ apiPort: 8103,
402
+ name: 'foo',
403
+ region: 'us-east-1',
404
+ accountNumber: 'account-123',
405
+ stackName: 'TestStack',
406
+ domainName: 'test.example.com',
407
+ apiDomainName: 'api.test.example.com',
408
+ appDomainName: 'app.test.example.com',
409
+ storageDomainName: 'storage.test.example.com',
410
+ storageBucketName: 'medplum-foo-storage',
411
+ maxAzs: 2,
412
+ rdsInstances: 1,
413
+ desiredServerCount: 1,
414
+ serverMemory: 512,
415
+ serverCpu: 256,
416
+ serverImage: 'medplum/medplum-server:latest',
417
+ storagePublicKey: expect.stringContaining('-----BEGIN PUBLIC KEY-----'),
418
+ apiSslCertArn: 'arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012',
419
+ appSslCertArn: 'arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012',
420
+ storageSslCertArn: 'arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012',
421
+ });
422
+ unlinkSync(filename);
423
+ });
424
+
366
425
  function mockReadline(...answers: string[]): readline.Interface {
367
426
  const result = { write: jest.fn(), question: jest.fn() };
368
427
  const debug = false;
package/src/init.ts CHANGED
@@ -242,8 +242,13 @@ export async function main(t: readline.Interface): Promise<void> {
242
242
  print('You can now proceed to deploying the Medplum infrastructure with CDK.');
243
243
  print('Run:');
244
244
  print('');
245
+ print(` npx cdk bootstrap -c config=${configFileName}`);
245
246
  print(` npx cdk synth -c config=${configFileName}`);
246
- print(` npx cdk deploy -c config=${configFileName}`);
247
+ if (config.region === 'us-east-1') {
248
+ print(` npx cdk deploy -c config=${configFileName}`);
249
+ } else {
250
+ print(` npx cdk deploy -c config=${configFileName} --all`);
251
+ }
247
252
  print('');
248
253
  print('See Medplum documentation for more information:');
249
254
  print('');
@@ -491,7 +496,10 @@ async function writeParameter(region: string, key: string, value: string): Promi
491
496
  */
492
497
  async function writeParameters(region: string, prefix: string, params: Record<string, string | number>): Promise<void> {
493
498
  for (const [key, value] of Object.entries(params)) {
494
- await writeParameter(region, prefix + key, value.toString());
499
+ const valueStr = value.toString();
500
+ if (valueStr) {
501
+ await writeParameter(region, prefix + key, valueStr);
502
+ }
495
503
  }
496
504
  }
497
505
 
package/src/oai.ts ADDED
@@ -0,0 +1,39 @@
1
+ import { aws_cloudfront as cloudfront, aws_iam as iam, aws_s3 as s3 } from 'aws-cdk-lib';
2
+ import { Construct } from 'constructs';
3
+
4
+ /**
5
+ * Grants S3 bucket read access to the CloudFront Origin Access Identity (OAI).
6
+ *
7
+ * Under normal circumstances, where CDK creates both the S3 bucket and the OAI,
8
+ * you can achieve this same behavior by simply calling:
9
+ *
10
+ * bucket.grantRead(identity);
11
+ *
12
+ * However, if importing an S3 bucket via `s3.Bucket.fromBucketAttributes()`, that does not work.
13
+ *
14
+ * See: https://stackoverflow.com/a/60917015
15
+ *
16
+ * @param scope The CDK construct scope.
17
+ * @param bucket The S3 bucket.
18
+ * @param identity The CloudFront Origin Access Identity.
19
+ */
20
+ export function grantBucketAccessToOriginAccessIdentity(
21
+ scope: Construct,
22
+ bucket: s3.IBucket,
23
+ identity: cloudfront.OriginAccessIdentity
24
+ ): void {
25
+ const policyStatement = new iam.PolicyStatement();
26
+ policyStatement.addActions('s3:GetObject*');
27
+ policyStatement.addActions('s3:GetBucket*');
28
+ policyStatement.addActions('s3:List*');
29
+ policyStatement.addResources(bucket.bucketArn);
30
+ policyStatement.addResources(`${bucket.bucketArn}/*`);
31
+ policyStatement.addCanonicalUserPrincipal(identity.cloudFrontOriginAccessIdentityS3CanonicalUserId);
32
+
33
+ let policy = bucket.policy;
34
+ if (!policy) {
35
+ policy = new s3.BucketPolicy(scope, 'Policy', { bucket });
36
+ }
37
+
38
+ policy.document.addStatements(policyStatement);
39
+ }
package/src/storage.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  import { ServerlessClamscan } from 'cdk-serverless-clamscan';
12
12
  import { Construct } from 'constructs';
13
13
  import { MedplumInfraConfig } from './config';
14
+ import { grantBucketAccessToOriginAccessIdentity } from './oai';
14
15
  import { awsManagedRules } from './waf';
15
16
 
16
17
  /**
@@ -101,7 +102,7 @@ export class Storage extends Construct {
101
102
 
102
103
  // Origin access identity
103
104
  const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {});
104
- storageBucket.grantRead(originAccessIdentity);
105
+ grantBucketAccessToOriginAccessIdentity(this, storageBucket, originAccessIdentity);
105
106
 
106
107
  // CloudFront distribution
107
108
  const distribution = new cloudfront.Distribution(this, 'StorageDistribution', {
@@ -0,0 +1,18 @@
1
+ {
2
+ "apiPort": 8103,
3
+ "region": "us-east-1",
4
+ "name": "foo",
5
+ "accountNumber": "account-123",
6
+ "stackName": "TestStack",
7
+ "domainName": "test.example.com",
8
+ "apiDomainName": "api.test.example.com",
9
+ "appDomainName": "app.test.example.com",
10
+ "storageDomainName": "storage.test.example.com",
11
+ "storageBucketName": "medplum-foo-storage",
12
+ "maxAzs": 2,
13
+ "rdsInstances": 1,
14
+ "desiredServerCount": 1,
15
+ "serverMemory": 512,
16
+ "serverCpu": 256,
17
+ "serverImage": "medplum/medplum-server:latest"
18
+ }