Skip to content

Commit

Permalink
fix(aws-ecs): Support configuring Windows capacity for cluster ASGs (#…
Browse files Browse the repository at this point in the history
…12365)

Fixes #4295
Closes #5033

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
SoManyHs authored Jan 12, 2021
1 parent 175a257 commit 6d9a0f1
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 31 deletions.
93 changes: 62 additions & 31 deletions packages/@aws-cdk/aws-ecs/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,38 +231,42 @@ export class Cluster extends Resource implements ICluster {
this._hasEc2Capacity = true;
this.connections.connections.addSecurityGroup(...autoScalingGroup.connections.securityGroups);

// Tie instances to cluster
switch (options.machineImageType) {
// Bottlerocket AMI
case MachineImageType.BOTTLEROCKET: {
autoScalingGroup.addUserData(
// Connect to the cluster
// Source: https://github.com/bottlerocket-os/bottlerocket/blob/develop/QUICKSTART-ECS.md#connecting-to-your-cluster
'[settings.ecs]',
`cluster = "${this.clusterName}"`,
);
// Enabling SSM
// Source: https://github.com/bottlerocket-os/bottlerocket/blob/develop/QUICKSTART-ECS.md#enabling-ssm
autoScalingGroup.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'));
// required managed policy
autoScalingGroup.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonEC2ContainerServiceforEC2Role'));
break;
}
default:
// Amazon ECS-optimized AMI for Amazon Linux 2
autoScalingGroup.addUserData(`echo ECS_CLUSTER=${this.clusterName} >> /etc/ecs/ecs.config`);
if (!options.canContainersAccessInstanceRole) {
// Deny containers access to instance metadata service
// Source: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html
autoScalingGroup.addUserData('sudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP');
autoScalingGroup.addUserData('sudo service iptables save');
// The following is only for AwsVpc networking mode, but doesn't hurt for the other modes.
autoScalingGroup.addUserData('echo ECS_AWSVPC_BLOCK_IMDS=true >> /etc/ecs/ecs.config');
}

if (autoScalingGroup.spotPrice && options.spotInstanceDraining) {
autoScalingGroup.addUserData('echo ECS_ENABLE_SPOT_INSTANCE_DRAINING=true >> /etc/ecs/ecs.config');
if ( autoScalingGroup.osType === ec2.OperatingSystemType.WINDOWS ) {
this.configureWindowsAutoScalingGroup(autoScalingGroup, options);
} else {
// Tie instances to cluster
switch (options.machineImageType) {
// Bottlerocket AMI
case MachineImageType.BOTTLEROCKET: {
autoScalingGroup.addUserData(
// Connect to the cluster
// Source: https://github.com/bottlerocket-os/bottlerocket/blob/develop/QUICKSTART-ECS.md#connecting-to-your-cluster
'[settings.ecs]',
`cluster = "${this.clusterName}"`,
);
// Enabling SSM
// Source: https://github.com/bottlerocket-os/bottlerocket/blob/develop/QUICKSTART-ECS.md#enabling-ssm
autoScalingGroup.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'));
// required managed policy
autoScalingGroup.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonEC2ContainerServiceforEC2Role'));
break;
}
default:
// Amazon ECS-optimized AMI for Amazon Linux 2
autoScalingGroup.addUserData(`echo ECS_CLUSTER=${this.clusterName} >> /etc/ecs/ecs.config`);
if (!options.canContainersAccessInstanceRole) {
// Deny containers access to instance metadata service
// Source: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html
autoScalingGroup.addUserData('sudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP');
autoScalingGroup.addUserData('sudo service iptables save');
// The following is only for AwsVpc networking mode, but doesn't hurt for the other modes.
autoScalingGroup.addUserData('echo ECS_AWSVPC_BLOCK_IMDS=true >> /etc/ecs/ecs.config');
}

if (autoScalingGroup.spotPrice && options.spotInstanceDraining) {
autoScalingGroup.addUserData('echo ECS_ENABLE_SPOT_INSTANCE_DRAINING=true >> /etc/ecs/ecs.config');
}
}
}

// ECS instances must be able to do these things
Expand Down Expand Up @@ -319,6 +323,33 @@ export class Cluster extends Resource implements ICluster {
}
}

private configureWindowsAutoScalingGroup(autoScalingGroup: autoscaling.AutoScalingGroup, options: AddAutoScalingGroupCapacityOptions = {}) {
// clear the cache of the agent
autoScalingGroup.addUserData('Remove-Item -Recurse C:\\ProgramData\\Amazon\\ECS\\Cache');

// pull the latest ECS Tools
autoScalingGroup.addUserData('Import-Module ECSTools');

// set the cluster name environment variable
autoScalingGroup.addUserData(`[Environment]::SetEnvironmentVariable("ECS_CLUSTER", "${this.clusterName}", "Machine")`);
autoScalingGroup.addUserData('[Environment]::SetEnvironmentVariable("ECS_ENABLE_AWSLOGS_EXECUTIONROLE_OVERRIDE", "true", "Machine")');
// tslint:disable-next-line: max-line-length
autoScalingGroup.addUserData('[Environment]::SetEnvironmentVariable("ECS_AVAILABLE_LOGGING_DRIVERS", "[\"json-file\",\"awslogs\"]", "Machine")');

// enable instance draining
if (autoScalingGroup.spotPrice && options.spotInstanceDraining) {
autoScalingGroup.addUserData('[Environment]::SetEnvironmentVariable("ECS_ENABLE_SPOT_INSTANCE_DRAINING", "true", "Machine")');
}

// enable task iam role
if (!options.canContainersAccessInstanceRole) {
autoScalingGroup.addUserData('[Environment]::SetEnvironmentVariable("ECS_ENABLE_TASK_IAM_ROLE", "true", "Machine")');
autoScalingGroup.addUserData(`Initialize-ECSAgent -Cluster '${this.clusterName}' -EnableTaskIAMRole'`);
} else {
autoScalingGroup.addUserData(`Initialize-ECSAgent -Cluster '${this.clusterName}'`);
}
}

/**
* Getter for autoscaling group added to cluster
*/
Expand Down
53 changes: 53 additions & 0 deletions packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,59 @@ export = {
test.done();
},

'configures userdata with powershell if windows machine image is specified'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'MyVpc', {});

const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc });
cluster.addCapacity('WindowsAutoScalingGroup', {
instanceType: new ec2.InstanceType('t2.micro'),
machineImage: new ecs.EcsOptimizedAmi({
windowsVersion: ecs.WindowsOptimizedVersion.SERVER_2019,
}),
});

// THEN
expect(stack).to(haveResource('AWS::AutoScaling::LaunchConfiguration', {
ImageId: {
Ref: 'SsmParameterValueawsserviceecsoptimizedamiwindowsserver2019englishfullrecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter',
},
InstanceType: 't2.micro',
IamInstanceProfile: {
Ref: 'EcsClusterWindowsAutoScalingGroupInstanceProfile65DFA6BB',
},
SecurityGroups: [
{
'Fn::GetAtt': [
'EcsClusterWindowsAutoScalingGroupInstanceSecurityGroupDA468DF1',
'GroupId',
],
},
],
UserData: {
'Fn::Base64': {
'Fn::Join': [
'',
[
'<powershell>Remove-Item -Recurse C:\\ProgramData\\Amazon\\ECS\\Cache\nImport-Module ECSTools\n[Environment]::SetEnvironmentVariable("ECS_CLUSTER", "',
{
Ref: 'EcsCluster97242B84',
},
"\", \"Machine\")\n[Environment]::SetEnvironmentVariable(\"ECS_ENABLE_AWSLOGS_EXECUTIONROLE_OVERRIDE\", \"true\", \"Machine\")\n[Environment]::SetEnvironmentVariable(\"ECS_AVAILABLE_LOGGING_DRIVERS\", \"[\"json-file\",\"awslogs\"]\", \"Machine\")\n[Environment]::SetEnvironmentVariable(\"ECS_ENABLE_TASK_IAM_ROLE\", \"true\", \"Machine\")\nInitialize-ECSAgent -Cluster '",
{
Ref: 'EcsCluster97242B84',
},
"' -EnableTaskIAMRole'</powershell>",
],
],
},
},
}));

test.done();
},

/*
* TODO:v2.0.0 BEGINNING OF OBSOLETE BLOCK
*/
Expand Down

0 comments on commit 6d9a0f1

Please sign in to comment.