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

Open
1 task
hrko opened this issue Aug 22, 2024 · 3 comments · May be fixed by #31374
Open
1 task
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
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/small Small work item – less than a day of effort p2
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants