ECS Copilot with SQS Autoscaling
Tags: uncategorized, aws, ecscopilot, typescript
Published: 2023-10-27 19:08:14

---
title: ECS Copilot with SQS Autoscaling
published: true
date: 2023-10-27 18:46:21 UTC
tags: Uncategorized,AWS,EcsCopilot,Typescript
canonical_url: https://jaysonrawlins.com/ecs-copilot-with-sqs-autoscaling/
---

### Prerequisites:

- **ECS Copilot:** ECS Copilot that has been overridden to expose CDK. See [svc override — AWS Copilot CLI](https://aws.github.io/copilot-cli/docs/commands/svc-override/). Probably could use the same principles with regular CDK as well but this particular example assumes we are using ECS Copilot.
- **Typescript**
- **CDK v2**
- **NPM**
- **CDK IAM Floyd (Library for helping manage AWS IAM)**

<svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewbox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewbox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg>
```
npm install cdk-iam-floyd
```

- **tranformService():** This is the function that starts of with 

<svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewbox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewbox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg>
```
const service = this.template.getResource("Service") as ecs.CfnService;
```

### Notes:

As mentioned before, this could be used for any CDK project. However, for this demonstration ECS Copilot is assumed which will include some commands that simply would not be necessary.

### Lets get started:

<svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewbox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewbox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg>
```
transformService() {
  let minCapacity = 2;
  let maxCapacity = 48;
  let queueName = "your_queue_name"
  
  ## With regular CDK, you will have the Service Object directly.
  const service = this.template.getResource("Service") as ecs.CfnService;
  const clusterName = service.cluster;
  const serviceName = service.attrName;

  const scalingRole = new iam.Role(this, 'ServiceScalingRole', {
      assumedBy: new iam.ServicePrincipal('ecs.application-autoscaling.amazonaws.com'),
      inlinePolicies: {
          'ScalingRoleInlinePolicy': new PolicyDocument({
              statements: [
                  new floyd.ApplicationAutoscaling().allow().onAllResources().allActions(),
                  new floyd.Ecs().allow().onAllResources()
                      .toDescribeServices()
                      .toUpdateService(),
                  new floyd.Cloudwatch().allow().onAllResources()
                      .toDescribeAlarms()
                      .toPutMetricAlarm()
                      .toDeleteAlarms()
                      .toDescribeAlarmHistory()
                      .toDescribeAlarmsForMetric()
                      .toGetMetricStatistics()
                      .toListMetrics()
                      .toDisableAlarmActions()
                      .toEnableAlarmActions(),
                  new floyd.Iam().allow().onAllResources()
                      .toCreateServiceLinkedRole(),
                  new floyd.Sns().allow().onAllResources()
                      .toCreateTopic()
                      .toSubscribe(),
                  new floyd.Sns({
                      actions: [
                          "sns:Get*",
                          "sns:List*"
                      ]
                  }).allow().onAllResources()
              ]
          })
      }
  });

  // First, get the ScalableTarget associated with the service
  const resourceId = `service/${clusterName}/${serviceName}`;

  const scalableTarget = new appscaling.CfnScalableTarget(this, 'ServiceScalableTarget', {
      maxCapacity: maxCapacity,
      minCapacity: minCapacity,
      resourceId: resourceId,
      roleArn: scalingRole.roleArn,
      scalableDimension: 'ecs:service:DesiredCount',
      serviceNamespace: 'ecs',
  });

 // This and the previous are only necessary because of ECS Copilot.
 // Could just declare the ScalableTarget directly with CDK.
 const cdkScalableTarget = appscaling.ScalableTarget.fromScalableTargetId(
      this,
      'CdkServiceScalableTarget',
      scalableTarget.ref,
  )
  
 const metric = new cloudwatch.Metric({
    namespace: 'AWS/SQS',
    metricName: 'ApproximateNumberOfMessagesVisible',
    dimensionsMap: {
        QueueName: queueName,
    },
    statistic: 'Average',
});

}
```