Skip to content

Commit 04d88fb

Browse files
author
Elad Ben-Israel
committed
feat(eks): output update-kubeconfig command
Synthesize a CloudFormation output that shows the `aws eks update-kubeconfig` that needs to be executed in order to connect to the cluster. This command will include the IAM masters role ARN if applicable. Disable all other outputs by default, but added `outputXxx` options to enable. Fixes #3664 BREAKING CHANGE: cluster name output will not be synthesized by default. instead we synthesize an output that includes the full `aws eks update-kubeconfig` command. You can enable synthesis of the cluster name output using the `outputClusterName: true` options.
1 parent 77671ad commit 04d88fb

File tree

3 files changed

+192
-16
lines changed

3 files changed

+192
-16
lines changed

packages/@aws-cdk/aws-eks/README.md

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ new eks.Cluster(this, 'cluster', {
6969
defaultCapacityInstance: new ec2.InstanceType('m2.xlarge')
7070
});
7171
```
72-
To disable the default capacity, simply set `defaultCapacity` to `0`:
7372

73+
To disable the default capacity, simply set `defaultCapacity` to `0`:
7474

7575
```ts
7676
new eks.Cluster(this, 'cluster-with-no-capacity', { defaultCapacity: 0 });
@@ -121,11 +121,22 @@ new eks.Cluster(this, 'Cluster', {
121121
});
122122
```
123123

124-
Now, given AWS credentials for a user that is trusted by the masters role, you
125-
will be able to interact with your cluster like this:
124+
When you `cdk deploy` this CDK app, you will notice that an output will be printed
125+
with the `update-kubeconfig` command:
126+
127+
```console
128+
aws eks update-kubeconfig --name CLUSTER-NAME --role-arn ROLE-ARN
129+
```
130+
131+
Copy & paste this `aws eks` command to your shell in order to connect to your EKS
132+
cluster with the "masters" role.
133+
134+
Now, given [AWS CLI](https://aws.amazon.com/cli/) is configured to use AWS
135+
credentials for a user that is trusted by the masters role, you should be able
136+
to interact with your cluster through `kubectl` (the above example will trust
137+
all users in the account):
126138

127139
```console
128-
$ aws eks update-kubeconfig --name CLUSTER-NAME
129140
$ kubectl get all -n kube-system
130141
...
131142
```
@@ -274,7 +285,7 @@ not have administrative privileges on the EKS cluster.
274285
if you wish to be able to manually interact with your cluster, you will need
275286
to map an IAM role or user to the `system:masters` group. This can be either
276287
done by specifying a `mastersRole` when the cluster is defined, calling
277-
`cluster.addMastersRole` or explicitly mapping an IAM role or IAM user to the
288+
`cluster.awsAuth.addMastersRole` or explicitly mapping an IAM role or IAM user to the
278289
relevant Kubernetes RBAC groups using `cluster.addRoleMapping` and/or
279290
`cluster.addUserMapping`.
280291

@@ -293,12 +304,13 @@ and a new cluster to be created.
293304

294305
When kubectl is disabled, you should be aware of the following:
295306

296-
1. When you log-in to your cluster, you don't need to specify `--role-arn` as long as you are using the same user that created
297-
the cluster.
307+
1. When you log-in to your cluster, you don't need to specify `--role-arn` as
308+
long as you are using the same user that created the cluster.
298309
2. As described in the Amazon EKS User Guide, you will need to manually
299-
edit the [aws-auth ConfigMap](https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html) when you add capacity in order to map
300-
the IAM instance role to RBAC to allow nodes to join the cluster.
301-
3. Any `eks.Cluster` APIs that depend on programmatic kubectl support will fail with an error: `cluster.addResource`, `cluster.awsAuth`, `props.mastersRole`.
310+
edit the [aws-auth ConfigMap](https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html)
311+
when you add capacity in order to map the IAM instance role to RBAC to allow nodes to join the cluster.
312+
3. Any `eks.Cluster` APIs that depend on programmatic kubectl support will fail
313+
with an error: `cluster.addResource`, `cluster.awsAuth`, `props.mastersRole`.
302314

303315
### Roadmap
304316

packages/@aws-cdk/aws-eks/lib/cluster.ts

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,31 @@ export interface ClusterProps {
196196
* @default m5.large
197197
*/
198198
readonly defaultCapacityInstance?: ec2.InstanceType;
199+
200+
/**
201+
* Determines whether a CloudFormation output with the name of the cluster
202+
* will be synthesized.
203+
*
204+
* @default false
205+
*/
206+
readonly outputClusterName?: boolean;
207+
208+
/**
209+
* Determines whether a CloudFormation output with the ARN of the "masters"
210+
* IAM role will be synthesized (if `mastersRole` is specified).
211+
*
212+
* @default false
213+
*/
214+
readonly outputMastersRoleArn?: boolean;
215+
216+
/**
217+
* Determines whether a CloudFormation output with the `aws eks
218+
* update-kubeconfig` command will be synthesized. This command will include
219+
* the cluster name and, if applicable, the ARN of the masters IAM role.
220+
*
221+
* @default true
222+
*/
223+
readonly outputConfigCommand?: boolean;
199224
}
200225

201226
/**
@@ -362,7 +387,11 @@ export class Cluster extends Resource implements ICluster {
362387
this.clusterEndpoint = resource.attrEndpoint;
363388
this.clusterCertificateAuthorityData = resource.attrCertificateAuthorityData;
364389

365-
new CfnOutput(this, 'ClusterName', { value: this.clusterName });
390+
let configCommand = `aws eks update-kubeconfig --name ${this.clusterName}`;
391+
392+
if (props.outputClusterName) {
393+
new CfnOutput(this, 'ClusterName', { value: this.clusterName });
394+
}
366395

367396
// we maintain a single manifest custom resource handler per cluster since
368397
// permissions and role are scoped. This will return `undefined` if kubectl
@@ -376,6 +405,12 @@ export class Cluster extends Resource implements ICluster {
376405
}
377406

378407
this.awsAuth.addMastersRole(props.mastersRole);
408+
409+
if (props.outputMastersRoleArn) {
410+
new CfnOutput(this, 'MastersRoleArn', { value: props.mastersRole.roleArn });
411+
}
412+
413+
configCommand += ` --role-arn ${props.mastersRole.roleArn}`;
379414
}
380415

381416
// allocate default capacity if non-zero (or default).
@@ -384,6 +419,11 @@ export class Cluster extends Resource implements ICluster {
384419
const instanceType = props.defaultCapacityInstance || DEFAULT_CAPACITY_TYPE;
385420
this.defaultCapacity = this.addCapacity('DefaultCapacity', { instanceType, desiredCapacity });
386421
}
422+
423+
const outputConfigCommand = props.outputConfigCommand === undefined ? true : props.outputConfigCommand;
424+
if (outputConfigCommand) {
425+
new CfnOutput(this, 'ConfigCommand', { value: configCommand });
426+
}
387427
}
388428

389429
/**
@@ -456,11 +496,6 @@ export class Cluster extends Resource implements ICluster {
456496
// EKS Required Tags
457497
autoScalingGroup.node.applyAspect(new Tag(`kubernetes.io/cluster/${this.clusterName}`, 'owned', { applyToLaunchedInstances: true }));
458498

459-
// Create an CfnOutput for the Instance Role ARN (need to paste it into aws-auth-cm.yaml)
460-
new CfnOutput(autoScalingGroup, 'InstanceRoleARN', {
461-
value: autoScalingGroup.role.roleArn
462-
});
463-
464499
if (options.mapRole === true && !this.kubectlEnabled) {
465500
throw new Error(`Cannot map instance IAM role to RBAC if kubectl is disabled for the cluster`);
466501
}
@@ -477,6 +512,12 @@ export class Cluster extends Resource implements ICluster {
477512
'system:nodes'
478513
]
479514
});
515+
} else {
516+
// since we are not mapping the instance role to RBAC, synthesize an
517+
// output so it can be pasted into `aws-auth-cm.yaml`
518+
new CfnOutput(autoScalingGroup, 'InstanceRoleARN', {
519+
value: autoScalingGroup.role.roleArn
520+
});
480521
}
481522
}
482523

packages/@aws-cdk/aws-eks/test/test.cluster.ts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,129 @@ export = {
327327
// THEN
328328
expect(stack).to(not(haveResource(KubernetesResource.RESOURCE_TYPE)));
329329
test.done();
330+
},
331+
332+
'outputs': {
333+
'aws eks update-kubeconfig is the only output synthesized by default'(test: Test) {
334+
// GIVEN
335+
const { app, stack } = testFixtureNoVpc();
336+
337+
// WHEN
338+
new eks.Cluster(stack, 'Cluster');
339+
340+
// THEN
341+
const assembly = app.synth();
342+
const template = assembly.getStack(stack.stackName).template;
343+
test.deepEqual(template.Outputs, {
344+
ClusterConfigCommand43AAE40F: {
345+
Value: { 'Fn::Join': [ '', [ 'aws eks update-kubeconfig --name ', { Ref: 'Cluster9EE0221C' } ] ] }
346+
}
347+
});
348+
test.done();
349+
},
350+
351+
'if masters role is defined, it should be included in the config command'(test: Test) {
352+
// GIVEN
353+
const { app, stack } = testFixtureNoVpc();
354+
355+
// WHEN
356+
const mastersRole = new iam.Role(stack, 'masters', { assumedBy: new iam.AccountRootPrincipal() });
357+
new eks.Cluster(stack, 'Cluster', { mastersRole });
358+
359+
// THEN
360+
const assembly = app.synth();
361+
const template = assembly.getStack(stack.stackName).template;
362+
test.deepEqual(template.Outputs, {
363+
ClusterConfigCommand43AAE40F: {
364+
Value: { 'Fn::Join': [ '', [
365+
'aws eks update-kubeconfig --name ',
366+
{ Ref: 'Cluster9EE0221C' },
367+
' --role-arn ',
368+
{ 'Fn::GetAtt': [ 'masters0D04F23D', 'Arn' ] }
369+
] ] }
370+
}
371+
});
372+
test.done();
373+
},
374+
375+
'if `outputConfigCommand=false` will disabled the output'(test: Test) {
376+
// GIVEN
377+
const { app, stack } = testFixtureNoVpc();
378+
379+
// WHEN
380+
const mastersRole = new iam.Role(stack, 'masters', { assumedBy: new iam.AccountRootPrincipal() });
381+
new eks.Cluster(stack, 'Cluster', {
382+
mastersRole,
383+
outputConfigCommand: false,
384+
});
385+
386+
// THEN
387+
const assembly = app.synth();
388+
const template = assembly.getStack(stack.stackName).template;
389+
test.ok(!template.Outputs); // no outputs
390+
test.done();
391+
},
392+
393+
'`outputClusterName` can be used to synthesize an output with the cluster name'(test: Test) {
394+
// GIVEN
395+
const { app, stack } = testFixtureNoVpc();
396+
397+
// WHEN
398+
new eks.Cluster(stack, 'Cluster', {
399+
outputConfigCommand: false,
400+
outputClusterName: true
401+
});
402+
403+
// THEN
404+
const assembly = app.synth();
405+
const template = assembly.getStack(stack.stackName).template;
406+
test.deepEqual(template.Outputs, {
407+
ClusterClusterNameEB26049E: { Value: { Ref: 'Cluster9EE0221C' } }
408+
});
409+
test.done();
410+
},
411+
412+
'`outputMastersRoleArn` can be used to synthesize an output with the arn of the masters role if defined'(test: Test) {
413+
// GIVEN
414+
const { app, stack } = testFixtureNoVpc();
415+
416+
// WHEN
417+
new eks.Cluster(stack, 'Cluster', {
418+
outputConfigCommand: false,
419+
outputMastersRoleArn: true,
420+
mastersRole: new iam.Role(stack, 'masters', { assumedBy: new iam.AccountRootPrincipal() })
421+
});
422+
423+
// THEN
424+
const assembly = app.synth();
425+
const template = assembly.getStack(stack.stackName).template;
426+
test.deepEqual(template.Outputs, {
427+
ClusterMastersRoleArnB15964B1: { Value: { 'Fn::GetAtt': [ 'masters0D04F23D', 'Arn' ] } }
428+
});
429+
test.done();
430+
},
431+
432+
'when adding capacity, instance role ARN will not be outputed only if we do not auto-map aws-auth'(test: Test) {
433+
// GIVEN
434+
const { app, stack } = testFixtureNoVpc();
435+
436+
// WHEN
437+
new eks.Cluster(stack, 'Cluster', {
438+
outputConfigCommand: false,
439+
kubectlEnabled: false
440+
});
441+
442+
// THEN
443+
const assembly = app.synth();
444+
const template = assembly.getStack(stack.stackName).template;
445+
test.deepEqual(template.Outputs, {
446+
ClusterDefaultCapacityInstanceRoleARN7DADF219: {
447+
Value: { 'Fn::GetAtt': [ 'ClusterDefaultCapacityInstanceRole3E209969', 'Arn' ] }
448+
}
449+
});
450+
test.done();
451+
}
452+
330453
}
331454

332455
};

0 commit comments

Comments
 (0)