Hello everyone, at our company we're trying to mig...
# aws
b
Hello everyone, at our company we're trying to migrate from Heroku to AWS. I'd like to define a awsx.ecs.FargateService in a custom vpc which has private/public subnet, currently I do this:
Copy code
const lbSecurityGroup = new aws.ec2.SecurityGroup(
    `ls-lb-sg-${stackName}`,
    {
      vpcId: vpc.vpcId,
      description: 'Security group for load balancer',
      ingress: [
        { protocol: 'tcp', fromPort: 80, toPort: 80, cidrBlocks: ['0.0.0.0/0'] },
        { protocol: 'tcp', fromPort: 443, toPort: 443, cidrBlocks: ['0.0.0.0/0'] },
      ],
      egress: [{ protocol: '-1', fromPort: 0, toPort: 0, cidrBlocks: ['0.0.0.0/0'] }],
      tags: {
        environment: stackName,
      },
    },
    { provider },
  )

  // Create security group for the Fargate services
  const serviceSecurityGroup = new aws.ec2.SecurityGroup(
    `ls-service-sg-${stackName}`,
    {
      vpcId: vpc.vpcId,
      description: 'Security group for Fargate services',
      ingress: [
        {
          protocol: 'tcp',
          fromPort: 3000,
          toPort: 3000,
          securityGroups: [lbSecurityGroup.id], 
        },
      ],
      egress: [{ protocol: '-1', fromPort: 0, toPort: 0, cidrBlocks: ['0.0.0.0/0'] }],
      tags: {
        Name: `ls-service-sg-${stackName}`,
        environment: stackName,
      },
    },
    { provider },
  )

  const loadBalancer = new awsx.lb.ApplicationLoadBalancer(
    `ls-loadbalancer-${stackName}`,
    {
      internal: false,
      subnetIds: publicSubnetIds, 
      securityGroups: [lbSecurityGroup.id],
      tags: {
        environment: stackName,
      },
    },
    {
      provider,
    },
  )

  const apiService = new awsx.ecs.FargateService(
    `ls-api-service-${stackName}`,
    {
      cluster: cluster.id,
      taskDefinitionArgs: {
        containers: {
          api: {
            name: `ls-api-${stackName}`,
            image: img.imageUri,
            memory: 4096,
            cpu: 1024,
            essential: true,
            command: ['build/app.js'],
            portMappings: [
              {
                containerPort: 3000,
                hostPort: 3000,
                targetGroup: loadBalancer.defaultTargetGroup,
              },
            ],
            environment: environmentVariables,
            healthCheck: {
              command: ['CMD-SHELL', 'curl -f <http://localhost:3000/health> || exit 1'],
              interval: 30,
              timeout: 5,
              retries: 3,
              startPeriod: 60,
            },
          },
          datadog: {
            name: `datadog-agent-${stackName}`,
            image: 'datadog/agent:latest',
            cpu: 256,
            memory: 512,
            essential: false,
            environment: [datadogApiKey, { name: 'ECS_FARGATE', value: 'true' }],
          },
        },
      },
      desiredCount: stackName === 'production' ? 2 : 1,
      networkConfiguration: {
        subnets: privateSubnetIds,
        securityGroups: [serviceSecurityGroup.id],
        assignPublicIp: false,
      },
      tags: {
        environment: stackName,
      },
    },
    { provider },
  )
the problem is whenever I try to deploy, I get
Copy code
aws:lb:LoadBalancer (ls-loadbalancer-dev):
    error:   sdk-v2/provider2.go:520: sdk.helper_schema: setting ELBv2 Load Balancer (arn:aws:elasticloadbalancing:us-east-1:662016171348:loadbalancer/app/ls-loadbalancer-dev-2873ccc/062c2c77270e2e27) security groups: operation error Elastic Load Balancing v2: SetSecurityGroups, https response error StatusCode: 400, RequestID: c079a5d7-2486-45b2-b8ce-8593168d5989, InvalidConfigurationRequest: One or more security groups are invalid: provider=aws@6.56.1
    error: 1 error occurred:
        * setting ELBv2 Load Balancer (arn:aws:elasticloadbalancing:us-east-1:662016171348:loadbalancer/app/ls-loadbalancer-dev-2873ccc/062c2c77270e2e27) security groups: operation error Elastic Load Balancing v2: SetSecurityGroups, https response error StatusCode: 400, RequestID: c079a5d7-2486-45b2-b8ce-8593168d5989, InvalidConfigurationRequest: One or more security groups are invalid
Can anyone point me to what is invalid about my security groups?
actually this reproduces even with just
Copy code
const loadBalancer = new awsx.lb.ApplicationLoadBalancer(
    `ls-loadbalancer-${stackName}`,
    {
      subnetIds: publicSubnetIds,
      // securityGroups: [lbSecurityGroup.id],
      tags: {
        Name: `ls-loadbalancer-${stackName}`,
        environment: stackName,
      },
    },
    {
      provider,
    },
  )
when I comment out
subnetIds
it works but it creates the load balancer in a default vpc
how to create an awsx.lb.ApplicationLoadBalancer in some specific VPC?
q
The LB will be placed in the VPC the subnets (
subnetIds
) are in. Can you confirm whether the
publicSubnetIds
and
privateSubnetIds
you use in your program are in the same VPC that you use for the security groups?
b
yes
the security group is defined like this:
Copy code
const lbSecurityGroup = new aws.ec2.SecurityGroup(
    `ls-lb-sg-${stackName}`,
    {
      vpcId: vpc.vpcId,
      description: 'Security group for load balancer',
      ingress: [
        { protocol: 'tcp', fromPort: 80, toPort: 80, cidrBlocks: ['0.0.0.0/0'] },
        { protocol: 'tcp', fromPort: 443, toPort: 443, cidrBlocks: ['0.0.0.0/0'] },
      ],
      egress: [{ protocol: '-1', fromPort: 0, toPort: 0, cidrBlocks: ['0.0.0.0/0'] }],
      tags: {
        environment: stackName,
      },
    },
    { provider },
  )
this is the complete code:
Copy code
import * as awsx from '@pulumi/awsx'
import * as aws from '@pulumi/aws'
import * as pulumi from '@pulumi/pulumi'
import { EnvironmentType } from './env'
import { provider } from './provider'

const stackName = pulumi.getStack() as EnvironmentType

// Create a new VPC using awsx
export const vpc = new awsx.ec2.Vpc(
  `ls-vpc-${stackName}`,
  {
    numberOfAvailabilityZones: 2,
    cidrBlock: '10.0.0.0/16',
    subnetStrategy: 'Auto',
    subnetSpecs: [
      {
        type: awsx.ec2.SubnetType.Public,
        name: 'public',
        cidrMask: 24,
      },
      {
        type: awsx.ec2.SubnetType.Private,
        name: 'private',
        cidrMask: 24,
      },
    ],
    tags: {
      Name: `ls-vpc-${stackName}`,
      environment: stackName,
    },
  },
  { provider },
)

const lbSecurityGroup = new aws.ec2.SecurityGroup(
  `ls-lb-sg-${stackName}`,
  {
    vpcId: vpc.vpcId,
    description: 'Security group for load balancer',
    ingress: [
      { protocol: 'tcp', fromPort: 80, toPort: 80, cidrBlocks: ['0.0.0.0/0'] },
      { protocol: 'tcp', fromPort: 443, toPort: 443, cidrBlocks: ['0.0.0.0/0'] },
    ],
    egress: [{ protocol: '-1', fromPort: 0, toPort: 0, cidrBlocks: ['0.0.0.0/0'] }],
    tags: {
      environment: stackName,
    },
  },
  { provider },
)

const loadBalancer = new awsx.lb.ApplicationLoadBalancer(
  `ls-loadbalancer-${stackName}`,
  {
    subnetIds: vpc.publicSubnetIds,
    securityGroups: [lbSecurityGroup.id],
    tags: {
      Name: `ls-loadbalancer-${stackName}`,
      environment: stackName,
    },
  },
  {
    provider,
  },
)
q
That is odd. Your example worked for me. I've used those settings for the provider and stackname:
Copy code
const stackName = "flo-test"
const provider = new aws.Provider('ls-provider', {
  region: 'us-west-2',
})
Are you running the latest versions of awsx and aws?
b
Copy code
pulumi version
v3.136.1
and these in my package.json
Copy code
"@pulumi/aws": "^6.56.1",
    "@pulumi/awsx": "^2.16.1",
    "@pulumi/esc-sdk": "^0.10.2",
    "@pulumi/pulumi": "^3.137.0",
that is quite odd. Will try to destroy my stack and rerun from scratch
q
I'm using the same versions, so that shouldn't be the issue here. Let me know the outcome of re-running it from scratch!
b
running
Copy code
View in Browser (Ctrl+O): <https://app.pulumi.com/littlespoon/littlespoon-api/dev/previews/5343fa8e-96ff-4964-8d3d-52188dbb11a9>

     Type                                          Name                   Plan       
 +   pulumi:pulumi:Stack                           littlespoon-api-dev    create     
 +   ├─ pulumi:providers:aws                       privileged             create     
 +   ├─ awsx:ec2:Vpc                               ls-vpc-dev             create     
 +   │  └─ aws:ec2:Vpc                             ls-vpc-dev             create     
 +   │     ├─ aws:ec2:InternetGateway              ls-vpc-dev             create     
 +   │     ├─ aws:ec2:Subnet                       ls-vpc-dev-public-2    create     
 +   │     │  ├─ aws:ec2:RouteTable                ls-vpc-dev-public-2    create     
 +   │     │  │  ├─ aws:ec2:Route                  ls-vpc-dev-public-2    create     
 +   │     │  │  └─ aws:ec2:RouteTableAssociation  ls-vpc-dev-public-2    create     
 +   │     │  ├─ aws:ec2:Eip                       ls-vpc-dev-2           create     
 +   │     │  └─ aws:ec2:NatGateway                ls-vpc-dev-2           create     
 +   │     ├─ aws:ec2:Subnet                       ls-vpc-dev-private-1   create     
 +   │     │  └─ aws:ec2:RouteTable                ls-vpc-dev-private-1   create     
 +   │     │     ├─ aws:ec2:RouteTableAssociation  ls-vpc-dev-private-1   create     
 +   │     │     └─ aws:ec2:Route                  ls-vpc-dev-private-1   create     
 +   │     ├─ aws:ec2:Subnet                       ls-vpc-dev-private-2   create     
 +   │     │  └─ aws:ec2:RouteTable                ls-vpc-dev-private-2   create     
 +   │     │     ├─ aws:ec2:Route                  ls-vpc-dev-private-2   create     
 +   │     │     └─ aws:ec2:RouteTableAssociation  ls-vpc-dev-private-2   create     
 +   │     └─ aws:ec2:Subnet                       ls-vpc-dev-public-1    create     
 +   │        ├─ aws:ec2:RouteTable                ls-vpc-dev-public-1    create     
 +   │        │  ├─ aws:ec2:Route                  ls-vpc-dev-public-1    create     
 +   │        │  └─ aws:ec2:RouteTableAssociation  ls-vpc-dev-public-1    create     
 +   │        ├─ aws:ec2:Eip                       ls-vpc-dev-1           create     
 +   │        └─ aws:ec2:NatGateway                ls-vpc-dev-1           create     
 +   ├─ aws:ec2:SecurityGroup                      ls-lb-sg-dev           create     
 +   └─ awsx:lb:ApplicationLoadBalancer            ls-loadbalancer-dev    create     
 +      ├─ aws:lb:TargetGroup                      ls-loadbalancer-dev    create     
 +      ├─ aws:lb:LoadBalancer                     ls-loadbalancer-dev    create     
 +      └─ aws:lb:Listener                         ls-loadbalancer-dev-0  create     

Outputs:
    out: output<string>

Resources:
    + 30 to create

Do you want to perform this update? yes
worked now
Copy code
Resources:
    + 30 created

Duration: 6m26s
f
have you tried turning
b
ok, let's see if I can get those fargate services to run properly now
q
Just checking in, did everything work out with getting the fargate services deployed?
b
yes it did in the end, thanks for following up, whole infra runs smoothly now. The only other problem I bumped into was that I forgot to specify
Copy code
loadBalancers: [
        {
          targetGroupArn: loadBalancer.defaultTargetGroup.arn,
          containerName: 'api',
          containerPort: 3000,
        },
      ],
for my
awsx.ecs.FargateService
so it was always creating default one and then it was a 502 bad gateway as the default one requires port 80 and I just added it blindly following the error message the error message could be improved there, because I had defined this
Copy code
portMappings: [
              {
                containerPort: 3000,
                hostPort: 3000,
                targetGroup: loadBalancer.defaultTargetGroup,
              },
            ],
but the crosswalk message was something like: you're missing port 80 mapping would be nice if the message was instead something like:
oh you got this port 3000 mapping defined, are you missing loadBalancer for this FargateService ?
q
Ah that's good feedback, thanks! I tracked that error message down. It's actually not coming from awsx, but directly from the AWS ECS API via the pulumi aws classic provider. I think the actual problem here is that we use the target group port as the container port here: https://github.com/pulumi/pulumi-awsx/blob/a959603aeb765540c7db4bbdcbc02064bab05915/awsx/ecs/containers.ts#L156. That seems wrong to me and essentially means that target group port and container port need to match. Ideally what you had initially should just work.
Do you mind sending your full working example so I can get an issue for this started? Thanks!
b
no I don't have the code anymore. We have migrated and I have deleted all my code as my contract ended sorry