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.ExternalService: enableExecuteCommand is shown as not supported (while in fact it is supported) #31181

Closed
1 task
hrko opened this issue Aug 22, 2024 · 4 comments · Fixed by #31374 or softwaremill/tapir#4137 · May be fixed by NOUIY/aws-solutions-constructs#135, NOUIY/aws-solutions-constructs#136 or bensullivan2002/photo-website#3
Labels
@aws-cdk/aws-ecs Related to Amazon Elastic Container bug This issue is a bug. effort/small Small work item – less than a day of effort p2

Comments

@hrko
Copy link

hrko commented Aug 22, 2024

Describe the bug

When creating an ExternalService, specifying the enableExecuteCommand: true option for the property will result in the following error when running cdk synth.

Error: Enable Execute Command options are not supported for External service

However, the enableExecuteCommand option is actually supported by the ExternalService as well, and can be manually enabled using the AWS CLI.

Regression Issue

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

Last Known Working CDK Version

No response

Expected Behavior

When creating an ExternalService, the enableExecuteCommand: true option can be set for the property.

Current Behavior

When creating an ExternalService, setting the enableExecuteCommand: true option to the property causes an error when executing cdk synth.

Reproduction Steps

A code snippet that reproduces the problem is like as follows:

    const ecsService = new ecs.ExternalService(this, 'ExternalService', {
      serviceName: "EcsAnywhereService",
      cluster: EcsAnywhereCluster,
      taskDefinition,
      desiredCount: 1,
      enableExecuteCommand: true,
    });
Click here to show entire stack code

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

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

    // VPC
    const vpc = new ec2.Vpc(this, "EcsAnywhereVPC", {
      ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'),
      natGateways: 0,
      maxAzs: 2,
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: "EcsAnywherePublic",
          subnetType: ec2.SubnetType.PUBLIC,
        },
        {
          cidrMask: 24,
          name: "EcsAnywherePrivate",
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
        }
      ]
    }
    )

    // ECS Cluster
    const EcsAnywhereCluster = new ecs.Cluster(this, "EcsAnywhereCluster", {
      vpc: vpc,
      clusterName: "EcsAnywhereCluster",

    })

    // Task role
    const taskRole = new iam.Role(this, 'TaskRole', {
      assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
    });
    taskRole.addToPolicy(new iam.PolicyStatement({
      actions: [
        // for ECS Exec
        'ssmmessages:CreateControlChannel',
        'ssmmessages:CreateDataChannel',
        'ssmmessages:OpenControlChannel',
        'ssmmessages:OpenDataChannel',
      ],
      resources: ['*'],
    }));

    // ECS task definition
    const taskDefinition = new ecs.ExternalTaskDefinition(this, 'ExternalTaskDefinition', {
      // executionRole: taskExecutionRole,
      taskRole: taskRole,
    });

    taskDefinition.addContainer('NginxContainer', {
      image: ecs.ContainerImage.fromRegistry(
        "public.ecr.aws/nginx/nginx:latest"
      ),
      cpu: 256,
      memoryLimitMiB: 512,
      containerName: "EcsAnywhereContainer",
      // for ECS Exec
      linuxParameters: new ecs.LinuxParameters(this, 'LinuxParameters', {
        initProcessEnabled: true,
      }),
    })

    // ECS service
    const ecsService = new ecs.ExternalService(this, 'ExternalService', {
      serviceName: "EcsAnywhereService",
      cluster: EcsAnywhereCluster,
      taskDefinition,
      desiredCount: 1,
      enableExecuteCommand: true, // for ECS Exec
    });

    // Instance role for ECS Anywhere
    const instance_iam_role = new iam.Role(this, 'EcsAnywhereInstanceRole', {
      roleName: "EcsAnywhereInstanceRole",
      assumedBy: new iam.ServicePrincipal("ssm.amazonaws.com"),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMManagedInstanceCore"),
        iam.ManagedPolicy.fromManagedPolicyArn(this, "EcsAnywhereEC2Policy", "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"),
      ]
    })

    // Output
    new CfnOutput(this, "Step1RegisterExternalInstance", {
      description: "Create an Systems Manager activation pair",
      value: `aws ssm create-activation --iam-role ${instance_iam_role.roleName}`,
      exportName: "1-RegisterExternalInstance",
    })

    const ecsAnywhereInstallScriptUrl = "https://amazon-ecs-agent.s3.amazonaws.com/ecs-anywhere-install-latest.sh"
    new CfnOutput(this, "Step2DownloadInstallationScript", {
      description: "On your VM, download installation script",
      value: `curl -o "ecs-anywhere-install.sh" "${ecsAnywhereInstallScriptUrl}" && sudo chmod +x ecs-anywhere-install.sh`,
      exportName: "2-DownloadInstallationScript",
    });

    new CfnOutput(this, "Step3ExecuteScript", {
      description: "Run installation script on VM",
      value: `sudo ./ecs-anywhere-install.sh  --region ${this.region} --cluster ${EcsAnywhereCluster.clusterName} --activation-id $ACTIVATION_ID --activation-code $ACTIVATION_CODE`,
      exportName: "3-ExecuteInstallationScript",
    });
  }
}

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

2.154.0 (build 0fc07f3)

Framework Version

No response

Node.js Version

v20.17.0

OS

Debian GNU/Linux 11 (bullseye)

Language

TypeScript

Language Version

No response

Other information

No response

@hrko hrko added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Aug 22, 2024
@github-actions github-actions bot added the @aws-cdk/aws-ecs Related to Amazon Elastic Container label Aug 22, 2024
@ashishdhingra ashishdhingra self-assigned this Aug 22, 2024
@ashishdhingra ashishdhingra added p2 investigating This issue is being investigated and/or work is in progress to resolve the issue. and removed needs-triage This issue or PR still needs to be triaged. labels Aug 22, 2024
@ashishdhingra
Copy link
Contributor

ashishdhingra commented Aug 22, 2024

Reproducible using customer code.

Error: Enable Execute Command options are not supported for External service
    at new ExternalService (/Users/testuser/dev/repros/cdk/cdktest/node_modules/aws-cdk-lib/aws-ecs/lib/external/external-service.js:1:2388)
    at new EcsAnywhereStack (/Users/testuser/dev/repros/cdk/cdktest/lib/ecs-anywhere-stack.ts:74:24)
    at Object.<anonymous> (/Users/testuser/dev/repros/cdk/cdktest/bin/cdktest.ts:30:1)
    at Module._compile (node:internal/modules/cjs/loader:1376:14)
    at Module.m._compile (/Users/testuser/dev/repros/cdk/cdktest/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/testuser/dev/repros/cdk/cdktest/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1207:32)
    at Function.Module._load (node:internal/modules/cjs/loader:1023:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12)

The exception is thrown due to validation check here.

Looking at the commit history, the validation is in place since last 3 years per commit 3592b26.

@hrko Good afternoon. Could you please point me to documentation or example which demonstrates that enableExecuteCommand is supported for ExternalService (this service essentially uses LaunchType.EXTERNAL)?

Thanks,
Ashish

@hrko
Copy link
Author

hrko commented Aug 23, 2024

@ashishdhingra
Thank you for your response.

As an example, extract a CfnService from an ExternalService and then enableExecuteCommand can be enabled for that CfnService. The example is shown in the following code:

     const ecsService = new ecs.ExternalService(this, 'ExternalService', {
       serviceName: "EcsAnywhereService",
       cluster: EcsAnywhereCluster,
       taskDefinition,
       desiredCount: 1,
     });
+    const cfnService = ecsService.node.defaultChild as ecs.CfnService;
+    cfnService.enableExecuteCommand = true;

Once the above example code is deployed, you can use aws ecs execute-command to connect to an container in the external service.

(I did not find either a statement in the documentation that ExternalService supports enableExecuteCommand, or that it does not.)

@ashishdhingra
Copy link
Contributor

@hrko Thanks for sharing your inputs. I was able to run cdk deploy successfully after adding the escape hatch as you suggested, thereafter verifying enableExecuteCommand being set to true using AWS CLI command aws ecs describe-services --cluster EcsAnywhereCluster --region us-east-2 --services EcsAnywhereService. Also noticed that documentation at Amazon ECS clusters for the external launch type mentioning ECS Exec is supported on external instances.. So looks like the validation should be removed.

Thanks,
Ashish

@ashishdhingra ashishdhingra added effort/small Small work item – less than a day of effort and removed investigating This issue is being investigated and/or work is in progress to resolve the issue. labels Aug 23, 2024
@ashishdhingra ashishdhingra removed their assignment Aug 23, 2024
@mergify mergify bot closed this as completed in #31374 Oct 7, 2024
mergify bot pushed a commit that referenced this issue Oct 7, 2024
…rvice) (#31374)

### Issue # (if applicable)

Closes #31181.

### Reason for this change

In the `ecs.ExternalService` class (ECS Anywhere), the `enableExecuteCommand` property cannot be set to true, so it is not possible to enable ECS exec.
However, the [documentation](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec.html) states that ECS Anywhere supports ECS Exec.

> ECS Exec is supported for tasks that run on the following infrastructure:
> Linux and Windows containers on external instances (Amazon ECS Anywhere)



### Description of changes
Remove unnecessary if statement.



### Description of how you validated changes
Fix an unit test.



### Checklist
- [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Copy link

github-actions bot commented Oct 7, 2024

Comments on closed issues and PRs are hard for our team to see.
If you need help, please open a new issue that references this one.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 7, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.