Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(ecs): FargateService fails on imported Subnets if availability zone attribute is missing #30104

Open
so0k opened this issue May 8, 2024 · 2 comments
Labels
@aws-cdk/aws-ecs Related to Amazon Elastic Container bug This issue is a bug. effort/medium Medium work item – several days of effort p2

Comments

@so0k
Copy link

so0k commented May 8, 2024

Describe the bug

Creating a new ecs.FargateService with specific subnet-ids via its vpcSubnets: ec2.SubnetSelection property fails even when only subnet-id is required.

Note: we have an environment constraint and can't use vpc.fromLookup() during CDK Synth to use CDK Context and automatically load all subnets and their attributes.

Due to environment constraints, our AWS CDK Construct must depend on existing CloudFormation Stack Exported Outputs in the AWS Environment.

Sample code:

const privateSubnetsRef = Fn.importValue(
  stackName + "-privatesubnets",
);
const privateSubnets = Fn.split(",", privateSubnetsRef).map(s => ec2.Subnet.fromSubnetId(this, s, s))

vpc.selectSubnets({
  subnets: privateSubnets,
})

Work Around:

// Create FargateService with default `networkConfiguration`
const svc = new ecs.FargateService(this, "FargateService", {
  taskDefinition,
  cluster: this.ecsCluster,
  desiredCount: this.data.desired,
  // Use default subnetSelection ...
  // Public subnets if `assignPublicIp` is set, otherwise the first available one of Private, Isolated, Public, in that order.
  // vpcSubnets: this.vpc.selectSubnets({
  //   subnets: privateSubnets,
  // }),
  // required, else CDK creates new SecurityGroup
  securityGroups: [this.securityGroup],
});

// Use AWS CDK escape hatch to override networkConfiguration without `vpc.selectSubnets` limitations....
// https://docs.aws.amazon.com/cdk/v2/guide/cfn_layer.html#develop-customize-escape-l2
const cfnSvc = svc.node.defaultChild as ecs.CfnService;
cfnSvc.networkConfiguration = {
  awsvpcConfiguration: {
    subnets: Fn.split(",", privateSubnetsRef),
    securityGroups: [Fn.ref("SecurityGroup")],
  },
};

Expected Behavior

Providing the ecsFargateService an explicit list of SubnetIds should not require the Availability Zone attribute per Subnet ... (the bug is caused by - L480:

public selectSubnets(selection: SubnetSelection = {}): SelectedSubnets {
const subnets = this.selectSubnetObjects(selection);
const pubs = new Set(this.publicSubnets);
return {
subnetIds: subnets.map(s => s.subnetId),
get availabilityZones(): string[] { return subnets.map(s => s.availabilityZone); },
internetConnectivityEstablished: tap(new CompositeDependable(), d => subnets.forEach(s => d.add(s.internetConnectivityEstablished))),
subnets,
hasPublic: subnets.some(s => pubs.has(s)),
isPendingLookup: this.incompleteSubnetDefinition,
};
}

Current Behavior

Error is thrown

You cannot reference a Subnet's availability zone if it was not supplied. Add the availabilityZone when importing using Subnet.fromSubnetAttributes()

      236 |       // ecs.FargateService -> launchType == "FARGATE"
      237 |       // TODO: is ServiceName: &configCode, required?
    > 238 |       const svc = new ecs.FargateService(this, "FargateService", {
          |                   ^
      239 |         taskDefinition,
      240 |         cluster: this.ecsCluster,
      241 |         desiredCount: this.data.desired,

      at ImportedSubnet.get availabilityZone [as availabilityZone] (node_modules/.pnpm/aws-cdk-lib@2.140.0_constructs@10.3.0/node_modules/aws-cdk-lib/aws-ec2/lib/vpc.js:1:35138)
      at node_modules/.pnpm/aws-cdk-lib@2.140.0_constructs@10.3.0/node_modules/aws-cdk-lib/aws-ec2/lib/vpc.js:1:2861
          at Array.map (<anonymous>)
      at Object.get availabilityZones [as availabilityZones] (node_modules/.pnpm/aws-cdk-lib@2.140.0_constructs@10.3.0/node_modules/aws-cdk-lib/aws-ec2/lib/vpc.js:1:2852)
      at ImportedVpc.reifySelectionDefaults (node_modules/.pnpm/aws-cdk-lib@2.140.0_constructs@10.3.0/node_modules/aws-cdk-lib/aws-ec2/lib/vpc.js:1:7431)
      at ImportedVpc.selectSubnetObjects (node_modules/.pnpm/aws-cdk-lib@2.140.0_constructs@10.3.0/node_modules/aws-cdk-lib/aws-ec2/lib/vpc.js:1:4733)
      at ImportedVpc.selectSubnets (node_modules/.pnpm/aws-cdk-lib@2.140.0_constructs@10.3.0/node_modules/aws-cdk-lib/aws-ec2/lib/vpc.js:1:2705)
      at FargateService.configureAwsVpcNetworkingWithSecurityGroups (node_modules/.pnpm/aws-cdk-lib@2.140.0_constructs@10.3.0/node_modules/aws-cdk-lib/aws-ecs/lib/base/base-service.js:1:25843)
      at new FargateService (node_modules/.pnpm/aws-cdk-lib@2.140.0_constructs@10.3.0/node_modules/aws-cdk-lib/aws-ecs/lib/fargate/fargate-service.js:1:3382)
      at new EcsService (src/index.ts:238:19)
      at Object.<anonymous> (test/ecs-service-stack.test.ts:113:5)

Reproduction Steps

import { Fn } from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as ecs from "aws-cdk-lib/aws-ecs";
import { Construct } from "constructs";

export interface EcsServiceTestProps {
  /**
   * The environment stack name passed in during synth
   *
   * required to import environment stack exported outputs
   */
  environmentStackName: string;
}

export class EcsServiceTest extends Construct {
  constructor(scope: Construct, name: string, props: EcsServiceTestProps) {
    {
      super(scope, name);

      // Define "Environment" Stack Imports
      const vpcRef = Fn.importValue(props.environmentStackName + "-vpc");
      const availabilityZonesRef = Fn.importValue(
        props.environmentStackName + "-azs",
      );
      const ecsClusterNameRef = Fn.importValue(
        props.environmentStackName + "-cluster",
      );
      const privateSubnetsRef = Fn.importValue(
        props.environmentStackName + "-privatesubnets",
      );

      // Import VPC, Subnets and ECS Cluster
      const vpc = ec2.Vpc.fromVpcAttributes(this, "Vpc", {
        availabilityZones: Fn.split(",", availabilityZonesRef),
        privateSubnetIds: Fn.split(",", privateSubnetsRef),
        vpcId: vpcRef,
      });
      const privateSubnets = Fn.split(",", privateSubnetsRef).map((s) =>
        ec2.Subnet.fromSubnetId(this, s, s),
      );
      const cluster = ecs.Cluster.fromClusterAttributes(this, "Cluster", {
        clusterName: ecsClusterNameRef.toString(),
        vpc: vpc,
      });

      const cpu = 1014;
      const memoryLimitMiB = 100;
      const taskDefinition = new ecs.FargateTaskDefinition(
        this,
        "TaskDefinition",
        {
          cpu,
          memoryLimitMiB,
        },
      );

      taskDefinition.addContainer("MyService", {
        image: ecs.ContainerImage.fromRegistry("my-repo/my-image"),
        cpu,
        memoryLimitMiB,
      });

      new ecs.FargateService(this, "FargateService", {
        taskDefinition,
        cluster,
        desiredCount: 2,
        // This fails
        vpcSubnets: vpc.selectSubnets({
          subnets: privateSubnets,
        }),
        // required, else CDK creates new SecurityGroup
        securityGroups: [
          ec2.SecurityGroup.fromSecurityGroupId(
            this,
            "SecurityGroup",
            Fn.ref("SecurityGroup"),
          ),
        ],
      });

      // // AWS CDK escape hatch to override networkConfiguration and work around failure
      // // https://docs.aws.amazon.com/cdk/v2/guide/cfn_layer.html#develop-customize-escape-l2
      // const cfnSvc = svc.node.defaultChild as ecs.CfnService;
      // cfnSvc.networkConfiguration = {
      //   awsvpcConfiguration: {
      //     subnets: Fn.split(",", privateSubnetsRef),
      //     securityGroups: [Fn.ref("SecurityGroup")],
      //   },
      // };
    }
  }
}

Possible Solution

Either:

  • the ecs.BaseService allows passing in Subnets by ISubnet[] , or
  • the vpc.selectSubnets() should not error if the availabilityZone attribute does not exist on the subnet?

Additional Information/Context

No response

CDK CLI Version

2.140.0

Framework Version

2.140.0

Node.js Version

20.12.2

OS

WSL

Language

TypeScript

Language Version

5.3.3

Other information

No response

@so0k so0k added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels May 8, 2024
@github-actions github-actions bot added the @aws-cdk/aws-ecs Related to Amazon Elastic Container label May 8, 2024
@so0k so0k changed the title (ecs): FargateService fails on imported Subnets if Availabilty Zone attribute is missing (ecs): FargateService fails on imported Subnets if availability zone attribute is missing May 8, 2024
@khushail khushail added needs-reproduction This issue needs reproduction. and removed needs-triage This issue or PR still needs to be triaged. labels May 8, 2024
@khushail khushail self-assigned this May 8, 2024
@so0k
Copy link
Author

so0k commented May 25, 2024

@khushail - any details missing to reproduce?

@khushail khushail added p2 effort/medium Medium work item – several days of effort investigating This issue is being investigated and/or work is in progress to resolve the issue. and removed needs-reproduction This issue needs reproduction. labels Jun 14, 2024
@khushail khushail removed the investigating This issue is being investigated and/or work is in progress to resolve the issue. label Jul 16, 2024
@khushail khushail removed their assignment Jul 16, 2024
@khushail
Copy link
Contributor

@so0k, thanks for reporting this issue and providing detailed repro steps with workaround and root cause findings

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-ecs Related to Amazon Elastic Container bug This issue is a bug. effort/medium Medium work item – several days of effort p2
Projects
None yet
Development

No branches or pull requests

2 participants