UNPKG

@cumulus/deployment

Version:
1,247 lines (1,155 loc) 35.4 kB
AWSTemplateFormatVersion: '2010-09-09' Description: 'stack: {{stackName}} | deployed by Kes' Parameters: CmrPassword: Type: String Description: 'Password used to publish CMR records. This is encrypted by Custom::Cumulus' Default: "" NoEcho: true DockerPassword: Type: String Description: 'Password used to access a private docker repository (not required)' Default: "" NoEcho: true DockerEmail: Type: String Description: 'Email used to login to a private docker repository (not required)' Default: "" NoEcho: true LaunchpadPassphrase: Type: String Description: 'Passphrase of the Launchpad PIK certificate. This is encrypted by Custom::Cumulus' Default: "" NoEcho: true {{#if es.name}} {{es.name}}DomainEndpoint: Type: String Description: 'ElasticSearch domain endpoint' NoEcho: true {{/if}} {{#each dynamos}} {{@key}}DynamoDBStreamArn: Type: String Description: 'DynamoDBStreamArn for {{@key}}' NoEcho: true {{/each}} Resources: ################################################# # BEGIN ################################################# {{#each nested_templates}} {{#ifEquals @key "WorkflowLambdaVersions"}} {{#ifEquals ../useWorkflowLambdaVersions true}} {{@key}}NestedStack: Type: "AWS::CloudFormation::Stack" Properties: Parameters: {{#each ../workflowLambdas}} {{@key}}LambdaFunction: Ref: {{@key}}LambdaFunction {{/each}} TemplateURL: {{this.url}} {{/ifEquals}} {{/ifEquals}} {{#ifNotEquals @key "WorkflowLambdaVersions"}} {{#ifDeployApi @key ../deployDistributionApi}} {{@key}}NestedStack: Type: "AWS::CloudFormation::Stack" Properties: Parameters: CmrPassword: Fn::GetAtt: - CumulusCustomResource - CmrPassword LaunchpadPassphrase: Fn::GetAtt: - CumulusCustomResource - LaunchpadPassphrase {{#if ../../es.name}} ElasticSearchDomain: Ref: {{../../es.name}}DomainEndpoint {{/if}} log2elasticsearchLambdaFunctionArn: Fn::GetAtt: - log2elasticsearchLambdaFunction - Arn {{#ifEquals @key "CumulusApiBackend"}} # Only the backend API lambdas need these references. EcsCluster: Ref: CumulusECSCluster AsyncOperationTaskDefinition: Ref: AsyncOperationTaskDefinition BulkDeleteLambdaFunctionArn: Fn::GetAtt: - BulkDeleteLambdaFunction - Arn CreateReconciliationReportLambdaFunctionArn: Fn::GetAtt: - CreateReconciliationReportLambdaFunction - Arn EmsIngestReportLambdaFunctionArn: Fn::GetAtt: - EmsIngestReportLambdaFunction - Arn EmsDistributionReportLambdaFunctionArn: Fn::GetAtt: - EmsDistributionReportLambdaFunction - Arn EmsProductMetadataReportLambdaFunctionArn: Fn::GetAtt: - EmsProductMetadataReportLambdaFunction - Arn messageConsumerLambdaFunctionArn: Fn::GetAtt: - messageConsumerLambdaFunction - Arn ScheduleSFLambdaFunctionArn: Fn::GetAtt: - ScheduleSFLambdaFunction - Arn KinesisInboundEventLoggerLambdaFunctionArn: Fn::GetAtt: - KinesisInboundEventLoggerLambdaFunction - Arn IndexFromDatabaseLambdaFunctionArn: Fn::GetAtt: - IndexFromDatabaseLambdaFunction - Arn BulkOperationLambdaFunctionArn: Fn::GetAtt: - BulkOperationLambdaFunction - Arn # The backend API needs a reference to the distributionRestApi so # it can construct values for the DISTRIBUTION_REDIRECT_ENDPOINT # environment variable. The /granules endpoint uses this variable. {{#ifEquals ../../deployDistributionApi true}} distributionRestApi: Fn::GetAtt: - CumulusApiDistributionNestedStack - Outputs.distributionRestApiResource {{/ifEquals}} {{/ifEquals}} {{# each ../../dynamos}} {{@key}}DynamoDB: {{../../../prefix}}-{{@key}} {{/each}} {{# if ../../vpc }} SecurityGroupId: {{../../vpc.securityGroup}} {{/if}} TemplateURL: {{../this.url}} {{/ifDeployApi}} {{/ifNotEquals}} {{/each}} ################################################# # Nested CloudFormation Templates config BEGIN ################################################# ################################################# # Cumulus Custom Resource BEGIN ################################################# CumulusCustomResource: Type: Custom::Cumulus Properties: ServiceToken: Fn::GetAtt: - CustomBootstrapLambdaFunction - Arn Cmr: Password: Ref: CmrPassword Launchpad: Passphrase: Ref: LaunchpadPassphrase {{# if es.name}} ElasticSearch: host: Ref: {{es.name}}DomainEndpoint version: {{es.elasticSearchMapping}} {{/if}} {{# if dynamos}} DynamoDBTables: {{#each dynamos}} - name: {{../prefix}}-{{@key}} pointInTime: {{this.pointInTime}} {{/each}} {{/if}} Users: table: {{prefix}}-UsersTable records: {{# each users}} - username: {{username}} password: OAuth {{/each}} ################################################# # Cumulus Custom Resource END ################################################# ################################################# # SNS config BEGIN ################################################# {{#each sns}} {{#if this.arn}} {{#each this.subscriptions}} # Subscriptions only {{@../key}}{{@key}}Subscription: Type: "AWS::SNS::Subscription" Properties: {{# if this.endpoint.function}} Endpoint: {{this.endpoint.function}}: {{#each this.endpoint.array}} - {{@this}} {{/each}} {{else}} Endpoint: {{this.endpoint}} {{/if}} Protocol: {{this.protocol}} TopicArn: {{../this.arn}} {{/each}} {{else}} {{@key}}Sns: Type: "AWS::SNS::Topic" Properties: DisplayName: {{../prefix}}-{{@key}} Subscription: {{#each this.subscriptions}} {{# if this.endpoint.function}} - Endpoint: {{this.endpoint.function}}: {{#each this.endpoint.array}} - {{@this}} {{/each}} {{else}} - Endpoint: {{this.endpoint}} {{/if}} Protocol: {{this.protocol}} {{/each}} {{/if}} {{# each this.subscriptions}} {{@../key}}{{@key}}SubscriptionPermission: Type: AWS::Lambda::Permission Properties: {{# if this.endpoint.function}} FunctionName: {{this.endpoint.function}}: {{#each this.endpoint.array}} - {{@this}} {{/each}} {{else}} FunctionName: {{this.endpoint}} {{/if}} Action: lambda:InvokeFunction Principal: sns.amazonaws.com SourceArn: {{#if ../this.arn}} {{../this.arn}} {{else}} Ref: {{@../key}}Sns {{/if}} {{/each}} {{/each}} ################################################# # SNS config END ################################################# ################################################# # SQS config BEGIN ################################################# {{#each sqs}} {{@key}}SQS: Type: AWS::SQS::Queue Properties: QueueName: {{../prefix}}-{{@key}} ReceiveMessageWaitTimeSeconds: 20 {{#if this.retry}} RedrivePolicy: deadLetterTargetArn: Fn::GetAtt: - {{@key}}FailedSQS - Arn maxReceiveCount: {{this.retry}} {{/if}} VisibilityTimeout: {{this.visibilityTimeout}} {{#if this.retry}} {{@key}}FailedSQS: Type: AWS::SQS::Queue Properties: QueueName: {{../prefix}}-{{@key}}-failed {{/if}} {{# each this.consumer }} {{@../key}}WatcherRule: Type: AWS::Events::Rule Properties: ScheduleExpression: {{this.schedule}} State: {{# if this.state}}{{this.state}}{{ else }}DISABLED{{/if}} Targets: - Id: {{@../key}}WatcherScheduler Input: Fn::Sub: '{"queueUrl": "${ {{@../key}}SQS}", "messageLimit": {{this.messageLimit}}, "timeLimit": 60 }' Arn: Fn::GetAtt: - {{this.lambda}}LambdaFunction - Arn {{@../key}}InvokeLambdaPermission: Type: AWS::Lambda::Permission Properties: FunctionName: Fn::GetAtt: - {{this.lambda}}LambdaFunction - Arn Action: lambda:InvokeFunction Principal: events.amazonaws.com SourceArn: Fn::GetAtt: - {{@../key}}WatcherRule - Arn {{/each}} {{/each}} ################################################# # SQS config END ################################################# ################################################# # Step Functions config BEGIN ################################################# {{#each activities}} {{name}}Activity: Type: AWS::StepFunctions::Activity Properties: Name: {{../prefix}}-{{name}}-Activity {{/each}} {{#each stepFunctions}} {{../prefixNoDash}}{{@key}}StateMachine: Type: AWS::StepFunctions::StateMachine Properties: DefinitionString: {{#ifEquals ../useWorkflowLambdaVersions true}} Fn::Sub: - | {{{ToJson this}}} - {{#each ../workflowLambdas}} {{@key}}LambdaAliasOutput: Fn::GetAtt: - WorkflowLambdaVersionsNestedStack - Outputs.{{@key}}LambdaAliasOutput {{/each}} stackName: {{../stackName}} {{/ifEquals}} {{#ifNotEquals ../useWorkflowLambdaVersions true}} Fn::Sub: | {{{ToJson this}}} {{/ifNotEquals}} RoleArn: {{../iams.stepRoleArn}} {{/each}} ################################################# # Step Functions config END ################################################# ################################################# # CloudWatch RULE config BEGIN ################################################# {{# each rules }} {{@key}}Rule: Type: AWS::Events::Rule {{# if this.stateMachines}} DependsOn: {{#each this.stateMachines}} - {{this}} {{/each}} {{/if}} Properties: {{# if this.schedule}} ScheduleExpression: {{this.schedule}} {{/if}} {{# if this.eventPattern}} EventPattern: Fn::Sub: | {{{ToJson this.eventPattern}}} {{/if}} State: {{# if this.state}}{{this.state}}{{ else }}DISABLED{{/if}} Targets: {{# each this.targets}} - Id: {{@../key}}WatcherScheduler {{# if input}} Input: '{{{ToJson this.input}}}' {{/if}} Arn: Fn::GetAtt: - {{lambda}}LambdaFunction - Arn {{/each}} {{# each targets}} {{@../key}}RuleLambdaPermission: Type: AWS::Lambda::Permission Properties: FunctionName: Fn::GetAtt: - {{lambda}}LambdaFunction - Arn Action: lambda:InvokeFunction Principal: events.amazonaws.com SourceArn: Fn::GetAtt: - {{@../key}}Rule - Arn {{/each}} {{/each}} ################################################# # CloudWatch RULE config END ################################################# {{# if dynamo2ElasticSearch}} ################################################# # DynamoDB Event Source Mapping BEGIN ################################################# {{#each dynamo2ElasticSearch.tables}} {{this}}EventSourceMapping: Type: AWS::Lambda::EventSourceMapping Properties: EventSourceArn: Ref: {{this}}DynamoDBStreamArn FunctionName: Ref: {{../dynamo2ElasticSearch.lambda}}LambdaFunction BatchSize: {{../dynamo2ElasticSearch.batchSize}} StartingPosition: {{../dynamo2ElasticSearch.startingPosition}} {{/each}} ################################################# # DynamoDB Event Source Mapping END ################################################# {{/if}} ################################################# # Lambda config BEGIN ################################################# {{#each lambdas}} {{@key}}LambdaFunction: Type: AWS::Lambda::Function Properties: {{# if this.layers}} Layers: {{#each this.layers}} - {{this}} {{/each}} {{/if}} Code: S3Bucket: {{this.bucket}} S3Key: {{this.remote}} FunctionName: {{../prefix}}-{{@key}} Environment: Variables: stackName: {{../stackName}} CMR_ENVIRONMENT: {{../cmr.cmrEnvironment}} {{#if this.useElasticSearch }} {{#if ../es.name}} ES_HOST: Ref: {{../es.name}}DomainEndpoint {{/if}} {{/if}} {{#ifNotEquals this.useMessageAdapter true}} {{#if this.envs.CUMULUS_MESSAGE_ADAPTER_DIR}} {{else}} CUMULUS_MESSAGE_ADAPTER_DIR: {{../cmaDir}} {{/if}} {{/ifNotEquals}} {{#each this.tables}} {{this}}: {{../../prefix}}-{{this}} {{/each}} {{# if this.useDistributionApi}} {{# if ../api_distribution_url}} DISTRIBUTION_ENDPOINT: {{../api_distribution_url}} {{/if}} {{#if ../deployDistributionApi}} DISTRIBUTION_ENDPOINT: Fn::GetAtt: - CumulusApiDistributionNestedStack - Outputs.distributionRestApiResourceUrl {{/if}} {{/if}} {{#each this.envs}} {{# if this.function}} {{#if this.array}} {{@key}}: {{this.function}}: {{#each this.array}} - {{this}} {{/each}} {{/if}} {{#if this.value}} {{@key}}: {{this.function}}: {{this.value}} {{/if}} {{else}} {{@key}}: {{{this}}} {{/if}} {{/each}} Handler: {{this.handler}} MemorySize: {{this.memory}} {{# if ../useXray}} TracingConfig: Mode: Active {{/if}} {{# if this.apiRole }} Role: {{../iams.lambdaApiGatewayRoleArn}} {{else if this.distributionRole}} Role: {{../iams.distributionRoleArn}} {{else}} {{#ifEquals @key "executeMigrations"}} Role: {{../iams.migrationRoleArn}} {{/ifEquals}} {{#ifNotEquals @key "executeMigrations"}} Role: {{../iams.lambdaProcessingRoleArn}} {{/ifNotEquals}} {{/if}} Runtime: {{# if this.runtime}}{{this.runtime}}{{else}}nodejs8.10{{/if}} Timeout: {{this.timeout}} {{# if this.deadletterqueue}} DeadLetterConfig: TargetArn: Fn::GetAtt: - {{this.deadletterqueue}}SQS - Arn {{/if}} Tags: - Key: Project Value: {{../prefix}} {{# if this.launchInVpc }} {{# if ../vpc }} VpcConfig: SecurityGroupIds: - {{../vpc.securityGroup}} SubnetIds: {{#each ../vpc.subnets}} - {{this}} {{/each}} {{/if}} {{/if}} {{# if this.apiGateway }} {{@key}}LambdaPermissionApiGateway: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: Fn::GetAtt: - {{@key}}LambdaFunction - Arn Principal: apigateway.amazonaws.com {{/if}} {{# if this.logToElasticSearch }} {{@key}}LogSubscription: Type: AWS::Logs::SubscriptionFilter DependsOn: - {{@key}}LogGroup - log2elasticsearchLambdaPermissionLog Properties: DestinationArn: Fn::GetAtt: - log2elasticsearchLambdaFunction - Arn LogGroupName: '/aws/lambda/{{../prefix}}-{{@key}}' FilterPattern: "" {{@key}}LogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: '/aws/lambda/{{../prefix}}-{{@key}}' RetentionInDays: 30 {{/if}} {{#if this.logToSharedDestination }} # Configure Lambda log subscription to shareLogDestination {{@key}}LogSubscriptionToSharedDestination: Type: AWS::Logs::SubscriptionFilter DependsOn: - {{@key}}LogGroup Properties: DestinationArn: "{{this.logToSharedDestination}}" LogGroupName: '/aws/lambda/{{../stackName}}-{{@key}}' FilterPattern: "" {{@key}}LogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: '/aws/lambda/{{../stackName}}-{{@key}}' RetentionInDays: 30 {{/if}} {{#ifEquals @key "ScheduleSF"}} ## Generic lambda permission for custom rules ## created in the dashboard GenericLambdaPermission: Type: AWS::Lambda::Permission Properties: FunctionName: Fn::GetAtt: - ScheduleSFLambdaFunction - Arn Action: lambda:InvokeFunction Principal: events.amazonaws.com {{/ifEquals}} {{/each}} log2elasticsearchLambdaPermissionLog: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: Fn::GetAtt: - log2elasticsearchLambdaFunction - Arn Principal: Fn::Sub: 'logs.${AWS::Region}.amazonaws.com' ################################################# # Lambda config END ################################################# ################################################# # ECS config BEGIN ################################################# {{# if iams.instanceProfile}} CumulusECSCluster: Type: AWS::ECS::Cluster CumulusContainerInstanceLaunch: Type: AWS::AutoScaling::LaunchConfiguration Properties: {{# if vpc.subnets }} AssociatePublicIpAddress: {{#if ecs.publicIp}}{{ecs.publicIp}}{{else}}false{{/if}} {{/if}} {{# if vpc.securityGroup}} SecurityGroups: - {{vpc.securityGroup}} {{/if}} {{# if ecs.efs.mount}} - Fn::ImportValue: "{{prefix}}-EFSSecurityGroup" {{/if}} ImageId: {{ecs.amiid}} InstanceType: {{ecs.instanceType}} IamInstanceProfile: {{iams.instanceProfile}} BlockDeviceMappings: - DeviceName: "/dev/xvdcz" Ebs: DeleteOnTermination: true VolumeSize: {{ecs.volumeSize}} {{# if ecs.keyPairName }} KeyName: {{ ecs.keyPairName }} {{/if}} UserData: Fn::Base64: Fn::Sub: - | Content-Type: multipart/mixed; boundary="==BOUNDARY==" MIME-Version: 1.0 --==BOUNDARY== Content-Type: text/cloud-boothook; charset="us-ascii" sed -i '/^\s*DOCKER_STORAGE_OPTIONS=/d' /etc/sysconfig/docker-storage echo 'DOCKER_STORAGE_OPTIONS="--storage-driver {{ecs.docker.storageDriver}}"' >> /etc/sysconfig/docker-storage {{#ifEquals ecs.docker.storageDriver "devicemapper"}} sed -i '/^\s*OPTIONS=/d' /etc/sysconfig/docker echo 'OPTIONS="--default-ulimit nofile=1024:4096 --storage-opt dm.basesize={{ecs.volumeSize}}G"' >> /etc/sysconfig/docker {{/ifEquals}} --==BOUNDARY== Content-Type: text/x-shellscript; charset="us-ascii" {{# if ecs.efs.mount }} AZ=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone) if ! rpm -q nfs-utils >/dev/null 2>&1; then yum install -y nfs-utils fi mkdir -p {{ecs.efs.mount}} mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 ${!AZ}.${EFSFileSystemId}.efs.${AWS::Region}.amazonaws.com:/ {{ecs.efs.mount}} chmod 777 {{ecs.efs.mount}} service docker restart {{/if}} cat <<'EOF' >> /etc/ecs/ecs.config ECS_CLUSTER=${CumulusECSCluster} ECS_ENGINE_TASK_CLEANUP_WAIT_DURATION=1m ECS_CONTAINER_STOP_TIMEOUT={{ecs.container_stop_timeout}} EOF {{#ifEquals ecs.docker.registry "dockerhub"}} echo ECS_ENGINE_AUTH_TYPE=docker >> /etc/ecs/ecs.config echo 'ECS_ENGINE_AUTH_DATA={"https://index.docker.io/v1/":{"username":"{{ecs.docker.username}}","password": "${DockerPassword}","email":"${DockerEmail}"}}' >> /etc/ecs/ecs.config {{/ifEquals}} if ! which aws >/dev/null 2>&1; then yum install -y jq unzip curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip" unzip awscli-bundle.zip ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws rm -rf ./awscli-bundle awscli-bundle.zip fi aws s3 cp s3://{{bucket}}/{{stackName}}/deployment-staging/task-reaper.sh /usr/local/bin/task-reaper.sh chmod +x /usr/local/bin/task-reaper.sh cat <<'EOF' >> /etc/cron.d/task-reaper PATH=/bin:/usr/local/bin AWS_DEFAULT_REGION=${AWS::Region} LIFECYCLE_HOOK_NAME={{stackName}}-ecs-termination-hook * * * * * root /usr/local/bin/task-reaper.sh >> /var/log/task-reaper.log 2>&1 EOF --==BOUNDARY==-- {{# if ecs.efs.mount }} - EFSFileSystemId: Fn::ImportValue: "{{prefix}}-EFSFileSystemId" {{else}} # This is necessary because, if we are not setting a mount, # the Fn::Sub function still requires a variable map - Nothing: nothing {{/if}} CumulusECSAutoScalingGroup: Type: AWS::AutoScaling::AutoScalingGroup UpdatePolicy: AutoScalingRollingUpdate: MinInstancesInService: '{{ ecs.minInstances }}' Properties: AvailabilityZones: {{# if ecs.availabilityZones }} {{#each ecs.availabilityZones}} - {{this}} {{/each}} {{else if ecs.availabilityZone }} - {{ecs.availabilityZone}} {{/if}} {{# if vpc.subnets }} VPCZoneIdentifier: {{#each vpc.subnets}} - {{this}} {{/each}} {{/if}} LaunchConfigurationName: Ref: CumulusContainerInstanceLaunch MinSize: '{{ ecs.minInstances }}' DesiredCapacity: '{{ ecs.desiredInstances }}' MaxSize: '{{ ecs.maxInstances }}' Tags: - Key: Name Value: "{{prefix}}-cumulus-ecs" PropagateAtLaunch: true CumulusECSAutoScalingLifeCycleHook: Type: AWS::AutoScaling::LifecycleHook Properties: LifecycleHookName: {{stackName}}-ecs-termination-hook AutoScalingGroupName: Ref: CumulusECSAutoScalingGroup DefaultResult: CONTINUE HeartbeatTimeout: 150 LifecycleTransition: "autoscaling:EC2_INSTANCE_TERMINATING" CumulusECSScaleOutPolicy: Type: AWS::AutoScaling::ScalingPolicy Properties: AdjustmentType: PercentChangeInCapacity AutoScalingGroupName: Ref: CumulusECSAutoScalingGroup EstimatedInstanceWarmup: 180 MetricAggregationType: Average PolicyType: StepScaling StepAdjustments: - MetricIntervalLowerBound: 0 ScalingAdjustment: {{ecs.clusterAutoscaling.scaleOutAdjustmentPercent}} CumulusECSMemoryScaleOutAlarm: Type: AWS::CloudWatch::Alarm Properties: ActionsEnabled: true AlarmActions: - Ref: CumulusECSScaleOutPolicy ComparisonOperator: GreaterThanThreshold DatapointsToAlarm: 1 Dimensions: - Name: ClusterName Value: Ref: CumulusECSCluster EvaluationPeriods: 1 MetricName: MemoryReservation Namespace: AWS/ECS Period: 60 Statistic: Average Threshold: {{ecs.clusterAutoscaling.scaleOutThresholdPercent}} Unit: Percent CumulusECSCPUScaleOutAlarm: Type: AWS::CloudWatch::Alarm Properties: ActionsEnabled: true AlarmActions: - Ref: CumulusECSScaleOutPolicy ComparisonOperator: GreaterThanThreshold DatapointsToAlarm: 1 Dimensions: - Name: ClusterName Value: Ref: CumulusECSCluster EvaluationPeriods: 1 MetricName: CPUReservation Namespace: AWS/ECS Period: 60 Statistic: Average Threshold: {{ecs.clusterAutoscaling.scaleOutThresholdPercent}} Unit: Percent CumulusECSScaleInPolicy: Type: AWS::AutoScaling::ScalingPolicy Properties: AdjustmentType: PercentChangeInCapacity AutoScalingGroupName: Ref: CumulusECSAutoScalingGroup MetricAggregationType: Average PolicyType: StepScaling StepAdjustments: - MetricIntervalUpperBound: 0 ScalingAdjustment: {{ecs.clusterAutoscaling.scaleInAdjustmentPercent}} CumulusECSMemoryScaleInAlarm: Type: AWS::CloudWatch::Alarm Properties: ActionsEnabled: true AlarmActions: - Ref: CumulusECSScaleInPolicy ComparisonOperator: LessThanThreshold DatapointsToAlarm: 1 Dimensions: - Name: ClusterName Value: Ref: CumulusECSCluster EvaluationPeriods: 1 MetricName: MemoryReservation Namespace: AWS/ECS Period: 60 Statistic: Average Threshold: {{ecs.clusterAutoscaling.scaleInThresholdPercent}} Unit: Percent CumulusECSCPUScaleInAlarm: Type: AWS::CloudWatch::Alarm Properties: ActionsEnabled: true AlarmActions: - Ref: CumulusECSScaleInPolicy ComparisonOperator: LessThanThreshold DatapointsToAlarm: 1 Dimensions: - Name: ClusterName Value: Ref: CumulusECSCluster EvaluationPeriods: 1 MetricName: CPUReservation Namespace: AWS/ECS Period: 60 Statistic: Average Threshold: {{ecs.clusterAutoscaling.scaleInThresholdPercent}} Unit: Percent {{#each ecs.services}} # adding TaskDefinition for Lambda/ECS services {{@key}}TaskDefinition: Type: AWS::ECS::TaskDefinition Properties: {{# if this.volumes}} Volumes: {{# each this.volumes}} - Name: {{name}} Host: SourcePath: {{path}} {{/each}} {{/if}} {{# if this.networkMode}} NetworkMode: {{this.networkMode}} {{/if}} ContainerDefinitions: - Name: {{@key}} Cpu: {{#if this.cpu }}{{ this.cpu }}{{ else }}10{{/if}} Essential: true {{# if this.volumes}} MountPoints: {{# each this.volumes}} - SourceVolume: {{name}} ContainerPath: {{dst}} {{/each}} {{/if}} {{# if this.privileged }} Privileged: true {{/if}} Environment: {{#each this.envs}} {{# if this.function}} {{#if this.array}} - Name: {{@key}} Value: {{this.function}}: {{#each this.array}} - {{this}} {{/each}} {{/if}} {{#if this.value}} - Name: {{@key}} Value: {{this.function}}: {{this.value}} {{/if}} {{else}} - Name: {{@key}} Value: {{{this}}} {{/if}} {{/each}} {{# if ../ecs.docker}} Image: {{image}} {{else}} Image: Fn::Sub: ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/{{image}} {{/if}} MemoryReservation: {{#if this.memory }}{{ this.memory }}{{ else }}256{{/if}} {{# if this.commands }} Command: {{# each this.commands }} {{# if this.function}} - {{this.function}}: {{this.value}} {{else}} - {{{ @this }}} {{/if}} {{/each}} {{/if}} LogConfiguration: LogDriver: awslogs Options: awslogs-group: Ref: {{@key}}EcsLogs awslogs-region: Fn::Sub: ${AWS::Region} {{@key}}EcsLogs: Type: AWS::Logs::LogGroup Properties: LogGroupName: {{../prefix}}-{{@key}}EcsLogs RetentionInDays: 30 {{@key}}EcsLogSubscription: Type: AWS::Logs::SubscriptionFilter DependsOn: - {{@key}}EcsLogs - log2elasticsearchLambdaPermissionLog Properties: DestinationArn: Fn::GetAtt: - log2elasticsearchLambdaFunction - Arn LogGroupName: {{../prefix}}-{{@key}}EcsLogs FilterPattern: "" {{@key}}ECSService: Type: AWS::ECS::Service DependsOn: - CumulusECSAutoScalingGroup Properties: Cluster: Ref: CumulusECSCluster DesiredCount: {{# if this.count}}{{this.count}}{{ else }}0{{/if}} TaskDefinition: Ref: {{@key}}TaskDefinition DeploymentConfiguration: MaximumPercent: 100 MinimumHealthyPercent: 0 {{@key}}ECSServiceTaskCountLowAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmDescription: There are less tasks running than the desired AlarmName: {{../prefix}}-{{@key}}-TaskCountLowAlarm ComparisonOperator: LessThanThreshold EvaluationPeriods: 1 MetricName: MemoryUtilization Statistic: SampleCount Threshold: {{# if this.count}}{{this.count}}{{ else }} 0 {{/if}} Period: 60 Namespace: AWS/ECS Dimensions: - Name: ClusterName Value: Ref: CumulusECSCluster - Name: ServiceName Value: Fn::GetAtt: - {{@key}}ECSService - Name {{# if this.alarms}} # the service has cumstom alarms {{# each this.alarms}} {{@../key}}{{@key}}Alarm: Type: AWS::CloudWatch::Alarm Properties: {{#if alarm_description}} AlarmDescription: {{ alarm_description }} {{/if}} AlarmName: {{../../prefix}}-{{@../key}}-{{@key}}Alarm ComparisonOperator: {{ comparison_operator }} EvaluationPeriods: {{#if evaluation_periods }}{{ evaluation_periods }}{{ else }}5{{/if}} MetricName: {{ metric }} Statistic: {{#if statistic }}{{ statistic }}{{ else }}Average{{/if}} Threshold: {{ threshold }} Period: {{#if period }}{{ period }}{{ else }}60{{/if}} Namespace: AWS/ECS Dimensions: - Name: ClusterName Value: Ref: CumulusECSCluster - Name: ServiceName Value: Fn::GetAtt: - {{@../key}}ECSService - Name {{/each}} {{/if}} {{/each}} {{#each ecs.tasks}} # adding TaskDefinition for Lambda/ECS tasks {{@key}}TaskDefinition: Type: AWS::ECS::TaskDefinition Properties: {{# if this.volumes}} Volumes: {{# each this.volumes}} - Name: {{name}} Host: SourcePath: {{path}} {{/each}} {{/if}} {{# if this.networkMode}} NetworkMode: {{this.networkMode}} {{/if}} ContainerDefinitions: - Name: {{@key}} Cpu: {{#if this.cpu }}{{ this.cpu }}{{ else }}10{{/if}} Essential: true {{# if this.volumes}} MountPoints: {{# each this.volumes}} - SourceVolume: {{name}} ContainerPath: {{dst}} {{/each}} {{/if}} {{# if this.privileged }} Privileged: true {{/if}} Environment: {{#each this.envs}} {{# if this.function}} {{#if this.array}} - Name: {{@key}} Value: {{this.function}}: {{#each this.array}} - {{this}} {{/each}} {{/if}} {{#if this.value}} - Name: {{@key}} Value: {{this.function}}: {{this.value}} {{/if}} {{else}} - Name: {{@key}} Value: {{{this}}} {{/if}} {{/each}} {{# if ../ecs.docker}} Image: {{image}} {{else}} Image: Fn::Sub: ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/{{image}} {{/if}} MemoryReservation: {{#if this.memory }}{{ this.memory }}{{ else }}256{{/if}} {{# if this.commands }} Command: {{# each this.commands }} {{# if this.function}} - {{this.function}}: {{this.value}} {{else}} - {{{ @this }}} {{/if}} {{/each}} {{/if}} LogConfiguration: LogDriver: awslogs Options: awslogs-group: Ref: {{@key}}EcsLogs awslogs-region: Fn::Sub: ${AWS::Region} {{@key}}EcsLogs: Type: AWS::Logs::LogGroup Properties: LogGroupName: {{../prefix}}-{{@key}}EcsLogs RetentionInDays: 30 {{@key}}EcsLogSubscription: Type: AWS::Logs::SubscriptionFilter DependsOn: - {{@key}}EcsLogs - log2elasticsearchLambdaPermissionLog Properties: DestinationArn: Fn::GetAtt: - log2elasticsearchLambdaFunction - Arn LogGroupName: {{../prefix}}-{{@key}}EcsLogs FilterPattern: "" {{/each}} {{/if}} ################################################# # ECS config END ################################################# ################################################# # CloudWatch Dashboard BEGIN ################################################# CumulusCloudWatchDashboard: Type: AWS::CloudWatch::Dashboard Properties: DashboardName: {{prefix}}-CloudWatch-Dashboard DashboardBody: '{{#buildCWDashboard dashboard ecs es prefix}}{{/buildCWDashboard}}' ################################################# # CloudWatch Dashboard END ################################################# Outputs: Api: {{# if api_backend_url}} Value: {{api_backend_url}} {{else}} Value: Fn::GetAtt: - CumulusApiBackendNestedStack - Outputs.backendRestApiResourceUrl {{/if}} {{# if api_distribution_url}} Distribution: Value: {{api_distribution_url}} {{/if}} {{#if deployDistributionApi}} Distribution: Value: Fn::GetAtt: - CumulusApiDistributionNestedStack - Outputs.distributionRestApiResourceUrl {{/if}} ApiId: Value: Fn::GetAtt: - CumulusApiBackendNestedStack - Outputs.backendRestApiResource {{#if deployDistributionApi}} DistributionId: Value: Fn::GetAtt: - CumulusApiDistributionNestedStack - Outputs.distributionRestApiResource {{/if}} ApiStage: Value: {{apiStage}} {{#each stepFunctions}} {{@key}}StateMachine: Value: Ref: {{../prefixNoDash}}{{@key}}StateMachine {{/each}} {{#each sqs}} {{@key}}SQSOutput: Value: Ref: {{@key}}SQS {{/each}} {{#each sns}} {{#if this.arn}} {{@key}}: Value: {{this.arn}} {{else}} {{@key}}SnsArn: Value: Ref: {{@key}}Sns {{/if}} {{/each}} EncryptedCmrPassword: Value: Fn::GetAtt: - CumulusCustomResource - CmrPassword EncryptedLaunchpadPassphrase: Value: Fn::GetAtt: - CumulusCustomResource - LaunchpadPassphrase