AWSTemplateFormatVersion: '2010-09-09' Description: (WWPS-AMC-ORTHANC) CloudFormation template to deploy Orthanc on AWS Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Namespace Parameters: - Namespace - Label: default: Network Infrastructure Parameters: - ParameterVPCId - PublicSubnet1Id - PublicSubnet2Id - PrivateSubnet1Id - PrivateSubnet2Id - SecurityGroupIngressCIDR - Label: default: TLS and Encryption (Optional) Parameters: - ExistingHostedZoneId - FullDomainName - Label: default: ECS Parameters: - ContainerOnEC2 - UseExistingLogBucket - ExistingLogBucketName - ContainerImageUrl - KeyName - InstanceType - NumberofEC2Instances - NumberofContainers - OrthancContainerCPU - OrthancContainerMemory - Label: default: EFS Persistent Storage Parameters: - EnableEFSBackup - EFSStorageInfrequentAcessAfter - Label: default: RDS Database Parameters: - RDSDBInstanceClass - RDSDBStorageType - RDSDBAllocatedStorage - RDSDBUserName - RDSDBBackupRetentionDays - IsDBMultiAZ - Label: default: Orthanc Parameters: - OrthancUser - OrthancPassword - EnableDICOMWebPlugin - EnableOrthancWebViewerPlugin - EnableStoneWebViewerPlugin Parameters: Namespace: Description: A string of 4-20 lowercase letters and digits, starting with a letter. Included in resource names, allowing multiple deployments. Default: orthanc Type: String AllowedPattern: "^[a-z]{1}[a-z0-9]{3,19}$" ConstraintDescription: Between 4-20 letters and digits, starting with a letter ContainerOnEC2: Type: String Default: Y Description: Will the containers be hosted on ECS EC2? Y = EC2, N = Fargate AllowedValues: [Y, N] ExistingHostedZoneId: Description: (Optional) Existing HostedZone for Registered Domain used for ACM Certificate. Leave blank to deploy solution without TLS. Type: String FullDomainName: Description: (Optional) Fully Qualified Domain Nanme for ACM Certificate. Leave blank to deploy solution without TLS. Type: String ContainerImageUrl: Description: The url of a docker image that contains the application process that will handle the traffic for this service Default: osimis/orthanc Type: String PublicSubnet1Id: Description: PublicSubnetId, for Availability Zone 1 in the region in your VPC Type: AWS::EC2::Subnet::Id PublicSubnet2Id: Description: PublicSubnetId, for Availability Zone 2 in the region in your VPC Type: AWS::EC2::Subnet::Id PrivateSubnet1Id: Description: PrivateSubnetId, for Availability Zone 1 in the region in your VPC Type: AWS::EC2::Subnet::Id PrivateSubnet2Id: Description: PrivateSubnetId, for Availability Zone 2 in the region in your VPC Type: AWS::EC2::Subnet::Id ParameterVPCId: Description: ID of the VPC Type: AWS::EC2::VPC::Id SecurityGroupIngressCIDR: Type: String Default: '0.0.0.0/0' UseExistingLogBucket: Description: Use an existing log bucket? Type: String AllowedValues: ["Yes", "No"] Default: "No" ExistingLogBucketName: Type: String Default: existing-log-bucket-name Description: (Optional, Only if 'No' answered above) Name of bucket to create for logging (must be S3 compatible) ConstraintDescription: Must be a bucket name respecting S3 bucket naming conventions KeyName: Type: String AllowedPattern: "^.{1,255}$" Description: (Optional, only for EC2 hosts) Name of an existing EC2 KeyPair to enable SSH access to the ECS instances. For Fargate, use 'fargate'. InstanceType: Description: (Optional, only for EC2 hosts) EC2 instance type. Type: String Default: m4.large AllowedValues: [ '', t2.large, t2.xlarge, t2.2xlarge, t3.large, t3.xlarge, t3.2xlarge, m4.large, m4.xlarge, m4.2xlarge, m4.4xlarge, m4.8xlarge, m5.large, m5.xlarge, m5.2xlarge, m5.4xlarge, m5.8xlarge, c4.large, c4.xlarge, c4.2xlarge, c4.4xlarge, c4.8xlarge, c5.large, c5.xlarge, c5.2xlarge, c5.4xlarge, c5.8xlarge, r4.large, r4.xlarge, r4.2xlarge, r4.4xlarge, r4.8xlarge, r5.large, r5.xlarge, r5.2xlarge, r5.4xlarge, r5.8xlarge ] ConstraintDescription: 'Please choose a valid instance type: https://aws.amazon.com/ec2/instance-types/' NumberofEC2Instances: Description: (Optional, only for EC2 hosts) Number of EC2 instance to run container in ECS cluster. Type: Number Default: 1 NumberofContainers: Description: Number of docker containers to run tasks in ECS cluster Type: Number Default: 1 OrthancContainerCPU: Description: The number of CPU units the Amazon ECS container agent will reserve for the container. Type: Number Default: 2048 AllowedValues: [256, 512, 1024, 2048, 4096] OrthancContainerMemory: Description: The amount (in MiB) of memory to present to the container. . Memory should be at least two times of vCPU unit according to documentation. Type: Number Default: 4096 AllowedValues: [512, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 9216, 10240, 11264, 12288, 13312, 14336, 15360, 16384, 30720] RDSDBInstanceClass: Default: db.t3.medium Description: Database Instance Class. db.r5 instance classes are supported for Aurora PostgreSQL 10.6 or later. db.t3.medium instance class is supported for Aurora PostgreSQL 10.7 or later. Type: String AllowedValues: [ db.t3.medium, db.r4.large, db.r4.xlarge, db.r4.2xlarge, db.r4.4xlarge, db.r4.8xlarge, db.r4.16xlarge, db.r5.large, db.r5.xlarge, db.r5.2xlarge, db.r5.4xlarge, db.r5.8xlarge, db.r5.12xlarge, db.r5.16xlarge, db.r5.24xlarge ] RDSDBStorageType: Type: String Default: gp2 RDSDBAllocatedStorage: Type: Number Default: 20 OrthancUser: Type: String Description: Orthanc Login User OrthancPassword: Type: String Description: Orthanc Login Password NoEcho: true IsDBMultiAZ: Type: String Default: False RDSDBBackupRetentionDays: Type: Number Default: 30 Description: The number of days for which automated backups are retained. Setting this parameter to a positive number (from 1 to 35) enables backups. Setting this parameter to 0 disables automated backups. RDSDBUserName: Type: String Description: RDS PostgreSQL database username Default: postgres NoEcho: true EnableEFSBackup: Type: String Description: whether enable EFS backup or not. EFS backup has extra associated cost. Default: ENABLED AllowedValues: [ENABLED, DISABLED] EFSStorageInfrequentAcessAfter: Type: String Description: A value that describes the period of time that a file is not accessed, after which it transitions to the IA storage class. Default: AFTER_90_DAYS AllowedValues: [AFTER_14_DAYS, AFTER_30_DAYS, AFTER_60_DAYS, AFTER_7_DAYS, AFTER_90_DAYS] EnableDICOMWebPlugin: Type: String Description: Enable DICOMWeb plugin Default: Y AllowedValues: [Y, N] EnableOrthancWebViewerPlugin: Type: String Description: Enable Orthanc Web Viewer plugin Default: N AllowedValues: [Y, N] EnableStoneWebViewerPlugin: Type: String Description: Enable Stone Web Viewer plugin Default: N AllowedValues: [Y, N] Conditions: runContainerOnEC2: !Equals [!Ref ContainerOnEC2, Y] CreateBucket: !Equals [!Ref UseExistingLogBucket, "No"] NoDomain: !Equals [!Ref FullDomainName, ""] UseDomain: !Not [Condition: NoDomain] DoEnableDICOMWebPlugin: !Equals [!Ref EnableDICOMWebPlugin, Y] DoEnableOrthancWebViewerPlugin: !Equals [!Ref EnableOrthancWebViewerPlugin, Y] DoEnableStoneWebViewerPlugin: !Equals [!Ref EnableStoneWebViewerPlugin, Y] Mappings: AWSRegionToAMI: us-east-1: AMIID: ami-0c1f575380708aa63 us-east-2: AMIID: ami-015a2afe7e1a8af56 us-west-1: AMIID: ami-032a827d612b78a50 us-west-2: AMIID: ami-05edb14e89a5b98f3 ap-northeast-1: AMIID: ami-06ee72c3360fd7fad ap-northeast-2: AMIID: ami-0cfc5eb79eceeeec9 ap-south-1: AMIID: ami-078902ae8103daac8 ap-southeast-1: AMIID: ami-09dd721a797640468 ap-southeast-2: AMIID: ami-040bd2e2325535b3d ca-central-1: AMIID: ami-0a06b44c462364156 eu-central-1: AMIID: ami-09509e8f8dea8ab83 eu-north-1: AMIID: ami-015b157d082fd4e0d eu-west-1: AMIID: ami-0489c3efb4fe85f5d eu-west-2: AMIID: ami-037dd70536680c11f eu-west-3: AMIID: ami-0182381900083ba64 sa-east-1: AMIID: ami-05313c3a9e9148109 RegionELBAccountIdMap: us-east-1: AccountId: '127311923021' us-west-1: AccountId: '027434742980' us-west-2: AccountId: '797873946194' eu-west-1: AccountId: '156460612806' ap-northeast-1: AccountId: '582318560864' ap-northeast-2: AccountId: '600734575887' ap-southeast-1: AccountId: '114774131450' ap-southeast-2: AccountId: '783225319266' ap-south-1: AccountId: '718504428378' us-east-2: AccountId: '033677994240' sa-east-1: AccountId: '507241528517' cn-north-1: AccountId: '638102146993' eu-central-1: AccountId: '054676820928' Resources: CloudMap: Type: AWS::ServiceDiscovery::PrivateDnsNamespace Properties: Description: Service Map for Docker Compose project orthanconaws Name: !Sub "${Namespace}-orthanconaws.local" Vpc: !Ref ParameterVPCId ECSCluster: Type: AWS::ECS::Cluster Properties: ClusterName: !Sub "${Namespace}-orthanconaws-cluster" ClusterSettings: - Name: containerInsights Value: enabled Tags: - Key: project Value: !Sub "${Namespace}-orthanconaws" LogGroup: Properties: LogGroupName: !Sub "/docker-compose/${Namespace}-orthanconaws-loggroup" Type: AWS::Logs::LogGroup RDSDatabaseSecret: Type: AWS::SecretsManager::Secret Properties: Name: !Sub "${Namespace}-RDSDatabaseSecret" Description: !Join ['', ['RDS Database Master User Secret ', 'for CloudFormation Stack ', !Ref 'AWS::StackName']] Tags: - Key: project Value: !Sub "${Namespace}-orthanconaws" GenerateSecretString: SecretStringTemplate: !Join ['', ['{"username": "', !Ref RDSDBUserName, '"}']] GenerateStringKey: "password" ExcludeCharacters: '"@/\' PasswordLength: 16 SMSecretRDSDBAttachment: Type: AWS::SecretsManager::SecretTargetAttachment Properties: SecretId: !Ref RDSDatabaseSecret TargetId: !Ref RDSInstance TargetType: AWS::RDS::DBInstance DataBaseSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupName: !Sub "${Namespace}-dbsubnetgroup" DBSubnetGroupDescription: DBSubnetGroup for Aurora RDS instances SubnetIds: - !Ref PrivateSubnet1Id - !Ref PrivateSubnet2Id OrthancRDSSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: OrthancOnAWS RDS Security Group GroupName: !Sub "${Namespace}-OrthancRDSSecurityGroup" SecurityGroupIngress: - CidrIp: '0.0.0.0/0' Description: postgres:5432/tcp FromPort: 5432 IpProtocol: TCP ToPort: 5432 Tags: - Key: project Value: !Sub "${Namespace}-orthanconaws" - Key: com.docker.compose.network Value: rds VpcId: !Ref ParameterVPCId RDSInstance: Type: AWS::RDS::DBInstance DeletionPolicy: Snapshot UpdateReplacePolicy: Snapshot Properties: DBInstanceIdentifier: !Sub "${Namespace}-orthanc-instance" DBName: !Sub "${Namespace}OrthancDB" DBInstanceClass: !Ref RDSDBInstanceClass StorageType: !Ref RDSDBStorageType StorageEncrypted: True AllocatedStorage: !Ref RDSDBAllocatedStorage Engine: postgres EngineVersion: '11' MasterUsername: !Ref RDSDBUserName MasterUserPassword: !Join ['', ['{{resolve:secretsmanager:', !Ref RDSDatabaseSecret, ':SecretString:password}}' ]] PubliclyAccessible: False MultiAZ: !Ref IsDBMultiAZ Tags: - Key: project Value: !Sub "${Namespace}-orthanconaws" VPCSecurityGroups: - !Ref OrthancRDSSecurityGroup DBSubnetGroupName: !Ref DataBaseSubnetGroup BackupRetentionPeriod: !Ref RDSDBBackupRetentionDays FileSystem: Type: AWS::EFS::FileSystem DeletionPolicy: Snapshot Properties: Encrypted: True BackupPolicy: Status: !Ref EnableEFSBackup LifecyclePolicies: - TransitionToIA: !Ref EFSStorageInfrequentAcessAfter FileSystemTags: - Key: Name Value: !Sub "${Namespace}-orthanc-efs" MountTarget1: Type: AWS::EFS::MountTarget Properties: FileSystemId: !Ref FileSystem SubnetId: !Ref PrivateSubnet1Id SecurityGroups: - !Ref OrthancEFSNetwork MountTarget2: Type: AWS::EFS::MountTarget Properties: FileSystemId: !Ref FileSystem SubnetId: !Ref PrivateSubnet2Id SecurityGroups: - !Ref OrthancEFSNetwork NFSAccessPoint: Type: AWS::EFS::AccessPoint Properties: FileSystemId: !Ref FileSystem PosixUser: Gid: "0" Uid: "0" RootDirectory: Path: "/" OrthancServerNetwork: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: OrthancOnAWS default Security Group GroupName: !Sub "${Namespace}-OrthancServerNetwork" SecurityGroupIngress: - CidrIp: !Ref SecurityGroupIngressCIDR Description: orthanc:4242/tcp IpProtocol: TCP FromPort: 4242 ToPort: 4242 - CidrIp: !Ref SecurityGroupIngressCIDR Description: orthanc:8042/tcp IpProtocol: TCP FromPort: 8042 ToPort: 8042 - CidrIp: !Ref SecurityGroupIngressCIDR Description: ssh:22/tcp IpProtocol: TCP FromPort: 22 ToPort: 22 Tags: - Key: project Value: !Sub "${Namespace}-orthanconaws" - Key: com.docker.compose.network Value: default VpcId: !Ref ParameterVPCId OrthancServerNetworkIngress: Type: AWS::EC2::SecurityGroupIngress Properties: Description: Allow communication within network default GroupId: !Ref OrthancServerNetwork IpProtocol: '-1' SourceSecurityGroupId: !Ref OrthancServerNetwork OrthancEFSNetwork: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Orthanc EFS Security Group GroupName: !Sub "${Namespace}-OrthancEFSNetwork" SecurityGroupIngress: - SourceSecurityGroupId: !Ref OrthancServerNetwork Description: efs:2049/tcp FromPort: 2049 IpProtocol: TCP ToPort: 2049 Tags: - Key: project Value: !Sub "${Namespace}-orthanconaws" - Key: network Value: efs VpcId: !Ref ParameterVPCId OrthancLoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Name: !Sub "${Namespace}-lb" Scheme: internet-facing Subnets: - !Ref PublicSubnet1Id - !Ref PublicSubnet2Id Tags: - Key: project Value: !Sub "${Namespace}-orthanconaws" Type: network LoadBalancerAttributes: - Key: access_logs.s3.enabled Value: 'true' - Key: access_logs.s3.bucket Value: !GetAtt LogBucketName.Value LogBucketName: Type: AWS::SSM::Parameter Properties: Type: String Name: !Sub "/orthanconaws/${Namespace}/LogBucketName" Value: !If - CreateBucket - !Sub "${Namespace}-${AWS::AccountId}-${AWS::Region}-logs" - !Ref ExistingLogBucketName # Create log bucket and related access policy only if not using an existing log bucket NewLogBucket: Type: AWS::S3::Bucket Condition: CreateBucket Properties: AccessControl: LogDeliveryWrite BucketName: !GetAtt LogBucketName.Value BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 VersioningConfiguration: Status: Enabled # Create policy allowing logging to new log bucket, if creating that bucket LogBucketAccessPolicy: Type: AWS::S3::BucketPolicy Condition: CreateBucket Properties: Bucket: !Ref NewLogBucket PolicyDocument: Statement: - Action: - 's3:PutObject' Effect: Allow Resource: !Sub '${NewLogBucket.Arn}/*' Principal: AWS: !FindInMap [RegionELBAccountIdMap, !Ref 'AWS::Region', AccountId] - Action: - 's3:PutObject' Effect: Allow Resource: !Sub '${NewLogBucket.Arn}/*' Principal: Service: delivery.logs.amazonaws.com Condition: StringEquals: s3:x-amz-acl: bucket-owner-full-control - Action: - 's3:GetBucketAcl' Effect: Allow Resource: !Sub '${NewLogBucket.Arn}' Principal: Service: delivery.logs.amazonaws.com HostedZoneRecord: Condition: UseDomain DependsOn: - LoadBalancerSSLCert Type: AWS::Route53::RecordSet Properties: HostedZoneId: !Ref ExistingHostedZoneId Type: A Name: !Ref FullDomainName AliasTarget: DNSName: !GetAtt 'OrthancLoadBalancer.DNSName' HostedZoneId: !GetAtt 'OrthancLoadBalancer.CanonicalHostedZoneID' LoadBalancerSSLCert: Condition: UseDomain Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref FullDomainName DomainValidationOptions: - DomainName: !Ref FullDomainName HostedZoneId: !Ref ExistingHostedZoneId ValidationMethod: DNS CertificateTransparencyLoggingPreference: DISABLED OrthancService: Type: AWS::ECS::Service Properties: Cluster: !Ref ECSCluster DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 100 DeploymentController: Type: ECS DesiredCount: !Ref NumberofContainers LaunchType: !If [runContainerOnEC2, 'EC2', 'FARGATE'] LoadBalancers: - ContainerName: !Sub "${Namespace}-orthanc-container" ContainerPort: 8042 TargetGroupArn: !Ref OrthancTCP8042TargetGroup - ContainerName: !Sub "${Namespace}-orthanc-container" ContainerPort: 4242 TargetGroupArn: !Ref OrthancTCP4242TargetGroup NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: !If [runContainerOnEC2, 'DISABLED', 'ENABLED'] SecurityGroups: - !Ref OrthancServerNetwork Subnets: - !Ref PrivateSubnet1Id - !Ref PrivateSubnet2Id PropagateTags: SERVICE SchedulingStrategy: REPLICA ServiceName: !Sub "${Namespace}-orthanc-service" ServiceRegistries: - RegistryArn: !GetAtt OrthancServiceDiscoveryEntry.Arn Tags: - Key: project Value: !Sub "${Namespace}-orthanconaws" - Key: com.docker.compose.service Value: orthanc - Key: depends-on-8042 Value: !If - UseDomain - !GetAtt OrthancTCP8042HTTPSListener.ListenerArn - !GetAtt OrthancTCP8042HTTPListener.ListenerArn - Key: depends-on-4242 Value: !GetAtt OrthancTCP4242Listener.ListenerArn TaskDefinition: !Ref OrthancTaskDefinition OrthancServiceDiscoveryEntry: Type: AWS::ServiceDiscovery::Service Properties: Description: '"orthanc" service discovery entry in Cloud Map' DnsConfig: DnsRecords: - TTL: 60 Type: A RoutingPolicy: MULTIVALUE Name: !Sub "${Namespace}-orthanc-sdservice" NamespaceId: !Ref CloudMap OrthancTCP8042HTTPSListener: Condition: UseDomain Type: AWS::ElasticLoadBalancingV2::Listener Properties: Certificates: - CertificateArn: !Ref LoadBalancerSSLCert DefaultActions: - ForwardConfig: TargetGroups: - TargetGroupArn: !Ref OrthancTCP8042TargetGroup Type: forward LoadBalancerArn: !Ref OrthancLoadBalancer Port: 8042 Protocol: TLS OrthancTCP8042HTTPListener: Condition: NoDomain Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - ForwardConfig: TargetGroups: - TargetGroupArn: !Ref OrthancTCP8042TargetGroup Type: forward LoadBalancerArn: !Ref OrthancLoadBalancer Port: 8042 Protocol: TCP OrthancTCP8042TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Port: 8042 Protocol: TCP Tags: - Key: project Value: !Sub "${Namespace}-orthanconaws" TargetType: ip VpcId: !Ref ParameterVPCId OrthancTCP4242Listener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: Port: 4242 Protocol: TCP DefaultActions: - ForwardConfig: TargetGroups: - TargetGroupArn: !Ref OrthancTCP4242TargetGroup Type: forward LoadBalancerArn: !Ref OrthancLoadBalancer OrthancTCP4242TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Port: 4242 HealthCheckPort: '8042' Protocol: TCP Tags: - Key: project Value: !Sub "${Namespace}-orthanconaws" TargetType: ip VpcId: !Ref ParameterVPCId OrthancTaskDefinition: Type: AWS::ECS::TaskDefinition DependsOn: - RDSInstance Properties: ContainerDefinitions: - Environment: - Name: ORTHANC__POSTGRESQL__HOST Value: !GetAtt 'RDSInstance.Endpoint.Address' - Name: ORTHANC__POSTGRESQL__PORT Value: !GetAtt 'RDSInstance.Endpoint.Port' - Name: ORTHANC__POSTGRESQL__USERNAME Value: !Ref RDSDBUserName - Name: ORTHANC__POSTGRESQL__PASSWORD Value: !Join ['', ['{{resolve:secretsmanager:', !Ref RDSDatabaseSecret, ':SecretString:password}}' ]] - Name: LOCALDOMAIN Value: !Join - '' - - !Ref AWS::Region - .compute.internal - ' ' - !Sub "${Namespace}-orthanconaws.local" - Name: ORTHANC__REGISTERED_USERS Value: !Sub '{"${OrthancUser}": "${OrthancPassword}"}' - Name: DICOM_WEB_PLUGIN_ENABLED Value: !If [ DoEnableDICOMWebPlugin, 'true', 'false' ] - Name: ORTHANC_WEB_VIEWER_PLUGIN_ENABLED Value: !If [ DoEnableOrthancWebViewerPlugin, 'true', 'false' ] - Name: ORTHANC_STONE_VIEWER_PLUGIN_ENABLED Value: !If [ DoEnableStoneWebViewerPlugin, 'true', 'false' ] Essential: true Image: !Ref ContainerImageUrl LinuxParameters: {} LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref LogGroup awslogs-region: !Ref 'AWS::Region' awslogs-stream-prefix: orthanconaws Name: !Sub "${Namespace}-orthanc-container" MountPoints: - ContainerPath: /var/lib/orthanc/db SourceVolume: !Sub "${Namespace}-orthanc-efs" PortMappings: - ContainerPort: 4242 HostPort: 4242 Protocol: tcp - ContainerPort: 8042 HostPort: 8042 Protocol: tcp Volumes: - Name: !Sub "${Namespace}-orthanc-efs" EFSVolumeConfiguration: FilesystemId: !GetAtt 'FileSystem.FileSystemId' TransitEncryption: ENABLED AuthorizationConfig: AccessPointId: !Ref NFSAccessPoint IAM: ENABLED Cpu: !Ref OrthancContainerCPU Memory: !Ref OrthancContainerMemory ExecutionRoleArn: !Ref OrthancTaskExecutionRole TaskRoleArn: !Ref OrthancTaskRole Family: orthanconaws-orthanc NetworkMode: awsvpc RequiresCompatibilities: - !If [runContainerOnEC2, 'EC2', 'FARGATE'] OrthancTaskExecutionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub "${Namespace}-orthanctaskexecutionrole" AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: ecs-tasks.amazonaws.com Version: '2012-10-17' ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly OrthancTaskRole: Type: AWS::IAM::Role Properties: RoleName: !Sub "${Namespace}-orthanctaskrole" AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: ecs-tasks.amazonaws.com Version: '2012-10-17' ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonElasticFileSystemFullAccess ECSAutoScalingGroup: Type: AWS::AutoScaling::AutoScalingGroup Condition: runContainerOnEC2 Properties: AutoScalingGroupName: !Sub "${Namespace}-ecsautoscalinggroup" VPCZoneIdentifier: - !Ref PrivateSubnet1Id - !Ref PrivateSubnet2Id LaunchConfigurationName: !Ref ContainerInstances MinSize: '1' MaxSize: '2' DesiredCapacity: !Ref NumberofEC2Instances Tags: - Key: Name PropagateAtLaunch: true Value: !Sub "${Namespace}-ecs-instance" CreationPolicy: ResourceSignal: Timeout: PT15M UpdatePolicy: AutoScalingReplacingUpdate: WillReplace: true ContainerInstances: Type: AWS::AutoScaling::LaunchConfiguration Condition: runContainerOnEC2 Properties: ImageId: !FindInMap [AWSRegionToAMI, !Ref 'AWS::Region', AMIID] SecurityGroups: - !Ref OrthancServerNetwork InstanceType: !Ref InstanceType IamInstanceProfile: !Ref EC2InstanceProfile KeyName: !If [runContainerOnEC2, !Ref KeyName, !Ref 'AWS::NoValue'] LaunchConfigurationName: !Sub "${Namespace}-orthanc-launchconfig" UserData: Fn::Base64: !Sub | #!/bin/bash -xe echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config yum install -y aws-cfn-bootstrap bzip2 unzip git jq wget telnet java-11-amazon-corretto-headless /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region} amazon-linux-extras install postgresql11 -y curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip ./aws/install wget https://sourceforge.net/projects/dcm4che/files/dcm4che3/5.23.2/dcm4che-5.23.2-bin.zip -P /tmp # Configure and enable Amazon Cloudwatch Agent yum install -y amazon-cloudwatch-agent instance_id=`curl -s http://169.254.169.254/latest/meta-data/instance-id/` cat < /opt/aws/amazon-cloudwatch-agent/etc/cloudwatch-config.json { "agent": { "logfile": "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log" }, "logs": { "logs_collected": { "files": { "collect_list": [ { "file_path": "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log", "log_group_name": "/aws/ecs/container-instance/${Namespace}", "log_stream_name": "/aws/ecs/container-instance/${Namespace}/$instance_id/amazon-cloudwatch-agent.log" }, { "file_path": "/var/log/cloud-init.log", "log_group_name": "/aws/ecs/container-instance/${Namespace}", "log_stream_name": "/aws/ecs/container-instance/${Namespace}/$instance_id/cloud-init.log" }, { "file_path": "/var/log/cloud-init-output.log", "log_group_name": "/aws/ecs/container-instance/${Namespace}", "log_stream_name": "/aws/ecs/container-instance/${Namespace}/$instance_id/cloud-init-output.log" }, { "file_path": "/var/log/ecs/ecs-init.log", "log_group_name": "/aws/ecs/container-instance/${Namespace}", "log_stream_name": "/aws/ecs/container-instance/${Namespace}/$instance_id/ecs-init.log" }, { "file_path": "/var/log/ecs/ecs-agent.log", "log_group_name": "/aws/ecs/container-instance/${Namespace}", "log_stream_name": "/aws/ecs/container-instance/${Namespace}/$instance_id/ecs-agent.log" } ] } } } } EOF /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/etc/cloudwatch-config.json systemctl enable amazon-cloudwatch-agent systemctl start amazon-cloudwatch-agent EC2Role: Type: AWS::IAM::Role Condition: runContainerOnEC2 Properties: RoleName: !Sub "${Namespace}-ec2role" AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore Policies: - PolicyName: !Sub "${Namespace}-ecs-instance-logging-policy" PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Resource: "arn:aws:logs:*:*:*" Action: - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" - "logs:DescribeLogStreams" EC2InstanceProfile: Type: AWS::IAM::InstanceProfile Condition: runContainerOnEC2 Properties: InstanceProfileName: !Sub "${Namespace}-instanceprofile" Path: / Roles: [!Ref EC2Role] Outputs: OrthancEndpoint: Description: The URL of the reverse proxy for Orthanc web server endpoint Value: !If - UseDomain - !Sub "https://${FullDomainName}:8042" - !Sub "http://${OrthancLoadBalancer.DNSName}:8042" LogBucket: Description: Log bucket name Value: !GetAtt LogBucketName.Value