https://pulumi.com logo
Title
f

fresh-wire-95028

10/05/2021, 5:43 PM
Hi there! What's the best way to get an ECS service name? It seems that a unique hash is assigned when I specify a name. For example, if my service name is "blah" it will actually be called "blah-3hd8d".
const fgService = new awsx.ecs.FargateService('blah', {
...
});

const autoScaleTarget = new aws.appautoscaling.Target(`appautoscaling-target-api-${stack}`, {
  resourceId: `service/${apiClusterName}/${apiServiceName}`, <----- ???
});
b

bored-oyster-3147

10/05/2021, 5:45 PM
it should be an output on the resource you declared. Which also means the interpolation that you `???
'd won't work. you will need to do interpolation within an
.apply(...)`
you just need to set the name property
f

fresh-wire-95028

10/05/2021, 5:53 PM
so where exactly would I get the "output"? The TS types just say it returns a class, but I don't see anything "output" or name related
export declare class FargateService extends ecs.Service {
    readonly taskDefinition: FargateTaskDefinition;
    constructor(name: string, args: FargateServiceArgs, opts?: pulumi.ComponentResourceOptions);
}
b

bored-oyster-3147

10/05/2021, 5:54 PM
so
FargateService
extends the
ecs.Service
resource from the core
pulumi-aws
provider. Which should have a
name
property. You can use that after declaring the service if you allowed pulumi to generate a suffix
I might be full of crap - it looks like it's extending this class? Which is declaring the ecs service in its constructor. So it wouldn't have that property. You probably just need to pass in a name, like @billowy-army-68599 said. Ignore me lol Actually that class exposes the service so it might be
fargateService.service.name
?
f

fresh-wire-95028

10/05/2021, 6:05 PM
haha yeah just figured that out...about to test it but now my stack is in a weird updating state šŸ˜• so trying to fix that
b

billowy-army-68599

10/05/2021, 6:11 PM
@fresh-wire-95028 check the AWS console, the fargate service is probably pending šŸ™‚
fargate is fun
f

fresh-wire-95028

10/05/2021, 6:14 PM
haha yeah fixed it by exporting the pulumi config then getting rid of the pending items and then importing. dunno if that was the right thing to do...but at least it doesn't give me that error anymore.
hmmm so how exactly do I use the output of
fargateService.service.name
? It seems to output the following. Should I await the promise? or is there a better way of going about using it?
OutputImpl {
      __pulumiOutput: true,
      resources: [Function (anonymous)],
      allResources: [Function (anonymous)],
      isKnown: Promise { <pending> },
      isSecret: Promise { <pending> },
      promise: [Function (anonymous)],
      toString: [Function (anonymous)],
      toJSON: [Function (anonymous)]
    }
b

bored-oyster-3147

10/05/2021, 6:24 PM
See here: https://www.pulumi.com/docs/intro/concepts/inputs-outputs/ Outputs function similarly to promises. That value is not known at execution time so you need to use the
.apply(...)
delegate in order to transform the value of the output to your desired result, a lot like a
Promise.then(...)
function. So your
resourceId
parameter could look something like this:
const fgService = new awsx.ecs.FargateService('blah', {
...
});

const autoScaleTarget = new aws.appautoscaling.Target(`appautoscaling-target-api-${stack}`, {
  resourceId: fgService.service.name.apply(serviceName => `service/${apiClusterName}/${serviceName}`),
});
If
apiClusterName
is also an output than there is another function you would use to get those 2 outputs into a single apply delegate.
b

billowy-army-68599

10/05/2021, 6:27 PM
f

fresh-wire-95028

10/05/2021, 7:30 PM
so I think I figured out the
apply
thing:
pulumi.all([fgService.service.name, cluster.cluster.name]).apply(([serviceName, clusterName]) => console.log(`service/${clusterName}/${serviceName}`));
outputs
service/api-cluster-development-564b394/api-svc-development-4a8c1f0
which looks correct. However...when I do:
const autoScaleTarget = new aws.appautoscaling.Target(`appautoscaling-target-api-${stack}`, {
  // max/min task instances
  maxCapacity: 10,
  minCapacity: 1,
  resourceId: pulumi.all([fgService.service.name, cluster.cluster.name]).apply(([serviceName, clusterName]) => `service/${clusterName}/${serviceName}`),
  scalableDimension: 'ecs:service:DesiredCount',
  serviceNamespace: 'ecs',
});

new aws.appautoscaling.Policy(`appautoscaling-policy-api-${stack}`, {
  policyType: 'TargetTrackingScaling',
  resourceId: autoScaleTarget.resourceId,
  scalableDimension: autoScaleTarget.scalableDimension,
  serviceNamespace: autoScaleTarget.serviceNamespace,
  targetTrackingScalingPolicyConfiguration: {
    predefinedMetricSpecification: {
      predefinedMetricType: 'ECSServiceAverageCPUUtilization',
    },
    // scale so that we use 20% CPU
    targetValue: 20,
  },
});
I get the following error:
aws:appautoscaling:Policy (appautoscaling-policy-api-development):
    error: 1 error occurred:
        * Failed to create scaling policy: ObjectNotFoundException: No scalable target registered for service namespace: ecs, resource ID: service/api-cluster-development-564b394/api-svc-development-4a8c1f0, scalable dimension: ecs:service:DesiredCount
which is weird, because the target didn't throw the error with the same
resourceId
b

bored-oyster-3147

10/05/2021, 8:07 PM
What am I missing? Because I see the same
resourceId
that you said got logged and in the error
f

fresh-wire-95028

10/05/2021, 9:02 PM
hmmm I might have had to specify the IAM role, even though it was optional. Here's my final working code, nicely modularized šŸ˜„
import * as pulumi from '@pulumi/pulumi';
import * as aws from '@pulumi/aws';
import * as awsx from '@pulumi/awsx';

interface IAutoScaleFargateServiceOpts {
  iamRole: aws.iam.Role,
  service: awsx.ecs.FargateService

  cluster: awsx.ecs.Cluster

  serviceName: string

  maxCount: number

  minCount: number

  CPUThreshold: number

  memoryThreshold: number
}
export default ({
  iamRole,
  service,
  cluster,
  serviceName,
  maxCount,
  minCount,
  CPUThreshold,
  memoryThreshold,
}: IAutoScaleFargateServiceOpts) => {
  const stack = pulumi.getStack();

  const autoScaleTarget = new aws.appautoscaling.Target(`appautoscaling-target-${serviceName}-${stack}`, {
    // max/min task instances
    maxCapacity: maxCount,
    minCapacity: minCount,
    roleArn: iamRole.arn,
    resourceId: pulumi.interpolate`service/${cluster.cluster.name}/${service.service.name}`,
    scalableDimension: 'ecs:service:DesiredCount',
    serviceNamespace: 'ecs',
  });

  const cpuScalingPolicy = new aws.appautoscaling.Policy(`appautoscaling-policy-cpu-api-${stack}`, {
    policyType: 'TargetTrackingScaling',
    resourceId: autoScaleTarget.resourceId,
    scalableDimension: autoScaleTarget.scalableDimension,
    serviceNamespace: autoScaleTarget.serviceNamespace,
    targetTrackingScalingPolicyConfiguration: {
      predefinedMetricSpecification: {
        predefinedMetricType: 'ECSServiceAverageCPUUtilization',
      },
      // scale so that we use max x% CPU
      targetValue: CPUThreshold,
    },
  });

  const memoryScalingPolicy = new aws.appautoscaling.Policy(`appautoscaling-policy-memory-api-${stack}`, {
    policyType: 'TargetTrackingScaling',
    resourceId: autoScaleTarget.resourceId,
    scalableDimension: autoScaleTarget.scalableDimension,
    serviceNamespace: autoScaleTarget.serviceNamespace,
    targetTrackingScalingPolicyConfiguration: {
      predefinedMetricSpecification: {
        predefinedMetricType: 'ECSServiceAverageMemoryUtilization',
      },
      // scale so that we use max x% Memory
      targetValue: memoryThreshold,
    },
  });

  return {
    cpuScalingPolicy,
    memoryScalingPolicy,
  };
};
b

bored-oyster-3147

10/05/2021, 9:17 PM
awesome! glad you got it working. Btw I don't know if you're doing it for some other reason, but appending the stack name to the end of your resources is a little redundant. All of the resources in that stack are scoped to the stack just by way of the stack having its own state
f

fresh-wire-95028

10/05/2021, 9:22 PM
hmm...but in the aws GUI I'd like to see the stack name. For example, in clusters I'd like to see: ā€¢ api-cluster-development ā€¢ api-cluster-staging
b

billowy-army-68599

10/05/2021, 9:41 PM
@fresh-wire-95028 if you want to modularize for reuse, you could consider making what you have a component resource
lmk if that sounds good to you and I can show you some examples
b

bored-oyster-3147

10/05/2021, 9:48 PM
in that case you can use
NamePrefix
resource argument instead of relying on pulumi using the pulumi name. Then your resource name in AWS GUI can differ from your pulumi name
f

fresh-wire-95028

10/06/2021, 8:17 PM
@billowy-army-68599 yeah that sounds like a great idea. I'm wondering if there are existing community component resources. like I can't imagine I'm the first one to need to scale fargate resources. but it took me quite a while to figure out how to do it. Or say, setting up a bucket with a custom domain. It's something that everyone needs to do, and requires a few steps (route 53, cert, dns validation, bucket policies, etc). Takes a good 150 lines of code with pulumi.
like I'm thinking something along the lines of the Github Action marketplace (or CircleCI orbs), but for pulumi https://github.com/marketplace?type=actions
b

billowy-army-68599

10/06/2021, 8:30 PM
@fresh-wire-95028 we are working on something exactly like this, but it's not ready just yet for public consumption
f

fresh-wire-95028

10/06/2021, 8:35 PM
Oooh excellent! Can't wait. Lmk if/how I can contribute or be part of a beta when it's available. I would say that's my #1 gripe with DevOps. All the tools out there are powerful but insanely verbose and require tons of background knowledge. I'd love to just have a bunch of prebuilt strategies that give me what I probably want, with the ability to customize as needed
šŸ™Œ 1