Skip to content

ecs: example in managed instances documentation does not work #35742

@isker

Description

@isker

Describe the bug

This example uses a managed instances capacity provider with an Ec2Service. It fails to synth because the cluster lacks EC2 capacity providers.
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs-readme.html#managed-instances-capacity-providers

Regression Issue

  • Select this option if this issue appears to be a regression.

Last Known Working CDK Library Version

No response

Expected Behavior

Examples work. Examples are tested in integration tests. (This one is not, the integ test only tests managed instances capacity provider with a Fargate Service.)

Current Behavior

λ npx cdk synth
/private/tmp/foo/node_modules/aws-cdk-lib/core/lib/private/synthesis.js:3
  `);throw new(errors_1()).UnscopedValidationError(`Validation failed with the following errors:
           ^
UnscopedValidationError: Validation failed with the following errors:
  [FooStack/EC2Service] Cluster for this service needs Ec2 capacity. Call addXxxCapacity() on the cluster.
    at path [undefined]

  [FooStack/EC2Service] Cluster for this service needs Ec2 capacity. Call addXxxCapacity() on the cluster.
    at validateTree (/private/tmp/foo/node_modules/aws-cdk-lib/core/lib/private/synthesis.js:3:12)
    at synthesize (/private/tmp/foo/node_modules/aws-cdk-lib/core/lib/private/synthesis.js:1:1893)
    at App.synth (/private/tmp/foo/node_modules/aws-cdk-lib/core/lib/stage.js:1:2912)
    at process.<anonymous> (/private/tmp/foo/node_modules/aws-cdk-lib/core/lib/app.js:1:2041)
    at Object.onceWrapper (node:events:633:26)
    at process.emit (node:events:518:28)
    at process.emit (node:domain:489:12)
    at process.emit.sharedData.processEmitHook.installedValue [as emit] (/private/tmp/foo/node_modules/@cspotcode/source-map-support/source-map-support.js:745:40)
npx ts-node --prefer-ts-exts bin/foo.ts: Subprocess exited with error 1

Reproduction Steps

I ran npx cdk init app --language typescript, put that example in the stack, and filled in the "blanks" of the declare consts in the example.

Resulting stack:

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import { Size } from 'aws-cdk-lib';

export class FooStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const managedInstancesInstanceProfile = new iam.InstanceProfile(
      this,
      "ManagedInstancesInstanceProfile",
    );
    managedInstancesInstanceProfile.role?.addManagedPolicy(
      iam.ManagedPolicy.fromAwsManagedPolicyName(
        "AmazonECSInstanceRolePolicyForManagedInstances",
      ),
    );
    const managedInstancesInfrastructureRole = new iam.Role(
      this,
      "ManagedInstancesInfrastructureRole",
      {
        assumedBy: new iam.ServicePrincipal("ecs.amazonaws.com"),
        managedPolicies: [
          iam.ManagedPolicy.fromAwsManagedPolicyName(
            "AmazonECSInfrastructureRolePolicyForManagedInstances",
          ),
        ],
      },
    );
    managedInstancesInstanceProfile.role?.grantPassRole(
      managedInstancesInfrastructureRole,
    );

    const cluster = new ecs.Cluster(this, 'Cluster',);

    // Create a Managed Instances Capacity Provider
    const miCapacityProvider = new ecs.ManagedInstancesCapacityProvider(this, 'MICapacityProvider', {
      infrastructureRole: managedInstancesInfrastructureRole,
      ec2InstanceProfile: managedInstancesInstanceProfile,
      subnets: cluster.vpc.privateSubnets,
      securityGroups: [new ec2.SecurityGroup(this, 'MISecurityGroup', { vpc: cluster.vpc })],
      instanceRequirements: {
        vCpuCountMin: 1,
        memoryMin: Size.gibibytes(2),
        cpuManufacturers: [ec2.CpuManufacturer.INTEL],
        acceleratorManufacturers: [ec2.AcceleratorManufacturer.NVIDIA],
      },
      propagateTags: ecs.PropagateManagedInstancesTags.CAPACITY_PROVIDER,
    });

    // Add the capacity provider to the cluster
    cluster.addManagedInstancesCapacityProvider(miCapacityProvider);

    const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');

    taskDefinition.addContainer('web', {
      image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
      memoryReservationMiB: 256,
    });

    new ecs.Ec2Service(this, 'EC2Service', {
      cluster,
      taskDefinition,
      minHealthyPercent: 100,
      capacityProviderStrategies: [
        {
          capacityProvider: miCapacityProvider.capacityProviderName,
          weight: 1,
        },
      ],
    });
  }
}

Possible Solution

No response

Additional Information/Context

No response

AWS CDK Library version (aws-cdk-lib)

2.220.0

AWS CDK CLI version

N/A

Node.js Version

N/A

OS

N/A

Language

TypeScript

Language Version

No response

Other information

@kg-aws

While I've got your attention:

  • I wish the documentation would explain the tradeoffs between using Ec2Service and FargateService. Why did this feature not gets its own ManagedInstancesService? Why do we have to use one of these other constructs instead?
  • Why does the user have to explicitly grant required iam permissions to the relevant infrastructure/instance profile roles? CDK typically provides these kinds of things automatically.
  • I don't see why managed instances capacity providers have to drive creation of ClusterCapacityProviderAssociations: https://github.com/aws/aws-cdk/blame/cfbc6fbc1d6e4b960b48613b97ffd8dc79727882/packages/aws-cdk-lib/aws-ecs/lib/cluster.ts#L1966. Managed Instances capacity providers do not use capacity provider associations.
  • Why doesn't ManagedInstancesCapacityProvider implement things like IConnectable? Most CDK L2 constructs don't require users to "drop down" to raw SecurityGroups.
  • Similar question about subnets. Most L2 constructs accept ec2.SubnetSelection instead of a raw array of subnets.

Metadata

Metadata

Assignees

No one assigned

    Labels

    @aws-cdk/aws-ecsRelated to Amazon Elastic ContainerbugThis issue is a bug.effort/mediumMedium work item – several days of effortp2

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions